diff --git a/internal/cmd/commands.go b/internal/cmd/commands.go index 6480380de..cae956f21 100644 --- a/internal/cmd/commands.go +++ b/internal/cmd/commands.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/commands/authenticate" + "github.com/hashicorp/boundary/internal/cmd/commands/authtokens" "github.com/hashicorp/boundary/internal/cmd/commands/config" "github.com/hashicorp/boundary/internal/cmd/commands/controller" "github.com/hashicorp/boundary/internal/cmd/commands/dev" @@ -54,6 +55,59 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { SigUSR2Ch: MakeSigUSR2Ch(), }, nil }, + + "authenticate": func() (cli.Command, error) { + return &authenticate.Command{ + Command: base.NewCommand(ui), + }, nil + }, + "authenticate password": func() (cli.Command, error) { + return &authenticate.PasswordCommand{ + Command: base.NewCommand(ui), + }, nil + }, + + "auth-tokens": func() (cli.Command, error) { + return &authtokens.Command{ + Command: base.NewCommand(ui), + }, nil + }, + "auth-tokens read": func() (cli.Command, error) { + return &authtokens.Command{ + Command: base.NewCommand(ui), + Func: "read", + }, nil + }, + "auth-tokens delete": func() (cli.Command, error) { + return &authtokens.Command{ + Command: base.NewCommand(ui), + Func: "delete", + }, nil + }, + "auth-tokens list": func() (cli.Command, error) { + return &authtokens.Command{ + Command: base.NewCommand(ui), + Func: "list", + }, nil + }, + + "config": func() (cli.Command, error) { + return &config.Command{ + Command: base.NewCommand(ui), + }, nil + }, + "config encrypt": func() (cli.Command, error) { + return &config.EncryptDecryptCommand{ + Command: base.NewCommand(ui), + Encrypt: true, + }, nil + }, + "config decrypt": func() (cli.Command, error) { + return &config.EncryptDecryptCommand{ + Command: base.NewCommand(ui), + }, nil + }, + "groups": func() (cli.Command, error) { return &groups.Command{ Command: base.NewCommand(ui), @@ -107,72 +161,13 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Func: "remove-members", }, nil }, + "hosts create": func() (cli.Command, error) { return &hosts.CreateCommand{ Command: base.NewCommand(ui), }, nil }, - "scopes": func() (cli.Command, error) { - return &scopes.Command{ - Command: base.NewCommand(ui), - }, nil - }, - "scopes create": func() (cli.Command, error) { - return &scopes.Command{ - Command: base.NewCommand(ui), - Func: "create", - }, nil - }, - "scopes read": func() (cli.Command, error) { - return &scopes.Command{ - Command: base.NewCommand(ui), - Func: "read", - }, nil - }, - "scopes update": func() (cli.Command, error) { - return &scopes.Command{ - Command: base.NewCommand(ui), - Func: "update", - }, nil - }, - "scopes delete": func() (cli.Command, error) { - return &scopes.Command{ - Command: base.NewCommand(ui), - Func: "delete", - }, nil - }, - "scopes list": func() (cli.Command, error) { - return &scopes.Command{ - Command: base.NewCommand(ui), - Func: "list", - }, nil - }, - "config": func() (cli.Command, error) { - return &config.Command{ - Command: base.NewCommand(ui), - }, nil - }, - "config encrypt": func() (cli.Command, error) { - return &config.EncryptDecryptCommand{ - Command: base.NewCommand(ui), - Encrypt: true, - }, nil - }, - "config decrypt": func() (cli.Command, error) { - return &config.EncryptDecryptCommand{ - Command: base.NewCommand(ui), - }, nil - }, - "authenticate": func() (cli.Command, error) { - return &authenticate.Command{ - Command: base.NewCommand(ui), - }, nil - }, - "authenticate password": func() (cli.Command, error) { - return &authenticate.PasswordCommand{ - Command: base.NewCommand(ui), - }, nil - }, + "roles": func() (cli.Command, error) { return &roles.Command{ Command: base.NewCommand(ui), @@ -244,6 +239,42 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Func: "remove-grants", }, nil }, + + "scopes": func() (cli.Command, error) { + return &scopes.Command{ + Command: base.NewCommand(ui), + }, nil + }, + "scopes create": func() (cli.Command, error) { + return &scopes.Command{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "scopes read": func() (cli.Command, error) { + return &scopes.Command{ + Command: base.NewCommand(ui), + Func: "read", + }, nil + }, + "scopes update": func() (cli.Command, error) { + return &scopes.Command{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, + "scopes delete": func() (cli.Command, error) { + return &scopes.Command{ + Command: base.NewCommand(ui), + Func: "delete", + }, nil + }, + "scopes list": func() (cli.Command, error) { + return &scopes.Command{ + Command: base.NewCommand(ui), + Func: "list", + }, nil + }, } } diff --git a/internal/cmd/commands/authtokens/authtokens.go b/internal/cmd/commands/authtokens/authtokens.go new file mode 100644 index 000000000..cff704653 --- /dev/null +++ b/internal/cmd/commands/authtokens/authtokens.go @@ -0,0 +1,186 @@ +package authtokens + +import ( + "fmt" + "time" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/authtokens" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/boundary/internal/types/resource" + "github.com/hashicorp/vault/sdk/helper/strutil" + "github.com/kr/pretty" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*Command)(nil) +var _ cli.CommandAutocomplete = (*Command)(nil) + +type Command struct { + *base.Command + + Func string +} + +func (c *Command) Synopsis() string { + return common.SynopsisFunc(c.Func, "auth-token") +} + +var flagsMap = map[string][]string{ + "read": {"id"}, + "delete": {"id"}, +} + +func (c *Command) Help() string { + helpMap := common.HelpMap(resource.AuthToken) + if c.Func == "" { + return helpMap["base"]() + } + return helpMap[c.Func]() + "\n\n" + c.Flags().Help() +} + +func (c *Command) Flags() *base.FlagSets { + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + + f := set.NewFlagSet("Command Options") + + common.PopulateCommonFlags(c.Command, f, resource.AuthToken, flagsMap[c.Func]) + return set +} + +func (c *Command) AutocompleteArgs() complete.Predictor { + return complete.PredictAnything +} + +func (c *Command) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *Command) Run(args []string) int { + if c.Func == "" { + return cli.RunResultHelp + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" { + c.UI.Error("ID is required but not passed in via -id") + return 1 + } + + client, err := c.Client() + if err != nil { + c.UI.Error(fmt.Sprintf("Error creating API client: %s", err.Error())) + return 2 + } + + authtokenClient := authtokens.NewAuthTokensClient(client) + + var existed bool + var token *authtokens.AuthToken + var listedTokens []*authtokens.AuthToken + var apiErr *api.Error + + switch c.Func { + case "read": + token, apiErr, err = authtokenClient.Read(c.Context, c.FlagId) + case "delete": + existed, apiErr, err = authtokenClient.Delete(c.Context, c.FlagId) + case "list": + listedTokens, apiErr, err = authtokenClient.List(c.Context) + } + + plural := "auth token" + if c.Func == "list" { + plural = "auth tokens" + } + if err != nil { + c.UI.Error(fmt.Sprintf("Error trying to %s %s: %s", c.Func, plural, err.Error())) + return 2 + } + if apiErr != nil { + c.UI.Error(fmt.Sprintf("Error from controller when performing %s on %s: %s", c.Func, plural, pretty.Sprint(apiErr))) + return 1 + } + + switch c.Func { + case "delete": + switch base.Format(c.UI) { + case "json": + c.UI.Output("null") + case "table": + output := "The delete operation completed successfully" + switch existed { + case true: + output += "." + default: + output += ", however the resource did not exist at the time." + } + c.UI.Output(output) + } + return 0 + + case "list": + switch base.Format(c.UI) { + case "json": + if len(listedTokens) == 0 { + c.UI.Output("null") + return 0 + } + b, err := base.JsonFormatter{}.Format(listedTokens) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting to JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + + case "table": + if len(listedTokens) == 0 { + c.UI.Output("No auth tokens found") + return 0 + } + var output []string + output = []string{ + "", + "Auth Token information:", + } + for i, t := range listedTokens { + if i > 0 { + output = append(output, "") + } + output = append(output, + fmt.Sprintf(" ID: %s", t.Id), + fmt.Sprintf(" Auth Method ID: %s", t.AuthMethodId), + fmt.Sprintf(" User ID: %s", t.UserId), + fmt.Sprintf(" Expiration Time: %s", t.ExpirationTime.Local().Format(time.RFC3339)), + fmt.Sprintf(" Approximate Last Used Time: %s", t.ApproximateLastUsedTime.Local().Format(time.RFC3339)), + fmt.Sprintf(" Created Time: %s", t.CreatedTime.Local().Format(time.RFC3339)), + fmt.Sprintf(" Updated Time: %s", t.UpdatedTime.Local().Format(time.RFC3339)), + ) + } + c.UI.Output(base.WrapForHelpText(output)) + } + return 0 + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateAuthTokenTableOutput(token)) + case "json": + b, err := base.JsonFormatter{}.Format(token) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting to JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + } + + return 0 +} diff --git a/internal/cmd/commands/authtokens/funcs.go b/internal/cmd/commands/authtokens/funcs.go new file mode 100644 index 000000000..1a7b6fc1f --- /dev/null +++ b/internal/cmd/commands/authtokens/funcs.go @@ -0,0 +1,28 @@ +package authtokens + +import ( + "fmt" + "time" + + "github.com/hashicorp/boundary/api/authtokens" + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func generateAuthTokenTableOutput(in *authtokens.AuthToken) string { + var ret []string + ret = append(ret, []string{ + "", + "Auth Token information:", + fmt.Sprintf(" ID: %s", in.Id), + fmt.Sprintf(" Scope ID: %s", in.Scope.Id), + fmt.Sprintf(" Auth Method ID: %s", in.AuthMethodId), + fmt.Sprintf(" User ID: %s", in.UserId), + fmt.Sprintf(" Expiration Time: %s", in.ExpirationTime.Local().Format(time.RFC3339)), + fmt.Sprintf(" Approximate Last Used Time: %s", in.ApproximateLastUsedTime.Local().Format(time.RFC3339)), + fmt.Sprintf(" Created Time: %s", in.CreatedTime.Local().Format(time.RFC3339)), + fmt.Sprintf(" Updated Time: %s", in.UpdatedTime.Local().Format(time.RFC3339)), + }..., + ) + + return base.WrapForHelpText(ret) +}