Skip to content

Commit

Permalink
Auth "init", "switch", "remove" Forces Lowercase (#1362)
Browse files Browse the repository at this point in the history
* Auth init, switch, remove forces lowercase

* check if context exists before switching

* Add tests, update command_config

* add strings.ToLower(Context) to doit
  • Loading branch information
danaelhe authored Apr 13, 2023
1 parent 5e7df0b commit 698d7ef
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 6 deletions.
31 changes: 25 additions & 6 deletions commands/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ To remove accounts from the configuration file, you can run ` + "`" + `doctl aut
You will need an API token, which you can generate in the control panel at https://cloud.digitalocean.com/account/api/tokens.
You can provide a name to this initialization via the `+"`"+`--context`+"`"+` flag, and then it will be saved as an "authentication context". Authentication contexts are accessible via `+"`"+`doctl auth switch`+"`"+`, which re-initializes doctl, or by providing the `+"`"+`--context`+"`"+` flag when using any doctl command (to specify that auth context for just one command). This enables you to use multiple DigitalOcean accounts with doctl, or tokens that have different authentication scopes.
You can provide a (case insensitive) name to this initialization via the `+"`"+`--context`+"`"+` flag, and then it will be saved as an "authentication context". Authentication contexts are accessible via `+"`"+`doctl auth switch`+"`"+`, which re-initializes doctl, or by providing the `+"`"+`--context`+"`"+` flag when using any doctl command (to specify that auth context for just one command). This enables you to use multiple DigitalOcean accounts with doctl, or tokens that have different authentication scopes.
If the `+"`"+`--context`+"`"+` flag is not specified, a default authentication context will be created during initialization.
Expand Down Expand Up @@ -146,9 +146,9 @@ To create new contexts, see the help for `+"`"+`doctl auth init`+"`"+`.`, Writer
func RunAuthInit(retrieveUserTokenFunc func() (string, error)) func(c *CmdConfig) error {
return func(c *CmdConfig) error {
token := c.getContextAccessToken()
context := Context
context := strings.ToLower(Context)
if context == "" {
context = viper.GetString("context")
context = strings.ToLower(viper.GetString("context"))
}

if token == "" {
Expand Down Expand Up @@ -188,7 +188,7 @@ func RunAuthInit(retrieveUserTokenFunc func() (string, error)) func(c *CmdConfig

// RunAuthRemove remove available auth contexts from the user's doctl config.
func RunAuthRemove(c *CmdConfig) error {
context := Context
context := strings.ToLower(Context)

if context == "" {
return fmt.Errorf("You must provide a context name")
Expand Down Expand Up @@ -243,9 +243,28 @@ func displayAuthContexts(out io.Writer, currentContext string, contexts map[stri
// RunAuthSwitch changes the default context and writes it to the
// configuration.
func RunAuthSwitch(c *CmdConfig) error {
context := Context
context := strings.ToLower(Context)
if context == "" {
context = viper.GetString("context")
context = strings.ToLower(viper.GetString("context"))
}

// check that context exists
contextsAvail := viper.GetStringMap("auth-contexts")
contextsAvail[doctl.ArgDefaultContext] = true
keys := make([]string, 0)
for ctx := range contextsAvail {
keys = append(keys, ctx)
}

var contextExists bool
for _, ctx := range keys {
if ctx == context {
contextExists = true
}
}

if !contextExists {
return errors.New("context does not exist")
}

// The two lines below aren't required for doctl specific functionality,
Expand Down
39 changes: 39 additions & 0 deletions commands/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,45 @@ func TestAuthInitWithProvidedToken(t *testing.T) {
})
}

func TestAuthForcesLowercase(t *testing.T) {
cfw := cfgFileWriter
viper.Set(doctl.ArgAccessToken, "valid-token")
defer func() {
cfgFileWriter = cfw
viper.Set(doctl.ArgAccessToken, nil)
}()

retrieveUserTokenFunc := func() (string, error) {
return "", errors.New("should not have called this")
}

cfgFileWriter = func() (io.WriteCloser, error) { return &nopWriteCloser{Writer: ioutil.Discard}, nil }

withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
tm.oauth.EXPECT().TokenInfo(gomock.Any()).Return(&do.OAuthTokenInfo{}, nil)

contexts := map[string]interface{}{doctl.ArgDefaultContext: true, "TestCapitalCase": true}
context := "TestCapitalCase"
viper.Set("auth-contexts", contexts)
viper.Set("context", context)

err := RunAuthInit(retrieveUserTokenFunc)(config)
assert.NoError(t, err)

contexts = map[string]interface{}{doctl.ArgDefaultContext: true, "TestCapitalCase": true}
viper.Set("auth-contexts", contexts)
viper.Set("context", "contextDoesntExist")
err = RunAuthSwitch(config)
// should error because context doesn't exist
assert.Error(t, err)

viper.Set("context", "testcapitalcase")
err = RunAuthSwitch(config)
// should not error because context does exist
assert.NoError(t, err)
})
}

func TestAuthList(t *testing.T) {
buf := &bytes.Buffer{}
config := &CmdConfig{Out: buf}
Expand Down
1 change: 1 addition & 0 deletions commands/doit.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ func initConfig() {

viper.SetDefault("output", "text")
viper.SetDefault(doctl.ArgContext, doctl.ArgDefaultContext)
Context = strings.ToLower(Context)

if _, err := os.Stat(cfgFile); err == nil {
if err := viper.ReadInConfig(); err != nil {
Expand Down

0 comments on commit 698d7ef

Please sign in to comment.