diff --git a/CHANGELOG.md b/CHANGELOG.md index c137fbca..aaa7a6a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Add `aws-sso-cli completion --source` flag to generate completion script and print to stdout. #779 +* UrlExecCommand now supports commands in `~` and the `$HOME` environment variable. #816 ## [v1.14.3] - 2024-01-15 diff --git a/docs/config.md b/docs/config.md index 28753248..884fa22c 100644 --- a/docs/config.md +++ b/docs/config.md @@ -310,6 +310,14 @@ UrlExecCommand: - "%s" ``` +###### Use custom shell script +```yaml +UrlAction: exec +UrlExecCommand: + - ~/bin/open_url.sh + - "%s" +``` + **Note:** If your `ProfileFormat` generates a _ProfileName_ with an `&`, then `{{ .AccountId }}:{{ .RoleName }}` will be used as the Firefox container name instead. diff --git a/internal/url/url.go b/internal/url/url.go index 75408ae4..821cc4ec 100644 --- a/internal/url/url.go +++ b/internal/url/url.go @@ -194,7 +194,7 @@ func (h *HandleUrl) Open() error { if err == nil { log.Infof("Please open URL copied to clipboard.\n") } else { - err = fmt.Errorf("Unable to copy URL to clipboard: %s", err.Error()) + err = fmt.Errorf("unable to copy URL to clipboard: %s", err.Error()) } case Exec: @@ -223,13 +223,13 @@ func (h *HandleUrl) Open() error { err = urlOpenerWith(h.Url, h.Browser) } if err != nil { - err = fmt.Errorf("Unable to open URL with %s: %s", browser, err.Error()) + err = fmt.Errorf("unable to open URL with %s: %s", browser, err.Error()) } else { log.Infof("Opening URL in: %s\n", browser) } default: - err = fmt.Errorf("Unsupported Open action: %s", string(h.Action)) + err = fmt.Errorf("unsupported Open action: %s", string(h.Action)) } return err @@ -280,11 +280,14 @@ func execWithUrl(command []string, url string) error { log.Debugf("exec command as array: %s", cmdStr) cmd = exec.Command(program, cmdList...) + // add $HOME to our environment + cmd.Env = append(cmd.Environ(), fmt.Sprintf("HOME=%s", os.Getenv("HOME"))) + // var stderr bytes.Buffer // cmd.Stderr = &stderr err = cmd.Start() // Don't use Run() because sometimes firefox does bad things? if err != nil { - err = fmt.Errorf("Unable to exec `%s`: %s", cmdStr, err) + err = fmt.Errorf("unable to exec `%s`: %s", cmdStr, err) } log.Debugf("Opened our URL with %s", command[0]) return err @@ -297,7 +300,7 @@ func commandBuilder(command []string, url string) (string, []string, error) { replaced := false if len(command) < 2 { - return program, cmdList, fmt.Errorf("Invalid UrlExecCommand has fewer than 2 arguments") + return program, cmdList, fmt.Errorf("invalid UrlExecCommand has fewer than 2 arguments") } for i, v := range command { @@ -312,9 +315,12 @@ func commandBuilder(command []string, url string) (string, []string, error) { } if !replaced { - return program, cmdList, fmt.Errorf("Invalid UrlExecCommand has no `%%s` for URL") + return program, cmdList, fmt.Errorf("invalid UrlExecCommand has no `%%s` for URL") } + // if program is ~/something, expand it + program = utils.GetHomePath(program) + return program, cmdList, nil } diff --git a/internal/url/url_test.go b/internal/url/url_test.go index b6a2de88..a14f583e 100644 --- a/internal/url/url_test.go +++ b/internal/url/url_test.go @@ -23,6 +23,7 @@ import ( "fmt" "net" "net/url" + "os" "testing" "github.com/sirupsen/logrus" @@ -173,6 +174,10 @@ func TestCommandBuilder(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "foo", cmd) assert.Equal(t, []string{"bar", "url"}, l) + + cmd, _, err = commandBuilder([]string{"~/foo", "bar", "%s"}, "url") + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("%s/foo", os.Getenv("HOME")), cmd) } func TestSelectElement(t *testing.T) { diff --git a/sso/awssso_auth.go b/sso/awssso_auth.go index 3912130c..1436602f 100644 --- a/sso/awssso_auth.go +++ b/sso/awssso_auth.go @@ -90,7 +90,7 @@ func (as *AWSSSO) reauthenticate() error { err := as.registerClient(false) log.Tracef("<- reauthenticate()") if err != nil { - return fmt.Errorf("Unable to register client with AWS SSO: %s", err.Error()) + return fmt.Errorf("unable to register client with AWS SSO: %s", err.Error()) } err = as.startDeviceAuthorization() @@ -99,17 +99,17 @@ func (as *AWSSSO) reauthenticate() error { log.Debugf("startDeviceAuthorization failed. Forcing refresh of registerClient") // startDeviceAuthorization can fail if our cached registerClient token is invalid if err = as.registerClient(true); err != nil { - return fmt.Errorf("Unable to register client with AWS SSO: %s", err.Error()) + return fmt.Errorf("unable to register client with AWS SSO: %s", err.Error()) } if err = as.startDeviceAuthorization(); err != nil { - return fmt.Errorf("Unable to start device authorization with AWS SSO: %s", err.Error()) + return fmt.Errorf("unable to start device authorization with AWS SSO: %s", err.Error()) } } auth, err := as.getDeviceAuthInfo() log.Tracef("<- reauthenticate()") if err != nil { - return fmt.Errorf("Unable to get device auth info from AWS SSO: %s", err.Error()) + return fmt.Errorf("unable to get device auth info from AWS SSO: %s", err.Error()) } action := as.urlAction @@ -129,7 +129,7 @@ func (as *AWSSSO) reauthenticate() error { err = as.createToken() if err != nil { - return fmt.Errorf("Unable to create new AWS SSO token: %s", err.Error()) + return fmt.Errorf("unable to create new AWS SSO token: %s", err.Error()) } return nil @@ -177,7 +177,7 @@ func (as *AWSSSO) registerClient(force bool) error { } err = as.store.SaveRegisterClientData(as.StoreKey(), as.ClientData) if err != nil { - log.WithError(err).Errorf("Unable to save RegisterClientData for %s", as.StoreKey()) + log.WithError(err).Errorf("unable to save RegisterClientData for %s", as.StoreKey()) } return nil } @@ -222,7 +222,7 @@ type DeviceAuthInfo struct { func (as *AWSSSO) getDeviceAuthInfo() (DeviceAuthInfo, error) { log.Tracef("getDeviceAuthInfo()") if as.DeviceAuth.VerificationUri == "" { - return DeviceAuthInfo{}, fmt.Errorf("No valid verification url is available for %s", as.StoreKey()) + return DeviceAuthInfo{}, fmt.Errorf("no valid verification url is available for %s", as.StoreKey()) } info := DeviceAuthInfo{ @@ -290,7 +290,7 @@ func (as *AWSSSO) createToken() error { err = as.store.SaveCreateTokenResponse(as.StoreKey(), as.Token) as.tokenLock.RUnlock() if err != nil { - log.WithError(err).Errorf("Unable to save CreateTokenResponse") + log.WithError(err).Errorf("unable to save CreateTokenResponse") } return nil @@ -310,7 +310,7 @@ func (as *AWSSSO) Logout() error { // delete the value from the store so we don't think we have a valid token if err := as.store.DeleteCreateTokenResponse(as.key); err != nil { - log.WithError(err).Errorf("Unable to delete AccessToken from secure store") + log.WithError(err).Errorf("unable to delete AccessToken from secure store") } } diff --git a/sso/awssso_auth_test.go b/sso/awssso_auth_test.go index 7bcdc36b..f08ab582 100644 --- a/sso/awssso_auth_test.go +++ b/sso/awssso_auth_test.go @@ -417,23 +417,23 @@ func TestAuthenticateFailure(t *testing.T) { } err = as.Authenticate("print", "fake-browser") - assert.Contains(t, err.Error(), "Unable to register client with AWS SSO") + assert.Contains(t, err.Error(), "unable to register client with AWS SSO") err = as.Authenticate("print", "fake-browser") - assert.Contains(t, err.Error(), "Unable to start device authorization") + assert.Contains(t, err.Error(), "unable to start device authorization") err = as.Authenticate("print", "fake-browser") assert.Contains(t, err.Error(), "createToken:") err = as.Authenticate("print", "fake-browser") - assert.Contains(t, err.Error(), "No valid verification url") + assert.Contains(t, err.Error(), "no valid verification url") err = as.Authenticate("invalid", "fake-browser") - assert.Contains(t, err.Error(), "Unsupported Open action") + assert.Contains(t, err.Error(), "unsupported Open action") as.SSOConfig.AuthUrlAction = "invalid" err = as.Authenticate("print", "fake-browser") - assert.Contains(t, err.Error(), "Unsupported Open action") + assert.Contains(t, err.Error(), "unsupported Open action") } func TestReauthenticate(t *testing.T) { @@ -528,7 +528,7 @@ func TestReauthenticate(t *testing.T) { } err = as.reauthenticate() - assert.Contains(t, err.Error(), "Unable to exec") + assert.Contains(t, err.Error(), "unable to exec") } func TestLogout(t *testing.T) { diff --git a/sso/roles.go b/sso/roles.go index ee643dec..9157cf73 100644 --- a/sso/roles.go +++ b/sso/roles.go @@ -67,7 +67,7 @@ type AWSRole struct { } // AccountIds returns all the configured AWS SSO AccountIds -func (r *Roles) AccountIds() []int64 { +func (r *Roles) AccountIds() []int64 { // nolint: revive ret := []int64{} for id := range r.Accounts { ret = append(ret, id)