refactor upstream refresh test helpers to be more specific to IDP type

This commit is contained in:
Ryan Richard
2024-05-23 13:35:31 -07:00
parent 02ffff01d5
commit f323690049
10 changed files with 295 additions and 198 deletions
@@ -344,7 +344,7 @@ func (c *activeDirectoryWatcherController) validateUpstream(ctx context.Context,
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){
"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID"),
},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
pwdLastSetAttribute: attributeUnchangedSinceLogin(pwdLastSetAttribute),
userAccountControlAttribute: validUserAccountControl,
userAccountControlComputedAttribute: validComputedUserAccountControl,
@@ -445,7 +445,7 @@ func getDomainFromDistinguishedName(distinguishedName string) (string, error) {
}
//nolint:gochecknoglobals // this needs to be a global variable so that tests can check pointer equality
var validUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider.RefreshAttributes) error {
var validUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider.LDAPRefreshAttributes) error {
userAccountControl, err := strconv.Atoi(entry.GetAttributeValue(userAccountControlAttribute))
if err != nil {
return err
@@ -459,7 +459,7 @@ var validUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider.Refresh
}
//nolint:gochecknoglobals // this needs to be a global variable so that tests can check pointer equality
var validComputedUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider.RefreshAttributes) error {
var validComputedUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider.LDAPRefreshAttributes) error {
userAccountControl, err := strconv.Atoi(entry.GetAttributeValue(userAccountControlComputedAttribute))
if err != nil {
return err
@@ -473,8 +473,8 @@ var validComputedUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider
}
//nolint:gochecknoglobals // this needs to be a global variable so that tests can check pointer equality
var attributeUnchangedSinceLogin = func(attribute string) func(*ldap.Entry, upstreamprovider.RefreshAttributes) error {
return func(entry *ldap.Entry, storedAttributes upstreamprovider.RefreshAttributes) error {
var attributeUnchangedSinceLogin = func(attribute string) func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error {
return func(entry *ldap.Entry, storedAttributes upstreamprovider.LDAPRefreshAttributes) error {
prevAttributeValue := storedAttributes.AdditionalAttributes[attribute]
newValues := entry.GetRawAttributeValues(attribute)
@@ -228,7 +228,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -571,7 +571,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -641,7 +641,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: "sAMAccountName",
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -714,7 +714,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -794,7 +794,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -858,7 +858,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -1009,7 +1009,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -1159,7 +1159,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -1231,7 +1231,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -1498,7 +1498,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
GroupAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"sAMAccountName": groupSAMAccountNameWithDomainSuffix},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -1558,7 +1558,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -1622,7 +1622,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -1686,7 +1686,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -1898,7 +1898,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
GroupNameAttribute: testGroupSearchNameAttrName,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -1961,7 +1961,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
SkipGroupRefresh: true,
},
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
"userAccountControl": validUserAccountControl,
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
@@ -2102,8 +2102,8 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
expectedRefreshAttributeChecks := copyOfExpectedValueForResultingCache.RefreshAttributeChecks
actualRefreshAttributeChecks := actualConfig.RefreshAttributeChecks
copyOfExpectedValueForResultingCache.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{}
actualConfig.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{}
copyOfExpectedValueForResultingCache.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{}
actualConfig.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{}
require.Equal(t, len(expectedRefreshAttributeChecks), len(actualRefreshAttributeChecks))
for k, v := range expectedRefreshAttributeChecks {
require.NotNil(t, actualRefreshAttributeChecks[k])
@@ -2352,7 +2352,7 @@ func TestValidUserAccountControl(t *testing.T) {
for _, test := range tests {
tt := test
t.Run(tt.name, func(t *testing.T) {
err := validUserAccountControl(tt.entry, upstreamprovider.RefreshAttributes{})
err := validUserAccountControl(tt.entry, upstreamprovider.LDAPRefreshAttributes{})
if tt.wantErr != "" {
require.Error(t, err)
@@ -2413,7 +2413,7 @@ func TestValidComputedUserAccountControl(t *testing.T) {
for _, test := range tests {
tt := test
t.Run(tt.name, func(t *testing.T) {
err := validComputedUserAccountControl(tt.entry, upstreamprovider.RefreshAttributes{})
err := validComputedUserAccountControl(tt.entry, upstreamprovider.LDAPRefreshAttributes{})
if tt.wantErr != "" {
require.Error(t, err)
@@ -2488,7 +2488,7 @@ func TestAttributeUnchangedSinceLogin(t *testing.T) {
tt := test
t.Run(tt.name, func(t *testing.T) {
initialValRawEncoded := base64.RawURLEncoding.EncodeToString([]byte(initialVal))
err := attributeUnchangedSinceLogin(attributeName)(tt.entry, upstreamprovider.RefreshAttributes{AdditionalAttributes: map[string]string{attributeName: initialValRawEncoded}})
err := attributeUnchangedSinceLogin(attributeName)(tt.entry, upstreamprovider.LDAPRefreshAttributes{AdditionalAttributes: map[string]string{attributeName: initialValRawEncoded}})
if tt.wantErr != "" {
require.Error(t, err)
require.Equal(t, tt.wantErr, err.Error())
@@ -269,30 +269,43 @@ var (
}
)
type expectedUpstreamRefresh struct {
type expectedOIDCUpstreamRefresh struct {
performedByUpstreamName string
args *oidctestutil.PerformRefreshArgs
args *oidctestutil.PerformOIDCRefreshArgs
}
type expectedUpstreamValidateTokens struct {
type expectedLDAPUpstreamRefresh struct {
performedByUpstreamName string
args *oidctestutil.PerformLDAPRefreshArgs
}
type expectedGithubUpstreamRefresh struct {
performedByUpstreamName string
args *oidctestutil.GetUserArgs
}
type expectedOIDCUpstreamValidateTokens struct {
performedByUpstreamName string
args *oidctestutil.ValidateTokenAndMergeWithUserInfoArgs
}
type tokenEndpointResponseExpectedValues struct {
wantStatus int
wantSuccessBodyFields []string
wantErrorResponseBody string
wantClientID string
wantRequestedScopes []string
wantGrantedScopes []string
wantUsername string
wantGroups []string
wantUpstreamRefreshCall *expectedUpstreamRefresh
wantUpstreamOIDCValidateTokenCall *expectedUpstreamValidateTokens
wantCustomSessionDataStored *psession.CustomSessionData
wantWarnings []RecordedWarning
wantAdditionalClaims map[string]interface{}
wantStatus int
wantSuccessBodyFields []string
wantErrorResponseBody string
wantClientID string
wantRequestedScopes []string
wantGrantedScopes []string
wantUsername string
wantGroups []string
wantOIDCUpstreamRefreshCall *expectedOIDCUpstreamRefresh
wantLDAPUpstreamRefreshCall *expectedLDAPUpstreamRefresh
wantActiveDirectoryUpstreamRefreshCall *expectedLDAPUpstreamRefresh
wantGithubUpstreamRefreshCall *expectedGithubUpstreamRefresh
wantUpstreamOIDCValidateTokenCall *expectedOIDCUpstreamValidateTokens
wantCustomSessionDataStored *psession.CustomSessionData
wantWarnings []RecordedWarning
wantAdditionalClaims map[string]interface{}
// The expected lifetime of the ID tokens issued by authcode exchange and refresh, but not token exchange.
// When zero, will assume that the test wants the default value for ID token lifetime.
wantIDTokenLifetimeSeconds int
@@ -1925,48 +1938,63 @@ func TestRefreshGrant(t *testing.T) {
return sessionData
}
happyOIDCUpstreamRefreshCall := func() *expectedUpstreamRefresh {
return &expectedUpstreamRefresh{
happyOIDCUpstreamRefreshCall := func() *expectedOIDCUpstreamRefresh {
return &expectedOIDCUpstreamRefresh{
performedByUpstreamName: oidcUpstreamName,
args: &oidctestutil.PerformRefreshArgs{
args: &oidctestutil.PerformOIDCRefreshArgs{
Ctx: nil, // this will be filled in with the actual request context by the test below
RefreshToken: oidcUpstreamInitialRefreshToken,
},
}
}
happyGitHubUpstreamRefreshCall := func() *expectedUpstreamRefresh {
return &expectedUpstreamRefresh{
happyGitHubUpstreamRefreshCall := func() *expectedGithubUpstreamRefresh {
return &expectedGithubUpstreamRefresh{
performedByUpstreamName: githubUpstreamName,
args: &oidctestutil.GetUserArgs{
Ctx: nil, // this will be filled in with the actual request context by the test below
AccessToken: githubUpstreamAccessToken,
IDPDisplayName: githubUpstreamName,
},
}
}
happyLDAPUpstreamRefreshCall := func() *expectedUpstreamRefresh {
return &expectedUpstreamRefresh{
happyLDAPUpstreamRefreshCall := func() *expectedLDAPUpstreamRefresh {
return &expectedLDAPUpstreamRefresh{
performedByUpstreamName: ldapUpstreamName,
args: &oidctestutil.PerformRefreshArgs{
Ctx: nil,
DN: ldapUpstreamDN,
ExpectedSubject: goodSubject,
ExpectedUsername: goodUsername,
args: &oidctestutil.PerformLDAPRefreshArgs{
Ctx: nil, // this will be filled in with the actual request context by the test below
StoredRefreshAttributes: upstreamprovider.LDAPRefreshAttributes{
Username: goodUsername,
Subject: goodSubject,
DN: ldapUpstreamDN,
Groups: goodGroups,
AdditionalAttributes: nil,
},
IDPDisplayName: ldapUpstreamName,
},
}
}
happyActiveDirectoryUpstreamRefreshCall := func() *expectedUpstreamRefresh {
return &expectedUpstreamRefresh{
happyActiveDirectoryUpstreamRefreshCall := func() *expectedLDAPUpstreamRefresh {
return &expectedLDAPUpstreamRefresh{
performedByUpstreamName: activeDirectoryUpstreamName,
args: &oidctestutil.PerformRefreshArgs{
Ctx: nil,
DN: activeDirectoryUpstreamDN,
ExpectedSubject: goodSubject,
ExpectedUsername: goodUsername,
args: &oidctestutil.PerformLDAPRefreshArgs{
Ctx: nil, // this will be filled in with the actual request context by the test below
StoredRefreshAttributes: upstreamprovider.LDAPRefreshAttributes{
Username: goodUsername,
Subject: goodSubject,
DN: activeDirectoryUpstreamDN,
Groups: goodGroups,
AdditionalAttributes: nil,
},
IDPDisplayName: activeDirectoryUpstreamName,
},
}
}
happyUpstreamValidateTokenCall := func(expectedTokens *oauth2.Token, requireIDToken bool) *expectedUpstreamValidateTokens {
return &expectedUpstreamValidateTokens{
happyUpstreamValidateTokenCall := func(expectedTokens *oauth2.Token, requireIDToken bool) *expectedOIDCUpstreamValidateTokens {
return &expectedOIDCUpstreamValidateTokens{
performedByUpstreamName: oidcUpstreamName,
args: &oidctestutil.ValidateTokenAndMergeWithUserInfoArgs{
Ctx: nil, // this will be filled in with the actual request context by the test below
@@ -2014,7 +2042,7 @@ func TestRefreshGrant(t *testing.T) {
// same as the same values as the authcode exchange case.
want := happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccess(wantCustomSessionDataStored)
// Should always try to perform an upstream refresh.
want.wantUpstreamRefreshCall = happyOIDCUpstreamRefreshCall()
want.wantOIDCUpstreamRefreshCall = happyOIDCUpstreamRefreshCall()
if expectToValidateToken != nil {
want.wantUpstreamOIDCValidateTokenCall = happyUpstreamValidateTokenCall(expectToValidateToken, true)
}
@@ -2026,7 +2054,7 @@ func TestRefreshGrant(t *testing.T) {
// same as the same values as the authcode exchange case.
want := happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccessWithUsernameAndGroups(wantCustomSessionDataStored, wantDownstreamUsername, wantDownstreamGroups)
// Should always try to perform an upstream refresh.
want.wantUpstreamRefreshCall = happyOIDCUpstreamRefreshCall()
want.wantOIDCUpstreamRefreshCall = happyOIDCUpstreamRefreshCall()
if expectToValidateToken != nil {
want.wantUpstreamOIDCValidateTokenCall = happyUpstreamValidateTokenCall(expectToValidateToken, true)
}
@@ -2038,7 +2066,7 @@ func TestRefreshGrant(t *testing.T) {
// same as the same values as the authcode exchange case.
want := happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccessWithUsernameAndGroups(wantCustomSessionDataStored, wantDownstreamUsername, wantDownstreamGroups)
// Should always try to perform an upstream refresh.
want.wantUpstreamRefreshCall = happyGitHubUpstreamRefreshCall()
want.wantGithubUpstreamRefreshCall = happyGitHubUpstreamRefreshCall()
return want
}
@@ -2050,19 +2078,19 @@ func TestRefreshGrant(t *testing.T) {
happyRefreshTokenResponseForLDAP := func(wantCustomSessionDataStored *psession.CustomSessionData) tokenEndpointResponseExpectedValues {
want := happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccess(wantCustomSessionDataStored)
want.wantUpstreamRefreshCall = happyLDAPUpstreamRefreshCall()
want.wantLDAPUpstreamRefreshCall = happyLDAPUpstreamRefreshCall()
return want
}
happyRefreshTokenResponseForLDAPWithUsernameAndGroups := func(wantCustomSessionDataStored *psession.CustomSessionData, wantDownstreamUsername string, wantDownstreamGroups []string) tokenEndpointResponseExpectedValues {
want := happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccessWithUsernameAndGroups(wantCustomSessionDataStored, wantDownstreamUsername, wantDownstreamGroups)
want.wantUpstreamRefreshCall = happyLDAPUpstreamRefreshCall()
want.wantLDAPUpstreamRefreshCall = happyLDAPUpstreamRefreshCall()
return want
}
happyRefreshTokenResponseForActiveDirectory := func(wantCustomSessionDataStored *psession.CustomSessionData) tokenEndpointResponseExpectedValues {
want := happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccess(wantCustomSessionDataStored)
want.wantUpstreamRefreshCall = happyActiveDirectoryUpstreamRefreshCall()
want.wantActiveDirectoryUpstreamRefreshCall = happyActiveDirectoryUpstreamRefreshCall()
return want
}
@@ -2252,7 +2280,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: transformationUsernamePrefix + goodUsername,
wantGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, goodGroups),
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithRefreshTokenWithoutIDToken(), false),
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshTokenWithUsername(oidcUpstreamRefreshedRefreshToken, transformationUsernamePrefix+goodUsername),
},
@@ -2286,7 +2314,7 @@ func TestRefreshGrant(t *testing.T) {
},
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
@@ -2326,7 +2354,7 @@ func TestRefreshGrant(t *testing.T) {
},
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
@@ -2576,7 +2604,7 @@ func TestRefreshGrant(t *testing.T) {
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshToken(oidcUpstreamRefreshedRefreshToken),
wantUsername: "",
wantGroups: goodGroups,
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
}),
},
@@ -2631,7 +2659,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: goodUsername,
wantGroups: goodGroups,
wantUpstreamOIDCValidateTokenCall: &expectedUpstreamValidateTokens{
wantUpstreamOIDCValidateTokenCall: &expectedOIDCUpstreamValidateTokens{
oidcUpstreamName,
&oidctestutil.ValidateTokenAndMergeWithUserInfoArgs{
Ctx: nil, // this will be filled in with the actual request context by the test below
@@ -2674,7 +2702,7 @@ func TestRefreshGrant(t *testing.T) {
wantSuccessBodyFields: []string{"refresh_token", "access_token", "token_type", "expires_in", "scope"},
wantRequestedScopes: []string{"offline_access"},
wantGrantedScopes: []string{"offline_access", "username", "groups"},
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithRefreshTokenWithoutIDToken(), false),
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshToken(oidcUpstreamRefreshedRefreshToken),
wantUsername: goodUsername,
@@ -2700,7 +2728,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: goodUsername,
wantGroups: goodGroups,
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithRefreshTokenWithoutIDToken(), false),
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshToken(oidcUpstreamRefreshedRefreshToken),
},
@@ -2727,7 +2755,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: goodUsername,
wantGroups: []string{"new-group1", "new-group2", "new-group3"},
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshToken(oidcUpstreamRefreshedRefreshToken),
wantWarnings: []RecordedWarning{
@@ -2768,7 +2796,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: goodUsername,
wantGroups: []string{"new-group1", "new-group2", "new-group3"},
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshToken(oidcUpstreamRefreshedRefreshToken),
wantWarnings: nil, // dynamic clients should not get these warnings which are intended for the pinniped-cli client
@@ -2796,7 +2824,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: goodUsername,
wantGroups: []string{"new-group1", "new-group2", "new-group3"},
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshToken(oidcUpstreamRefreshedRefreshToken),
wantWarnings: []RecordedWarning{
@@ -2827,7 +2855,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: goodUsername,
wantGroups: []string{}, // the user no longer belongs to any groups
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshToken(oidcUpstreamRefreshedRefreshToken),
wantWarnings: []RecordedWarning{
@@ -2857,7 +2885,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: goodUsername,
wantGroups: goodGroups, // the same groups as from the initial login
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshToken(oidcUpstreamRefreshedRefreshToken),
},
@@ -2882,7 +2910,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: goodUsername,
wantGroups: []string{"new-group1", "new-group2", "new-group3"},
wantUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantLDAPUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantCustomSessionDataStored: happyLDAPCustomSessionData,
wantWarnings: []RecordedWarning{
{Text: `User "some-username" has been added to the following groups: ["new-group1" "new-group2" "new-group3"]`},
@@ -2920,7 +2948,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: goodUsername,
wantGroups: []string{"new-group1", "new-group2", "new-group3"},
wantUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantLDAPUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantCustomSessionDataStored: happyLDAPCustomSessionData,
wantWarnings: nil, // dynamic clients should not get these warnings which are intended for the pinniped-cli client
},
@@ -2945,7 +2973,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: goodUsername,
wantGroups: []string{},
wantUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantLDAPUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantCustomSessionDataStored: happyLDAPCustomSessionData,
wantWarnings: []RecordedWarning{
{Text: `User "some-username" has been removed from the following groups: ["group1" "groups2"]`},
@@ -2988,7 +3016,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"}, // username and groups were not requested, but granted anyway for backwards compatibility
wantUsername: goodUsername,
wantGroups: []string{"new-group1", "new-group2", "new-group3"},
wantUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantLDAPUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantCustomSessionDataStored: happyLDAPCustomSessionData,
wantWarnings: []RecordedWarning{
{Text: `User "some-username" has been added to the following groups: ["new-group1" "new-group2" "new-group3"]`},
@@ -3034,7 +3062,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"}, // username and groups were not requested, but granted anyway for backwards compatibility
wantUsername: goodUsername,
wantGroups: []string{"new-group1", "new-group2", "new-group3"},
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshToken(oidcUpstreamRefreshedRefreshToken),
wantWarnings: []RecordedWarning{
@@ -3087,7 +3115,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username"},
wantUsername: goodUsername,
wantGroups: nil,
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshToken(oidcUpstreamRefreshedRefreshToken),
},
@@ -3138,7 +3166,7 @@ func TestRefreshGrant(t *testing.T) {
r.SetBasicAuth(dynamicClientID, testutil.PlaintextPassword1) // Use basic auth header instead.
},
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantStatus: http.StatusUnauthorized,
// auth was rejected because of the upstream group to which the user belonged, as shown by the configured RejectedAuthenticationMessage appearing here
@@ -3188,7 +3216,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantUsername: goodUsername,
wantGroups: []string{"new-group1", "new-group2", "new-group3"}, // groups are updated even though the scope was not included
wantUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantLDAPUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantCustomSessionDataStored: happyLDAPCustomSessionData,
wantWarnings: []RecordedWarning{
{Text: `User "some-username" has been added to the following groups: ["new-group1" "new-group2" "new-group3"]`},
@@ -3213,7 +3241,7 @@ func TestRefreshGrant(t *testing.T) {
want: tokenEndpointResponseExpectedValues{
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: fositeUpstreamGroupClaimErrorBody,
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
},
},
@@ -3295,7 +3323,7 @@ func TestRefreshGrant(t *testing.T) {
wantGrantedScopes: []string{"openid", "offline_access", "pinniped:request-audience", "username", "groups"},
wantUsername: goodUsername,
wantGroups: goodGroups,
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantCustomSessionDataStored: upstreamOIDCCustomSessionDataWithNewRefreshToken(oidcUpstreamRefreshedRefreshToken),
},
@@ -3793,8 +3821,8 @@ func TestRefreshGrant(t *testing.T) {
authcodeExchange: happyAuthcodeExchangeInputsForOIDCUpstream,
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantStatus: http.StatusUnauthorized,
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
{
"error": "error",
@@ -3814,7 +3842,7 @@ func TestRefreshGrant(t *testing.T) {
authcodeExchange: happyAuthcodeExchangeInputsForOIDCUpstream,
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
@@ -3842,7 +3870,7 @@ func TestRefreshGrant(t *testing.T) {
authcodeExchange: happyAuthcodeExchangeInputsForOIDCUpstream,
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
@@ -3867,7 +3895,7 @@ func TestRefreshGrant(t *testing.T) {
authcodeExchange: happyAuthcodeExchangeInputsForOIDCUpstream,
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
@@ -3894,7 +3922,7 @@ func TestRefreshGrant(t *testing.T) {
authcodeExchange: happyAuthcodeExchangeInputsForOIDCUpstream,
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
@@ -3921,7 +3949,7 @@ func TestRefreshGrant(t *testing.T) {
authcodeExchange: happyAuthcodeExchangeInputsForOIDCUpstream,
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantOIDCUpstreamRefreshCall: happyOIDCUpstreamRefreshCall(),
wantUpstreamOIDCValidateTokenCall: happyUpstreamValidateTokenCall(refreshedUpstreamTokensWithIDAndRefreshTokens(), true),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
@@ -4011,8 +4039,8 @@ func TestRefreshGrant(t *testing.T) {
},
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantStatus: http.StatusUnauthorized,
wantLDAPUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
{
"error": "error",
@@ -4051,8 +4079,8 @@ func TestRefreshGrant(t *testing.T) {
},
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantStatus: http.StatusUnauthorized,
wantLDAPUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
{
"error": "error",
@@ -4125,7 +4153,7 @@ func TestRefreshGrant(t *testing.T) {
wantCustomSessionDataStored: happyLDAPCustomSessionData,
wantUsername: "",
wantGroups: goodGroups,
wantUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantLDAPUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
}),
},
},
@@ -4319,8 +4347,8 @@ func TestRefreshGrant(t *testing.T) {
authcodeExchange: happyAuthcodeExchangeInputsForLDAPUpstream,
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantStatus: http.StatusUnauthorized,
wantLDAPUpstreamRefreshCall: happyLDAPUpstreamRefreshCall(),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
{
"error": "error",
@@ -4348,8 +4376,8 @@ func TestRefreshGrant(t *testing.T) {
},
refreshRequest: refreshRequestInputs{
want: tokenEndpointResponseExpectedValues{
wantUpstreamRefreshCall: happyActiveDirectoryUpstreamRefreshCall(),
wantStatus: http.StatusUnauthorized,
wantActiveDirectoryUpstreamRefreshCall: happyActiveDirectoryUpstreamRefreshCall(),
wantStatus: http.StatusUnauthorized,
wantErrorResponseBody: here.Doc(`
{
"error": "error",
@@ -4602,7 +4630,7 @@ func TestRefreshGrant(t *testing.T) {
// Performing an authcode exchange should not have caused any upstream refresh, which should only
// happen during a downstream refresh.
test.idps.RequireExactlyZeroCallsToPerformRefresh(t)
test.idps.RequireExactlyZeroCallsToAnyUpstreamRefresh(t)
test.idps.RequireExactlyZeroCallsToValidateToken(t)
// Wait one second before performing the refresh so we can see that the refreshed ID token has new issued
@@ -4634,21 +4662,38 @@ func TestRefreshGrant(t *testing.T) {
t.Logf("second response: %#v", refreshResponse)
t.Logf("second response body: %q", refreshResponse.Body.String())
// Test that we did or did not make a call to the upstream OIDC provider interface to perform a token refresh.
if test.refreshRequest.want.wantUpstreamRefreshCall != nil {
if test.authcodeExchange.customSessionData.ProviderType != "github" {
test.refreshRequest.want.wantUpstreamRefreshCall.args.Ctx = reqContext
}
test.idps.RequireExactlyOneCallToPerformRefresh(t,
test.refreshRequest.want.wantUpstreamRefreshCall.performedByUpstreamName,
test.refreshRequest.want.wantUpstreamRefreshCall.args,
// Test that we did or did not make a call to the upstream provider's interface to perform refresh.
switch {
case test.refreshRequest.want.wantOIDCUpstreamRefreshCall != nil:
test.refreshRequest.want.wantOIDCUpstreamRefreshCall.args.Ctx = reqContext
test.idps.RequireExactlyOneCallToOIDCPerformRefresh(t,
test.refreshRequest.want.wantOIDCUpstreamRefreshCall.performedByUpstreamName,
test.refreshRequest.want.wantOIDCUpstreamRefreshCall.args,
)
} else {
test.idps.RequireExactlyZeroCallsToPerformRefresh(t)
case test.refreshRequest.want.wantLDAPUpstreamRefreshCall != nil:
test.refreshRequest.want.wantLDAPUpstreamRefreshCall.args.Ctx = reqContext
test.idps.RequireExactlyOneCallToLDAPPerformRefresh(t,
test.refreshRequest.want.wantLDAPUpstreamRefreshCall.performedByUpstreamName,
test.refreshRequest.want.wantLDAPUpstreamRefreshCall.args,
)
case test.refreshRequest.want.wantActiveDirectoryUpstreamRefreshCall != nil:
test.refreshRequest.want.wantActiveDirectoryUpstreamRefreshCall.args.Ctx = reqContext
test.idps.RequireExactlyOneCallToActiveDirectoryPerformRefresh(t,
test.refreshRequest.want.wantActiveDirectoryUpstreamRefreshCall.performedByUpstreamName,
test.refreshRequest.want.wantActiveDirectoryUpstreamRefreshCall.args,
)
case test.refreshRequest.want.wantGithubUpstreamRefreshCall != nil:
test.refreshRequest.want.wantGithubUpstreamRefreshCall.args.Ctx = reqContext
test.idps.RequireExactlyOneCallToGithubGetUser(t,
test.refreshRequest.want.wantGithubUpstreamRefreshCall.performedByUpstreamName,
test.refreshRequest.want.wantGithubUpstreamRefreshCall.args,
)
default:
test.idps.RequireExactlyZeroCallsToAnyUpstreamRefresh(t)
}
// Test that we did or did not make a call to the upstream OIDC provider interface to validate the
// new ID token that was returned by the upstream refresh.
// new ID token that was returned by the upstream refresh, in the case of an OIDC upstream.
if test.refreshRequest.want.wantUpstreamOIDCValidateTokenCall != nil {
test.refreshRequest.want.wantUpstreamOIDCValidateTokenCall.args.Ctx = reqContext
test.idps.RequireExactlyOneCallToValidateToken(t,
@@ -221,7 +221,7 @@ func (p *FederationDomainResolvedLDAPIdentityProvider) UpstreamRefresh(
plog.Debug("attempting upstream refresh request",
"providerName", p.Provider.GetName(), "providerType", p.GetSessionProviderType(), "providerUID", p.Provider.GetResourceUID())
refreshedUntransformedGroups, err := p.Provider.PerformRefresh(ctx, upstreamprovider.RefreshAttributes{
refreshedUntransformedGroups, err := p.Provider.PerformRefresh(ctx, upstreamprovider.LDAPRefreshAttributes{
Username: identity.UpstreamUsername,
Subject: identity.DownstreamSubject,
DN: dn,
@@ -26,9 +26,9 @@ const (
AccessTokenType RevocableTokenType = "access_token"
)
// RefreshAttributes contains information about the user from the original login request
// LDAPRefreshAttributes contains information about the user from the original login request
// and previous refreshes to be used during an LDAP session refresh.
type RefreshAttributes struct {
type LDAPRefreshAttributes struct {
Username string
Subject string
DN string
@@ -125,7 +125,7 @@ type UpstreamLDAPIdentityProviderI interface {
authenticators.UserAuthenticator
// PerformRefresh performs a refresh against the upstream LDAP identity provider
PerformRefresh(ctx context.Context, storedRefreshAttributes RefreshAttributes, idpDisplayName string) (groups []string, err error)
PerformRefresh(ctx context.Context, storedRefreshAttributes LDAPRefreshAttributes, idpDisplayName string) (groups []string, err error)
}
type GitHubUser struct {
@@ -14,6 +14,12 @@ import (
"go.pinniped.dev/internal/idtransform"
)
type PerformLDAPRefreshArgs struct {
Ctx context.Context
StoredRefreshAttributes upstreamprovider.LDAPRefreshAttributes
IDPDisplayName string
}
func NewTestUpstreamLDAPIdentityProviderBuilder() *TestUpstreamLDAPIdentityProviderBuilder {
return &TestUpstreamLDAPIdentityProviderBuilder{}
}
@@ -102,7 +108,7 @@ type TestUpstreamLDAPIdentityProvider struct {
// Fields for tracking actual calls make to mock functions.
performRefreshCallCount int
performRefreshArgs []*PerformRefreshArgs
performRefreshArgs []*PerformLDAPRefreshArgs
}
var _ upstreamprovider.UpstreamLDAPIdentityProviderI = &TestUpstreamLDAPIdentityProvider{}
@@ -123,16 +129,15 @@ func (u *TestUpstreamLDAPIdentityProvider) GetURL() *url.URL {
return u.URL
}
func (u *TestUpstreamLDAPIdentityProvider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.RefreshAttributes, _idpDisplayName string) ([]string, error) {
func (u *TestUpstreamLDAPIdentityProvider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.LDAPRefreshAttributes, idpDisplayName string) ([]string, error) {
if u.performRefreshArgs == nil {
u.performRefreshArgs = make([]*PerformRefreshArgs, 0)
u.performRefreshArgs = make([]*PerformLDAPRefreshArgs, 0)
}
u.performRefreshCallCount++
u.performRefreshArgs = append(u.performRefreshArgs, &PerformRefreshArgs{
Ctx: ctx,
DN: storedRefreshAttributes.DN,
ExpectedUsername: storedRefreshAttributes.Username,
ExpectedSubject: storedRefreshAttributes.Subject,
u.performRefreshArgs = append(u.performRefreshArgs, &PerformLDAPRefreshArgs{
Ctx: ctx,
StoredRefreshAttributes: storedRefreshAttributes,
IDPDisplayName: idpDisplayName,
})
if u.PerformRefreshErr != nil {
return nil, u.PerformRefreshErr
@@ -144,9 +149,9 @@ func (u *TestUpstreamLDAPIdentityProvider) PerformRefreshCallCount() int {
return u.performRefreshCallCount
}
func (u *TestUpstreamLDAPIdentityProvider) PerformRefreshArgs(call int) *PerformRefreshArgs {
func (u *TestUpstreamLDAPIdentityProvider) PerformRefreshArgs(call int) *PerformLDAPRefreshArgs {
if u.performRefreshArgs == nil {
u.performRefreshArgs = make([]*PerformRefreshArgs, 0)
u.performRefreshArgs = make([]*PerformLDAPRefreshArgs, 0)
}
return u.performRefreshArgs[call]
}
@@ -36,14 +36,11 @@ type PasswordCredentialsGrantAndValidateTokensArgs struct {
Password string
}
// PerformRefreshArgs is used to spy on calls to
// PerformOIDCRefreshArgs is used to spy on calls to
// TestUpstreamOIDCIdentityProvider.PerformRefreshFunc().
type PerformRefreshArgs struct {
Ctx context.Context
RefreshToken string
DN string
ExpectedUsername string
ExpectedSubject string
type PerformOIDCRefreshArgs struct {
Ctx context.Context
RefreshToken string
}
// RevokeTokenArgs is used to spy on calls to
@@ -105,7 +102,7 @@ type TestUpstreamOIDCIdentityProvider struct {
passwordCredentialsGrantAndValidateTokensCallCount int
passwordCredentialsGrantAndValidateTokensArgs []*PasswordCredentialsGrantAndValidateTokensArgs
performRefreshCallCount int
performRefreshArgs []*PerformRefreshArgs
performRefreshArgs []*PerformOIDCRefreshArgs
revokeTokenCallCount int
revokeTokenArgs []*RevokeTokenArgs
validateTokenAndMergeWithUserInfoCallCount int
@@ -217,10 +214,10 @@ func (u *TestUpstreamOIDCIdentityProvider) PasswordCredentialsGrantAndValidateTo
func (u *TestUpstreamOIDCIdentityProvider) PerformRefresh(ctx context.Context, refreshToken string) (*oauth2.Token, error) {
if u.performRefreshArgs == nil {
u.performRefreshArgs = make([]*PerformRefreshArgs, 0)
u.performRefreshArgs = make([]*PerformOIDCRefreshArgs, 0)
}
u.performRefreshCallCount++
u.performRefreshArgs = append(u.performRefreshArgs, &PerformRefreshArgs{
u.performRefreshArgs = append(u.performRefreshArgs, &PerformOIDCRefreshArgs{
Ctx: ctx,
RefreshToken: refreshToken,
})
@@ -244,9 +241,9 @@ func (u *TestUpstreamOIDCIdentityProvider) PerformRefreshCallCount() int {
return u.performRefreshCallCount
}
func (u *TestUpstreamOIDCIdentityProvider) PerformRefreshArgs(call int) *PerformRefreshArgs {
func (u *TestUpstreamOIDCIdentityProvider) PerformRefreshArgs(call int) *PerformOIDCRefreshArgs {
if u.performRefreshArgs == nil {
u.performRefreshArgs = make([]*PerformRefreshArgs, 0)
u.performRefreshArgs = make([]*PerformOIDCRefreshArgs, 0)
}
return u.performRefreshArgs[call]
}
@@ -330,60 +330,111 @@ func (b *UpstreamIDPListerBuilder) RequireExactlyZeroAuthcodeExchanges(t *testin
)
}
func (b *UpstreamIDPListerBuilder) RequireExactlyOneCallToPerformRefresh(
func (b *UpstreamIDPListerBuilder) RequireExactlyOneCallToOIDCPerformRefresh(
t *testing.T,
expectedPerformedByUpstreamName string,
expectedArgs *oidctestutil.PerformRefreshArgs,
expectedArgs *oidctestutil.PerformOIDCRefreshArgs,
) {
t.Helper()
var actualArgs *oidctestutil.PerformRefreshArgs
var actualArgs *oidctestutil.PerformOIDCRefreshArgs
var actualNameOfUpstreamWhichMadeCall string
actualCallCountAcrossAllUpstreams := 0
for _, upstreamOIDC := range b.upstreamOIDCIdentityProviders {
callCountOnThisUpstream := upstreamOIDC.PerformRefreshCallCount()
actualCallCountAcrossAllUpstreams += callCountOnThisUpstream
if callCountOnThisUpstream == 1 {
actualNameOfUpstreamWhichMadeCall = upstreamOIDC.Name
actualArgs = upstreamOIDC.PerformRefreshArgs(0)
}
}
for _, upstreamLDAP := range b.upstreamLDAPIdentityProviders {
callCountOnThisUpstream := upstreamLDAP.PerformRefreshCallCount()
actualCallCountAcrossAllUpstreams += callCountOnThisUpstream
if callCountOnThisUpstream == 1 {
actualNameOfUpstreamWhichMadeCall = upstreamLDAP.Name
actualArgs = upstreamLDAP.PerformRefreshArgs(0)
}
}
for _, upstreamAD := range b.upstreamActiveDirectoryIdentityProviders {
callCountOnThisUpstream := upstreamAD.PerformRefreshCallCount()
actualCallCountAcrossAllUpstreams += callCountOnThisUpstream
if callCountOnThisUpstream == 1 {
actualNameOfUpstreamWhichMadeCall = upstreamAD.Name
actualArgs = upstreamAD.PerformRefreshArgs(0)
}
}
for _, upstream := range b.upstreamGitHubIdentityProviders {
// Remember that GitHub does not have a traditional PerformRefresh function.
// GitHub calls GetUser during both the original authcode exchange and the refresh.
callCountOnThisUpstream := upstream.GetUserCallCount()
actualCallCountAcrossAllUpstreams += callCountOnThisUpstream
for _, upstream := range b.upstreamOIDCIdentityProviders {
callCountOnThisUpstream := upstream.PerformRefreshCallCount()
if callCountOnThisUpstream == 1 {
actualNameOfUpstreamWhichMadeCall = upstream.Name
actualArgs = nil
actualArgs = upstream.PerformRefreshArgs(0)
}
}
require.Equal(t, 1, actualCallCountAcrossAllUpstreams,
"should have been exactly one call to PerformRefresh() by all upstreams",
require.Equal(t, 1, b.CountAllCallsToAnyUpstreamRefresh(),
"should have been exactly one call to upstream refresh by all upstreams",
)
require.Equal(t, expectedPerformedByUpstreamName, actualNameOfUpstreamWhichMadeCall,
"PerformRefresh() was called on the wrong upstream",
"upstream refresh was called on the wrong upstream",
)
require.Equal(t, expectedArgs, actualArgs)
}
func (b *UpstreamIDPListerBuilder) RequireExactlyZeroCallsToPerformRefresh(t *testing.T) {
func (b *UpstreamIDPListerBuilder) RequireExactlyOneCallToActiveDirectoryPerformRefresh(
t *testing.T,
expectedPerformedByUpstreamName string,
expectedArgs *oidctestutil.PerformLDAPRefreshArgs,
) {
t.Helper()
var actualArgs *oidctestutil.PerformLDAPRefreshArgs
var actualNameOfUpstreamWhichMadeCall string
for _, upstream := range b.upstreamActiveDirectoryIdentityProviders {
callCountOnThisUpstream := upstream.PerformRefreshCallCount()
if callCountOnThisUpstream == 1 {
actualNameOfUpstreamWhichMadeCall = upstream.Name
actualArgs = upstream.PerformRefreshArgs(0)
}
}
require.Equal(t, 1, b.CountAllCallsToAnyUpstreamRefresh(),
"should have been exactly one call to upstream refresh by all upstreams",
)
require.Equal(t, expectedPerformedByUpstreamName, actualNameOfUpstreamWhichMadeCall,
"upstream refresh was called on the wrong upstream",
)
require.Equal(t, expectedArgs, actualArgs)
}
func (b *UpstreamIDPListerBuilder) RequireExactlyOneCallToLDAPPerformRefresh(
t *testing.T,
expectedPerformedByUpstreamName string,
expectedArgs *oidctestutil.PerformLDAPRefreshArgs,
) {
t.Helper()
var actualArgs *oidctestutil.PerformLDAPRefreshArgs
var actualNameOfUpstreamWhichMadeCall string
for _, upstream := range b.upstreamLDAPIdentityProviders {
callCountOnThisUpstream := upstream.PerformRefreshCallCount()
if callCountOnThisUpstream == 1 {
actualNameOfUpstreamWhichMadeCall = upstream.Name
actualArgs = upstream.PerformRefreshArgs(0)
}
}
require.Equal(t, 1, b.CountAllCallsToAnyUpstreamRefresh(),
"should have been exactly one call to upstream refresh by all upstreams",
)
require.Equal(t, expectedPerformedByUpstreamName, actualNameOfUpstreamWhichMadeCall,
"upstream refresh was called on the wrong upstream",
)
require.Equal(t, expectedArgs, actualArgs)
}
func (b *UpstreamIDPListerBuilder) RequireExactlyOneCallToGithubGetUser(
t *testing.T,
expectedPerformedByUpstreamName string,
expectedArgs *oidctestutil.GetUserArgs,
) {
t.Helper()
var actualArgs *oidctestutil.GetUserArgs
var actualNameOfUpstreamWhichMadeCall string
for _, upstream := range b.upstreamGitHubIdentityProviders {
// GitHub calls GetUser during both the original authcode exchange and the refresh.
callCountOnThisUpstream := upstream.GetUserCallCount()
if callCountOnThisUpstream == 1 {
actualNameOfUpstreamWhichMadeCall = upstream.Name
actualArgs = upstream.GetUserArgs(0)
}
}
require.Equal(t, 1, b.CountAllCallsToAnyUpstreamRefresh(),
"should have been exactly one call to upstream refresh by all upstreams",
)
require.Equal(t, expectedPerformedByUpstreamName, actualNameOfUpstreamWhichMadeCall,
"upstream refresh was called on the wrong upstream",
)
require.Equal(t, expectedArgs, actualArgs)
}
func (b *UpstreamIDPListerBuilder) RequireExactlyZeroCallsToAnyUpstreamRefresh(t *testing.T) {
t.Helper()
require.Equal(t, 0, b.CountAllCallsToAnyUpstreamRefresh(),
"expected exactly zero calls to any upstream refresh mocks",
)
}
func (b *UpstreamIDPListerBuilder) CountAllCallsToAnyUpstreamRefresh() int {
actualCallCountAcrossAllUpstreams := 0
for _, upstreamOIDC := range b.upstreamOIDCIdentityProviders {
actualCallCountAcrossAllUpstreams += upstreamOIDC.PerformRefreshCallCount()
@@ -394,11 +445,10 @@ func (b *UpstreamIDPListerBuilder) RequireExactlyZeroCallsToPerformRefresh(t *te
for _, upstreamActiveDirectory := range b.upstreamActiveDirectoryIdentityProviders {
actualCallCountAcrossAllUpstreams += upstreamActiveDirectory.PerformRefreshCallCount()
}
// TODO: probably add GitHub loop once we flesh out the structs
require.Equal(t, 0, actualCallCountAcrossAllUpstreams,
"expected exactly zero calls to PerformRefresh()",
)
for _, upstreamGithub := range b.upstreamGitHubIdentityProviders {
actualCallCountAcrossAllUpstreams += upstreamGithub.GetUserCallCount()
}
return actualCallCountAcrossAllUpstreams
}
func (b *UpstreamIDPListerBuilder) RequireExactlyOneCallToValidateToken(
+2 -2
View File
@@ -118,7 +118,7 @@ type ProviderConfig struct {
GroupAttributeParsingOverrides map[string]func(*ldap.Entry) (string, error)
// RefreshAttributeChecks are extra checks that attributes in a refresh response are as expected.
RefreshAttributeChecks map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error
RefreshAttributeChecks map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error
}
// UserSearchConfig contains information about how to search for users in the upstream LDAP IDP.
@@ -186,7 +186,7 @@ func closeAndLogError(conn Conn, doingWhat string) {
}
}
func (p *Provider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.RefreshAttributes, idpDisplayName string) ([]string, error) {
func (p *Provider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.LDAPRefreshAttributes, idpDisplayName string) ([]string, error) {
t := trace.FromContext(ctx).Nest("slow ldap refresh attempt", trace.Field{Key: "providerName", Value: p.GetName()})
defer t.LogIfLong(500 * time.Millisecond) // to help users debug slow LDAP searches
userDN := storedRefreshAttributes.DN
+9 -9
View File
@@ -641,8 +641,8 @@ func TestEndUserAuthentication(t *testing.T) {
username: testUpstreamUsername,
password: testUpstreamPassword,
providerConfig: providerConfig(func(p *ProviderConfig) {
p.RefreshAttributeChecks = map[string]func(entry *ldap.Entry, attributes upstreamprovider.RefreshAttributes) error{
"some-attribute-to-check-during-refresh": func(entry *ldap.Entry, attributes upstreamprovider.RefreshAttributes) error {
p.RefreshAttributeChecks = map[string]func(entry *ldap.Entry, attributes upstreamprovider.LDAPRefreshAttributes) error{
"some-attribute-to-check-during-refresh": func(entry *ldap.Entry, attributes upstreamprovider.LDAPRefreshAttributes) error {
return nil
},
}
@@ -679,8 +679,8 @@ func TestEndUserAuthentication(t *testing.T) {
username: testUpstreamUsername,
password: testUpstreamPassword,
providerConfig: providerConfig(func(p *ProviderConfig) {
p.RefreshAttributeChecks = map[string]func(entry *ldap.Entry, attributes upstreamprovider.RefreshAttributes) error{
"some-attribute-to-check-during-refresh": func(entry *ldap.Entry, attributes upstreamprovider.RefreshAttributes) error {
p.RefreshAttributeChecks = map[string]func(entry *ldap.Entry, attributes upstreamprovider.LDAPRefreshAttributes) error{
"some-attribute-to-check-during-refresh": func(entry *ldap.Entry, attributes upstreamprovider.LDAPRefreshAttributes) error {
return nil
},
}
@@ -1527,8 +1527,8 @@ func TestUpstreamRefresh(t *testing.T) {
Filter: testGroupSearchFilter,
GroupNameAttribute: testGroupSearchGroupNameAttribute,
},
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
pwdLastSetAttribute: func(*ldap.Entry, upstreamprovider.RefreshAttributes) error { return nil },
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
pwdLastSetAttribute: func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error { return nil },
},
}
if editFunc != nil {
@@ -2124,8 +2124,8 @@ func TestUpstreamRefresh(t *testing.T) {
{
name: "search result has a changed pwdLastSet value",
providerConfig: providerConfig(func(p *ProviderConfig) {
p.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
pwdLastSetAttribute: func(*ldap.Entry, upstreamprovider.RefreshAttributes) error {
p.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
pwdLastSetAttribute: func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error {
return errors.New(`value for attribute "pwdLastSet" has changed since initial value at login`)
},
}
@@ -2201,7 +2201,7 @@ func TestUpstreamRefresh(t *testing.T) {
"ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&idpName=%s&sub=c29tZS11cHN0cmVhbS11aWQtdmFsdWU",
testUpstreamName,
)
groups, err := ldapProvider.PerformRefresh(context.Background(), upstreamprovider.RefreshAttributes{
groups, err := ldapProvider.PerformRefresh(context.Background(), upstreamprovider.LDAPRefreshAttributes{
Username: testUserSearchResultUsernameAttributeValue,
Subject: subject,
DN: tt.refreshUserDN,