From 3533093b9dd7e89fdd447abecd852772f25ba1fe Mon Sep 17 00:00:00 2001 From: Indent Bot Date: Tue, 2 Jan 2024 16:32:16 -0500 Subject: [PATCH] sync(cmd/access): pull from indent core GitOrigin-RevId: 945ecd7e32ab5e9f8d9dae3be32f76544c3faf5a --- cmd/access/cmd/root.go | 7 +- cmd/access/cmd/tokens/create.go | 130 ++++++++++++++++++++++++++++++++ cmd/access/cmd/tokens/tokens.go | 19 +++++ cmd/access/logger.go | 2 +- 4 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 cmd/access/cmd/tokens/create.go create mode 100644 cmd/access/cmd/tokens/tokens.go diff --git a/cmd/access/cmd/root.go b/cmd/access/cmd/root.go index 9237818..180f546 100644 --- a/cmd/access/cmd/root.go +++ b/cmd/access/cmd/root.go @@ -9,6 +9,7 @@ import ( configcmd "go.indent.com/access/cmd/access/cmd/config" "go.indent.com/access/cmd/access/cmd/petitions" "go.indent.com/access/cmd/access/cmd/resources" + "go.indent.com/access/cmd/access/cmd/tokens" "go.indent.com/indent-go/pkg/cliutil" ) @@ -25,12 +26,16 @@ func NewRoot(logger *zap.Logger) *cobra.Command { rootCmd.AddCommand(configcmd.NewCmdConfig(f)) rootCmd.AddCommand(petitions.NewCmdPetitions(f)) rootCmd.AddCommand(resources.NewCmdResources(f)) + rootCmd.AddCommand(tokens.NewCmdTokens(f)) flags := rootCmd.PersistentFlags() flags.StringVarP(&config.Space, "space", "s", config.Space, "Space to perform operations in") flags.BoolVar(&config.Staging, "staging", config.Staging, "Use staging environment for request") flags.BoolVarP(&config.Verbose, "verbose", "v", config.Verbose, "Include debug messages and additional context in logs") flags.BoolVar(&config.Headless, "headless", config.Headless, "Run in headless mode (no browser login prompt)") - f.Setup() + + rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { + f.Setup() + } return rootCmd } diff --git a/cmd/access/cmd/tokens/create.go b/cmd/access/cmd/tokens/create.go new file mode 100644 index 0000000..e9c290d --- /dev/null +++ b/cmd/access/cmd/tokens/create.go @@ -0,0 +1,130 @@ +package tokens + +import ( + "context" + "strconv" + + "github.com/spf13/cobra" + "go.uber.org/zap" + + indentv1 "go.indent.com/indent-go/api/indent/v1" + "go.indent.com/indent-go/pkg/cliutil" + "go.indent.com/indent-go/pkg/common" +) + +const ( + // defaultServiceAccount is the default service account to use when creating tokens. + defaultServiceAccount = "access-cli-default-service-account" + + // defaultServiceAccountDisplayName is the default service account display name to use when creating tokens. + defaultServiceAccountDisplayName = "Default Service Account" + + // defaultExpiryDays is the default number of days until a token expires. + defaultExpiryDays = 25 +) + +// NewCreateOptions returns a CreateOptions with the defaults set. +func NewCreateOptions() *CreateOptions { + return &CreateOptions{ + CreateTokenRequest: new(indentv1.CreateTokenRequest), + } +} + +// CreateOptions defines how a token is created. +type CreateOptions struct { + *indentv1.CreateTokenRequest + + // CreateAccessToken indicates that an access token should be created. + CreateAccessToken bool +} + +// NewCmdCreate returns a command that creates tokens. +func NewCmdCreate(f cliutil.Factory) *cobra.Command { + opts := NewCreateOptions() + cmd := &cobra.Command{ + Use: "create [service_account]", + Short: "Creates new refresh tokens", + Long: `Creates new refresh tokens for a service account`, + Run: func(cmd *cobra.Command, args []string) { + logger := f.Logger() + client := f.API(cmd.Context()).Accounts() + + opts.ServiceAccountId = serviceAccountID(cmd.Context(), logger, f, client, args) + logger = logger.With(zap.Uint64("serviceAccountID", opts.ServiceAccountId)) + + logger.Debug("Creating token") + opts.SpaceName = f.Config().Space + token, err := client.CreateToken(cmd.Context(), opts.CreateTokenRequest) + if err != nil { + logger.Fatal("Failed to create token", zap.Error(err)) + } + + logger = logger.With(zap.String("refreshToken", token.GetRefreshToken())) + logger.Info("Refresh token created") + + if opts.CreateAccessToken { + logger.Debug("Creating access token") + tokenClient := f.API(cmd.Context()).Tokens() + accessToken, err := tokenClient.ExchangeToken(cmd.Context(), &indentv1.ExchangeTokenRequest{ + RefreshToken: token.GetRefreshToken(), + }) + if err != nil { + logger.Fatal("Failed to create access token", zap.Error(err)) + } + + logger = logger.With(zap.String("accessToken", accessToken.GetAccessToken())) + logger.Info("Access token created") + } + }, + } + + flags := cmd.Flags() + flags.Uint64Var(&opts.ExpiryDays, "expiry-days", defaultExpiryDays, "Number of days until token expires") + flags.BoolVar(&opts.CreateAccessToken, "access-token", false, "Create an access token also") + return cmd +} + +func serviceAccountID(ctx context.Context, logger *zap.Logger, f cliutil.Factory, client indentv1.AccountAPIClient, + args []string) (svcAccountID uint64) { + var err error + if len(args) != 0 { + if svcAccountID, err = strconv.ParseUint(args[0], common.Base10, common.BitSize64); err != nil { + logger.Fatal("Failed to parse service account ID", zap.Error(err)) + } + } + + // if service account is specified, use it + if svcAccountID != 0 { + return svcAccountID + } + + // use default service account if none specified + logger.Debug("Looking up default service account") + space := f.Config().Space + var resp *indentv1.ListServiceAccountsResponse + if resp, err = client.ListServicesAccounts(ctx, &indentv1.ListServiceAccountsRequest{ + SpaceName: space, + }); err != nil { + logger.Fatal("Failed to list service accounts", zap.Error(err)) + } + + for _, serviceAccount := range resp.GetAccounts() { + meta := serviceAccount.GetMeta() + if meta.GetSpace() == space && meta.GetName() == defaultServiceAccount { + return serviceAccount.GetServiceAccountId() + } + } + + logger.Debug("Creating default service account") + var svcAcct *indentv1.ServiceAccount + if svcAcct, err = client.CreateServiceAccount(ctx, &indentv1.CreateServiceAccountRequest{ + SpaceName: space, + Name: defaultServiceAccount, + DisplayName: defaultServiceAccountDisplayName, + }); err != nil { + logger.Fatal("Failed to create default service account", zap.Error(err)) + } + svcAccountID = svcAcct.GetServiceAccountId() + logger.Debug("Default service account created", zap.Uint64("svcAccountID", svcAccountID)) + return svcAccountID +} diff --git a/cmd/access/cmd/tokens/tokens.go b/cmd/access/cmd/tokens/tokens.go new file mode 100644 index 0000000..cace702 --- /dev/null +++ b/cmd/access/cmd/tokens/tokens.go @@ -0,0 +1,19 @@ +// Package tokens is a command to manage tokens. +package tokens + +import ( + "github.com/spf13/cobra" + + "go.indent.com/indent-go/pkg/cliutil" +) + +// NewCmdTokens returns a set of commands used to manage tokens. +func NewCmdTokens(f cliutil.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "tokens", + Aliases: []string{"t"}, + Short: "Manage tokens", + } + cmd.AddCommand(NewCmdCreate(f)) + return cmd +} diff --git a/cmd/access/logger.go b/cmd/access/logger.go index a52a8e0..5149f38 100644 --- a/cmd/access/logger.go +++ b/cmd/access/logger.go @@ -6,7 +6,7 @@ import ( ) func newLogger() *zap.Logger { - cfg := zap.NewProductionConfig() + cfg := zap.NewDevelopmentConfig() cfg.DisableCaller = true cfg.DisableStacktrace = true cfg.Encoding = "console"