Skip to content
This repository was archived by the owner on Jan 3, 2024. It is now read-only.

Commit a4fb309

Browse files
committed
Use SharedCredentialsProvider instead of StaticProvider for shared credentials
Previously, we were using the StaticProvider for all credentials resolved while reading shared config files. There is logic in the AfterRetryHandler that marks a credential object as expired whenever an expired token exception occurs. When that happens, the ideal workflow is for the provider to trigger a credentials refresh by re-reading the profile file, but this is not possible with the StaticProvider. This commit addresses that problem by using the SharedCredentialsProvider instead of the StaticProvider when resolving such credentials. With this change, the SDK should automatically refreshes the credentials whenever we get a expired token exception from AWS. This should also implicitly address the feature requested in aws#1993, but instead of refreshing explicitly, the trigger here would be an exception from AWS.
1 parent d93723b commit a4fb309

File tree

6 files changed

+44
-24
lines changed

6 files changed

+44
-24
lines changed

CHANGELOG_PENDING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
### SDK Enhancements
44

55
### SDK Bugs
6+
* `aws/session`: SDK should now trigger a credential refresh for shared config files if we receive an expired token exception.
7+
* This change updates the SDK to use a `SharedCredentialProvider` instead of a `StaticProvider` so that the credentials file will be loaded again whenever we mark the credential as expired. If you're using the default `AfterRetryHandler`, credentials should be reloaded automatically whenever we receive an `ExpiredToken`, `ExpiredTokenException`, or `RequestExpired` error.

