Skip to content

Commit a98af8c

Browse files
authored
fix: add validator to the final config struct (#27)
* fix: add validator to the final config struct * fix: De Morgans law fix in lint * fix: add more tests to sso edge case * fix: add some logging * fix: remove comment REVERT: when needed we can bring back struct for credentialexchange * fix: layout of tests in cmdutils * fix: update sonar org * fix: CI workflow tasks * fix: semver * fix: see token * fix: unit test and coverage * fix: add sonar prep * fix: correct context * fix: update task for sonarscanner * fix: debug token * fix: clean up * fix: sonar prep * fix: yaml lint
1 parent 26a7f29 commit a98af8c

File tree

18 files changed

+452
-344
lines changed

18 files changed

+452
-344
lines changed

.github/workflows/ci.yml

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: CI
22

33
on:
44
pull_request:
5-
branches: [ master, main ]
5+
branches: [master, main]
66
push:
7-
branches: [ master, main ]
7+
branches: [master, main]
88

99
permissions:
1010
contents: write
@@ -16,22 +16,22 @@ jobs:
1616
set-version:
1717
runs-on: ubuntu-22.04
1818
container:
19-
image: mcr.microsoft.com/dotnet/sdk:6.0
19+
image: mcr.microsoft.com/dotnet/sdk:10.0
2020
outputs:
2121
semVer: ${{ steps.gitversion.outputs.semVer }}
2222
steps:
23-
- uses: actions/checkout@v4
23+
- uses: actions/checkout@v6
2424
with:
2525
fetch-depth: 0
2626
- name: set global dir
2727
run: git config --system --add safe.directory "$GITHUB_WORKSPACE"
2828

2929
- name: Install GitVersion
30-
uses: gittools/actions/gitversion/setup@v1
30+
uses: gittools/actions/gitversion/setup@v4
3131
with:
32-
versionSpec: '5.x'
32+
versionSpec: "6.x"
3333
- name: Set SemVer Version
34-
uses: gittools/actions/gitversion/execute@v1
34+
uses: gittools/actions/gitversion/execute@v4
3535
id: gitversion
3636

3737
pr:
@@ -41,10 +41,13 @@ jobs:
4141
REVISION: $GITHUB_SHA
4242
SEMVER: ${{ needs.set-version.outputs.semVer }}
4343
steps:
44-
- uses: actions/checkout@v4
44+
- uses: actions/checkout@v6
45+
with:
46+
# Disabling shallow clones is recommended for improving the relevancy of reporting
47+
fetch-depth: 0
4548

4649
- uses: ensono/actions/eirctl-setup@v0.3.1
47-
with:
50+
with:
4851
version: latest
4952
isPrerelease: false
5053

@@ -60,23 +63,27 @@ jobs:
6063
6164
- name: Unit Tests
6265
run: |
63-
eirctl run unit:test:run
66+
eirctl run pipeline gha:unit:test
6467
6568
- name: Publish Test Report
66-
uses: mikepenz/action-junit-report@v5
69+
uses: mikepenz/action-junit-report@v6
6770
if: success() || failure()
6871
with:
69-
report_paths: '.coverage/report-junit.xml'
72+
report_paths: ".coverage/report-junit.xml"
7073
commit: ${{ github.sha }}
7174
fail_on_failure: true
7275
check_name: aws-cli-auth Unit Tests
7376

7477
- name: Analyze with SonarCloud
75-
uses: SonarSource/sonarqube-scan-action@v5
78+
uses: SonarSource/sonarqube-scan-action@v6
7679
env:
80+
# Needed to get PR information
7781
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
82+
# Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret)
7883
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
7984
with:
8085
projectBaseDir: .
8186
args: >
8287
-Dsonar.projectVersion=${{ needs.set-version.outputs.semVer }}
88+
-Dsonar.working.directory=.scannerwork
89+
-Dsonar.scm.provider=git

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ vendor/
2828
.ignore*
2929
local/
3030
.deps/
31-
.cache/
31+
.cache/
32+
*.env

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
[![Go Report Card](https://goreportcard.com/badge/github.com/DevLabFoundry/aws-cli-auth)](https://goreportcard.com/report/github.com/DevLabFoundry/aws-cli-auth)
2-
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=dnitsch_aws-cli-auth&metric=bugs)](https://sonarcloud.io/summary/new_code?id=dnitsch_aws-cli-auth)
3-
[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=dnitsch_aws-cli-auth&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=dnitsch_aws-cli-auth)
4-
[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=dnitsch_aws-cli-auth&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=dnitsch_aws-cli-auth)
5-
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=dnitsch_aws-cli-auth&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=dnitsch_aws-cli-auth)
6-
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=dnitsch_aws-cli-auth&metric=coverage)](https://sonarcloud.io/summary/new_code?id=dnitsch_aws-cli-auth)
2+
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=DevLabFoundry_aws-cli-auth&metric=bugs)](https://sonarcloud.io/summary/new_code?id=dnitsch_aws-cli-auth)
3+
[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=DevLabFoundry_aws-cli-auth&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=dnitsch_aws-cli-auth)
4+
[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=DevLabFoundry_aws-cli-auth&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=dnitsch_aws-cli-auth)
5+
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=DevLabFoundry_aws-cli-auth&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=dnitsch_aws-cli-auth)
6+
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=DevLabFoundry_aws-cli-auth&metric=coverage)](https://sonarcloud.io/summary/new_code?id=dnitsch_aws-cli-auth)
77

88
# AWS CLI AUTH
99

aws-cli-auth.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,33 @@ package main
22

33
import (
44
"context"
5-
"log"
65
"os"
76
"os/signal"
87
"syscall"
8+
"time"
99

1010
"github.com/DevLabFoundry/aws-cli-auth/cmd"
11+
"github.com/rs/zerolog"
1112
)
1213

1314
func main() {
1415
ctx, stop := signal.NotifyContext(context.Background(), []os.Signal{os.Interrupt, syscall.SIGTERM, os.Kill}...)
1516
defer stop()
17+
logger := zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).
18+
Level(zerolog.ErrorLevel).
19+
With().Timestamp().
20+
Logger()
1621

1722
go func() {
1823
<-ctx.Done()
1924
stop()
20-
// log.Printf("\x1b[31minterrupted: %s\x1b[0m", ctx.Err())
21-
os.Exit(0)
25+
logger.Fatal().Msgf("\x1b[31minterrupted: %s\x1b[0m", ctx.Err())
2226
}()
2327

24-
c := cmd.New()
28+
c := cmd.New(logger)
2529
c.WithSubCommands(cmd.SubCommands()...)
2630

2731
if err := c.Execute(ctx); err != nil {
28-
log.Fatalf("\x1b[31m%s\x1b[0m", err)
32+
logger.Fatal().Msgf("\x1b[31m%s\x1b[0m", err)
2933
}
3034
}

