code-intelligence.com/cifuzz@v0.40.0/internal/cmd/login/login.go (about)

     1  package login
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/pkg/errors"
     9  	"github.com/spf13/cobra"
    10  	"github.com/spf13/viper"
    11  	"golang.org/x/term"
    12  
    13  	"code-intelligence.com/cifuzz/internal/api"
    14  	"code-intelligence.com/cifuzz/internal/cmdutils"
    15  	"code-intelligence.com/cifuzz/internal/cmdutils/auth"
    16  	"code-intelligence.com/cifuzz/internal/tokenstorage"
    17  )
    18  
    19  type loginOpts struct {
    20  	Interactive bool   `mapstructure:"interactive"`
    21  	Server      string `mapstructure:"server"`
    22  }
    23  
    24  type loginCmd struct {
    25  	*cobra.Command
    26  	opts      *loginOpts
    27  	apiClient *api.APIClient
    28  }
    29  
    30  func New() *cobra.Command {
    31  	var bindFlags func()
    32  
    33  	cmd := &cobra.Command{
    34  		Use:   "login",
    35  		Short: "Authenticate with CI Sense",
    36  		Long: `This command is used to authenticate with CI Sense.
    37  To learn more, visit https://www.code-intelligence.com.`,
    38  		Example: "$ cifuzz login",
    39  		PreRunE: func(cmd *cobra.Command, args []string) error {
    40  			// Bind viper keys to flags. We can't do this in the New
    41  			// function, because that would re-bind viper keys which
    42  			// were bound to the flags of other commands before.
    43  			bindFlags()
    44  			return nil
    45  		},
    46  		RunE: func(c *cobra.Command, args []string) error {
    47  			opts := &loginOpts{
    48  				Interactive: viper.GetBool("interactive"),
    49  				Server:      viper.GetString("server"),
    50  			}
    51  
    52  			var err error
    53  			opts.Server, err = api.ValidateAndNormalizeServerURL(opts.Server)
    54  			if err != nil {
    55  				return err
    56  			}
    57  
    58  			cmd := loginCmd{Command: c, opts: opts}
    59  			cmd.apiClient = api.NewClient(opts.Server, cmd.Command.Root().Version)
    60  			return cmd.run()
    61  		},
    62  	}
    63  	bindFlags = cmdutils.AddFlags(cmd,
    64  		cmdutils.AddInteractiveFlag,
    65  		cmdutils.AddServerFlag,
    66  	)
    67  
    68  	cmdutils.DisableConfigCheck(cmd)
    69  
    70  	return cmd
    71  }
    72  
    73  func (c *loginCmd) run() error {
    74  	// Obtain the API access token
    75  	var token string
    76  	var err error
    77  
    78  	// First, if stdin is *not* a TTY, we try to read it from stdin,
    79  	// in case it was provided via `cifuzz login < token-file`
    80  	if !term.IsTerminal(int(os.Stdin.Fd())) {
    81  		// This should never block because stdin is not a TTY.
    82  		b, err := io.ReadAll(os.Stdin)
    83  		if err != nil {
    84  			return errors.WithStack(err)
    85  		}
    86  		token = strings.TrimSpace(string(b))
    87  		return auth.CheckAndStoreToken(c.apiClient, token)
    88  	}
    89  
    90  	// Try the access tokens config file
    91  	token = tokenstorage.Get(c.opts.Server)
    92  	if token != "" {
    93  		return auth.EnsureValidToken(*c.apiClient, token)
    94  	}
    95  
    96  	// Try reading it interactively
    97  	if c.opts.Interactive && term.IsTerminal(int(os.Stdin.Fd())) {
    98  		_, err = auth.ReadCheckAndStoreTokenInteractively(c.apiClient, nil)
    99  		return err
   100  	}
   101  
   102  	err = errors.Errorf(`No API access token provided. Please pass a valid token via stdin or run
   103  in interactive mode. You can generate a token here:
   104  %s/dashboard/settings/account/tokens?create&origin=cli.`+"\n", c.opts.Server)
   105  	return cmdutils.WrapIncorrectUsageError(err)
   106  }