started add units tests for identity transforms to token_handler_test.go
This commit is contained in:
parent
7f70fcf679
commit
5ad7e9a8ca
@ -186,7 +186,8 @@ func upstreamOIDCRefresh(
|
||||
}
|
||||
mergedClaims := validatedTokens.IDToken.Claims
|
||||
|
||||
// To the extent possible, check that the user's basic identity hasn't changed.
|
||||
// To the extent possible, check that the user's basic identity hasn't changed. We check that their downstream
|
||||
// username has not changed separately below, as part of reapplying the transformations.
|
||||
err = validateSubjectAndIssuerUnchangedSinceInitialLogin(mergedClaims, session)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -61,6 +61,7 @@ import (
|
||||
"go.pinniped.dev/internal/psession"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
"go.pinniped.dev/internal/testutil/oidctestutil"
|
||||
"go.pinniped.dev/internal/testutil/transformtestutil"
|
||||
"go.pinniped.dev/pkg/oidcclient/oidctypes"
|
||||
)
|
||||
|
||||
@ -1749,6 +1750,9 @@ func TestRefreshGrant(t *testing.T) {
|
||||
activeDirectoryUpstreamResourceUID = "ad-resource-uid"
|
||||
activeDirectoryUpstreamType = "activedirectory"
|
||||
activeDirectoryUpstreamDN = "some-ad-user-dn"
|
||||
|
||||
transformationUsernamePrefix = "username_prefix:"
|
||||
transformationGroupsPrefix = "groups_prefix:"
|
||||
)
|
||||
|
||||
ldapUpstreamURL, _ := url.Parse("some-url")
|
||||
@ -1861,6 +1865,13 @@ func TestRefreshGrant(t *testing.T) {
|
||||
return want
|
||||
}
|
||||
|
||||
happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccessWithUsernameAndGroups := func(wantCustomSessionDataStored *psession.CustomSessionData, wantDownstreamUsername string, wantDownsteamGroups []string) tokenEndpointResponseExpectedValues {
|
||||
want := happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccess(wantCustomSessionDataStored)
|
||||
want.wantUsername = wantDownstreamUsername
|
||||
want.wantGroups = wantDownsteamGroups
|
||||
return want
|
||||
}
|
||||
|
||||
withWantDynamicClientID := func(w tokenEndpointResponseExpectedValues) tokenEndpointResponseExpectedValues {
|
||||
w.wantClientID = dynamicClientID
|
||||
return w
|
||||
@ -1895,6 +1906,12 @@ func TestRefreshGrant(t *testing.T) {
|
||||
return want
|
||||
}
|
||||
|
||||
happyRefreshTokenResponseForLDAPWithUsernameAndGroups := func(wantCustomSessionDataStored *psession.CustomSessionData, wantDownstreamUsername string, wantDownsteamGroups []string) tokenEndpointResponseExpectedValues {
|
||||
want := happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccessWithUsernameAndGroups(wantCustomSessionDataStored, wantDownstreamUsername, wantDownsteamGroups)
|
||||
want.wantUpstreamRefreshCall = happyLDAPUpstreamRefreshCall()
|
||||
return want
|
||||
}
|
||||
|
||||
happyRefreshTokenResponseForActiveDirectory := func(wantCustomSessionDataStored *psession.CustomSessionData) tokenEndpointResponseExpectedValues {
|
||||
want := happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccess(wantCustomSessionDataStored)
|
||||
want.wantUpstreamRefreshCall = happyActiveDirectoryUpstreamRefreshCall()
|
||||
@ -1945,10 +1962,20 @@ func TestRefreshGrant(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
happyLDAPCustomSessionDataWithUsername := func(wantDownstreamUsername string) *psession.CustomSessionData {
|
||||
copyOfCustomSession := *happyLDAPCustomSessionData
|
||||
copyOfLDAP := *(happyLDAPCustomSessionData.LDAP)
|
||||
copyOfCustomSession.LDAP = ©OfLDAP
|
||||
copyOfCustomSession.Username = wantDownstreamUsername
|
||||
return ©OfCustomSession
|
||||
}
|
||||
|
||||
happyAuthcodeExchangeInputsForOIDCUpstream := authcodeExchangeInputs{
|
||||
customSessionData: initialUpstreamOIDCRefreshTokenCustomSessionData(),
|
||||
modifyAuthRequest: func(r *http.Request) { r.Form.Set("scope", "openid offline_access username groups") },
|
||||
want: happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccess(initialUpstreamOIDCRefreshTokenCustomSessionData()),
|
||||
customSessionData: initialUpstreamOIDCRefreshTokenCustomSessionData(),
|
||||
want: happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccess(
|
||||
initialUpstreamOIDCRefreshTokenCustomSessionData(),
|
||||
),
|
||||
}
|
||||
|
||||
happyAuthcodeExchangeInputsForLDAPUpstream := authcodeExchangeInputs{
|
||||
@ -1959,6 +1986,8 @@ func TestRefreshGrant(t *testing.T) {
|
||||
),
|
||||
}
|
||||
|
||||
prefixUsernameAndGroupsPipeline := transformtestutil.NewPrefixingPipeline(t, transformationUsernamePrefix, transformationGroupsPrefix)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
idps *oidctestutil.UpstreamIDPListerBuilder
|
||||
@ -3498,6 +3527,39 @@ func TestRefreshGrant(t *testing.T) {
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upstream ldap refresh happy path with identity transformations which modify the username and group names",
|
||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(oidctestutil.NewTestUpstreamLDAPIdentityProviderBuilder().
|
||||
WithName(ldapUpstreamName).
|
||||
WithResourceUID(ldapUpstreamResourceUID).
|
||||
WithURL(ldapUpstreamURL).
|
||||
WithPerformRefreshGroups(goodGroups).
|
||||
WithTransformsForFederationDomain(prefixUsernameAndGroupsPipeline).
|
||||
Build(),
|
||||
),
|
||||
authcodeExchange: authcodeExchangeInputs{
|
||||
modifyAuthRequest: func(r *http.Request) { r.Form.Set("scope", "openid offline_access username groups") },
|
||||
customSessionData: happyLDAPCustomSessionDataWithUsername(transformationUsernamePrefix + goodUsername),
|
||||
modifySession: func(session *psession.PinnipedSession) {
|
||||
// The authorization flow would have run the transformation pipeline and stored the transformed
|
||||
// downstream identity in this part of the session, so simulate that by setting the expected result.
|
||||
session.IDTokenClaims().Extra["username"] = transformationUsernamePrefix + goodUsername
|
||||
session.IDTokenClaims().Extra["groups"] = testutil.AddPrefixToEach(transformationGroupsPrefix, goodGroups)
|
||||
},
|
||||
want: happyAuthcodeExchangeTokenResponseForOpenIDAndOfflineAccessWithUsernameAndGroups(
|
||||
happyLDAPCustomSessionDataWithUsername(transformationUsernamePrefix+goodUsername),
|
||||
transformationUsernamePrefix+goodUsername,
|
||||
testutil.AddPrefixToEach(transformationGroupsPrefix, goodGroups),
|
||||
),
|
||||
},
|
||||
refreshRequest: refreshRequestInputs{
|
||||
want: happyRefreshTokenResponseForLDAPWithUsernameAndGroups(
|
||||
happyLDAPCustomSessionDataWithUsername(transformationUsernamePrefix+goodUsername),
|
||||
transformationUsernamePrefix+goodUsername,
|
||||
testutil.AddPrefixToEach(transformationGroupsPrefix, goodGroups),
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upstream ldap refresh happy path using dynamic client",
|
||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithLDAP(oidctestutil.NewTestUpstreamLDAPIdentityProviderBuilder().
|
||||
@ -4436,7 +4498,11 @@ func (s *singleUseJWKProvider) GetJWKS(issuerName string) (jwks *jose.JSONWebKey
|
||||
return s.DynamicJWKSProvider.GetJWKS(issuerName)
|
||||
}
|
||||
|
||||
// Simulate the auth endpoint running so Fosite code will fill the store with realistic values.
|
||||
// Simulate the results of the auth endpoint (and possibly also the related callback or login endpoints) by getting
|
||||
// fosite's code to fill the session store with realistic values. Regardless of the specific flow that the user uses to
|
||||
// become authorized, all authorization flows conclude with the user's identity saved into a fosite session and an
|
||||
// authorization code being issued to the client. So the goal of this function is to save the user's identity into a
|
||||
// session in the same way that the production code for those other endpoints would have done it.
|
||||
func simulateAuthEndpointHavingAlreadyRun(
|
||||
t *testing.T,
|
||||
authRequest *http.Request,
|
||||
@ -4459,9 +4525,6 @@ func simulateAuthEndpointHavingAlreadyRun(
|
||||
},
|
||||
Custom: initialCustomSessionData,
|
||||
}
|
||||
if modifySession != nil {
|
||||
modifySession(session)
|
||||
}
|
||||
|
||||
authRequester, err := oauthHelper.NewAuthorizeRequest(ctx, authRequest)
|
||||
require.NoError(t, err)
|
||||
@ -4475,9 +4538,13 @@ func simulateAuthEndpointHavingAlreadyRun(
|
||||
authRequester.GrantScope("pinniped:request-audience")
|
||||
}
|
||||
|
||||
// Set the downstream username and group names that normally would have been determined by the authorize and related
|
||||
// endpoints. These are stored into the fosite "extra" claims by the other endpoints, and when the token endpoint is
|
||||
// called later, it will be able to find this information inside the "extra" claims in the session.
|
||||
// The authorization endpoint makes a special exception for the pinniped-cli client for backwards compatibility
|
||||
// and grants the username and groups scopes to that client even if it did not ask for them. Simulate that
|
||||
// behavior here too.
|
||||
// behavior here too by always adding these extras when the client_id is the Pinniped CLI client.
|
||||
// Note that these (and anything else in the session) can be overridden by the modifySession param.
|
||||
if strings.Contains(authRequest.Form.Get("scope"), "username") || authRequest.Form.Get("client_id") == pinnipedCLIClientID {
|
||||
authRequester.GrantScope("username")
|
||||
session.Fosite.Claims.Extra["username"] = goodUsername
|
||||
@ -4490,6 +4557,11 @@ func simulateAuthEndpointHavingAlreadyRun(
|
||||
// The authorization endpoint sets the authorized party to the client ID of the original requester.
|
||||
session.Fosite.Claims.Extra["azp"] = authRequester.GetClient().GetID()
|
||||
|
||||
// Allow some tests to further modify the session before it is stored.
|
||||
if modifySession != nil {
|
||||
modifySession(session)
|
||||
}
|
||||
|
||||
authResponder, err := oauthHelper.NewAuthorizeResponse(ctx, authRequester, session)
|
||||
require.NoError(t, err)
|
||||
return authResponder
|
||||
|
Loading…
Reference in New Issue
Block a user