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 }