-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Add sso session and token provider support #4885
Changes from all commits
8855191
3bf12a6
9e666ea
e3644bf
22968a0
6354a34
3e9df76
df54980
14a703c
6f16394
d0894cf
d5e1072
45be7cf
7b47ebb
e35aaee
3154ff0
3c021ea
359ee1e
19a523b
da6f599
8cf9168
8affb62
2ffc7e2
6e4eb05
b2ca51c
9b2a2a1
7564841
1558f1a
89c821a
1077388
b646a31
29a8523
3216ed5
c479d68
1f7d087
62eb32d
bce0677
3003a27
6e4da8f
9cabea6
ba18cd2
d85ea03
3ab0945
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package bearer | ||
|
||
import ( | ||
"github.com/aws/aws-sdk-go/aws" | ||
"time" | ||
) | ||
|
||
// Token provides a type wrapping a bearer token and expiration metadata. | ||
type Token struct { | ||
Value string | ||
|
||
CanExpire bool | ||
Expires time.Time | ||
} | ||
|
||
// Expired returns if the token's Expires time is before or equal to the time | ||
// provided. If CanExpire is false, Expired will always return false. | ||
func (t Token) Expired(now time.Time) bool { | ||
if !t.CanExpire { | ||
return false | ||
} | ||
now = now.Round(0) | ||
return now.Equal(t.Expires) || now.After(t.Expires) | ||
} | ||
|
||
// TokenProvider provides interface for retrieving bearer tokens. | ||
type TokenProvider interface { | ||
RetrieveBearerToken(aws.Context) (Token, error) | ||
} | ||
|
||
// TokenProviderFunc provides a helper utility to wrap a function as a type | ||
// that implements the TokenProvider interface. | ||
type TokenProviderFunc func(aws.Context) (Token, error) | ||
|
||
// RetrieveBearerToken calls the wrapped function, returning the Token or | ||
// error. | ||
func (fn TokenProviderFunc) RetrieveBearerToken(ctx aws.Context) (Token, error) { | ||
return fn(ctx) | ||
} | ||
|
||
// StaticTokenProvider provides a utility for wrapping a static bearer token | ||
// value within an implementation of a token provider. | ||
type StaticTokenProvider struct { | ||
Token Token | ||
} | ||
|
||
// RetrieveBearerToken returns the static token specified. | ||
func (s StaticTokenProvider) RetrieveBearerToken(aws.Context) (Token, error) { | ||
return s.Token, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,11 +5,13 @@ package ssocreds | |
|
||
import ( | ||
"fmt" | ||
"path/filepath" | ||
"reflect" | ||
"testing" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/auth/bearer" | ||
"github.com/aws/aws-sdk-go/aws/credentials" | ||
"github.com/aws/aws-sdk-go/aws/request" | ||
"github.com/aws/aws-sdk-go/service/sso" | ||
|
@@ -24,14 +26,25 @@ type mockClient struct { | |
Output *sso.GetRoleCredentialsOutput | ||
Err error | ||
|
||
ExpectedAccountID string | ||
ExpectedAccessToken string | ||
ExpectedRoleName string | ||
ExpectedClientRegion string | ||
ExpectedAccountID string | ||
ExpectedAccessToken string | ||
ExpectedRoleName string | ||
|
||
Response func(mockClient) (*sso.GetRoleCredentialsOutput, error) | ||
} | ||
|
||
type mockTokenProvider struct { | ||
Response func() (bearer.Token, error) | ||
} | ||
|
||
func (p *mockTokenProvider) RetrieveBearerToken(ctx aws.Context) (bearer.Token, error) { | ||
if p.Response == nil { | ||
return bearer.Token{}, nil | ||
} | ||
|
||
return p.Response() | ||
} | ||
|
||
func (m mockClient) GetRoleCredentialsWithContext(ctx aws.Context, params *sso.GetRoleCredentialsInput, _ ...request.Option) (*sso.GetRoleCredentialsOutput, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i know this is not part of your changeset. so this is just a comment for the future. but i dont think we should be including the Go testing framework object in the mocked client. when validating the errors in the mocked client, it shouldnt directly invoke the testing object framework, but we should actually mock the returned errors, and then catch those in the test case execution |
||
m.t.Helper() | ||
|
||
|
@@ -88,11 +101,12 @@ func TestProvider(t *testing.T) { | |
defer restoreTime() | ||
|
||
cases := map[string]struct { | ||
Client mockClient | ||
AccountID string | ||
Region string | ||
RoleName string | ||
StartURL string | ||
Client mockClient | ||
AccountID string | ||
RoleName string | ||
StartURL string | ||
CachedTokenFilePath string | ||
TokenProvider *mockTokenProvider | ||
|
||
ExpectedErr bool | ||
ExpectedCredentials credentials.Value | ||
|
@@ -104,10 +118,9 @@ func TestProvider(t *testing.T) { | |
}, | ||
"valid required parameter values": { | ||
Client: mockClient{ | ||
ExpectedAccountID: "012345678901", | ||
ExpectedRoleName: "TestRole", | ||
ExpectedClientRegion: "us-west-2", | ||
ExpectedAccessToken: "dGhpcyBpcyBub3QgYSByZWFsIHZhbHVl", | ||
ExpectedAccountID: "012345678901", | ||
ExpectedRoleName: "TestRole", | ||
ExpectedAccessToken: "dGhpcyBpcyBub3QgYSByZWFsIHZhbHVl", | ||
Response: func(mock mockClient) (*sso.GetRoleCredentialsOutput, error) { | ||
return &sso.GetRoleCredentialsOutput{ | ||
RoleCredentials: &sso.RoleCredentials{ | ||
|
@@ -120,7 +133,6 @@ func TestProvider(t *testing.T) { | |
}, | ||
}, | ||
AccountID: "012345678901", | ||
Region: "us-west-2", | ||
RoleName: "TestRole", | ||
StartURL: "https://valid-required-only", | ||
ExpectedCredentials: credentials.Value{ | ||
|
@@ -131,22 +143,89 @@ func TestProvider(t *testing.T) { | |
}, | ||
ExpectedExpire: time.Date(2021, 01, 20, 21, 22, 23, 0.123e9, time.UTC), | ||
}, | ||
"custom cached token file": { | ||
isaiahvita marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Client: mockClient{ | ||
ExpectedAccountID: "012345678901", | ||
ExpectedRoleName: "TestRole", | ||
ExpectedAccessToken: "ZhbHVldGhpcyBpcyBub3QgYSByZWFsIH", | ||
Response: func(mock mockClient) (*sso.GetRoleCredentialsOutput, error) { | ||
return &sso.GetRoleCredentialsOutput{ | ||
RoleCredentials: &sso.RoleCredentials{ | ||
AccessKeyId: aws.String("AccessKey"), | ||
SecretAccessKey: aws.String("SecretKey"), | ||
SessionToken: aws.String("SessionToken"), | ||
Expiration: aws.Int64(1611177743123), | ||
}, | ||
}, nil | ||
}, | ||
}, | ||
CachedTokenFilePath: filepath.Join("testdata", "custom_cached_token.json"), | ||
AccountID: "012345678901", | ||
RoleName: "TestRole", | ||
ExpectedCredentials: credentials.Value{ | ||
AccessKeyID: "AccessKey", | ||
SecretAccessKey: "SecretKey", | ||
SessionToken: "SessionToken", | ||
ProviderName: ProviderName, | ||
}, | ||
ExpectedExpire: time.Date(2021, 01, 20, 21, 22, 23, 0.123e9, time.UTC), | ||
}, | ||
"access token retrieved by token provider": { | ||
Client: mockClient{ | ||
ExpectedAccountID: "012345678901", | ||
ExpectedRoleName: "TestRole", | ||
ExpectedAccessToken: "WFsIHZhbHVldGhpcyBpcyBub3QgYSByZ", | ||
Response: func(mock mockClient) (*sso.GetRoleCredentialsOutput, error) { | ||
return &sso.GetRoleCredentialsOutput{ | ||
RoleCredentials: &sso.RoleCredentials{ | ||
AccessKeyId: aws.String("AccessKey"), | ||
SecretAccessKey: aws.String("SecretKey"), | ||
SessionToken: aws.String("SessionToken"), | ||
Expiration: aws.Int64(1611177743123), | ||
}, | ||
}, nil | ||
}, | ||
}, | ||
TokenProvider: &mockTokenProvider{ | ||
Response: func() (bearer.Token, error) { | ||
return bearer.Token{ | ||
Value: "WFsIHZhbHVldGhpcyBpcyBub3QgYSByZ", | ||
}, nil | ||
}, | ||
}, | ||
AccountID: "012345678901", | ||
RoleName: "TestRole", | ||
StartURL: "ignored value", | ||
ExpectedCredentials: credentials.Value{ | ||
AccessKeyID: "AccessKey", | ||
SecretAccessKey: "SecretKey", | ||
SessionToken: "SessionToken", | ||
ProviderName: ProviderName, | ||
}, | ||
ExpectedExpire: time.Date(2021, 01, 20, 21, 22, 23, 0.123e9, time.UTC), | ||
}, | ||
"token provider return error": { | ||
TokenProvider: &mockTokenProvider{ | ||
Response: func() (bearer.Token, error) { | ||
return bearer.Token{}, fmt.Errorf("mock token provider return error") | ||
}, | ||
}, | ||
ExpectedErr: true, | ||
}, | ||
"expired access token": { | ||
StartURL: "https://expired", | ||
ExpectedErr: true, | ||
}, | ||
"api error": { | ||
Client: mockClient{ | ||
ExpectedAccountID: "012345678901", | ||
ExpectedRoleName: "TestRole", | ||
ExpectedClientRegion: "us-west-2", | ||
ExpectedAccessToken: "dGhpcyBpcyBub3QgYSByZWFsIHZhbHVl", | ||
ExpectedAccountID: "012345678901", | ||
ExpectedRoleName: "TestRole", | ||
ExpectedAccessToken: "dGhpcyBpcyBub3QgYSByZWFsIHZhbHVl", | ||
Response: func(mock mockClient) (*sso.GetRoleCredentialsOutput, error) { | ||
return nil, fmt.Errorf("api error") | ||
}, | ||
}, | ||
AccountID: "012345678901", | ||
Region: "us-west-2", | ||
RoleName: "TestRole", | ||
StartURL: "https://valid-required-only", | ||
ExpectedErr: true, | ||
|
@@ -158,10 +237,14 @@ func TestProvider(t *testing.T) { | |
tt.Client.t = t | ||
|
||
provider := &Provider{ | ||
Client: tt.Client, | ||
AccountID: tt.AccountID, | ||
RoleName: tt.RoleName, | ||
StartURL: tt.StartURL, | ||
Client: tt.Client, | ||
AccountID: tt.AccountID, | ||
RoleName: tt.RoleName, | ||
StartURL: tt.StartURL, | ||
CachedTokenFilepath: tt.CachedTokenFilePath, | ||
} | ||
if tt.TokenProvider != nil { | ||
provider.TokenProvider = tt.TokenProvider | ||
} | ||
|
||
provider.Expiry.CurrentTime = nowTime | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: did your IDE not complain about no comment/documentation here? this is a public type, so it should have some documentation here.