github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/authtoken/create.go (about) 1 package authtoken 2 3 import ( 4 "io" 5 "strings" 6 "time" 7 8 "github.com/fastly/go-fastly/v9/fastly" 9 "github.com/fastly/kingpin" 10 11 "github.com/fastly/cli/pkg/argparser" 12 "github.com/fastly/cli/pkg/global" 13 "github.com/fastly/cli/pkg/text" 14 ) 15 16 // Scopes is a list of purging scope options. 17 // https://developer.fastly.com/reference/api/auth/#scopes 18 var Scopes = []string{"global", "purge_select", "purge_all", "global:read"} 19 20 // NewCreateCommand returns a usable command registered under the parent. 21 func NewCreateCommand(parent argparser.Registerer, g *global.Data) *CreateCommand { 22 c := CreateCommand{ 23 Base: argparser.Base{ 24 Globals: g, 25 }, 26 } 27 c.CmdClause = parent.Command("create", "Create an API token").Alias("add") 28 29 // Required. 30 // 31 // NOTE: The go-fastly client internally calls `/sudo` before `/tokens` and 32 // the sudo endpoint requires a password to be provided alongside an API 33 // token. The password must be for the user account that created the token 34 // being passed as authentication to the API endpoint. 35 c.CmdClause.Flag("password", "User password corresponding with --token or $FASTLY_API_TOKEN").Required().StringVar(&c.password) 36 37 // Optional. 38 // 39 // NOTE: The API describes 'scope' as being space-delimited but we've opted 40 // for comma-separated as it means users don't have to worry about how best 41 // to handle issues with passing a flag value with whitespace. When 42 // constructing the input for the API call we convert from a comma-separated 43 // value to a space-delimited value. 44 c.CmdClause.Flag("expires", "Time-stamp (UTC) of when the token will expire").HintOptions("2016-07-28T19:24:50+00:00").TimeVar(time.RFC3339, &c.expires) 45 c.CmdClause.Flag("name", "Name of the token").StringVar(&c.name) 46 c.CmdClause.Flag("scope", "Authorization scope (repeat flag per scope)").HintOptions(Scopes...).EnumsVar(&c.scope, Scopes...) 47 c.CmdClause.Flag("services", "A comma-separated list of alphanumeric strings identifying services (default: access to all services)").StringsVar(&c.services, kingpin.Separator(",")) 48 return &c 49 } 50 51 // CreateCommand calls the Fastly API to create an appropriate resource. 52 type CreateCommand struct { 53 argparser.Base 54 55 expires time.Time 56 name string 57 password string 58 scope []string 59 services []string 60 } 61 62 // Exec invokes the application logic for the command. 63 func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { 64 input := c.constructInput() 65 66 r, err := c.Globals.APIClient.CreateToken(input) 67 if err != nil { 68 c.Globals.ErrLog.Add(err) 69 return err 70 } 71 72 expires := "never" 73 if r.ExpiresAt != nil { 74 expires = r.ExpiresAt.String() 75 } 76 77 text.Success(out, "Created token '%s' (name: %s, id: %s, scope: %s, expires: %s)", fastly.ToValue(r.AccessToken), fastly.ToValue(r.Name), fastly.ToValue(r.TokenID), fastly.ToValue(r.Scope), expires) 78 return nil 79 } 80 81 // constructInput transforms values parsed from CLI flags into an object to be used by the API client library. 82 func (c *CreateCommand) constructInput() *fastly.CreateTokenInput { 83 var input fastly.CreateTokenInput 84 85 input.Password = fastly.ToPointer(c.password) 86 87 if !c.expires.IsZero() { 88 input.ExpiresAt = &c.expires 89 } 90 if c.name != "" { 91 input.Name = fastly.ToPointer(c.name) 92 } 93 if len(c.scope) > 0 { 94 input.Scope = fastly.ToPointer(fastly.TokenScope(strings.Join(c.scope, " "))) 95 } 96 if len(c.services) > 0 { 97 input.Services = c.services 98 } 99 100 return &input 101 }