cmd/awscliauth.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99

1010
"github.com/DevLabFoundry/aws-cli-auth/internal/credentialexchange"
1111
"github.com/Ensono/eirctl/selfupdate"
12+
"github.com/rs/zerolog"
13+
"github.com/savioxavier/termlink"
1214
"github.com/spf13/cobra"
1315
)
1416

@@ -23,6 +25,7 @@ type Root struct {
2325
// ChannelErr io.Writer
2426
// viperConf *viper.Viper
2527
rootFlags *RootCmdFlags
28+
logger zerolog.Logger
2629
Datadir string
2730
}
2831

@@ -35,9 +38,10 @@ type RootCmdFlags struct {
3538
CustomIniLocation string
3639
}
3740

38-
func New() *Root {
41+
func New(logger zerolog.Logger) *Root {
3942
rf := &RootCmdFlags{}
4043
r := &Root{
44+
logger: logger,
4145
rootFlags: rf,
4246
Cmd: &cobra.Command{
4347
Use: "aws-cli-auth",
@@ -57,8 +61,8 @@ Stores them under the $HOME/.aws/credentials file under a specified path or retu
5761
r.Cmd.PersistentFlags().StringVarP(&rf.CfgSectionName, "cfg-section", "", "", "Config section name to use in the look up of the config ini file (~/.aws-cli-auth.ini) and in the AWS credentials file")
5862
// When specifying store in profile the config section name must be provided
5963
r.Cmd.MarkFlagsRequiredTogether("store-profile", "cfg-section")
60-
r.Cmd.PersistentFlags().IntVarP(&rf.Duration, "max-duration", "d", 900, `Override default max session duration, in seconds, of the role session [900-43200].
61-
NB: This cannot be higher than the 3600 as the API does not allow for AssumeRole for sessions longer than an hour`)
64+
r.Cmd.PersistentFlags().IntVarP(&rf.Duration, "max-duration", "d", 900, fmt.Sprintf("Override default max session duration, in seconds, of the role session [900-43200].\nNB: This cannot be higher than the 3600 as the API does not allow for AssumeRole for sessions longer than an hour\nMore info on this and especially around role-chaining\nSee %s",
65+
termlink.Link("AWS SDK Duration", "https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html#API_AssumeRole_RequestParameters")))
6266
r.Cmd.PersistentFlags().BoolVarP(&rf.Verbose, "verbose", "v", false, "Verbose output")
6367
r.Cmd.PersistentFlags().StringVarP(&rf.CustomIniLocation, "config-file", "c", "", "Specify the custom location of config file")
6468

cmd/awscliauth_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ import (
1212
"github.com/DevLabFoundry/aws-cli-auth/cmd"
1313
"github.com/DevLabFoundry/aws-cli-auth/internal/credentialexchange"
1414
"github.com/DevLabFoundry/aws-cli-auth/internal/web"
15+
"github.com/rs/zerolog"
1516
)
1617

1718
func cmdHelperExecutor(t *testing.T, args []string) (stdOut *bytes.Buffer, errOut *bytes.Buffer, err error) {
1819
t.Helper()
1920
errOut = new(bytes.Buffer)
2021
stdOut = new(bytes.Buffer)
21-
c := cmd.New()
22+
c := cmd.New(zerolog.New(io.Discard))
2223
c.WithSubCommands(cmd.SubCommands()...)
2324
c.Cmd.SetArgs(args)
2425
c.Cmd.SetErr(errOut)

cmd/saml.go

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ import (
1313
"github.com/DevLabFoundry/aws-cli-auth/internal/web"
1414
"github.com/aws/aws-sdk-go-v2/config"
1515
"github.com/aws/aws-sdk-go-v2/service/sts"
16+
validator "github.com/rezakhademix/govalidator/v2"
17+
"github.com/rs/zerolog"
1618
"github.com/spf13/cobra"
1719
"gopkg.in/ini.v1"
1820
)
1921

2022
var (
2123
ErrUnableToCreateSession = errors.New("sts - cannot start a new session")
24+
ErrValidationFailed = errors.New("missing values")
2225
)
2326

2427
const (
@@ -64,17 +67,24 @@ func newSamlCmd(r *Root) {
6467
if err != nil {
6568
return err
6669
}
67-
70+
if r.rootFlags.Verbose {
71+
r.logger = r.logger.Level(zerolog.DebugLevel)
72+
}
73+
r.logger.Debug().Str("CustomIniLocation", r.rootFlags.CustomIniLocation).Msg("if empty using default ~/.aws-cli-auth.ini")
6874
iniFile, err := samlInitConfig(r.rootFlags.CustomIniLocation)
6975
if err != nil {
7076
return err
7177
}
7278

79+
r.logger.Debug().Msgf("iniFile: %+v", iniFile)
80+
7381
conf, err := credentialexchange.LoadCliConfig(iniFile, r.rootFlags.CfgSectionName)
7482
if err != nil {
7583
return err
7684
}
7785

86+
r.logger.Debug().Str("section", r.rootFlags.CfgSectionName).Msgf("loaded section: %+v", conf)
87+
7888
if err := ConfigFromFlags(conf, r.rootFlags, flags, user.Username); err != nil {
7989
return err
8090
}
@@ -95,30 +105,35 @@ func newSamlCmd(r *Root) {
95105
saveRole = allRoles[len(allRoles)-1]
96106
}
97107

108+
r.logger.Debug().Str("saveRole", saveRole).
109+
Str("SsoEndpoint", conf.SsoUserEndpoint).
110+
Str("SsoCredFedEndpoint", conf.SsoCredFedEndpoint).
111+
Msg("")
112+
98113
secretStore, err := credentialexchange.NewSecretStore(saveRole,
99114
fmt.Sprintf("%s-%s", credentialexchange.SELF_NAME, credentialexchange.RoleKeyConverter(saveRole)),
100115
os.TempDir(), user.Username)
101116
if err != nil {
102117
return err
103118
}
104119

105-
// we want to remove any AWS_* env vars that could interfere with the default config
106-
// for _, envVar := range []string{"AWS_PROFILE", "AWS_ACCESS_KEY_ID",
107-
// "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"} {
108-
// os.Unsetenv(envVar)
109-
// }
110-
111-
awsConf, err := config.LoadDefaultConfig(ctx)
120+
cfg, err := config.LoadDefaultConfig(ctx)
112121
if err != nil {
113122
return fmt.Errorf("failed to create session %s, %w", err, ErrUnableToCreateSession)
114123
}
115124

116-
svc := sts.NewFromConfig(awsConf)
125+
if cfg.Region == "" {
126+
// cfg.en
127+
return fmt.Errorf("unable to deduce AWS region, AWS_REGION, AWS_DEFAULT_REGION, ~/.aws/config default or profile level region must be set")
128+
}
129+
130+
svc := sts.NewFromConfig(cfg)
131+
cre := credentialexchange.New(r.logger, svc)
117132
webConfig := web.NewWebConf(r.Datadir).
118133
WithTimeout(flags.SamlTimeout).
119134
WithCustomExecutable(conf.BaseConfig.BrowserExecutablePath)
120135

121-
return cmdutils.GetCredsWebUI(ctx, svc, secretStore, *conf, webConfig)
136+
return cmdutils.GetCredsWebUI(ctx, cre, secretStore, *conf, webConfig)
122137

123138
},
124139
PreRunE: func(cmd *cobra.Command, args []string) error {
@@ -165,7 +180,7 @@ If this flag is specified the --sso-role must also be specified.`)
165180
// sc.cmd.MarkFlagsRequiredTogether("principal", "role")
166181
// SSO flow for SAML
167182
sc.cmd.MarkFlagsRequiredTogether("is-sso", "sso-role", "sso-region")
168-
sc.cmd.PersistentFlags().Int32VarP(&flags.SamlTimeout, "saml-timeout", "", 120, "Timeout in seconds, before the operation of waiting for a response is cancelled via the chrome driver")
183+
sc.cmd.PersistentFlags().Int32VarP(&flags.SamlTimeout, "saml-timeout", "", 120, "Timeout in seconds, before the operation of waiting for a response is cancelled via CDP (ChromeDeubgProto)")
169184
// Add subcommand to root command
170185
r.Cmd.AddCommand(sc.cmd)
171186
}
@@ -219,5 +234,24 @@ func ConfigFromFlags(fileConfig *credentialexchange.CredentialConfig, rf *RootCm
219234

220235
fileConfig.BaseConfig = baseConf
221236
fileConfig.Duration = d
237+
238+
return configValid(fileConfig)
239+
}
240+
241+
func configValid(config *credentialexchange.CredentialConfig) error {
242+
v := validator.New()
243+
ssoVal := !config.IsSso
244+
if config.IsSso {
245+
ssoVal = len(config.SsoRole) > 0 && len(config.SsoRegion) > 0
246+
}
247+
v.RequiredString(config.ProviderUrl, "provider-url", "provider url must be specified").
248+
// RequiredString(config.BaseConfig.Role, "role", "role must be provided").
249+
RequiredString(config.PrincipalArn, "principal-arn", "principal ARN must be provided").
250+
CustomRule(ssoVal, "is-sso", "sso-role must be specified when is-sso is set").
251+
CustomRule((len(config.BaseConfig.Role) > 1 && len(config.SsoRole) < 1) || (len(config.BaseConfig.Role) < 1 && len(config.SsoRole) > 1), "role", "sso-role cannot be specified when role is also set")
252+
253+
if v.IsFailed() {
254+
return fmt.Errorf("%w %#q", ErrValidationFailed, v.Errors())
255+
}
222256
return nil
223257
}

0 commit comments

Comments
 (0)