Skip to content

Commit c0c7f38

Browse files
authored
fix: clear-cache pass username as per requirements of lib (#18)
* fix: clear-cache pass username as per requirements of lib +semver: feat add clean up to launcher and browser * fix: put back delete file for cache clear * fix: add consts * fix: bump version * fix: add global safe directory * fix: add compile time consts in helpers fix: ~~user supplied duration will be applied to the last role in the chain~~ API sessions only allow a maximum of an hour fix: add validations to flags * fix: add tests to command add additional notes to flags remove millisecond precision in timeout
1 parent ac79bd2 commit c0c7f38

File tree

16 files changed

+262
-87
lines changed

16 files changed

+262
-87
lines changed

.github/workflows/build.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@ jobs:
1616
with:
1717
fetch-depth: 0
1818
- name: Install GitVersion
19-
uses: gittools/actions/gitversion/setup@v0
19+
uses: gittools/actions/gitversion/setup@v1
2020
with:
2121
versionSpec: '5.x'
22+
- name: set global dir
23+
run: git config --system --add safe.directory "$GITHUB_WORKSPACE"
24+
2225
- name: Set SemVer Version
23-
uses: gittools/actions/gitversion/execute@v0
26+
uses: gittools/actions/gitversion/execute@v1
2427
id: gitversion
2528

2629
- name: echo VERSIONS

.github/workflows/pr.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ jobs:
1515
- uses: actions/checkout@v4
1616
with:
1717
fetch-depth: 0
18+
- name: set global dir
19+
run: git config --system --add safe.directory "$GITHUB_WORKSPACE"
20+
1821
- name: Install GitVersion
19-
uses: gittools/actions/gitversion/setup@v0
22+
uses: gittools/actions/gitversion/setup@v1
2023
with:
2124
versionSpec: '5.x'
2225
- name: Set SemVer Version
23-
uses: gittools/actions/gitversion/execute@v0
26+
uses: gittools/actions/gitversion/execute@v1
2427
id: gitversion
2528
pr:
2629
runs-on: ubuntu-latest

.github/workflows/release.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ jobs:
1919
with:
2020
fetch-depth: 0
2121
- name: Install GitVersion
22-
uses: gittools/actions/gitversion/setup@v0
22+
uses: gittools/actions/gitversion/setup@v1
2323
with:
2424
versionSpec: '5.x'
25+
26+
- name: set global dir
27+
run: git config --system --add safe.directory "$GITHUB_WORKSPACE"
28+
2529
- name: Set SemVer Version
26-
uses: gittools/actions/gitversion/execute@v0
30+
uses: gittools/actions/gitversion/execute@v1
2731
id: gitversion
2832

2933
- name: echo VERSIONS

Makefile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ LDFLAGS := -ldflags="-s -w -X \"github.com/$(OWNER)/$(NAME)/cmd.Version=$(VERSIO
99
.PHONY: test test_ci tidy install buildprep build buildmac buildwin
1010

1111
test: test_prereq
12-
go test ./... -v -mod=readonly -coverprofile=.coverage/out -race && \
13-
cat .coverage/out | go-junit-report > .coverage/report-junit.xml && \
14-
gocov convert .coverage/out | gocov-xml > .coverage/report-cobertura.xml
12+
go test ./... -v -mod=readonly -coverprofile=.coverage/out -race > .coverage/test.out ; \
13+
cat .coverage/test.out | go-junit-report > .coverage/report-junit.xml ; \
14+
gocov convert .coverage/out | gocov-xml > .coverage/report-cobertura.xml ; \
15+
cat .coverage/test.out
1516

1617
test_ci:
1718
go test ./... -mod=readonly

cmd/clear.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmd
33
import (
44
"fmt"
55
"os"
6+
"os/user"
67

78
"github.com/dnitsch/aws-cli-auth/internal/credentialexchange"
89
"github.com/dnitsch/aws-cli-auth/internal/web"
@@ -11,7 +12,7 @@ import (
1112

1213
var (
1314
force bool
14-
clearCmd = &cobra.Command{
15+
ClearCmd = &cobra.Command{
1516
Use: "clear-cache <flags>",
1617
Short: "Clears any stored credentials in the OS secret store",
1718
RunE: clear,
@@ -20,29 +21,41 @@ var (
2021

2122
func init() {
2223
cobra.OnInitialize(samlInitConfig)
23-
clearCmd.PersistentFlags().BoolVarP(&force, "force", "f", false, "If aws-cli-auth exited improprely in a previous run there is a chance that there could be hanging processes left over - this will clean them up forcefully")
24-
rootCmd.AddCommand(clearCmd)
24+
ClearCmd.PersistentFlags().BoolVarP(&force, "force", "f", false, `If aws-cli-auth exited improprely in a previous run there is a chance that there could be hanging processes left over.
25+
26+
This will forcefully all chromium processes.
27+
28+
If you are on a windows machine and also use chrome as your current/main browser this will also kill those processes.
29+
30+
Use with caution.
31+
`)
32+
RootCmd.AddCommand(ClearCmd)
2533
}
2634

2735
func clear(cmd *cobra.Command, args []string) error {
28-
36+
user, err := user.Current()
37+
if err != nil {
38+
return err
39+
}
2940
secretStore, err := credentialexchange.NewSecretStore("",
3041
fmt.Sprintf("%s-%s", credentialexchange.SELF_NAME, credentialexchange.RoleKeyConverter("")),
31-
os.TempDir(), "")
42+
os.TempDir(), user.Username)
43+
3244
if err != nil {
3345
return err
3446
}
3547

3648
if force {
3749
w := &web.Web{}
38-
w.WithConfig(web.NewWebConf(datadir))
39-
if err := w.ClearCache(); err != nil {
50+
if err := w.ForceKill(datadir); err != nil {
4051
return err
4152
}
4253
fmt.Fprint(os.Stderr, "Chromium Cache cleared")
4354
}
4455

45-
secretStore.ClearAll()
56+
if err := secretStore.ClearAll(); err != nil {
57+
fmt.Fprint(os.Stderr, err.Error())
58+
}
4659

4760
if err := os.Remove(credentialexchange.ConfigIniFile("")); err != nil {
4861
return err

cmd/cmd_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package cmd_test
2+
3+
import (
4+
"bytes"
5+
"io"
6+
"testing"
7+
8+
"github.com/dnitsch/aws-cli-auth/cmd"
9+
)
10+
11+
func Test_helpers_for_command(t *testing.T) {
12+
ttests := map[string]struct{}{
13+
"clear-cache": {},
14+
"saml": {},
15+
"specific": {},
16+
}
17+
for name := range ttests {
18+
t.Run(name, func(t *testing.T) {
19+
cmdArgs := []string{name, "--help"}
20+
b := new(bytes.Buffer)
21+
o := new(bytes.Buffer)
22+
cmd := cmd.RootCmd
23+
cmd.SetArgs(cmdArgs)
24+
cmd.SetErr(b)
25+
cmd.SetOut(o)
26+
cmd.Execute()
27+
err, _ := io.ReadAll(b)
28+
if len(err) > 0 {
29+
t.Fatal("got err, wanted nil")
30+
}
31+
out, _ := io.ReadAll(o)
32+
if len(out) <= 0 {
33+
t.Fatalf("got empty, wanted a help message")
34+
}
35+
})
36+
}
37+
}
38+
39+
func Test_Saml(t *testing.T) {
40+
t.Skip()
41+
t.Run("standard non sso should fail with incorrect saml URLs", func(t *testing.T) {
42+
cmdArgs := []string{"saml", "-p",
43+
"https://httpbin.org/anything/app123",
44+
"--principal",
45+
"arn:aws:iam::1234111111111:saml-provider/provider1",
46+
"--role",
47+
"arn:aws:iam::1234111111111:role/Role-ReadOnly",
48+
"--role-chain",
49+
"arn:aws:iam::1234111111111:role/Kubernetes-Cluster-Administrators",
50+
"--saml-timeout", "1",
51+
"-d",
52+
"14400",
53+
"--reload-before",
54+
"120"}
55+
b := new(bytes.Buffer)
56+
o := new(bytes.Buffer)
57+
cmd := cmd.RootCmd
58+
cmd.SetArgs(cmdArgs)
59+
cmd.SetErr(b)
60+
cmd.SetOut(o)
61+
if err := cmd.Execute(); err == nil {
62+
t.Error("got nil, wanted an error")
63+
}
64+
// err, _ := io.ReadAll(b)
65+
// fmt.Println(string(err))
66+
// if len(err) <= 0 {
67+
// t.Fatal("got nil, wanted an error")
68+
// }
69+
// out, _ := io.ReadAll(o)
70+
// fmt.Println(string(out))
71+
// if len(out) <= 0 {
72+
// t.Fatalf("got empty, wanted a help message")
73+
// }
74+
})
75+
}

cmd/root.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ var (
2020
role string
2121
roleChain []string
2222
verbose bool
23-
rootCmd = &cobra.Command{
23+
duration int
24+
RootCmd = &cobra.Command{
2425
Use: "aws-cli-auth",
2526
Short: "CLI tool for retrieving AWS temporary credentials",
2627
Long: `CLI tool for retrieving AWS temporary credentials using SAML providers, or specified method of retrieval - i.e. force AWS_WEB_IDENTITY.
@@ -31,17 +32,21 @@ Stores them under the $HOME/.aws/credentials file under a specified path or retu
3132
)
3233

3334
func Execute(ctx context.Context) {
34-
if err := rootCmd.ExecuteContext(ctx); err != nil {
35+
if err := RootCmd.ExecuteContext(ctx); err != nil {
3536
fmt.Errorf("cli error: %v", err)
3637
os.Exit(1)
3738
}
3839
os.Exit(0)
3940
}
4041

4142
func init() {
42-
rootCmd.PersistentFlags().StringVarP(&role, "role", "r", "", "Set the role you want to assume when SAML or OIDC process completes")
43-
rootCmd.PersistentFlags().StringSliceVarP(&roleChain, "role-chain", "", []string{}, "If specified it will assume the roles from the base credentials, in order they are specified in")
44-
rootCmd.PersistentFlags().StringVarP(&cfgSectionName, "cfg-section", "", "", "config section name in the yaml config file")
45-
rootCmd.PersistentFlags().BoolVarP(&storeInProfile, "store-profile", "s", false, "By default the credentials are returned to stdout to be used by the credential_process. Set this flag to instead store the credentials under a named profile section")
46-
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Verbose output")
43+
RootCmd.PersistentFlags().StringSliceVarP(&roleChain, "role-chain", "", []string{}, "If specified it will assume the roles from the base credentials, in order they are specified in")
44+
RootCmd.PersistentFlags().BoolVarP(&storeInProfile, "store-profile", "s", false, `By default the credentials are returned to stdout to be used by the credential_process.
45+
Set this flag to instead store the credentials under a named profile section. You can then reference that profile name via the CLI or for use in an SDK`)
46+
RootCmd.PersistentFlags().StringVarP(&cfgSectionName, "cfg-section", "", "", "Config section name in the default AWS credentials file. To enable priofi")
47+
// When specifying store in profile the config section name must be provided
48+
RootCmd.MarkFlagsRequiredTogether("store-profile", "cfg-section")
49+
RootCmd.PersistentFlags().IntVarP(&duration, "max-duration", "d", 900, `Override default max session duration, in seconds, of the role session [900-43200].
50+
NB: This cannot be higher than the 3600 as the API does not allow for AssumeRole for sessions longer than an hour`)
51+
RootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Verbose output")
4752
}

cmd/saml.go

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ var (
2020
ErrUnableToCreateSession = errors.New("sts - cannot start a new session")
2121
)
2222

23+
const (
24+
UserEndpoint = "https://portal.sso.%s.amazonaws.com/user"
25+
CredsEndpoint = "https://portal.sso.%s.amazonaws.com/federation/credentials/"
26+
SsoCredsEndpointQuery = "?account_id=%s&role_name=%s&debug=true"
27+
)
28+
29+
var (
30+
ssoRoleAccount, ssoRoleName string
31+
)
32+
2333
var (
2434
providerUrl string
2535
principalArn string
@@ -30,9 +40,9 @@ var (
3040
ssoUserEndpoint string
3141
ssoFedCredEndpoint string
3242
datadir string
33-
duration int
43+
samlTimeout int32
3444
reloadBeforeTime int
35-
samlCmd = &cobra.Command{
45+
SamlCmd = &cobra.Command{
3646
Use: "saml <SAML ProviderUrl>",
3747
Short: "Get AWS credentials and out to stdout",
3848
Long: `Get AWS credentials and out to stdout through your SAML provider authentication.`,
@@ -41,26 +51,46 @@ var (
4151
if reloadBeforeTime != 0 && reloadBeforeTime > duration {
4252
return fmt.Errorf("reload-before: %v, must be less than duration (-d): %v", reloadBeforeTime, duration)
4353
}
54+
if len(ssoRole) > 0 {
55+
sr := strings.Split(ssoRole, ":")
56+
if len(sr) != 2 {
57+
return fmt.Errorf("incorrectly formatted role for AWS SSO - must only be ACCOUNT:ROLE_NAME")
58+
}
59+
ssoRoleAccount, ssoRoleName = sr[0], sr[1]
60+
}
4461
return nil
4562
},
4663
}
4764
)
4865

4966
func init() {
5067
cobra.OnInitialize(samlInitConfig)
51-
samlCmd.PersistentFlags().StringVarP(&providerUrl, "provider", "p", "", "Saml Entity StartSSO Url")
52-
samlCmd.MarkPersistentFlagRequired("provider")
53-
samlCmd.PersistentFlags().StringVarP(&principalArn, "principal", "", "", "Principal Arn of the SAML IdP in AWS")
54-
samlCmd.PersistentFlags().StringVarP(&acsUrl, "acsurl", "a", "https://signin.aws.amazon.com/saml", "Override the default ACS Url, used for checkin the post of the SAMLResponse")
55-
samlCmd.PersistentFlags().StringVarP(&ssoUserEndpoint, "sso-user-endpoint", "", "https://portal.sso.%s.amazonaws.com/user", "UserEndpoint in a go style fmt.Sprintf string with a region placeholder")
56-
samlCmd.PersistentFlags().StringVarP(&ssoRole, "sso-role", "", "", "Sso Role name must be in this format - 12345678910:PowerUser")
57-
samlCmd.PersistentFlags().StringVarP(&ssoFedCredEndpoint, "sso-fed-endpoint", "", "https://portal.sso.%s.amazonaws.com/federation/credentials/", "FederationCredEndpoint in a go style fmt.Sprintf string with a region placeholder")
58-
samlCmd.PersistentFlags().StringVarP(&ssoRegion, "sso-region", "", "eu-west-1", "If using SSO, you must set the region")
59-
samlCmd.PersistentFlags().IntVarP(&duration, "max-duration", "d", 900, "Override default max session duration, in seconds, of the role session [900-43200]")
60-
samlCmd.PersistentFlags().BoolVarP(&isSso, "is-sso", "", false, `Enables the new AWS User portal login.
68+
SamlCmd.PersistentFlags().StringVarP(&providerUrl, "provider", "p", "", `Saml Entity StartSSO Url.
69+
This is the URL your Idp will make the first call to e.g.: https://company-xyz.okta.com/home/amazon_aws/12345SomeRandonId6789
70+
`)
71+
SamlCmd.MarkPersistentFlagRequired("provider")
72+
SamlCmd.PersistentFlags().StringVarP(&principalArn, "principal", "", "", `Principal Arn of the SAML IdP in AWS
73+
You should find it in the IAM portal e.g.: arn:aws:iam::1234567891012:saml-provider/MyCompany-Idp
74+
`)
75+
// samlCmd.MarkPersistentFlagRequired("principal")
76+
SamlCmd.PersistentFlags().StringVarP(&role, "role", "r", "", `Set the role you want to assume when SAML or OIDC process completes`)
77+
SamlCmd.PersistentFlags().StringVarP(&acsUrl, "acsurl", "a", "https://signin.aws.amazon.com/saml", "Override the default ACS Url, used for checkin the post of the SAMLResponse")
78+
SamlCmd.PersistentFlags().StringVarP(&ssoUserEndpoint, "sso-user-endpoint", "", UserEndpoint, "UserEndpoint in a go style fmt.Sprintf string with a region placeholder")
79+
SamlCmd.PersistentFlags().StringVarP(&ssoRole, "sso-role", "", "", "Sso Role name must be in this format - 12345678910:PowerUser")
80+
SamlCmd.PersistentFlags().StringVarP(&ssoFedCredEndpoint, "sso-fed-endpoint", "", CredsEndpoint, "FederationCredEndpoint in a go style fmt.Sprintf string with a region placeholder")
81+
SamlCmd.PersistentFlags().StringVarP(&ssoRegion, "sso-region", "", "eu-west-1", "If using SSO, you must set the region")
82+
SamlCmd.PersistentFlags().BoolVarP(&isSso, "is-sso", "", false, `Enables the new AWS User portal login.
6183
If this flag is specified the --sso-role must also be specified.`)
62-
samlCmd.PersistentFlags().IntVarP(&reloadBeforeTime, "reload-before", "", 0, "Triggers a credentials refresh before the specified max-duration. Value provided in seconds. Should be less than the max-duration of the session")
63-
rootCmd.AddCommand(samlCmd)
84+
SamlCmd.PersistentFlags().IntVarP(&reloadBeforeTime, "reload-before", "", 0, "Triggers a credentials refresh before the specified max-duration. Value provided in seconds. Should be less than the max-duration of the session")
85+
//
86+
SamlCmd.MarkFlagsMutuallyExclusive("role", "sso-role")
87+
// samlCmd.MarkFlagsMutuallyExclusive("principal", "sso-role")
88+
// Non-SSO flow for SAML
89+
SamlCmd.MarkFlagsRequiredTogether("principal", "role")
90+
// SSO flow for SAML
91+
SamlCmd.MarkFlagsRequiredTogether("is-sso", "sso-role", "sso-region")
92+
SamlCmd.PersistentFlags().Int32VarP(&samlTimeout, "saml-timeout", "", 120, "Timeout in seconds, before the operation of waiting for a response is cancelled via the chrome driver")
93+
RootCmd.AddCommand(SamlCmd)
6494
}
6595

6696
func getSaml(cmd *cobra.Command, args []string) error {
@@ -91,14 +121,9 @@ func getSaml(cmd *cobra.Command, args []string) error {
91121

92122
saveRole := role
93123
if isSso {
94-
sr := strings.Split(ssoRole, ":")
95-
if len(sr) != 2 {
96-
return fmt.Errorf("incorrectly formatted role for AWS SSO - must only be ACCOUNT:ROLE_NAME")
97-
}
98124
saveRole = ssoRole
99-
100-
conf.SsoUserEndpoint = fmt.Sprintf("https://portal.sso.%s.amazonaws.com/user", conf.SsoRegion)
101-
conf.SsoCredFedEndpoint = fmt.Sprintf("https://portal.sso.%s.amazonaws.com/federation/credentials/", conf.SsoRegion) + fmt.Sprintf("?account_id=%s&role_name=%s&debug=true", sr[0], sr[1])
125+
conf.SsoUserEndpoint = fmt.Sprintf(UserEndpoint, conf.SsoRegion)
126+
conf.SsoCredFedEndpoint = fmt.Sprintf(CredsEndpoint, conf.SsoRegion) + fmt.Sprintf(SsoCredsEndpointQuery, ssoRoleAccount, ssoRoleName)
102127
}
103128

104129
datadir := path.Join(credentialexchange.HomeDir(), fmt.Sprintf(".%s-data", credentialexchange.SELF_NAME))
@@ -121,7 +146,7 @@ func getSaml(cmd *cobra.Command, args []string) error {
121146
}
122147
svc := sts.NewFromConfig(cfg)
123148

124-
return cmdutils.GetCredsWebUI(ctx, svc, secretStore, conf, web.NewWebConf(datadir))
149+
return cmdutils.GetCredsWebUI(ctx, svc, secretStore, conf, web.NewWebConf(datadir).WithTimeout(samlTimeout))
125150
}
126151

127152
func samlInitConfig() {

0 commit comments

Comments
 (0)