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  }