Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support ~ and $HOME for UrlExecCommand #822

Merged
merged 1 commit into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 8 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
18 changes: 12 additions & 6 deletions internal/url/url.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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
}

Expand Down
5 changes: 5 additions & 0 deletions internal/url/url_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"net"
"net/url"
"os"
"testing"

"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -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) {
Expand Down
18 changes: 9 additions & 9 deletions sso/awssso_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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
Expand All @@ -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")
}
}

Expand Down
12 changes: 6 additions & 6 deletions sso/awssso_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion sso/roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading