From 24996b7eef80e5a0f62c13432deac19b45472693 Mon Sep 17 00:00:00 2001 From: Houssem Ben Mabrouk Date: Thu, 18 Apr 2024 15:30:42 +0200 Subject: [PATCH] adding tests for client_credentials flow Signed-off-by: Houssem Ben Mabrouk --- connector/oidc/oidc.go | 8 +- connector/oidc/oidc_test.go | 190 ++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+), 4 deletions(-) diff --git a/connector/oidc/oidc.go b/connector/oidc/oidc.go index bc793bc6e1..06485ba315 100644 --- a/connector/oidc/oidc.go +++ b/connector/oidc/oidc.go @@ -375,12 +375,12 @@ func (c *oidcConnector) getTokenViaClientCredentials(s connector.Scopes) (token // extract clientID & clientSecret from scopes for _, data := range s.Other { if strings.Contains(data, "id-") { - tokens := strings.Split(data, "id-") - clientID = tokens[len(tokens)-1] + scopeTokens := strings.Split(data, "id-") + clientID = scopeTokens[len(scopeTokens)-1] } if strings.Contains(data, "secret-") { - tokens := strings.Split(data, "secret-") - clientSecret = tokens[len(tokens)-1] + scopeTokens := strings.Split(data, "secret-") + clientSecret = scopeTokens[len(scopeTokens)-1] } } diff --git a/connector/oidc/oidc_test.go b/connector/oidc/oidc_test.go index 10550dfe79..e8ce0b8011 100644 --- a/connector/oidc/oidc_test.go +++ b/connector/oidc/oidc_test.go @@ -461,6 +461,169 @@ func TestHandleCallback(t *testing.T) { } } +func TestHandleClientCredentialsCallback(t *testing.T) { + t.Helper() + + tests := []struct { + name string + clientID string + clientSecret string + userIDKey string + userNameKey string + overrideClaimMapping bool + preferredUsernameKey string + emailKey string + groupsKey string + insecureSkipEmailVerified bool + scopes []string + expectUserID string + expectUserName string + expectGroups []string + expectPreferredUsername string + expectedEmailField string + token map[string]interface{} + newGroupFromClaims []NewGroupFromClaims + expectedHandlerError error + }{ + { + name: "withCorrectScopes", + userIDKey: "", // not configured + userNameKey: "", // not configured + clientID: "", // not configured + clientSecret: "", // not configured + expectUserID: "subvalue", + expectUserName: "namevalue", + expectGroups: nil, + expectedEmailField: "emailvalue", + scopes: []string{"openid", "id-clientidvalue", "secret-clientsecretvalue"}, + token: map[string]interface{}{ + "sub": "subvalue", + "name": "namevalue", + "email": "emailvalue", + "email_verified": false, + }, + expectedHandlerError: nil, + }, + { + name: "withCorrectScopesAndConfiguredClientCredentials", + userIDKey: "", // not configured + userNameKey: "", // not configured + clientID: "defaultClientID", + clientSecret: "defaultClientSecret", + expectUserID: "subvalue", + expectUserName: "namevalue", + expectGroups: nil, + expectedEmailField: "emailvalue", + scopes: []string{"openid", "id-clientidvalue", "secret-clientsecretvalue"}, + token: map[string]interface{}{ + "sub": "subvalue", + "name": "namevalue", + "email": "emailvalue", + "email_verified": false, + }, + expectedHandlerError: fmt.Errorf("expected audience \"defaultClientID\""), + }, + { + name: "withoutCredentials", + userIDKey: "", // not configured + userNameKey: "", // not configured + clientID: "", // not configured + clientSecret: "", // not configured + expectUserID: "", + expectUserName: "", + expectGroups: nil, + expectedEmailField: "", + scopes: []string{"openid"}, + token: nil, + expectedHandlerError: fmt.Errorf("oidc: unable to parse clientID or clientSecret"), + }, + { + name: "missingSingleCredentialPrefix", + userIDKey: "", // not configured + userNameKey: "", // not configured + clientID: "", // not configured + clientSecret: "", // not configured + expectUserID: "", + expectUserName: "", + expectGroups: nil, + expectedEmailField: "", + scopes: []string{"openid", "id-clientidvalue", "clientsecretvalue"}, + token: nil, + expectedHandlerError: fmt.Errorf("oidc: unable to parse clientID or clientSecret"), + }, + { + name: "missingBothCredentialPrefixes", + userIDKey: "", // not configured + userNameKey: "", // not configured + clientID: "", // not configured + clientSecret: "", // not configured + expectUserID: "", + expectUserName: "", + expectGroups: nil, + expectedEmailField: "", + scopes: []string{"openid", "clientidvalue", "clientsecretvalue"}, + token: nil, + expectedHandlerError: fmt.Errorf("oidc: unable to parse clientID or clientSecret"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + idTokenDesired := true + testServer, err := setupServer(tc.token, idTokenDesired) + if err != nil { + t.Fatal("failed to setup test server", err) + } + defer testServer.Close() + + serverURL := testServer.URL + basicAuth := true + config := Config{ + Issuer: serverURL, + ClientID: tc.clientID, + ClientSecret: tc.clientSecret, + Scopes: tc.scopes, + RedirectURI: fmt.Sprintf("%s/callback", serverURL), + UserIDKey: tc.userIDKey, + UserNameKey: tc.userNameKey, + InsecureSkipEmailVerified: tc.insecureSkipEmailVerified, + InsecureEnableGroups: true, + BasicAuthUnsupported: &basicAuth, + OverrideClaimMapping: tc.overrideClaimMapping, + } + config.ClaimMapping.PreferredUsernameKey = tc.preferredUsernameKey + config.ClaimMapping.EmailKey = tc.emailKey + config.ClaimMapping.GroupsKey = tc.groupsKey + config.ClaimMutations.NewGroupFromClaims = tc.newGroupFromClaims + + conn, err := newConnector(config) + if err != nil { + t.Fatal("failed to create new connector", err) + } + req, err := newRequestWithoutAuthCode(testServer.URL) + if err != nil { + t.Fatal("failed to create request", err) + } + + // mimic the functionality of server/oauth2 parseScopes + s := connector.Scopes{} + s.Other = append(s.Other, tc.scopes...) + + identity, err := conn.HandleCallback(s, req) + compareErrors(t, err, tc.expectedHandlerError) + if err != nil { + return + } + expectEquals(t, identity.UserID, tc.expectUserID) + expectEquals(t, identity.Username, tc.expectUserName) + expectEquals(t, identity.PreferredUsername, tc.expectPreferredUsername) + expectEquals(t, identity.Email, tc.expectedEmailField) + expectEquals(t, identity.EmailVerified, false) + expectEquals(t, identity.Groups, tc.expectGroups) + }) + } +} + func TestRefresh(t *testing.T) { t.Helper() @@ -828,6 +991,15 @@ func newRequestWithAuthCode(serverURL string, code string) (*http.Request, error return req, nil } +func newRequestWithoutAuthCode(serverURL string) (*http.Request, error) { + req, err := http.NewRequest("GET", serverURL, nil) + if err != nil { + return nil, fmt.Errorf("failed to create request: %v", err) + } + + return req, nil +} + func n(pub *rsa.PublicKey) string { return encode(pub.N.Bytes()) } @@ -848,3 +1020,21 @@ func expectEquals(t *testing.T, a interface{}, b interface{}) { t.Errorf("Expected %+v to equal %+v", a, b) } } + +func compareErrors(t *testing.T, a error, b error) { + if a == nil && b == nil { + return + } + if a == nil && b != nil { + t.Errorf("Expected \"%+v\" to be nil", b) + return + } + if a != nil && b == nil { + t.Errorf("Expected \"%+v\" to be \"%+v\"", b, a) + return + } + + if !strings.Contains(a.Error(), b.Error()) { + t.Errorf("Expected \"%+v\" to be a part of \"%+v\"", b, a) + } +}