add new unit tests in callback_handler_test.go

This commit is contained in:
Ryan Richard 2023-08-23 10:16:04 -07:00
parent d4611b829d
commit f653942065
3 changed files with 158 additions and 32 deletions

View File

@ -30,17 +30,16 @@ import (
supervisorfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake" supervisorfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake"
"go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/typed/config/v1alpha1" "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/typed/config/v1alpha1"
"go.pinniped.dev/internal/authenticators" "go.pinniped.dev/internal/authenticators"
"go.pinniped.dev/internal/celtransformer"
"go.pinniped.dev/internal/federationdomain/csrftoken" "go.pinniped.dev/internal/federationdomain/csrftoken"
"go.pinniped.dev/internal/federationdomain/endpoints/jwks" "go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/federationdomain/oidc" "go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator" "go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/storage" "go.pinniped.dev/internal/federationdomain/storage"
"go.pinniped.dev/internal/here" "go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/idtransform"
"go.pinniped.dev/internal/psession" "go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil" "go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/oidctestutil" "go.pinniped.dev/internal/testutil/oidctestutil"
"go.pinniped.dev/internal/testutil/transformtestutil"
"go.pinniped.dev/pkg/oidcclient/nonce" "go.pinniped.dev/pkg/oidcclient/nonce"
"go.pinniped.dev/pkg/oidcclient/pkce" "go.pinniped.dev/pkg/oidcclient/pkce"
) )
@ -612,23 +611,8 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
encodedIncomingCookieCSRFValue, err := happyCookieEncoder.Encode("csrf", incomingCookieCSRFValue) encodedIncomingCookieCSRFValue, err := happyCookieEncoder.Encode("csrf", incomingCookieCSRFValue)
require.NoError(t, err) require.NoError(t, err)
transformer, err := celtransformer.NewCELTransformer(5 * time.Second) // CI workers can be slow, so allow slow transforms prefixUsernameAndGroupsPipeline := transformtestutil.NewPrefixingPipeline(t, transformationUsernamePrefix, transformationGroupsPrefix)
require.NoError(t, err) rejectAuthPipeline := transformtestutil.NewRejectAllAuthPipeline(t)
prefixUsernameAndGroupsPipeline := idtransform.NewTransformationPipeline()
rejectAuthPipeline := idtransform.NewTransformationPipeline()
var compiledTransform idtransform.IdentityTransformation
compiledTransform, err = transformer.CompileTransformation(&celtransformer.UsernameTransformation{Expression: fmt.Sprintf(`"%s" + username`, transformationUsernamePrefix)}, nil)
require.NoError(t, err)
prefixUsernameAndGroupsPipeline.AppendTransformation(compiledTransform)
compiledTransform, err = transformer.CompileTransformation(&celtransformer.GroupsTransformation{Expression: fmt.Sprintf(`groups.map(g, "%s" + g)`, transformationGroupsPrefix)}, nil)
require.NoError(t, err)
prefixUsernameAndGroupsPipeline.AppendTransformation(compiledTransform)
compiledTransform, err = transformer.CompileTransformation(&celtransformer.AllowAuthenticationPolicy{Expression: `username == "someone-special"`}, nil)
require.NoError(t, err)
rejectAuthPipeline.AppendTransformation(compiledTransform)
type testCase struct { type testCase struct {
name string name string

View File

@ -28,6 +28,7 @@ import (
"go.pinniped.dev/internal/psession" "go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/testutil" "go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/oidctestutil" "go.pinniped.dev/internal/testutil/oidctestutil"
"go.pinniped.dev/internal/testutil/transformtestutil"
"go.pinniped.dev/pkg/oidcclient/nonce" "go.pinniped.dev/pkg/oidcclient/nonce"
oidcpkce "go.pinniped.dev/pkg/oidcclient/pkce" oidcpkce "go.pinniped.dev/pkg/oidcclient/pkce"
) )
@ -65,6 +66,9 @@ const (
downstreamPKCEChallengeMethod = "S256" downstreamPKCEChallengeMethod = "S256"
htmlContentType = "text/html; charset=utf-8" htmlContentType = "text/html; charset=utf-8"
transformationUsernamePrefix = "username_prefix:"
transformationGroupsPrefix = "groups_prefix:"
) )
var ( var (
@ -102,13 +106,13 @@ var (
UpstreamSubject: oidcUpstreamSubject, UpstreamSubject: oidcUpstreamSubject,
}, },
} }
happyDownstreamCustomSessionDataWithUsernameAndGroups = func(wantUsername string, wantGroups []string) *psession.CustomSessionData { happyDownstreamCustomSessionDataWithUsernameAndGroups = func(wantDownstreamUsername, wantUpstreamUsername string, wantUpstreamGroups []string) *psession.CustomSessionData {
copyOfCustomSession := *happyDownstreamCustomSessionData copyOfCustomSession := *happyDownstreamCustomSessionData
copyOfOIDC := *(happyDownstreamCustomSessionData.OIDC) copyOfOIDC := *(happyDownstreamCustomSessionData.OIDC)
copyOfCustomSession.OIDC = &copyOfOIDC copyOfCustomSession.OIDC = &copyOfOIDC
copyOfCustomSession.Username = wantUsername copyOfCustomSession.Username = wantDownstreamUsername
copyOfCustomSession.UpstreamUsername = wantUsername copyOfCustomSession.UpstreamUsername = wantUpstreamUsername
copyOfCustomSession.UpstreamGroups = wantGroups copyOfCustomSession.UpstreamGroups = wantUpstreamGroups
return &copyOfCustomSession return &copyOfCustomSession
} }
happyDownstreamAccessTokenCustomSessionData = &psession.CustomSessionData{ happyDownstreamAccessTokenCustomSessionData = &psession.CustomSessionData{
@ -172,6 +176,9 @@ func TestCallbackEndpoint(t *testing.T) {
require.NoError(t, kubeClient.Tracker().Add(secret)) require.NoError(t, kubeClient.Tracker().Add(secret))
} }
prefixUsernameAndGroupsPipeline := transformtestutil.NewPrefixingPipeline(t, transformationUsernamePrefix, transformationGroupsPrefix)
rejectAuthPipeline := transformtestutil.NewRejectAllAuthPipeline(t)
tests := []struct { tests := []struct {
name string name string
@ -439,7 +446,11 @@ func TestCallbackEndpoint(t *testing.T) {
wantDownstreamClientID: downstreamPinnipedClientID, wantDownstreamClientID: downstreamPinnipedClientID,
wantDownstreamPKCEChallenge: downstreamPKCEChallenge, wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(oidcUpstreamIssuer+"?sub="+oidcUpstreamSubjectQueryEscaped, nil), wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(
oidcUpstreamIssuer+"?sub="+oidcUpstreamSubjectQueryEscaped,
oidcUpstreamIssuer+"?sub="+oidcUpstreamSubjectQueryEscaped,
nil,
),
wantAuthcodeExchangeCall: &expectedAuthcodeExchange{ wantAuthcodeExchangeCall: &expectedAuthcodeExchange{
performedByUpstreamName: happyUpstreamIDPName, performedByUpstreamName: happyUpstreamIDPName,
args: happyExchangeAndValidateTokensArgs, args: happyExchangeAndValidateTokensArgs,
@ -465,7 +476,11 @@ func TestCallbackEndpoint(t *testing.T) {
wantDownstreamClientID: downstreamPinnipedClientID, wantDownstreamClientID: downstreamPinnipedClientID,
wantDownstreamPKCEChallenge: downstreamPKCEChallenge, wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups("joe@whitehouse.gov", oidcUpstreamGroupMembership), wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(
"joe@whitehouse.gov",
"joe@whitehouse.gov",
oidcUpstreamGroupMembership,
),
wantAuthcodeExchangeCall: &expectedAuthcodeExchange{ wantAuthcodeExchangeCall: &expectedAuthcodeExchange{
performedByUpstreamName: happyUpstreamIDPName, performedByUpstreamName: happyUpstreamIDPName,
args: happyExchangeAndValidateTokensArgs, args: happyExchangeAndValidateTokensArgs,
@ -493,7 +508,11 @@ func TestCallbackEndpoint(t *testing.T) {
wantDownstreamClientID: downstreamPinnipedClientID, wantDownstreamClientID: downstreamPinnipedClientID,
wantDownstreamPKCEChallenge: downstreamPKCEChallenge, wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups("joe@whitehouse.gov", oidcUpstreamGroupMembership), wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(
"joe@whitehouse.gov",
"joe@whitehouse.gov",
oidcUpstreamGroupMembership,
),
wantAuthcodeExchangeCall: &expectedAuthcodeExchange{ wantAuthcodeExchangeCall: &expectedAuthcodeExchange{
performedByUpstreamName: happyUpstreamIDPName, performedByUpstreamName: happyUpstreamIDPName,
args: happyExchangeAndValidateTokensArgs, args: happyExchangeAndValidateTokensArgs,
@ -522,7 +541,11 @@ func TestCallbackEndpoint(t *testing.T) {
wantDownstreamClientID: downstreamPinnipedClientID, wantDownstreamClientID: downstreamPinnipedClientID,
wantDownstreamPKCEChallenge: downstreamPKCEChallenge, wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups("joe", oidcUpstreamGroupMembership), wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(
"joe",
"joe",
oidcUpstreamGroupMembership,
),
wantAuthcodeExchangeCall: &expectedAuthcodeExchange{ wantAuthcodeExchangeCall: &expectedAuthcodeExchange{
performedByUpstreamName: happyUpstreamIDPName, performedByUpstreamName: happyUpstreamIDPName,
args: happyExchangeAndValidateTokensArgs, args: happyExchangeAndValidateTokensArgs,
@ -653,7 +676,11 @@ func TestCallbackEndpoint(t *testing.T) {
wantDownstreamClientID: downstreamPinnipedClientID, wantDownstreamClientID: downstreamPinnipedClientID,
wantDownstreamPKCEChallenge: downstreamPKCEChallenge, wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(oidcUpstreamSubject, oidcUpstreamGroupMembership), wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(
oidcUpstreamSubject,
oidcUpstreamSubject,
oidcUpstreamGroupMembership,
),
wantAuthcodeExchangeCall: &expectedAuthcodeExchange{ wantAuthcodeExchangeCall: &expectedAuthcodeExchange{
performedByUpstreamName: happyUpstreamIDPName, performedByUpstreamName: happyUpstreamIDPName,
args: happyExchangeAndValidateTokensArgs, args: happyExchangeAndValidateTokensArgs,
@ -679,7 +706,11 @@ func TestCallbackEndpoint(t *testing.T) {
wantDownstreamClientID: downstreamPinnipedClientID, wantDownstreamClientID: downstreamPinnipedClientID,
wantDownstreamPKCEChallenge: downstreamPKCEChallenge, wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(oidcUpstreamUsername, []string{"notAnArrayGroup1 notAnArrayGroup2"}), wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(
oidcUpstreamUsername,
oidcUpstreamUsername,
[]string{"notAnArrayGroup1 notAnArrayGroup2"},
),
wantAuthcodeExchangeCall: &expectedAuthcodeExchange{ wantAuthcodeExchangeCall: &expectedAuthcodeExchange{
performedByUpstreamName: happyUpstreamIDPName, performedByUpstreamName: happyUpstreamIDPName,
args: happyExchangeAndValidateTokensArgs, args: happyExchangeAndValidateTokensArgs,
@ -705,7 +736,11 @@ func TestCallbackEndpoint(t *testing.T) {
wantDownstreamClientID: downstreamPinnipedClientID, wantDownstreamClientID: downstreamPinnipedClientID,
wantDownstreamPKCEChallenge: downstreamPKCEChallenge, wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(oidcUpstreamUsername, []string{"group1", "group2"}), wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(
oidcUpstreamUsername,
oidcUpstreamUsername,
[]string{"group1", "group2"},
),
wantAuthcodeExchangeCall: &expectedAuthcodeExchange{ wantAuthcodeExchangeCall: &expectedAuthcodeExchange{
performedByUpstreamName: happyUpstreamIDPName, performedByUpstreamName: happyUpstreamIDPName,
args: happyExchangeAndValidateTokensArgs, args: happyExchangeAndValidateTokensArgs,
@ -857,6 +892,35 @@ func TestCallbackEndpoint(t *testing.T) {
args: happyExchangeAndValidateTokensArgs, args: happyExchangeAndValidateTokensArgs,
}, },
}, },
{
name: "using identity transformations which modify the username and group names",
idps: oidctestutil.NewUpstreamIDPListerBuilder().
WithOIDC(happyUpstream().WithTransformsForFederationDomain(prefixUsernameAndGroupsPipeline).Build()),
method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(),
csrfCookie: happyCSRFCookie,
wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: transformationUsernamePrefix + oidcUpstreamUsername,
wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, oidcUpstreamGroupMembership),
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
wantDownstreamNonce: downstreamNonce,
wantDownstreamClientID: downstreamPinnipedClientID,
wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(
transformationUsernamePrefix+oidcUpstreamUsername,
oidcUpstreamUsername,
oidcUpstreamGroupMembership,
),
wantAuthcodeExchangeCall: &expectedAuthcodeExchange{
performedByUpstreamName: happyUpstreamIDPName,
args: happyExchangeAndValidateTokensArgs,
},
},
// Pre-upstream-exchange verification // Pre-upstream-exchange verification
{ {
@ -1168,7 +1232,7 @@ func TestCallbackEndpoint(t *testing.T) {
}, },
}, },
{ {
name: "the OIDCIdentityProvider CRD has been deleted", name: "the OIDCIdentityProvider resource has been deleted",
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(otherUpstreamOIDCIdentityProvider), idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(otherUpstreamOIDCIdentityProvider),
method: http.MethodGet, method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(), path: newRequestPath().WithState(happyState).String(),
@ -1260,7 +1324,11 @@ func TestCallbackEndpoint(t *testing.T) {
wantDownstreamClientID: downstreamPinnipedClientID, wantDownstreamClientID: downstreamPinnipedClientID,
wantDownstreamPKCEChallenge: downstreamPKCEChallenge, wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod, wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(oidcUpstreamUsername, nil), wantDownstreamCustomSessionData: happyDownstreamCustomSessionDataWithUsernameAndGroups(
oidcUpstreamUsername,
oidcUpstreamUsername,
nil,
),
wantAuthcodeExchangeCall: &expectedAuthcodeExchange{ wantAuthcodeExchangeCall: &expectedAuthcodeExchange{
performedByUpstreamName: happyUpstreamIDPName, performedByUpstreamName: happyUpstreamIDPName,
args: happyExchangeAndValidateTokensArgs, args: happyExchangeAndValidateTokensArgs,
@ -1442,7 +1510,23 @@ func TestCallbackEndpoint(t *testing.T) {
args: happyExchangeAndValidateTokensArgs, args: happyExchangeAndValidateTokensArgs,
}, },
}, },
{
name: "using identity transformations which reject the authentication",
idps: oidctestutil.NewUpstreamIDPListerBuilder().
WithOIDC(happyUpstream().WithTransformsForFederationDomain(rejectAuthPipeline).Build()),
method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(),
csrfCookie: happyCSRFCookie,
wantStatus: http.StatusUnprocessableEntity,
wantContentType: htmlContentType,
wantBody: "Unprocessable Entity: configured identity policy rejected this authentication: authentication was rejected by a configured policy\n",
wantAuthcodeExchangeCall: &expectedAuthcodeExchange{
performedByUpstreamName: happyUpstreamIDPName,
args: happyExchangeAndValidateTokensArgs,
},
},
} }
for _, test := range tests { for _, test := range tests {
test := test test := test

View File

@ -0,0 +1,58 @@
// Copyright 2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package transformtestutil
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.pinniped.dev/internal/celtransformer"
"go.pinniped.dev/internal/idtransform"
)
func NewPrefixingPipeline(t *testing.T, usernamePrefix, groupsPrefix string) *idtransform.TransformationPipeline {
t.Helper()
transformer, err := celtransformer.NewCELTransformer(5 * time.Second)
require.NoError(t, err)
p := idtransform.NewTransformationPipeline()
userTransform, err := transformer.CompileTransformation(
&celtransformer.UsernameTransformation{Expression: fmt.Sprintf(`"%s" + username`, usernamePrefix)},
nil,
)
require.NoError(t, err)
p.AppendTransformation(userTransform)
groupsTransform, err := transformer.CompileTransformation(
&celtransformer.GroupsTransformation{Expression: fmt.Sprintf(`groups.map(g, "%s" + g)`, groupsPrefix)},
nil,
)
require.NoError(t, err)
p.AppendTransformation(groupsTransform)
return p
}
func NewRejectAllAuthPipeline(t *testing.T) *idtransform.TransformationPipeline {
t.Helper()
transformer, err := celtransformer.NewCELTransformer(5 * time.Second)
require.NoError(t, err)
p := idtransform.NewTransformationPipeline()
compiledTransform, err := transformer.CompileTransformation(
&celtransformer.AllowAuthenticationPolicy{Expression: `false`},
nil,
)
require.NoError(t, err)
p.AppendTransformation(compiledTransform)
return p
}