Add audit event tests for callback_handler

This commit is contained in:
Joshua Casey
2024-11-01 12:25:55 -05:00
parent 4df043a91c
commit dd56f2b47f
3 changed files with 175 additions and 67 deletions

View File

@@ -35,7 +35,6 @@ import (
"go.pinniped.dev/internal/federationdomain/endpoints/jwks"
"go.pinniped.dev/internal/federationdomain/oidc"
"go.pinniped.dev/internal/federationdomain/oidcclientvalidator"
"go.pinniped.dev/internal/federationdomain/requestlogger"
"go.pinniped.dev/internal/federationdomain/stateparam"
"go.pinniped.dev/internal/federationdomain/storage"
"go.pinniped.dev/internal/here"
@@ -660,10 +659,6 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
prefixUsernameAndGroupsPipeline := transformtestutil.NewPrefixingPipeline(t, transformationUsernamePrefix, transformationGroupsPrefix)
rejectAuthPipeline := transformtestutil.NewRejectAllAuthPipeline(t)
wantAuditLog := func(message string, params map[string]any) testutil.WantedAuditLog {
return testutil.WantAuditLog(message, params, "some-audit-id")
}
type testCase struct {
name string
@@ -730,20 +725,20 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantBodyStringWithLocationInHref: true,
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": false,
"Pinniped-Password": false,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": "client_id=pinniped-cli&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&pinniped_idp_name=some-oidc-idp&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+profile+email+username+groups&state=redacted",
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-oidc-idp",
"resourceName": "some-oidc-idp",
"resourceUID": "oidc-resource-uid",
"type": "oidc",
}),
wantAuditLog("Upstream Authorize Redirect", map[string]any{
testutil.WantAuditLog("Upstream Authorize Redirect", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
}
@@ -768,20 +763,20 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantBodyStringWithLocationInHref: true,
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": false,
"Pinniped-Password": false,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": `client_id=` + dynamicClientID + `&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&pinniped_idp_name=some-oidc-idp&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+offline_access+pinniped%3Arequest-audience+username+groups&state=redacted`,
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-oidc-idp",
"resourceName": "some-oidc-idp",
"resourceUID": "oidc-resource-uid",
"type": "oidc",
}),
wantAuditLog("Upstream Authorize Redirect", map[string]any{
testutil.WantAuditLog("Upstream Authorize Redirect", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
}
@@ -805,20 +800,20 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantBodyStringWithLocationInHref: true,
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": false,
"Pinniped-Password": false,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": "client_id=pinniped-cli&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&pinniped_idp_name=some-github-idp&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+profile+email+username+groups&state=redacted",
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-github-idp",
"resourceName": "some-github-idp",
"resourceUID": "github-resource-uid",
"type": "github",
}),
wantAuditLog("Upstream Authorize Redirect", map[string]any{
testutil.WantAuditLog("Upstream Authorize Redirect", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
}
@@ -843,20 +838,20 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantBodyStringWithLocationInHref: true,
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": false,
"Pinniped-Password": false,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": `client_id=` + dynamicClientID + `&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&pinniped_idp_name=some-github-idp&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+offline_access+pinniped%3Arequest-audience+username+groups&state=redacted`,
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-github-idp",
"resourceName": "some-github-idp",
"resourceUID": "github-resource-uid",
"type": "github",
}),
wantAuditLog("Upstream Authorize Redirect", map[string]any{
testutil.WantAuditLog("Upstream Authorize Redirect", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
}
@@ -880,20 +875,20 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantBodyStringWithLocationInHref: true,
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": false,
"Pinniped-Password": false,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": `client_id=pinniped-cli&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&pinniped_idp_name=some-ldap-idp&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+profile+email+username+groups&state=redacted`,
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-ldap-idp",
"resourceName": "some-ldap-idp",
"resourceUID": "ldap-resource-uid",
"type": "ldap",
}),
wantAuditLog("Upstream Authorize Redirect", map[string]any{
testutil.WantAuditLog("Upstream Authorize Redirect", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
}
@@ -918,20 +913,20 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantBodyStringWithLocationInHref: true,
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": false,
"Pinniped-Password": false,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": `client_id=pinniped-cli&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+profile+email+username+groups&state=redacted`,
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-oidc-idp",
"resourceName": "some-oidc-idp",
"resourceUID": "oidc-resource-uid",
"type": "oidc",
}),
wantAuditLog("Upstream Authorize Redirect", map[string]any{
testutil.WantAuditLog("Upstream Authorize Redirect", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
}
@@ -957,11 +952,11 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantBodyStringWithLocationInHref: true,
wantAuditLogs: func(_ stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": false,
"Pinniped-Password": false,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": `client_id=pinniped-cli&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+profile+email+username+groups&state=redacted`,
}),
}
@@ -987,20 +982,20 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantBodyStringWithLocationInHref: true,
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": false,
"Pinniped-Password": false,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": `client_id=pinniped-cli&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&pinniped_idp_name=some-oidc-idp&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+profile+email+username+groups&state=redacted`,
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-oidc-idp",
"resourceName": "some-oidc-idp",
"resourceUID": "oidc-resource-uid",
"type": "oidc",
}),
wantAuditLog("Upstream Authorize Redirect", map[string]any{
testutil.WantAuditLog("Upstream Authorize Redirect", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
}
@@ -1101,20 +1096,20 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantDownstreamCustomSessionData: expectedHappyOIDCPasswordGrantCustomSession,
wantAuditLogs: func(_ stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": true,
"Pinniped-Password": true,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": `client_id=pinniped-cli&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&pinniped_idp_name=some-password-granting-oidc-idp&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+profile+email+username+groups&state=redacted`,
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-password-granting-oidc-idp",
"resourceName": "some-password-granting-oidc-idp",
"resourceUID": "some-password-granting-resource-uid",
"type": "oidc",
}),
wantAuditLog("Identity From Upstream IDP", map[string]any{
testutil.WantAuditLog("Identity From Upstream IDP", map[string]any{
"upstreamIDPDisplayName": "some-password-granting-oidc-idp",
"upstreamIDPResourceName": "some-password-granting-oidc-idp",
"upstreamIDPResourceUID": "some-password-granting-resource-uid",
@@ -1122,7 +1117,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
"upstreamUsername": "test-oidc-pinniped-username",
"upstreamGroups": []any{"test-pinniped-group-0", "test-pinniped-group-1"},
}),
wantAuditLog("Session Started", map[string]any{
testutil.WantAuditLog("Session Started", map[string]any{
"sessionID": sessionID,
"username": "test-oidc-pinniped-username",
"groups": []any{"test-pinniped-group-0", "test-pinniped-group-1"},
@@ -1176,20 +1171,20 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantBodyString: "",
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": true,
"Pinniped-Password": true,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": `client_id=pinniped-cli&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&pinniped_idp_name=some-password-granting-oidc-idp&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+profile+email+username+groups&state=redacted`,
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-password-granting-oidc-idp",
"resourceName": "some-password-granting-oidc-idp",
"resourceUID": "some-password-granting-resource-uid",
"type": "oidc",
}),
wantAuditLog("Identity From Upstream IDP", map[string]any{
testutil.WantAuditLog("Identity From Upstream IDP", map[string]any{
"upstreamIDPDisplayName": "some-password-granting-oidc-idp",
"upstreamIDPResourceName": "some-password-granting-oidc-idp",
"upstreamIDPResourceUID": "some-password-granting-resource-uid",
@@ -1197,7 +1192,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
"upstreamUsername": "test-oidc-pinniped-username",
"upstreamGroups": []any{"test-pinniped-group-0", "test-pinniped-group-1"},
}),
wantAuditLog("Authentication Rejected By Transforms", map[string]any{
testutil.WantAuditLog("Authentication Rejected By Transforms", map[string]any{
"reason": "configured identity policy rejected this authentication: authentication was rejected by a configured policy",
}),
}
@@ -1287,20 +1282,20 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantDownstreamCustomSessionData: expectedHappyLDAPUpstreamCustomSession,
wantAuditLogs: func(_ stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": true,
"Pinniped-Password": true,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": `client_id=pinniped-cli&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&pinniped_idp_name=some-ldap-idp&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+profile+email+username+groups&state=redacted`,
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-ldap-idp",
"resourceName": "some-ldap-idp",
"resourceUID": "ldap-resource-uid",
"type": "ldap",
}),
wantAuditLog("Identity From Upstream IDP", map[string]any{
testutil.WantAuditLog("Identity From Upstream IDP", map[string]any{
"upstreamIDPDisplayName": "some-ldap-idp",
"upstreamIDPResourceName": "some-ldap-idp",
"upstreamIDPResourceUID": "ldap-resource-uid",
@@ -1308,7 +1303,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
"upstreamUsername": "some-ldap-username-from-authenticator",
"upstreamGroups": []any{"group1", "group2", "group3"},
}),
wantAuditLog("Session Started", map[string]any{
testutil.WantAuditLog("Session Started", map[string]any{
"sessionID": sessionID,
"username": "some-ldap-username-from-authenticator",
"groups": []any{"group1", "group2", "group3"},
@@ -1347,20 +1342,20 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
),
wantAuditLogs: func(_ stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": true,
"Pinniped-Password": true,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": `client_id=pinniped-cli&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&pinniped_idp_name=some-ldap-idp&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+profile+email+username+groups&state=redacted`,
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-ldap-idp",
"resourceName": "some-ldap-idp",
"resourceUID": "ldap-resource-uid",
"type": "ldap",
}),
wantAuditLog("Identity From Upstream IDP", map[string]any{
testutil.WantAuditLog("Identity From Upstream IDP", map[string]any{
"upstreamIDPDisplayName": "some-ldap-idp",
"upstreamIDPResourceName": "some-ldap-idp",
"upstreamIDPResourceUID": "ldap-resource-uid",
@@ -1368,7 +1363,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
"upstreamUsername": "some-ldap-username-from-authenticator",
"upstreamGroups": []any{"group1", "group2", "group3"},
}),
wantAuditLog("Session Started", map[string]any{
testutil.WantAuditLog("Session Started", map[string]any{
"sessionID": sessionID,
"username": "username_prefix:some-ldap-username-from-authenticator",
"groups": []any{"groups_prefix:group1", "groups_prefix:group2", "groups_prefix:group3"},
@@ -1719,14 +1714,14 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantBodyString: "",
wantAuditLogs: func(_ stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
wantAuditLog("HTTP Request Custom Headers Used", map[string]any{
testutil.WantAuditLog("HTTP Request Custom Headers Used", map[string]any{
"Pinniped-Username": false,
"Pinniped-Password": false,
}),
wantAuditLog("HTTP Request Parameters", map[string]any{
testutil.WantAuditLog("HTTP Request Parameters", map[string]any{
"params": `client_id=pinniped-cli&code_challenge=redacted&code_challenge_method=S256&nonce=redacted&pinniped_idp_name=some-oidc-idp&prompt=none&redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&response_type=code&scope=openid+profile+email+username+groups&state=redacted`,
}),
wantAuditLog("Using Upstream IDP", map[string]any{
testutil.WantAuditLog("Using Upstream IDP", map[string]any{
"displayName": "some-oidc-idp",
"resourceName": "some-oidc-idp",
"resourceUID": "oidc-resource-uid",
@@ -3810,9 +3805,6 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
}
rsp := httptest.NewRecorder()
req, _ = requestlogger.NewRequestWithAuditID(req, func() string {
return "some-audit-id"
})
subject.ServeHTTP(rsp, req)
t.Logf("response: %#v", rsp)
t.Logf("response body: %q", rsp.Body.String())

View File

@@ -247,6 +247,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantDownstreamAdditionalClaims map[string]any
wantOIDCAuthcodeExchangeCall *expectedOIDCAuthcodeExchange
wantGitHubAuthcodeExchangeCall *expectedGitHubAuthcodeExchange
wantAuditLogs func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog
}{
{
name: "OIDC: GET with good state and cookie and successful upstream token exchange with response_mode=form_post returns 200 with HTML+JS form",
@@ -278,6 +279,29 @@ func TestCallbackEndpoint(t *testing.T) {
performedByUpstreamName: happyOIDCUpstreamIDPName,
args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs,
},
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
testutil.WantAuditLog("AuthorizeID From Parameters", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
testutil.WantAuditLog("Identity From Upstream IDP", map[string]any{
"upstreamIDPDisplayName": "upstream-oidc-idp-name",
"upstreamIDPType": "oidc",
"upstreamIDPResourceName": "upstream-oidc-idp-name",
"upstreamIDPResourceUID": "upstream-oidc-resource-uid",
"upstreamUsername": "test-pinniped-username",
"upstreamGroups": []any{"test-pinniped-group-0", "test-pinniped-group-1"},
}),
testutil.WantAuditLog("Session Started", map[string]any{
"sessionID": sessionID,
"username": "test-pinniped-username",
"groups": []any{"test-pinniped-group-0", "test-pinniped-group-1"},
"subject": "https://my-upstream-issuer.com?idpName=upstream-oidc-idp-name&sub=abc123-some+guid",
"additionalClaims": map[string]any{}, // json: {}
"warnings": []any{}, // json: []
}),
}
},
},
{
name: "GitHub: GET with good state and cookie and successful upstream token exchange with response_mode=form_post returns 200 with HTML+JS form",
@@ -309,6 +333,29 @@ func TestCallbackEndpoint(t *testing.T) {
performedByUpstreamName: happyGithubIDPName,
args: happyGitHubUpstreamExchangeAuthcodeArgs,
},
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
testutil.WantAuditLog("AuthorizeID From Parameters", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
testutil.WantAuditLog("Identity From Upstream IDP", map[string]any{
"upstreamIDPDisplayName": "upstream-github-idp-name",
"upstreamIDPType": "github",
"upstreamIDPResourceName": "upstream-github-idp-name",
"upstreamIDPResourceUID": "upstream-github-idp-resource-uid",
"upstreamUsername": "some-github-login",
"upstreamGroups": []any{"org1/team1", "org2/team2"},
}),
testutil.WantAuditLog("Session Started", map[string]any{
"sessionID": sessionID,
"username": "some-github-login",
"groups": []any{"org1/team1", "org2/team2"},
"subject": "https://github.com?idpName=upstream-github-idp-name&sub=some-github-login",
"additionalClaims": nil, // json: null
"warnings": []any{}, // json: []
}),
}
},
},
{
name: "GET with good state and cookie with additional params",
@@ -657,6 +704,13 @@ func TestCallbackEndpoint(t *testing.T) {
performedByUpstreamName: happyOIDCUpstreamIDPName,
args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs,
},
wantAuditLogs: func(encodedStateParam stateparam.Encoded, _ string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
testutil.WantAuditLog("AuthorizeID From Parameters", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
}
},
},
{
name: "return an error when upstream IDP returned no refresh token and no access token",
@@ -1088,6 +1142,9 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusBadRequest,
wantContentType: htmlContentType,
wantBody: "Bad Request: state param not found\n",
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{}
},
},
{
name: "state param was not signed correctly, has expired, or otherwise cannot be decoded for any reason",
@@ -1720,6 +1777,24 @@ func TestCallbackEndpoint(t *testing.T) {
performedByUpstreamName: happyOIDCUpstreamIDPName,
args: happyOIDCUpstreamExchangeAuthcodeAndValidateTokenArgs,
},
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
testutil.WantAuditLog("AuthorizeID From Parameters", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
testutil.WantAuditLog("Identity From Upstream IDP", map[string]any{
"upstreamIDPDisplayName": "upstream-oidc-idp-name",
"upstreamIDPType": "oidc",
"upstreamIDPResourceName": "upstream-oidc-idp-name",
"upstreamIDPResourceUID": "upstream-oidc-resource-uid",
"upstreamUsername": "test-pinniped-username",
"upstreamGroups": []any{"test-pinniped-group-0", "test-pinniped-group-1"},
}),
testutil.WantAuditLog("Authentication Rejected By Transforms", map[string]any{
"reason": "configured identity policy rejected this authentication: authentication was rejected by a configured policy",
}),
}
},
},
{
name: "GitHub: using identity transformations which reject the authentication",
@@ -1735,6 +1810,24 @@ func TestCallbackEndpoint(t *testing.T) {
performedByUpstreamName: happyGithubIDPName,
args: happyGitHubUpstreamExchangeAuthcodeArgs,
},
wantAuditLogs: func(encodedStateParam stateparam.Encoded, sessionID string) []testutil.WantedAuditLog {
return []testutil.WantedAuditLog{
testutil.WantAuditLog("AuthorizeID From Parameters", map[string]any{
"authorizeID": encodedStateParam.AuthorizeID(),
}),
testutil.WantAuditLog("Identity From Upstream IDP", map[string]any{
"upstreamIDPDisplayName": "upstream-github-idp-name",
"upstreamIDPType": "github",
"upstreamIDPResourceName": "upstream-github-idp-name",
"upstreamIDPResourceUID": "upstream-github-idp-resource-uid",
"upstreamUsername": "some-github-login",
"upstreamGroups": []any{"org1/team1", "org2/team2"},
}),
testutil.WantAuditLog("Authentication Rejected By Transforms", map[string]any{
"reason": "configured identity policy rejected this authentication: authentication was rejected by a configured policy",
}),
}
},
},
}
@@ -1759,13 +1852,15 @@ func TestCallbackEndpoint(t *testing.T) {
jwksProviderIsUnused := jwks.NewDynamicJWKSProvider()
oauthHelper := oidc.FositeOauth2Helper(oauthStore, downstreamIssuer, hmacSecretFunc, jwksProviderIsUnused, timeoutsConfiguration)
logger, log := plog.TestLogger(t)
subject := NewHandler(
test.idps.BuildFederationDomainIdentityProvidersListerFinder(),
oauthHelper,
happyStateCodec,
happyCookieCodec,
happyUpstreamRedirectURI,
plog.New(),
logger,
)
reqContext := context.WithValue(context.Background(), struct{ name string }{name: "test"}, "request-context")
@@ -1800,6 +1895,8 @@ func TestCallbackEndpoint(t *testing.T) {
require.Equal(t, test.wantStatus, rsp.Code)
testutil.RequireEqualContentType(t, rsp.Header().Get("Content-Type"), test.wantContentType)
sessionID := ""
switch {
// If we want a specific static response body, assert that.
case test.wantBody != "":
@@ -1807,7 +1904,7 @@ func TestCallbackEndpoint(t *testing.T) {
// 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(
sessionID = oidctestutil.RequireAuthCodeRegexpMatch(
t,
rsp.Body.String(),
test.wantBodyFormResponseRegexp,
@@ -1835,7 +1932,7 @@ func TestCallbackEndpoint(t *testing.T) {
if test.wantRedirectLocationRegexp != "" {
require.Len(t, rsp.Header().Values("Location"), 1)
_ = oidctestutil.RequireAuthCodeRegexpMatch(
sessionID = oidctestutil.RequireAuthCodeRegexpMatch(
t,
rsp.Header().Get("Location"),
test.wantRedirectLocationRegexp,
@@ -1856,6 +1953,19 @@ func TestCallbackEndpoint(t *testing.T) {
test.wantDownstreamAdditionalClaims,
)
}
if test.wantAuditLogs != nil {
var encodedStateParam stateparam.Encoded
if test.path != "" {
var path *url.URL
path, err = url.Parse(test.path)
require.NoError(t, err)
encodedStateParam = stateparam.Encoded(path.Query().Get("state"))
}
wantAuditLogs := test.wantAuditLogs(encodedStateParam, sessionID)
testutil.CompareAuditLogs(t, wantAuditLogs, log.String())
}
})
}
}

View File

@@ -27,13 +27,13 @@ type WantedAuditLog struct {
Params map[string]any
}
func WantAuditLog(message string, params map[string]any, auditID string) WantedAuditLog {
func WantAuditLog(message string, params map[string]any, auditID ...string) WantedAuditLog {
result := WantedAuditLog{
Message: message,
Params: params,
}
if auditID != "" {
result.Params["auditID"] = auditID
if len(auditID) > 0 {
result.Params["auditID"] = auditID[0]
}
return result
}
@@ -41,6 +41,12 @@ func WantAuditLog(message string, params map[string]any, auditID string) WantedA
func CompareAuditLogs(t *testing.T, wantAuditLogs []WantedAuditLog, actualAuditLogsOneLiner string) {
t.Helper()
// There are tests that verify that no audit events were emitted
if len(wantAuditLogs) == 0 {
require.Empty(t, actualAuditLogsOneLiner, "no audit events were expected, but some were found")
return
}
wantJsonAuditLogs := make([]map[string]any, 0)
wantMessages := make([]string, 0)
for _, wantAuditLog := range wantAuditLogs {