diff --git a/internal/federationdomain/endpoints/auth/auth_handler_test.go b/internal/federationdomain/endpoints/auth/auth_handler_test.go index d84546835..5a1dd4e35 100644 --- a/internal/federationdomain/endpoints/auth/auth_handler_test.go +++ b/internal/federationdomain/endpoints/auth/auth_handler_test.go @@ -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()) diff --git a/internal/federationdomain/endpoints/callback/callback_handler_test.go b/internal/federationdomain/endpoints/callback/callback_handler_test.go index 1e4189aa2..5950f798b 100644 --- a/internal/federationdomain/endpoints/callback/callback_handler_test.go +++ b/internal/federationdomain/endpoints/callback/callback_handler_test.go @@ -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()) + } }) } } diff --git a/internal/testutil/log_lines.go b/internal/testutil/log_lines.go index 7ada0a320..02ce4f243 100644 --- a/internal/testutil/log_lines.go +++ b/internal/testutil/log_lines.go @@ -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 {