diff --git a/cmd/auth.go b/cmd/auth.go index e299ded18..f0ebdf244 100644 --- a/cmd/auth.go +++ b/cmd/auth.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "io" + "os" astroplatformcore "github.com/astronomer/astro-cli/astro-client-platform-core" @@ -38,7 +39,7 @@ func newLoginCommand(coreClient astrocore.CoreClient, platformCoreClient astropl } cmd.Flags().BoolVarP(&shouldDisplayLoginLink, "login-link", "l", false, "Get login link to login on a separate device for cloud CLI login") - cmd.Flags().StringVarP(&token, "token-login", "t", "", "Login with a token for browserless cloud CLI login") + cmd.Flags().StringVarP(&token, "token-login", "t", "", "Login with a token for browserless cloud CLI login. It is recommended to pass a file containing the token to avoid leaking into shell history and environment") cmd.Flags().BoolVarP(&oAuth, "oauth", "o", false, "Do not prompt for local auth for software login") return cmd } @@ -56,10 +57,29 @@ func newLogoutCommand(out io.Writer) *cobra.Command { return cmd } +func isFilePath(path string) bool { + _, err := os.Stat(path) + if err == nil { + return true + } + if os.IsNotExist(err) { + return false + } + return false +} + func login(cmd *cobra.Command, args []string, coreClient astrocore.CoreClient, platformCoreClient astroplatformcore.CoreClient, out io.Writer) error { // Silence Usage as we have now validated command input cmd.SilenceUsage = true + if isFilePath(token) { + tok, err := os.ReadFile(token) + if err != nil { + return err + } + token = string(tok) + } + if len(args) == 1 { // check if user provided a valid cloud domain if !context.IsCloudDomain(args[0]) { diff --git a/cmd/auth_test.go b/cmd/auth_test.go index 54f272926..c2cb0a11c 100644 --- a/cmd/auth_test.go +++ b/cmd/auth_test.go @@ -21,13 +21,35 @@ func (s *CmdSuite) TestAuthRootCommand() { s.Contains(output, "Authenticate to Astro or Astronomer Software") } +func setupTokenFile(token string) string { + tmpFile, err := os.CreateTemp("", "testfile_") + if err != nil { + return "" + } + defer tmpFile.Close() + + _, err = tmpFile.Write([]byte(token)) + if err != nil { + return "" + } + return tmpFile.Name() +} + +func withTokenFlag(cmd *cobra.Command, token string) *cobra.Command { + cmd.Flags().Set("token-login", token) + return cmd +} + func (s *CmdSuite) TestLogin() { buf := new(bytes.Buffer) cloudDomain := "astronomer.io" softwareDomain := "astronomer_dev.com" + token = "fake.jwt.claims" + tokenFilePath := setupTokenFile(token) - cloudLogin = func(domain, token string, coreClient astrocore.CoreClient, platformCoreClient astroplatformcore.CoreClient, out io.Writer, shouldDisplayLoginLink bool) error { + cloudLogin = func(domain, tok string, coreClient astrocore.CoreClient, platformCoreClient astroplatformcore.CoreClient, out io.Writer, shouldDisplayLoginLink bool) error { s.Equal(cloudDomain, domain) + s.Equal(tok, token) return nil } @@ -39,6 +61,12 @@ func (s *CmdSuite) TestLogin() { // cloud login success login(&cobra.Command{}, []string{cloudDomain}, nil, nil, buf) + // cloud login, token in flag value + login(withTokenFlag(&cobra.Command{}, token), []string{cloudDomain}, nil, nil, buf) + + // cloud login, token in file + login(withTokenFlag(&cobra.Command{}, tokenFilePath), []string{cloudDomain}, nil, nil, buf) + // software login success testUtil.InitTestConfig(testUtil.Initial) login(&cobra.Command{}, []string{softwareDomain}, nil, nil, buf) @@ -47,6 +75,12 @@ func (s *CmdSuite) TestLogin() { testUtil.InitTestConfig(testUtil.CloudPlatform) login(&cobra.Command{}, []string{}, nil, nil, buf) + // no domain, cloud login, token in flag value + login(withTokenFlag(&cobra.Command{}, token), []string{}, nil, nil, buf) + + // no domain, cloud login, token in file + login(withTokenFlag(&cobra.Command{}, tokenFilePath), []string{}, nil, nil, buf) + // no domain, software login testUtil.InitTestConfig(testUtil.SoftwarePlatform) login(&cobra.Command{}, []string{}, nil, nil, buf)