17
17
package config
18
18
19
19
import (
20
+ "bytes"
20
21
"fmt"
21
22
"net/http"
22
23
"net/url"
@@ -41,6 +42,10 @@ const (
41
42
// Version app version
42
43
Version = "2.0.1"
43
44
45
+ ////////////////////////////////////////////////////////////
46
+ // FORMATS
47
+ ////////////////////////////////////////////////////////////
48
+
44
49
// AWSCredentialsFormat format const
45
50
AWSCredentialsFormat = "aws-credentials"
46
51
// EnvVarFormat format const
@@ -50,6 +55,12 @@ const (
50
55
// NoopFormat format const
51
56
NoopFormat = "noop"
52
57
58
+ ////////////////////////////////////////////////////////////
59
+ // FLAGS
60
+ // NOTE: if a new Flag value is added be sure to update the
61
+ // OktaYamlConfigProfile struct with that new value.
62
+ ////////////////////////////////////////////////////////////
63
+
53
64
// AllProfilesFlag cli flag const
54
65
AllProfilesFlag = "all-profiles"
55
66
// AuthzIDFlag cli flag const
@@ -103,6 +114,10 @@ const (
103
114
// CacheAccessTokenFlag cli flag const
104
115
CacheAccessTokenFlag = "cache-access-token"
105
116
117
+ ////////////////////////////////////////////////////////////
118
+ // ENV VARS
119
+ ////////////////////////////////////////////////////////////
120
+
106
121
// AllProfilesEnvVar env var const
107
122
AllProfilesEnvVar = "OKTA_AWSCLI_ALL_PROFILES"
108
123
// AuthzIDEnvVar env var const
@@ -162,6 +177,10 @@ const (
162
177
// WriteAWSCredentialsEnvVar env var const
163
178
WriteAWSCredentialsEnvVar = "OKTA_AWSCLI_WRITE_AWS_CREDENTIALS"
164
179
180
+ ////////////////////////////////////////////////////////////
181
+ // Other
182
+ ////////////////////////////////////////////////////////////
183
+
165
184
// CannotBeBlankErrMsg error message const
166
185
CannotBeBlankErrMsg = "cannot be blank"
167
186
// OrgDomainMsg error message const
@@ -176,11 +195,42 @@ const (
176
195
// OktaYamlConfig represents config settings from $HOME/.okta/okta.yaml
177
196
type OktaYamlConfig struct {
178
197
AWSCLI struct {
179
- IDPS map [string ]string `yaml:"idps"`
180
- ROLES map [string ]string `yaml:"roles"`
198
+ IDPS map [string ]string `yaml:"idps"`
199
+ ROLES map [string ]string `yaml:"roles"`
200
+ PROFILES map [string ]OktaYamlConfigProfile `yaml:"profiles"`
181
201
} `yaml:"awscli"`
182
202
}
183
203
204
+ // OktaYamlConfigProfile represents config settings that are indexed by profile name
205
+ type OktaYamlConfigProfile struct {
206
+ AllProfiles string `yaml:"all-profiles"`
207
+ AuthzID string `yaml:"authz-id"`
208
+ AWSAcctFedAppID string `yaml:"aws-acct-fed-app-id"`
209
+ AWSCredentials string `yaml:"aws-credentials"`
210
+ AWSIAMIdP string `yaml:"aws-iam-idp"`
211
+ AWSIAMRole string `yaml:"aws-iam-role"`
212
+ AWSRegion string `yaml:"aws-region"`
213
+ CustomScope string `yaml:"custom-scope"`
214
+ Debug string `yaml:"debug"`
215
+ DebugAPICalls string `yaml:"debug-api-calls"`
216
+ Exec string `yaml:"exec"`
217
+ Format string `yaml:"format"`
218
+ OIDCClientID string `yaml:"oidc-client-id"`
219
+ OpenBrowser string `yaml:"open-browser"`
220
+ OpenBrowserCommand string `yaml:"open-browser-command"`
221
+ OrgDomain string `yaml:"org-domain"`
222
+ PrivateKey string `yaml:"private-key"`
223
+ PrivateKeyFile string `yaml:"private-key-file"`
224
+ KeyID string `yaml:"key-id"`
225
+ Profile string `yaml:"profile"`
226
+ QRCode string `yaml:"qr-code"`
227
+ SessionDuration string `yaml:"session-duration"`
228
+ WriteAWSCredentials string `yaml:"write-aws-credentials"`
229
+ LegacyAWSVariables string `yaml:"legacy-aws-variables"`
230
+ ExpiryAWSVariables string `yaml:"expiry-aws-variables"`
231
+ CacheAccessToken string `yaml:"cache-access-token"`
232
+ }
233
+
184
234
// Clock interface to abstract time operations
185
235
type Clock interface {
186
236
Now () time.Time
@@ -315,34 +365,68 @@ func NewConfig(attrs *Attributes) (*Config, error) {
315
365
return cfg , nil
316
366
}
317
367
368
+ func getFlagNameFromProfile (awsProfile string , flag string ) string {
369
+ profileKey := fmt .Sprintf ("%s.%s" , awsProfile , flag )
370
+ if awsProfile != "" && viper .IsSet (profileKey ) {
371
+ // NOTE: If the flag was from a multiple profiles keyed by aws profile
372
+ // name i.e. `staging.oidc-client-id`, set the base value to that as
373
+ // well, `oidc-client-id`, such that input validation is satisfied.
374
+ v := viper .Get (profileKey )
375
+ viper .Set (flag , v )
376
+
377
+ return profileKey
378
+ }
379
+ return flag
380
+ }
381
+
318
382
func readConfig () (Attributes , error ) {
383
+ // Side loading multiple profiles from okta.yaml file if it exists
384
+ if oktaConfig , err := OktaConfig (); err == nil {
385
+ profiles := oktaConfig .AWSCLI .PROFILES
386
+ viper .SetConfigType ("yaml" )
387
+ yamlData , err := yaml .Marshal (& profiles )
388
+ if err != nil {
389
+ path , _ := OktaConfigPath ()
390
+ fmt .Fprintf (os .Stderr , "WARNING: error reading from %q: %+v.\n \n " , path , err )
391
+ }
392
+ if err == nil {
393
+ r := bytes .NewReader (yamlData )
394
+ err = viper .MergeConfig (r )
395
+ if err != nil {
396
+ fmt .Fprintf (os .Stderr , "WARNING: error with okta.yaml %+v.\n \n " , err )
397
+ }
398
+ }
399
+ }
400
+
401
+ awsProfile := viper .GetString (ProfileFlag )
402
+
319
403
attrs := Attributes {
320
- AllProfiles : viper .GetBool (AllProfilesFlag ),
321
- AuthzID : viper .GetString (AuthzIDFlag ),
322
- AWSCredentials : viper .GetString (AWSCredentialsFlag ),
323
- AWSIAMIdP : viper .GetString (AWSIAMIdPFlag ),
324
- AWSIAMRole : viper .GetString (AWSIAMRoleFlag ),
325
- AWSSessionDuration : viper .GetInt64 ( SessionDurationFlag ),
326
- AWSRegion : viper .GetString ( AWSRegionFlag ),
327
- CustomScope : viper .GetString (CustomScopeFlag ),
328
- Debug : viper .GetBool (DebugFlag ),
329
- DebugAPICalls : viper .GetBool (DebugAPICallsFlag ),
330
- Exec : viper .GetBool (ExecFlag ),
331
- FedAppID : viper .GetString (AWSAcctFedAppIDFlag ),
332
- Format : viper .GetString (FormatFlag ),
333
- LegacyAWSVariables : viper .GetBool (LegacyAWSVariablesFlag ),
334
- ExpiryAWSVariables : viper .GetBool (ExpiryAWSVariablesFlag ),
335
- CacheAccessToken : viper .GetBool (CacheAccessTokenFlag ),
336
- OIDCAppID : viper .GetString (OIDCClientIDFlag ),
337
- OpenBrowser : viper .GetBool (OpenBrowserFlag ),
338
- OpenBrowserCommand : viper .GetString (OpenBrowserCommandFlag ),
339
- OrgDomain : viper .GetString (OrgDomainFlag ),
340
- PrivateKey : viper .GetString (PrivateKeyFlag ),
341
- PrivateKeyFile : viper .GetString (PrivateKeyFileFlag ),
342
- KeyID : viper .GetString (KeyIDFlag ),
343
- Profile : viper .GetString (ProfileFlag ),
344
- QRCode : viper .GetBool (QRCodeFlag ),
345
- WriteAWSCredentials : viper .GetBool (WriteAWSCredentialsFlag ),
404
+ AllProfiles : viper .GetBool (getFlagNameFromProfile ( awsProfile , AllProfilesFlag ) ),
405
+ AuthzID : viper .GetString (getFlagNameFromProfile ( awsProfile , AuthzIDFlag ) ),
406
+ AWSCredentials : viper .GetString (getFlagNameFromProfile ( awsProfile , AWSCredentialsFlag ) ),
407
+ AWSIAMIdP : viper .GetString (getFlagNameFromProfile ( awsProfile , AWSIAMIdPFlag ) ),
408
+ AWSIAMRole : viper .GetString (getFlagNameFromProfile ( awsProfile , AWSIAMRoleFlag ) ),
409
+ AWSRegion : viper .GetString ( getFlagNameFromProfile ( awsProfile , AWSRegionFlag ) ),
410
+ AWSSessionDuration : viper .GetInt64 ( getFlagNameFromProfile ( awsProfile , SessionDurationFlag ) ),
411
+ CustomScope : viper .GetString (getFlagNameFromProfile ( awsProfile , CustomScopeFlag ) ),
412
+ Debug : viper .GetBool (getFlagNameFromProfile ( awsProfile , DebugFlag ) ),
413
+ DebugAPICalls : viper .GetBool (getFlagNameFromProfile ( awsProfile , DebugAPICallsFlag ) ),
414
+ Exec : viper .GetBool (getFlagNameFromProfile ( awsProfile , ExecFlag ) ),
415
+ FedAppID : viper .GetString (getFlagNameFromProfile ( awsProfile , AWSAcctFedAppIDFlag ) ),
416
+ Format : viper .GetString (getFlagNameFromProfile ( awsProfile , FormatFlag ) ),
417
+ LegacyAWSVariables : viper .GetBool (getFlagNameFromProfile ( awsProfile , LegacyAWSVariablesFlag ) ),
418
+ ExpiryAWSVariables : viper .GetBool (getFlagNameFromProfile ( awsProfile , ExpiryAWSVariablesFlag ) ),
419
+ CacheAccessToken : viper .GetBool (getFlagNameFromProfile ( awsProfile , CacheAccessTokenFlag ) ),
420
+ OIDCAppID : viper .GetString (getFlagNameFromProfile ( awsProfile , OIDCClientIDFlag ) ),
421
+ OpenBrowser : viper .GetBool (getFlagNameFromProfile ( awsProfile , OpenBrowserFlag ) ),
422
+ OpenBrowserCommand : viper .GetString (getFlagNameFromProfile ( awsProfile , OpenBrowserCommandFlag ) ),
423
+ OrgDomain : viper .GetString (getFlagNameFromProfile ( awsProfile , OrgDomainFlag ) ),
424
+ PrivateKey : viper .GetString (getFlagNameFromProfile ( awsProfile , PrivateKeyFlag ) ),
425
+ PrivateKeyFile : viper .GetString (getFlagNameFromProfile ( awsProfile , PrivateKeyFileFlag ) ),
426
+ KeyID : viper .GetString (getFlagNameFromProfile ( awsProfile , KeyIDFlag ) ),
427
+ Profile : viper .GetString (getFlagNameFromProfile ( awsProfile , ProfileFlag ) ),
428
+ QRCode : viper .GetBool (getFlagNameFromProfile ( awsProfile , QRCodeFlag ) ),
429
+ WriteAWSCredentials : viper .GetBool (getFlagNameFromProfile ( awsProfile , WriteAWSCredentialsFlag ) ),
346
430
}
347
431
if attrs .Format == "" {
348
432
attrs .Format = EnvVarFormat
@@ -798,14 +882,25 @@ func (c *Config) SetQRCode(qrCode bool) error {
798
882
return nil
799
883
}
800
884
801
- // OktaConfig returns an Okta YAML Config object representation of $HOME/.okta/okta.yaml
802
- func (c * Config ) OktaConfig () (config * OktaYamlConfig , err error ) {
803
- homeDir , err := os .UserHomeDir ()
885
+ // OktaConfigPath returns OS specific path to the okta config file, for example
886
+ // $HOME/.okta/okta.yaml
887
+ func OktaConfigPath () (path string , err error ) {
888
+ var homeDir string
889
+ homeDir , err = os .UserHomeDir ()
804
890
if err != nil {
805
891
return
806
892
}
807
893
808
- configPath := filepath .Join (homeDir , DotOkta , OktaYaml )
894
+ path = filepath .Join (homeDir , DotOkta , OktaYaml )
895
+ return
896
+ }
897
+
898
+ // OktaConfig returns an Okta YAML Config object representation of $HOME/.okta/okta.yaml
899
+ func OktaConfig () (config * OktaYamlConfig , err error ) {
900
+ configPath , err := OktaConfigPath ()
901
+ if err != nil {
902
+ return
903
+ }
809
904
yamlConfig , err := os .ReadFile (configPath )
810
905
if err != nil {
811
906
return
@@ -943,6 +1038,28 @@ awscli:
943
1038
944
1039
fmt .Fprintf (os .Stderr , "okta.yaml \" awscli.roles\" section is a map of %d ARN string keys to friendly string label values\n " , len (_roles ))
945
1040
1041
+ profiles , ok := _awscli ["profiles" ]
1042
+ if ! ok {
1043
+ fmt .Fprintf (os .Stderr , "WARNING: okta.yaml missing \" awscli.profiles\" section\n " )
1044
+ return
1045
+ }
1046
+ if profiles == nil {
1047
+ fmt .Fprintf (os .Stderr , "WARNING: okta.yaml \" awscli.profiles\" section has no values\n " )
1048
+ return
1049
+ }
1050
+
1051
+ _profiles , ok := profiles .(map [any ]any )
1052
+ if ! ok {
1053
+ fmt .Fprintf (os .Stderr , "WARNING: okta.yaml \" awscli.profiles\" section is not a map of separate config settings keyed by profile name\n " )
1054
+ return
1055
+ }
1056
+ if len (_profiles ) == 0 {
1057
+ fmt .Fprintf (os .Stderr , "WARNING: okta.yaml \" awscli.profiles\" section is an empty map of separate config settings keyed by profile name\n " )
1058
+ return
1059
+ }
1060
+
1061
+ fmt .Fprintf (os .Stderr , "okta.yaml \" awscli.profiles\" section is a map of %d separate config settings keyed by profile name\n " , len (_profiles ))
1062
+
946
1063
fmt .Fprintf (os .Stderr , "okta.yaml is OK\n " )
947
1064
return nil
948
1065
}
0 commit comments