diff --git a/pkg/login/social/connectors/gitlab_oauth.go b/pkg/login/social/connectors/gitlab_oauth.go index 8545040aa0f..917a4503218 100644 --- a/pkg/login/social/connectors/gitlab_oauth.go +++ b/pkg/login/social/connectors/gitlab_oauth.go @@ -302,6 +302,8 @@ func (s *SocialGitlab) extractFromToken(ctx context.Context, client *http.Client data.Groups = userInfo.Groups } + data.raw = rawJSON + s.log.Debug("Resolved user data", "data", fmt.Sprintf("%+v", data)) return &data, nil } diff --git a/pkg/login/social/connectors/gitlab_oauth_test.go b/pkg/login/social/connectors/gitlab_oauth_test.go index 09a621f645f..60fdc27d6ee 100644 --- a/pkg/login/social/connectors/gitlab_oauth_test.go +++ b/pkg/login/social/connectors/gitlab_oauth_test.go @@ -37,6 +37,8 @@ const ( rootUserRespBody = `{"id":1,"username":"root","name":"Administrator","state":"active","email":"root@example.org", "confirmed_at":"2022-09-13T19:38:04.891Z","is_admin":true,"namespace_id":1}` editorUserRespBody = `{"id":3,"username":"gitlab-editor","name":"Gitlab Editor","state":"active","email":"gitlab-editor@example.org", "confirmed_at":"2022-09-13T19:38:04.891Z","is_admin":false,"namespace_id":1}` + editorUserIDToken = `{"sub":"3","preferred_username":"gitlab-editor","name":"Gitlab Editor","email":"gitlab-editor@example.org","email_verified":true,"groups_direct":["editors", "viewers"]}` // #nosec G101 not a hardcoded credential + adminGroup = `{"id":4,"web_url":"http://grafana-gitlab.local/groups/admins","name":"Admins","path":"admins","project_creation_level":"developer","full_name":"Admins","full_path":"admins","created_at":"2022-09-13T19:38:04.891Z"}` editorGroup = `{"id":5,"web_url":"http://grafana-gitlab.local/groups/editors","name":"Editors","path":"editors","project_creation_level":"developer","full_name":"Editors","full_path":"editors","created_at":"2022-09-13T19:38:15.074Z"}` viewerGroup = `{"id":6,"web_url":"http://grafana-gitlab.local/groups/viewers","name":"Viewers","path":"viewers","project_creation_level":"developer","full_name":"Viewers","full_path":"viewers","created_at":"2022-09-13T19:38:25.777Z"}` @@ -61,6 +63,7 @@ func TestSocialGitlab_UserInfo(t *testing.T) { GroupsRespBody string GroupHeaders map[string]string RoleAttributePath string + IDToken string ExpectedLogin string ExpectedEmail string ExpectedRoles map[int64]org.RoleType @@ -180,6 +183,24 @@ func TestSocialGitlab_UserInfo(t *testing.T) { ExpectedEmail: "gitlab-editor@example.org", ExpectedRoles: map[int64]org.RoleType{4: "Editor", 5: "Viewer"}, }, + { + Name: "Maps roles from ID token attributes if available", + RoleAttributePath: `email=='gitlab-editor@example.org' && 'Editor' || 'Viewer'`, + IDToken: editorUserIDToken, + ExpectedLogin: "gitlab-editor", + ExpectedEmail: "gitlab-editor@example.org", + ExpectedRoles: map[int64]org.RoleType{1: "Editor"}, + ExpectedGrafanaAdmin: nilPointer, + }, + { + Name: "Maps groups from ID token groups if available", + RoleAttributePath: gitlabAttrPath, + IDToken: editorUserIDToken, + ExpectedLogin: "gitlab-editor", + ExpectedEmail: "gitlab-editor@example.org", + ExpectedRoles: map[int64]org.RoleType{1: "Editor"}, + ExpectedGrafanaAdmin: nilPointer, + }, { Name: "Should return error when neither role attribute path nor org mapping evaluates to a role and role attribute strict is enabled", Cfg: conf{RoleAttributeStrict: true, OrgMapping: []string{"other:Org4:Editor"}}, @@ -230,8 +251,17 @@ func TestSocialGitlab_UserInfo(t *testing.T) { require.Fail(t, "unexpected request URI: "+r.RequestURI) } })) + + token := &oauth2.Token{} + if tt.IDToken != "" { + emptyJWTHeader := base64.RawURLEncoding.EncodeToString([]byte("{}")) + JWTBody := base64.RawURLEncoding.EncodeToString([]byte(tt.IDToken)) + idToken := fmt.Sprintf("%s.%s.signature", emptyJWTHeader, JWTBody) + token = token.WithExtra(map[string]any{"id_token": idToken}) + } + provider.info.ApiUrl = ts.URL + apiURI - actualResult, err := provider.UserInfo(context.Background(), ts.Client(), &oauth2.Token{}) + actualResult, err := provider.UserInfo(context.Background(), ts.Client(), token) if tt.ExpectedError != nil { require.ErrorIs(t, err, tt.ExpectedError) return @@ -382,6 +412,9 @@ func TestSocialGitlab_extractFromToken(t *testing.T) { } for _, tc := range testCases { + if tc.wantUser != nil { + tc.wantUser.raw = []byte(tc.payload) + } t.Run(tc.name, func(t *testing.T) { // Create a test client with a dummy token client := oauth2.NewClient(context.Background(), &tokenSource{accessToken: "dummy_access_token"})