aws/session/credentials.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package session
33
import (
44
"fmt"
55
"os"
6+
"regexp"
67
"time"
78

89
"github.com/aws/aws-sdk-go/aws"
@@ -18,6 +19,12 @@ import (
1819
"github.com/aws/aws-sdk-go/service/sts"
1920
)
2021

22+
// sharedCfgCredProviderNameRE is a regex that describes the ProviderName of
23+
// credentials retrieved via sharedConfig. This will be used to extract the
24+
// resolved filename and section fields that indicate where the credentials
25+
// came from.
26+
var sharedCfgCredProviderNameRE = regexp.MustCompile("SharedConfigCredentials: filename=(.*?),section=(.*?)$")
27+
2128
// CredentialsProviderOptions specifies additional options for configuring
2229
// credentials providers.
2330
type CredentialsProviderOptions struct {
@@ -113,10 +120,19 @@ func resolveCredsFromProfile(cfg *aws.Config,
113120
)
114121

115122
case sharedCfg.Creds.HasKeys():
116-
// Static Credentials from Shared Config/Credentials file.
117-
creds = credentials.NewStaticCredentialsFromCreds(
118-
sharedCfg.Creds,
119-
)
123+
// Credentials from Shared Config/Credentials file. We will use a
124+
// SharedCredentialsProvider that has the ability to re-read the file
125+
// if the credential was marked as expired.
126+
matches := sharedCfgCredProviderNameRE.FindStringSubmatch(sharedCfg.Creds.ProviderName)
127+
if len(matches) == 3 {
128+
filename, profile := matches[1], matches[2]
129+
creds = credentials.NewSharedCredentials(filename, profile)
130+
} else {
131+
// sharedCfg.Creds must be populated via SharedConfigCredentials,
132+
// so this case is not possible. Use a static credential regardless
133+
// to maintain the existing behavior.
134+
creds = credentials.NewStaticCredentialsFromCreds(sharedCfg.Creds)
135+
}
120136

121137
case len(sharedCfg.CredentialSource) != 0:
122138
creds, err = resolveCredsFromSource(cfg, envCfg,

aws/session/credentials_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ func TestSessionAssumeRole_DisableSharedConfig(t *testing.T) {
633633
if e, a := "assume_role_w_creds_secret", creds.SecretAccessKey; e != a {
634634
t.Errorf("expect %v, got %v", e, a)
635635
}
636-
if e, a := "SharedConfigCredentials", creds.ProviderName; !strings.Contains(a, e) {
636+
if e, a := "SharedCredentialsProvider", creds.ProviderName; !strings.Contains(a, e) {
637637
t.Errorf("expect %v, to be in %v", e, a)
638638
}
639639
}

aws/session/session_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ func TestNewSessionWithOptions_OverrideProfile(t *testing.T) {
238238
if v := creds.SessionToken; len(v) != 0 {
239239
t.Errorf("expect empty, got %v", v)
240240
}
241-
if e, a := "SharedConfigCredentials", creds.ProviderName; !strings.Contains(a, e) {
241+
if e, a := "SharedCredentialsProvider", creds.ProviderName; !strings.Contains(a, e) {
242242
t.Errorf("expect %v, to be in %v", e, a)
243243
}
244244
}
@@ -275,7 +275,7 @@ func TestNewSessionWithOptions_OverrideSharedConfigEnable(t *testing.T) {
275275
if v := creds.SessionToken; len(v) != 0 {
276276
t.Errorf("expect empty, got %v", v)
277277
}
278-
if e, a := "SharedConfigCredentials", creds.ProviderName; !strings.Contains(a, e) {
278+
if e, a := "SharedCredentialsProvider", creds.ProviderName; !strings.Contains(a, e) {
279279
t.Errorf("expect %v, to be in %v", e, a)
280280
}
281281
}
@@ -312,7 +312,7 @@ func TestNewSessionWithOptions_OverrideSharedConfigDisable(t *testing.T) {
312312
if v := creds.SessionToken; len(v) != 0 {
313313
t.Errorf("expect empty, got %v", v)
314314
}
315-
if e, a := "SharedConfigCredentials", creds.ProviderName; !strings.Contains(a, e) {
315+
if e, a := "SharedCredentialsProvider", creds.ProviderName; !strings.Contains(a, e) {
316316
t.Errorf("expect %v, to be in %v", e, a)
317317
}
318318
}
@@ -349,7 +349,7 @@ func TestNewSessionWithOptions_OverrideSharedConfigFiles(t *testing.T) {
349349
if v := creds.SessionToken; len(v) != 0 {
350350
t.Errorf("expect empty, got %v", v)
351351
}
352-
if e, a := "SharedConfigCredentials", creds.ProviderName; !strings.Contains(a, e) {
352+
if e, a := "SharedCredentialsProvider", creds.ProviderName; !strings.Contains(a, e) {
353353
t.Errorf("expect %v, to be in %v", e, a)
354354
}
355355
}
@@ -372,7 +372,7 @@ func TestNewSessionWithOptions_Overrides(t *testing.T) {
372372
OutCreds: credentials.Value{
373373
AccessKeyID: "full_profile_akid",
374374
SecretAccessKey: "full_profile_secret",
375-
ProviderName: "SharedConfigCredentials",
375+
ProviderName: "SharedCredentialsProvider",
376376
},
377377
},
378378
"env creds with env profile": {
@@ -405,7 +405,7 @@ func TestNewSessionWithOptions_Overrides(t *testing.T) {
405405
OutCreds: credentials.Value{
406406
AccessKeyID: "full_profile_akid",
407407
SecretAccessKey: "full_profile_secret",
408-
ProviderName: "SharedConfigCredentials",
408+
ProviderName: "SharedCredentialsProvider",
409409
},
410410
},
411411
"cfg and cred file with opt profile": {
@@ -420,7 +420,7 @@ func TestNewSessionWithOptions_Overrides(t *testing.T) {
420420
OutCreds: credentials.Value{
421421
AccessKeyID: "shared_config_akid",
422422
SecretAccessKey: "shared_config_secret",
423-
ProviderName: "SharedConfigCredentials",
423+
ProviderName: "SharedCredentialsProvider",
424424
},
425425
},
426426
}

aws/session/shared_config.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,12 @@ func (cfg *sharedConfig) setFromIniFiles(profiles map[string]struct{}, profile s
378378
// example if a config file only includes aws_access_key_id but no
379379
// aws_secret_access_key the aws_access_key_id will be ignored.
380380
func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile, exOpts bool) error {
381-
section, ok := file.IniData.GetSection(profile)
381+
sectionName := profile
382+
section, ok := file.IniData.GetSection(sectionName)
382383
if !ok {
383384
// Fallback to to alternate profile name: profile <name>
384-
section, ok = file.IniData.GetSection(fmt.Sprintf("profile %s", profile))
385+
sectionName = fmt.Sprintf("profile %s", profile)
386+
section, ok = file.IniData.GetSection(sectionName)
385387
if !ok {
386388
return SharedConfigProfileNotExistsError{Profile: profile, Err: nil}
387389
}
@@ -458,7 +460,7 @@ func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile, e
458460
AccessKeyID: section.String(accessKeyIDKey),
459461
SecretAccessKey: section.String(secretAccessKey),
460462
SessionToken: section.String(sessionTokenKey),
461-
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", file.Filename),
463+
ProviderName: fmt.Sprintf("SharedConfigCredentials: filename=%s,section=%s", file.Filename, sectionName),
462464
}
463465
if creds.HasKeys() {
464466
cfg.Creds = creds

aws/session/shared_config_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func TestLoadSharedConfig(t *testing.T) {
5252
Creds: credentials.Value{
5353
AccessKeyID: "shared_config_akid",
5454
SecretAccessKey: "shared_config_secret",
55-
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
55+
ProviderName: fmt.Sprintf("SharedConfigCredentials: filename=%s,section=config_file_load_order", testConfigFilename),
5656
},
5757
},
5858
},
@@ -65,7 +65,7 @@ func TestLoadSharedConfig(t *testing.T) {
6565
Creds: credentials.Value{
6666
AccessKeyID: "shared_config_other_akid",
6767
SecretAccessKey: "shared_config_other_secret",
68-
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigOtherFilename),
68+
ProviderName: fmt.Sprintf("SharedConfigCredentials: filename=%s,section=config_file_load_order", testConfigOtherFilename),
6969
},
7070
},
7171
},
@@ -81,7 +81,7 @@ func TestLoadSharedConfig(t *testing.T) {
8181
Creds: credentials.Value{
8282
AccessKeyID: "complete_creds_akid",
8383
SecretAccessKey: "complete_creds_secret",
84-
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
84+
ProviderName: fmt.Sprintf("SharedConfigCredentials: filename=%s,section=complete_creds", testConfigFilename),
8585
},
8686
},
8787
},
@@ -113,7 +113,7 @@ func TestLoadSharedConfig(t *testing.T) {
113113
Creds: credentials.Value{
114114
AccessKeyID: "assume_role_w_creds_akid",
115115
SecretAccessKey: "assume_role_w_creds_secret",
116-
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
116+
ProviderName: fmt.Sprintf("SharedConfigCredentials: filename=%s,section=assume_role_w_creds", testConfigFilename),
117117
},
118118
},
119119
},
@@ -161,7 +161,7 @@ func TestLoadSharedConfig(t *testing.T) {
161161
Creds: credentials.Value{
162162
AccessKeyID: "complete_creds_akid",
163163
SecretAccessKey: "complete_creds_secret",
164-
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
164+
ProviderName: fmt.Sprintf("SharedConfigCredentials: filename=%s,section=complete_creds", testConfigFilename),
165165
},
166166
},
167167
},
@@ -252,7 +252,7 @@ func TestLoadSharedConfig(t *testing.T) {
252252
AccessKeyID: "sso_and_static_akid",
253253
SecretAccessKey: "sso_and_static_secret",
254254
SessionToken: "sso_and_static_token",
255-
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
255+
ProviderName: fmt.Sprintf("SharedConfigCredentials: filename=%s,section=profile sso_and_static", testConfigFilename),
256256
},
257257
SSOAccountID: "012345678901",
258258
SSORegion: "us-west-2",
@@ -496,7 +496,7 @@ func TestLoadSharedConfigFromFile(t *testing.T) {
496496
Creds: credentials.Value{
497497
AccessKeyID: "complete_creds_akid",
498498
SecretAccessKey: "complete_creds_secret",
499-
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
499+
ProviderName: fmt.Sprintf("SharedConfigCredentials: filename=%s,section=complete_creds", testConfigFilename),
500500
},
501501
},
502502
},
@@ -507,7 +507,7 @@ func TestLoadSharedConfigFromFile(t *testing.T) {
507507
AccessKeyID: "complete_creds_with_token_akid",
508508
SecretAccessKey: "complete_creds_with_token_secret",
509509
SessionToken: "complete_creds_with_token_token",
510-
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
510+
ProviderName: fmt.Sprintf("SharedConfigCredentials: filename=%s,section=complete_creds_with_token", testConfigFilename),
511511
},
512512
},
513513
},
@@ -517,7 +517,7 @@ func TestLoadSharedConfigFromFile(t *testing.T) {
517517
Creds: credentials.Value{
518518
AccessKeyID: "full_profile_akid",
519519
SecretAccessKey: "full_profile_secret",
520-
ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
520+
ProviderName: fmt.Sprintf("SharedConfigCredentials: filename=%s,section=full_profile", testConfigFilename),
521521
},
522522
Region: "full_profile_region",
523523
},

0 commit comments

Comments
 (0)