Unit test response_mode=form_post in internal/oidc/callback.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
This commit is contained in:
Matt Moyer 2021-06-16 13:11:07 -05:00
parent c27eb17f23
commit 6d83ecb420
No known key found for this signature in database
GPG Key ID: EAE88AD172C5AE2D
3 changed files with 62 additions and 10 deletions

View File

@ -1156,7 +1156,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
require.Len(t, kubeClient.Actions(), test.wantUnnecessaryStoredRecords) require.Len(t, kubeClient.Actions(), test.wantUnnecessaryStoredRecords)
case test.wantRedirectLocationRegexp != "": case test.wantRedirectLocationRegexp != "":
require.Len(t, rsp.Header().Values("Location"), 1) require.Len(t, rsp.Header().Values("Location"), 1)
oidctestutil.RequireAuthcodeRedirectLocation( oidctestutil.RequireAuthCodeRegexpMatch(
t, t,
rsp.Header().Get("Location"), rsp.Header().Get("Location"),
test.wantRedirectLocationRegexp, test.wantRedirectLocationRegexp,

View File

@ -122,6 +122,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantContentType string wantContentType string
wantBody string wantBody string
wantRedirectLocationRegexp string wantRedirectLocationRegexp string
wantBodyFormResponseRegexp string
wantDownstreamGrantedScopes []string wantDownstreamGrantedScopes []string
wantDownstreamIDTokenSubject string wantDownstreamIDTokenSubject string
wantDownstreamIDTokenUsername string wantDownstreamIDTokenUsername string
@ -133,6 +134,32 @@ func TestCallbackEndpoint(t *testing.T) {
wantExchangeAndValidateTokensCall *oidctestutil.ExchangeAuthcodeAndValidateTokenArgs wantExchangeAndValidateTokensCall *oidctestutil.ExchangeAuthcodeAndValidateTokenArgs
}{ }{
{
name: "GET with good state and cookie and successful upstream token exchange with response_mode=form_post returns 200 with HTML+JS form",
idp: happyUpstream().Build(),
method: http.MethodGet,
path: newRequestPath().WithState(
happyUpstreamStateParam().WithAuthorizeRequestParams(
shallowCopyAndModifyQuery(
happyDownstreamRequestParamsQuery,
map[string]string{"response_mode": "form_post"},
).Encode(),
).Build(t, happyStateCodec),
).String(),
csrfCookie: happyCSRFCookie,
wantStatus: http.StatusOK,
wantContentType: "text/html;charset=UTF-8",
wantBodyFormResponseRegexp: `<input type="hidden" name="code" value="(.+)"/>`,
wantDownstreamIDTokenSubject: upstreamIssuer + "?sub=" + queryEscapedUpstreamSubject,
wantDownstreamIDTokenUsername: upstreamUsername,
wantDownstreamIDTokenGroups: upstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
wantDownstreamNonce: downstreamNonce,
wantDownstreamPKCEChallenge: downstreamPKCEChallenge,
wantDownstreamPKCEChallengeMethod: downstreamPKCEChallengeMethod,
wantExchangeAndValidateTokensCall: happyExchangeAndValidateTokensArgs,
},
{ {
name: "GET with good state and cookie and successful upstream token exchange returns 302 to downstream client callback with its state and code", name: "GET with good state and cookie and successful upstream token exchange returns 302 to downstream client callback with its state and code",
idp: happyUpstream().Build(), idp: happyUpstream().Build(),
@ -666,15 +693,40 @@ func TestCallbackEndpoint(t *testing.T) {
require.Equal(t, test.wantStatus, rsp.Code) require.Equal(t, test.wantStatus, rsp.Code)
testutil.RequireEqualContentType(t, rsp.Header().Get("Content-Type"), test.wantContentType) testutil.RequireEqualContentType(t, rsp.Header().Get("Content-Type"), test.wantContentType)
if test.wantBody != "" { switch {
// If we want a specific static response body, assert that.
case test.wantBody != "":
require.Equal(t, test.wantBody, rsp.Body.String()) require.Equal(t, test.wantBody, rsp.Body.String())
} else {
// Else if we want a body that contains a regex-matched auth code, assert that (for "response_mode=form_post").
case test.wantBodyFormResponseRegexp != "":
oidctestutil.RequireAuthCodeRegexpMatch(
t,
rsp.Body.String(),
test.wantBodyFormResponseRegexp,
client,
secrets,
oauthStore,
test.wantDownstreamGrantedScopes,
test.wantDownstreamIDTokenSubject,
test.wantDownstreamIDTokenUsername,
test.wantDownstreamIDTokenGroups,
test.wantDownstreamRequestedScopes,
test.wantDownstreamPKCEChallenge,
test.wantDownstreamPKCEChallengeMethod,
test.wantDownstreamNonce,
downstreamClientID,
downstreamRedirectURI,
)
// Otherwise, expect an empty response body.
default:
require.Empty(t, rsp.Body.String()) require.Empty(t, rsp.Body.String())
} }
if test.wantRedirectLocationRegexp != "" { //nolint:nestif // don't mind have several sequential if statements in this test if test.wantRedirectLocationRegexp != "" { //nolint:nestif // don't mind have several sequential if statements in this test
require.Len(t, rsp.Header().Values("Location"), 1) require.Len(t, rsp.Header().Values("Location"), 1)
oidctestutil.RequireAuthcodeRedirectLocation( oidctestutil.RequireAuthCodeRegexpMatch(
t, t,
rsp.Header().Get("Location"), rsp.Header().Get("Location"),
test.wantRedirectLocationRegexp, test.wantRedirectLocationRegexp,

View File

@ -235,10 +235,10 @@ func VerifyECDSAIDToken(
return token return token
} }
func RequireAuthcodeRedirectLocation( func RequireAuthCodeRegexpMatch(
t *testing.T, t *testing.T,
actualRedirectLocation string, actualContent string,
wantRedirectLocationRegexp string, wantRegexp string,
kubeClient *fake.Clientset, kubeClient *fake.Clientset,
secretsClient v1.SecretInterface, secretsClient v1.SecretInterface,
oauthStore fositestoragei.AllFositeStorage, oauthStore fositestoragei.AllFositeStorage,
@ -256,9 +256,9 @@ func RequireAuthcodeRedirectLocation(
t.Helper() t.Helper()
// Assert that Location header matches regular expression. // Assert that Location header matches regular expression.
regex := regexp.MustCompile(wantRedirectLocationRegexp) regex := regexp.MustCompile(wantRegexp)
submatches := regex.FindStringSubmatch(actualRedirectLocation) submatches := regex.FindStringSubmatch(actualContent)
require.Lenf(t, submatches, 2, "no regexp match in actualRedirectLocation: %q", actualRedirectLocation) require.Lenf(t, submatches, 2, "no regexp match in actualContent: %", actualContent)
capturedAuthCode := submatches[1] capturedAuthCode := submatches[1]
// fosite authcodes are in the format `data.signature`, so grab the signature part, which is the lookup key in the storage interface // fosite authcodes are in the format `data.signature`, so grab the signature part, which is the lookup key in the storage interface