Auth: Add org to role mappings support to Okta integration (#88770)

* Add org mapping support to Okta

* Update docs and configs

* Prettier docs

* Apply suggestions from code review

Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>

* Improve tests

---------

Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
This commit is contained in:
Misi 2024-06-06 10:35:06 +02:00 committed by GitHub
parent 50b3269ef0
commit 4f2a9a47f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 184 additions and 67 deletions

View File

@ -783,6 +783,8 @@ allowed_domains =
allowed_groups =
role_attribute_path =
role_attribute_strict = false
org_attribute_path =
org_mapping =
allow_assign_grafana_admin = false
skip_org_role_sync = false
tls_skip_verify_insecure = false

View File

@ -715,6 +715,8 @@
;allowed_groups =
;role_attribute_path =
;role_attribute_strict = false
; org_attribute_path =
; org_mapping =
;allow_assign_grafana_admin = false
;skip_org_role_sync = false
;use_pkce = true

View File

@ -116,7 +116,7 @@ Refer to [configuration options]({{< relref "#configuration-options" >}}) for mo
If no valid role is found, the user is assigned the role specified by [the `auto_assign_org_role` option]({{< relref "../../../configure-grafana#auto_assign_org_role" >}}).
You can disable this default role assignment by setting `role_attribute_strict = true`. This setting denies user access if no role or an invalid role is returned after evaluating the `role_attribute_path` and the `org_mapping` expressions.
You can use the `org_attribute_path` and `org_mapping` configuration options to assign the user to organizations and specify their role. For more information, refer to [Org roles mapping example](#org-roles-mapping-example). If both org role mapping (`org_mapping`) and the regular role mapping (`role_attribute_path`) are specified, then the user will get the highest of the two mapped roles.
You can use the `org_mapping` configuration options to assign the user to organizations and specify their role based on their GitHub team membership. For more information, refer to [Org roles mapping example](#org-roles-mapping-example). If both org role mapping (`org_mapping`) and the regular role mapping (`role_attribute_path`) are specified, then the user will get the highest of the two mapped roles.
To ease configuration of a proper JMESPath expression, go to [JMESPath](http://jmespath.org/) to test and evaluate expressions with custom payloads.
@ -231,7 +231,7 @@ The table below describes all GitHub OAuth configuration options. Like any other
| `allow_sign_up` | No | Whether to allow new Grafana user creation through GitHub login. If set to `false`, then only existing Grafana users can log in with GitHub OAuth. | `true` |
| `auto_login` | No | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` |
| `role_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the user information obtained from the UserInfo endpoint. If no role is found, Grafana creates a JSON data with `groups` key that maps to GitHub teams obtained from GitHub's [`/api/user/teams`](https://docs.github.com/en/rest/teams/teams#list-teams-for-the-authenticated-user) endpoint, and evaluates the expression using this data. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping](#org-roles-mapping-example). | |
| `role_attribute_strict` | No | et to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping](#org-roles-mapping-example). | `false` |
| `role_attribute_strict` | No | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping](#org-roles-mapping-example). | `false` |
| `org_mapping` | No | List of comma- or space-separated `<ExternalGitHubTeamName>:<OrgIdOrName>:<Role>` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | |
| `allow_assign_grafana_admin` | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` |
| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. | `false` |

View File

@ -212,7 +212,9 @@ At the configuration file, extend the `scopes` in `[auth.okta]` section with `of
The user's role is retrieved using a [JMESPath](http://jmespath.org/examples.html) expression from the `role_attribute_path` configuration option against the `api_url` (`/userinfo` OIDC endpoint) endpoint payload.
If no valid role is found, the user is assigned the role specified by [the `auto_assign_org_role` option]({{< relref "../../../configure-grafana#auto_assign_org_role" >}}).
You can disable this default role assignment by setting `role_attribute_strict = true`. This setting denies user access if no role or an invalid role is returned.
You can disable this default role assignment by setting `role_attribute_strict = true`. This setting denies user access if no role or an invalid role is returned after evaluating the `role_attribute_path` and the `org_mapping` expressions.
You can use the `org_attribute_path` and `org_mapping` configuration options to assign the user to organizations and specify their role. For more information, refer to [Org roles mapping example](#org-roles-mapping-example). If both org role mapping (`org_mapping`) and the regular role mapping (`role_attribute_path`) are specified, then the user will get the highest of the two mapped roles.
To allow mapping Grafana server administrator role, use the `allow_assign_grafana_admin` configuration option.
Refer to [configuration options]({{< relref "../generic-oauth/index.md#configuration-options" >}}) for more information.
@ -223,6 +225,17 @@ If you want to map the role based on the user's group, you can use the `groups`
To learn about adding custom claims to the user info in Okta, refer to [add custom claims](https://developer.okta.com/docs/guides/customize-tokens-returned-from-okta/main/#add-a-custom-claim-to-a-token).
#### Org roles mapping example
In this example, the `org_mapping` uses the `groups` attribute as the source (`org_attribute_path`) to map the current user to different organizations and roles. The user has been granted the role of a `Viewer` in the `org_foo` org if they are a member of the `Group 1` group, the role of an `Editor` in the `org_bar` org if they are a member of the `Group 2` group, and the role of an `Editor` in the `org_baz`(OrgID=3) org.
Config:
```ini
org_attribute_path = groups
org_mapping = ["Group 1:org_foo:Viewer", "Group 2:org_bar:Editor", "*:3:Editor"]
```
### Configure team synchronization (Enterprise only)
> **Note:** Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise" >}}) and [Grafana Cloud]({{< relref "../../../../introduction/grafana-cloud" >}}).
@ -254,7 +267,9 @@ The following table outlines the various Okta OIDC configuration options. You ca
| `allow_sign_up` | No | Controls Grafana user creation through the Okta OIDC login. Only existing Grafana users can log in with Okta OIDC if set to `false`. | `true` |
| `auto_login` | No | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` |
| `role_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the Okta OIDC ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | |
| `role_attribute_strict` | No | Set to `true` to deny user login if the Grafana role cannot be extracted using `role_attribute_path`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` |
| `role_attribute_strict` | No | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` |
| `org_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | |
| `org_mapping` | No | List of comma- or space-separated `<ExternalOrgName>:<OrgIdOrName>:<Role>` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | |
| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` |
| `allowed_groups` | No | List of comma- or space-separated groups. The user should be a member of at least one group to log in. | |
| `allowed_domains` | No | List comma- or space-separated domains. The user should belong to at least one domain to log in. | |

View File

@ -11,7 +11,6 @@ import (
"golang.org/x/oauth2"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/models/roletype"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ssosettings"
@ -139,32 +138,41 @@ func (s *SocialOkta) UserInfo(ctx context.Context, client *http.Client, token *o
return nil, errMissingGroupMembership
}
var role roletype.RoleType
var isGrafanaAdmin *bool
userInfo := &social.BasicUserInfo{
Id: claims.ID,
Name: claims.Name,
Email: email,
Login: email,
Groups: groups,
}
if !s.info.SkipOrgRoleSync {
var grafanaAdmin bool
role, grafanaAdmin, err = s.extractRoleAndAdmin(data.rawJSON, groups)
directlyMappedRole, grafanaAdmin, err := s.extractRoleAndAdminOptional(data.rawJSON, groups)
if err != nil {
return nil, err
s.log.Warn("Failed to extract role", "err", err)
}
if s.info.AllowAssignGrafanaAdmin {
isGrafanaAdmin = &grafanaAdmin
userInfo.IsGrafanaAdmin = &grafanaAdmin
}
externalOrgs, err := s.extractOrgs(data.rawJSON)
if err != nil {
s.log.Warn("Failed to extract orgs", "err", err)
return nil, err
}
userInfo.OrgRoles = s.orgRoleMapper.MapOrgRoles(s.orgMappingCfg, externalOrgs, directlyMappedRole)
if s.info.RoleAttributeStrict && len(userInfo.OrgRoles) == 0 {
return nil, errRoleAttributeStrictViolation.Errorf("could not evaluate any valid roles using IdP provided data")
}
}
if s.info.AllowAssignGrafanaAdmin && s.info.SkipOrgRoleSync {
s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other")
}
return &social.BasicUserInfo{
Id: claims.ID,
Name: claims.Name,
Email: email,
Login: email,
Role: role,
IsGrafanaAdmin: isGrafanaAdmin,
Groups: groups,
}, nil
return userInfo, nil
}
func (s *SocialOkta) extractAPI(ctx context.Context, data *OktaUserInfoJson, client *http.Client) error {

View File

@ -13,9 +13,10 @@ import (
"golang.org/x/oauth2"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/models/roletype"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgtest"
"github.com/grafana/grafana/pkg/services/ssosettings"
ssoModels "github.com/grafana/grafana/pkg/services/ssosettings/models"
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingstests"
@ -29,63 +30,142 @@ func TestSocialOkta_UserInfo(t *testing.T) {
tests := []struct {
name string
userRawJSON string
OAuth2Extra any
autoAssignOrgRole string
settingSkipOrgRoleSync bool
oAuth2Extra any
skipOrgRoleSync bool
allowAssignGrafanaAdmin bool
RoleAttributePath string
ExpectedEmail string
ExpectedRole roletype.RoleType
ExpectedGrafanaAdmin *bool
ExpectedErr error
wantErr bool
roleAttributePath string
roleAttributeStrict bool
orgMapping []string
orgAttributePath string
expectedEmail string
expectedOrgRoles map[int64]org.RoleType
expectedGrafanaAdmin *bool
expectedErr error
}{
{
name: "Should give role from JSON and email from id token",
name: "should give role from JSON and email from id token",
userRawJSON: `{ "email": "okta-octopus@grafana.com", "role": "Admin" }`,
RoleAttributePath: "role",
OAuth2Extra: map[string]any{
roleAttributePath: "role",
oAuth2Extra: map[string]any{
// {
// "email": "okto.octopus@test.com"
// },
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQWRtaW4iLCJlbWFpbCI6Im9rdG8ub2N0b3B1c0B0ZXN0LmNvbSJ9.yhg0nvYCpMVCVrRvwtmHzhF0RJqid_YFbjJ_xuBCyHs",
},
ExpectedEmail: "okto.octopus@test.com",
ExpectedRole: "Admin",
ExpectedGrafanaAdmin: boolPointer,
wantErr: false,
expectedEmail: "okto.octopus@test.com",
expectedOrgRoles: map[int64]org.RoleType{1: org.RoleAdmin},
expectedGrafanaAdmin: boolPointer,
},
{
name: "Should give empty role and nil pointer for GrafanaAdmin when skip org role sync enable",
userRawJSON: `{ "email": "okta-octopus@grafana.com", "role": "Admin" }`,
RoleAttributePath: "role",
settingSkipOrgRoleSync: true,
OAuth2Extra: map[string]any{
name: "should give empty role and nil pointer for GrafanaAdmin when skip org role sync enable",
userRawJSON: `{ "email": "okta-octopus@grafana.com", "role": "Admin" }`,
roleAttributePath: "role",
skipOrgRoleSync: true,
oAuth2Extra: map[string]any{
// {
// "email": "okto.octopus@test.com"
// },
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQWRtaW4iLCJlbWFpbCI6Im9rdG8ub2N0b3B1c0B0ZXN0LmNvbSJ9.yhg0nvYCpMVCVrRvwtmHzhF0RJqid_YFbjJ_xuBCyHs",
},
ExpectedEmail: "okto.octopus@test.com",
ExpectedRole: "",
ExpectedGrafanaAdmin: boolPointer,
wantErr: false,
expectedEmail: "okto.octopus@test.com",
expectedOrgRoles: nil,
expectedGrafanaAdmin: boolPointer,
},
{
name: "Should give grafanaAdmin role for specific GrafanaAdmin in the role assignement",
name: "should give grafanaAdmin role for specific GrafanaAdmin in the role assignement",
userRawJSON: fmt.Sprintf(`{ "email": "okta-octopus@grafana.com", "role": "%s" }`, social.RoleGrafanaAdmin),
RoleAttributePath: "role",
roleAttributePath: "role",
allowAssignGrafanaAdmin: true,
OAuth2Extra: map[string]any{
oAuth2Extra: map[string]any{
// {
// "email": "okto.octopus@test.com"
// },
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQWRtaW4iLCJlbWFpbCI6Im9rdG8ub2N0b3B1c0B0ZXN0LmNvbSJ9.yhg0nvYCpMVCVrRvwtmHzhF0RJqid_YFbjJ_xuBCyHs",
},
ExpectedEmail: "okto.octopus@test.com",
ExpectedRole: "Admin",
ExpectedGrafanaAdmin: trueBoolPtr(),
wantErr: false,
expectedEmail: "okto.octopus@test.com",
expectedOrgRoles: map[int64]org.RoleType{1: org.RoleAdmin},
expectedGrafanaAdmin: trueBoolPtr(),
},
{
name: "should fallback to default org role when role attribute path is empty",
userRawJSON: fmt.Sprintf(`{ "email": "okta-octopus@grafana.com", "groups": ["Group 1"], "role": "%s" }`, org.RoleEditor),
oAuth2Extra: map[string]any{
// {
// "email": "okto.octopus@test.com"
// },
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQWRtaW4iLCJlbWFpbCI6Im9rdG8ub2N0b3B1c0B0ZXN0LmNvbSJ9.yhg0nvYCpMVCVrRvwtmHzhF0RJqid_YFbjJ_xuBCyHs",
},
expectedEmail: "okto.octopus@test.com",
expectedOrgRoles: map[int64]org.RoleType{1: org.RoleViewer},
},
{
name: "should map role when only org mapping is set",
userRawJSON: fmt.Sprintf(`{ "email": "okta-octopus@grafana.com", "groups": ["Group 1"], "role": "%s" }`, org.RoleEditor),
orgAttributePath: "groups",
orgMapping: []string{"Group 1:Org4:Editor", "*:Org5:Viewer"},
roleAttributeStrict: false,
oAuth2Extra: map[string]any{
// {
// "email": "okto.octopus@test.com"
// },
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQWRtaW4iLCJlbWFpbCI6Im9rdG8ub2N0b3B1c0B0ZXN0LmNvbSJ9.yhg0nvYCpMVCVrRvwtmHzhF0RJqid_YFbjJ_xuBCyHs",
},
expectedEmail: "okto.octopus@test.com",
expectedOrgRoles: map[int64]org.RoleType{4: org.RoleEditor, 5: org.RoleViewer},
},
{
name: "should map role when only org mapping is set and role attribute strict is enabled",
userRawJSON: fmt.Sprintf(`{ "email": "okta-octopus@grafana.com", "groups": ["Group 1"], "role": "%s" }`, org.RoleEditor),
orgAttributePath: "groups",
orgMapping: []string{"Group 1:Org4:Editor", "*:Org5:Viewer"},
roleAttributeStrict: true,
oAuth2Extra: map[string]any{
// {
// "email": "okto.octopus@test.com"
// },
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQWRtaW4iLCJlbWFpbCI6Im9rdG8ub2N0b3B1c0B0ZXN0LmNvbSJ9.yhg0nvYCpMVCVrRvwtmHzhF0RJqid_YFbjJ_xuBCyHs",
},
expectedEmail: "okto.octopus@test.com",
expectedOrgRoles: map[int64]org.RoleType{4: org.RoleEditor, 5: org.RoleViewer},
},
{
name: "should return nil OrgRoles when SkipOrgRoleSync is enabled",
userRawJSON: fmt.Sprintf(`{ "email": "okta-octopus@grafana.com", "role": "%s" }`, org.RoleEditor),
roleAttributePath: "role",
skipOrgRoleSync: true,
oAuth2Extra: map[string]any{
// {
// "email": "okto.octopus@test.com"
// },
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQWRtaW4iLCJlbWFpbCI6Im9rdG8ub2N0b3B1c0B0ZXN0LmNvbSJ9.yhg0nvYCpMVCVrRvwtmHzhF0RJqid_YFbjJ_xuBCyHs",
},
expectedOrgRoles: nil,
expectedEmail: "okto.octopus@test.com",
},
{
name: "should return error when neither role attribute path nor org mapping evaluates to a role and role attribute strict is enabled",
userRawJSON: fmt.Sprintf(`{ "email": "okta-octopus@grafana.com", "role": "%s" }`, org.RoleEditor),
roleAttributePath: "invalid_role_path",
roleAttributeStrict: true,
oAuth2Extra: map[string]any{
// {
// "email": "okto.octopus@test.com"
// },
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQWRtaW4iLCJlbWFpbCI6Im9rdG8ub2N0b3B1c0B0ZXN0LmNvbSJ9.yhg0nvYCpMVCVrRvwtmHzhF0RJqid_YFbjJ_xuBCyHs",
},
expectedErr: errRoleAttributeStrictViolation,
},
{
name: "should return error when neither role attribute path nor org mapping is set and role attribute strict is enabled",
userRawJSON: fmt.Sprintf(`{ "email": "okta-octopus@grafana.com", "role": "%s" }`, org.RoleEditor),
roleAttributeStrict: true,
oAuth2Extra: map[string]any{
// {
// "email": "okto.octopus@test.com"
// },
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQWRtaW4iLCJlbWFpbCI6Im9rdG8ub2N0b3B1c0B0ZXN0LmNvbSJ9.yhg0nvYCpMVCVrRvwtmHzhF0RJqid_YFbjJ_xuBCyHs",
},
expectedErr: errRoleAttributeStrictViolation,
},
}
for _, tt := range tests {
@ -103,17 +183,23 @@ func TestSocialOkta_UserInfo(t *testing.T) {
}))
defer server.Close()
cfg := &setting.Cfg{
AutoAssignOrgRole: "Viewer", // default role
}
provider := NewOktaProvider(
&social.OAuthInfo{
ApiUrl: server.URL + "/user",
RoleAttributePath: tt.RoleAttributePath,
RoleAttributePath: tt.roleAttributePath,
RoleAttributeStrict: tt.roleAttributeStrict,
OrgMapping: tt.orgMapping,
OrgAttributePath: tt.orgAttributePath,
AllowAssignGrafanaAdmin: tt.allowAssignGrafanaAdmin,
SkipOrgRoleSync: tt.settingSkipOrgRoleSync,
SkipOrgRoleSync: tt.skipOrgRoleSync,
},
&setting.Cfg{
AutoAssignOrgRole: tt.autoAssignOrgRole,
},
nil,
cfg,
ProvideOrgRoleMapper(cfg,
&orgtest.FakeOrgService{ExpectedOrgs: []*org.OrgDTO{{ID: 4, Name: "Org4"}, {ID: 5, Name: "Org5"}}}),
&ssosettingstests.MockService{},
featuremgmt.WithFeatures())
@ -124,15 +210,19 @@ func TestSocialOkta_UserInfo(t *testing.T) {
RefreshToken: "",
Expiry: time.Now(),
}
token := staticToken.WithExtra(tt.OAuth2Extra)
got, err := provider.UserInfo(context.Background(), server.Client(), token)
if (err != nil) != tt.wantErr {
t.Errorf("UserInfo() error = %v, wantErr %v", err, tt.wantErr)
token := staticToken.WithExtra(tt.oAuth2Extra)
actual, err := provider.UserInfo(context.Background(), server.Client(), token)
if tt.expectedErr != nil {
require.Error(t, err)
require.ErrorIs(t, err, tt.expectedErr)
return
}
require.Equal(t, tt.ExpectedEmail, got.Email)
require.Equal(t, tt.ExpectedRole, got.Role)
require.Equal(t, tt.ExpectedGrafanaAdmin, got.IsGrafanaAdmin)
require.Equal(t, tt.expectedEmail, actual.Email)
require.Equal(t, tt.expectedOrgRoles, actual.OrgRoles)
require.Equal(t, tt.expectedGrafanaAdmin, actual.IsGrafanaAdmin)
})
}
}

View File

@ -168,7 +168,7 @@ func (c *OAuth) Authenticate(ctx context.Context, r *authn.Request) (*authn.Iden
// This is required to implement OrgRole mapping for OAuth providers step by step
switch c.providerName {
case social.GenericOAuthProviderName, social.GitHubProviderName, social.GitlabProviderName:
case social.GenericOAuthProviderName, social.GitHubProviderName, social.GitlabProviderName, social.OktaProviderName:
// Do nothing, these providers already supports OrgRole mapping
default:
userInfo.OrgRoles, userInfo.IsGrafanaAdmin, _ = getRoles(c.cfg, func() (org.RoleType, *bool, error) {