mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Auth: Add org to role mappings support to Gitlab integration (#88751)
* Conf: Add org_mapping and org_attribute_path to github and gitlab conf * Gitlab: Implement org role mapping * Update docs --------- Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
This commit is contained in:
parent
1ceb9e8e9d
commit
f28905f8c4
@ -647,6 +647,7 @@ team_ids =
|
|||||||
allowed_organizations =
|
allowed_organizations =
|
||||||
role_attribute_path =
|
role_attribute_path =
|
||||||
role_attribute_strict = false
|
role_attribute_strict = false
|
||||||
|
org_mapping =
|
||||||
allow_assign_grafana_admin = false
|
allow_assign_grafana_admin = false
|
||||||
skip_org_role_sync = false
|
skip_org_role_sync = false
|
||||||
tls_skip_verify_insecure = false
|
tls_skip_verify_insecure = false
|
||||||
@ -674,6 +675,7 @@ allowed_domains =
|
|||||||
allowed_groups =
|
allowed_groups =
|
||||||
role_attribute_path =
|
role_attribute_path =
|
||||||
role_attribute_strict = false
|
role_attribute_strict = false
|
||||||
|
org_mapping =
|
||||||
allow_assign_grafana_admin = false
|
allow_assign_grafana_admin = false
|
||||||
skip_org_role_sync = false
|
skip_org_role_sync = false
|
||||||
tls_skip_verify_insecure = false
|
tls_skip_verify_insecure = false
|
||||||
|
@ -608,6 +608,7 @@
|
|||||||
;allowed_organizations =
|
;allowed_organizations =
|
||||||
;role_attribute_path =
|
;role_attribute_path =
|
||||||
;role_attribute_strict = false
|
;role_attribute_strict = false
|
||||||
|
;org_mapping =
|
||||||
;allow_assign_grafana_admin = false
|
;allow_assign_grafana_admin = false
|
||||||
;skip_org_role_sync = false
|
;skip_org_role_sync = false
|
||||||
|
|
||||||
@ -629,6 +630,7 @@
|
|||||||
;allowed_groups =
|
;allowed_groups =
|
||||||
;role_attribute_path =
|
;role_attribute_path =
|
||||||
;role_attribute_strict = false
|
;role_attribute_strict = false
|
||||||
|
;org_mapping =
|
||||||
;allow_assign_grafana_admin = false
|
;allow_assign_grafana_admin = false
|
||||||
;skip_org_role_sync = false
|
;skip_org_role_sync = false
|
||||||
;tls_skip_verify_insecure = false
|
;tls_skip_verify_insecure = false
|
||||||
|
@ -149,8 +149,7 @@ To map the server administrator role, use the `allow_assign_grafana_admin` confi
|
|||||||
Refer to [configuration options]({{< relref "#configuration-options" >}}) for more information.
|
Refer to [configuration options]({{< relref "#configuration-options" >}}) for more information.
|
||||||
|
|
||||||
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" >}}).
|
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`.
|
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.
|
||||||
This setting denies user access if no role or an invalid role is returned.
|
|
||||||
|
|
||||||
To ease configuration of a proper JMESPath expression, go to [JMESPath](http://jmespath.org/) to test and evaluate expressions with custom payloads.
|
To ease configuration of a proper JMESPath expression, go to [JMESPath](http://jmespath.org/) to test and evaluate expressions with custom payloads.
|
||||||
|
|
||||||
@ -158,6 +157,20 @@ To ease configuration of a proper JMESPath expression, go to [JMESPath](http://j
|
|||||||
|
|
||||||
This section includes examples of JMESPath expressions used for role mapping.
|
This section includes examples of JMESPath expressions used for role mapping.
|
||||||
|
|
||||||
|
##### Org roles mapping example
|
||||||
|
|
||||||
|
The GitLab integration uses the external users' groups in the `org_mapping` configuration to map organizations and roles based on their GitLab group membership.
|
||||||
|
|
||||||
|
In this example, the user has been granted the role of a `Viewer` in the `org_foo` organization, and the role of an `Editor` in the `org_bar` and `org_baz` orgs.
|
||||||
|
|
||||||
|
The external user is part of the following GitLab groups: `groupd-1` and `group-2`.
|
||||||
|
|
||||||
|
Config:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
org_mapping = group-1:org_foo:Viewer groupd-1:org_bar:Editor *:org_baz:Editor
|
||||||
|
```
|
||||||
|
|
||||||
#### Map roles using user information from OAuth token
|
#### Map roles using user information from OAuth token
|
||||||
|
|
||||||
In this example, the user with email `admin@company.com` has been granted the `Admin` role.
|
In this example, the user with email `admin@company.com` has been granted the `Admin` role.
|
||||||
@ -251,6 +264,7 @@ The table below describes all GitLab OAuth configuration options. Like any other
|
|||||||
| `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` |
|
| `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 GitLab OAuth token. If no role is found, Grafana creates a JSON data with `groups` key that maps to groups obtained from GitLab's `/oauth/userinfo` endpoint, and evaluates the expression using this data. Finally, if a valid role is still not found, the expression is evaluated against the user information retrieved from `api_url/users` endpoint and groups retrieved from `api_url/groups` 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_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the GitLab OAuth token. If no role is found, Grafana creates a JSON data with `groups` key that maps to groups obtained from GitLab's `/oauth/userinfo` endpoint, and evaluates the expression using this data. Finally, if a valid role is still not found, the expression is evaluated against the user information retrieved from `api_url/users` endpoint and groups retrieved from `api_url/groups` 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 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` |
|
||||||
|
| `org_mapping` | No | List of comma- or space-separated `<ExternalGitlabGroupName>:<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` |
|
| `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` |
|
| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. | `false` |
|
||||||
| `allowed_domains` | No | List of comma or space-separated domains. User must belong to at least one domain to log in. | |
|
| `allowed_domains` | No | List of comma or space-separated domains. User must belong to at least one domain to log in. | |
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/login/social"
|
"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/auth/identity"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/ssosettings"
|
"github.com/grafana/grafana/pkg/services/ssosettings"
|
||||||
@ -49,9 +48,8 @@ type userData struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Groups []string `json:"groups_direct"`
|
Groups []string `json:"groups_direct"`
|
||||||
|
|
||||||
EmailVerified bool `json:"email_verified"`
|
EmailVerified bool `json:"email_verified"`
|
||||||
Role roletype.RoleType `json:"-"`
|
raw []byte
|
||||||
IsGrafanaAdmin *bool `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGitLabProvider(info *social.OAuthInfo, cfg *setting.Cfg, orgRoleMapper *OrgRoleMapper, ssoSettings ssosettings.Service, features featuremgmt.FeatureToggles) *SocialGitlab {
|
func NewGitLabProvider(info *social.OAuthInfo, cfg *setting.Cfg, orgRoleMapper *OrgRoleMapper, ssoSettings ssosettings.Service, features featuremgmt.FeatureToggles) *SocialGitlab {
|
||||||
@ -195,23 +193,37 @@ func (s *SocialGitlab) UserInfo(ctx context.Context, client *http.Client, token
|
|||||||
}
|
}
|
||||||
|
|
||||||
userInfo := &social.BasicUserInfo{
|
userInfo := &social.BasicUserInfo{
|
||||||
Id: data.ID,
|
Id: data.ID,
|
||||||
Name: data.Name,
|
Name: data.Name,
|
||||||
Login: data.Login,
|
Login: data.Login,
|
||||||
Email: data.Email,
|
Email: data.Email,
|
||||||
Groups: data.Groups,
|
Groups: data.Groups,
|
||||||
Role: data.Role,
|
|
||||||
IsGrafanaAdmin: data.IsGrafanaAdmin,
|
|
||||||
}
|
|
||||||
|
|
||||||
if !s.isGroupMember(data.Groups) {
|
|
||||||
return nil, errMissingGroupMembership
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.info.AllowAssignGrafanaAdmin && s.info.SkipOrgRoleSync {
|
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")
|
s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !s.info.SkipOrgRoleSync {
|
||||||
|
directlyMappedRole, grafanaAdmin, err := s.extractRoleAndAdminOptional(data.raw, userInfo.Groups)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Warn("Failed to extract role", "err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.info.AllowAssignGrafanaAdmin {
|
||||||
|
userInfo.IsGrafanaAdmin = &grafanaAdmin
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfo.OrgRoles = s.orgRoleMapper.MapOrgRoles(s.orgMappingCfg, userInfo.Groups, 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.isGroupMember(data.Groups) {
|
||||||
|
return nil, errMissingGroupMembership
|
||||||
|
}
|
||||||
|
|
||||||
return userInfo, nil
|
return userInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,20 +253,7 @@ func (s *SocialGitlab) extractFromAPI(ctx context.Context, client *http.Client,
|
|||||||
Email: apiResp.Email,
|
Email: apiResp.Email,
|
||||||
Name: apiResp.Name,
|
Name: apiResp.Name,
|
||||||
Groups: s.getGroups(ctx, client),
|
Groups: s.getGroups(ctx, client),
|
||||||
}
|
raw: response.Body,
|
||||||
|
|
||||||
if !s.info.SkipOrgRoleSync {
|
|
||||||
var grafanaAdmin bool
|
|
||||||
role, grafanaAdmin, err := s.extractRoleAndAdmin(response.Body, idData.Groups)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.info.AllowAssignGrafanaAdmin {
|
|
||||||
idData.IsGrafanaAdmin = &grafanaAdmin
|
|
||||||
}
|
|
||||||
|
|
||||||
idData.Role = role
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.cfg.Env == setting.Dev {
|
if s.cfg.Env == setting.Dev {
|
||||||
@ -300,19 +299,6 @@ func (s *SocialGitlab) extractFromToken(ctx context.Context, client *http.Client
|
|||||||
data.Groups = userInfo.Groups
|
data.Groups = userInfo.Groups
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.info.SkipOrgRoleSync {
|
|
||||||
role, grafanaAdmin, errRole := s.extractRoleAndAdmin(rawJSON, data.Groups)
|
|
||||||
if errRole != nil {
|
|
||||||
return nil, errRole
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.info.AllowAssignGrafanaAdmin {
|
|
||||||
data.IsGrafanaAdmin = &grafanaAdmin
|
|
||||||
}
|
|
||||||
|
|
||||||
data.Role = role
|
|
||||||
}
|
|
||||||
|
|
||||||
s.log.Debug("Resolved user data", "data", fmt.Sprintf("%+v", data))
|
s.log.Debug("Resolved user data", "data", fmt.Sprintf("%+v", data))
|
||||||
return &data, nil
|
return &data, nil
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
|
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
||||||
"github.com/grafana/grafana/pkg/services/ssosettings"
|
"github.com/grafana/grafana/pkg/services/ssosettings"
|
||||||
ssoModels "github.com/grafana/grafana/pkg/services/ssosettings/models"
|
ssoModels "github.com/grafana/grafana/pkg/services/ssosettings/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingstests"
|
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingstests"
|
||||||
@ -45,13 +46,12 @@ const (
|
|||||||
func TestSocialGitlab_UserInfo(t *testing.T) {
|
func TestSocialGitlab_UserInfo(t *testing.T) {
|
||||||
var nilPointer *bool
|
var nilPointer *bool
|
||||||
|
|
||||||
provider := NewGitLabProvider(&social.OAuthInfo{SkipOrgRoleSync: false}, &setting.Cfg{}, nil, &ssosettingstests.MockService{}, featuremgmt.WithFeatures())
|
|
||||||
|
|
||||||
type conf struct {
|
type conf struct {
|
||||||
AllowAssignGrafanaAdmin bool
|
AllowAssignGrafanaAdmin bool
|
||||||
RoleAttributeStrict bool
|
RoleAttributeStrict bool
|
||||||
AutoAssignOrgRole org.RoleType
|
AutoAssignOrgRole org.RoleType
|
||||||
SkipOrgRoleSync bool
|
SkipOrgRoleSync bool
|
||||||
|
OrgMapping []string
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@ -63,7 +63,7 @@ func TestSocialGitlab_UserInfo(t *testing.T) {
|
|||||||
RoleAttributePath string
|
RoleAttributePath string
|
||||||
ExpectedLogin string
|
ExpectedLogin string
|
||||||
ExpectedEmail string
|
ExpectedEmail string
|
||||||
ExpectedRole org.RoleType
|
ExpectedRoles map[int64]org.RoleType
|
||||||
ExpectedGrafanaAdmin *bool
|
ExpectedGrafanaAdmin *bool
|
||||||
ExpectedError error
|
ExpectedError error
|
||||||
}{
|
}{
|
||||||
@ -81,7 +81,7 @@ func TestSocialGitlab_UserInfo(t *testing.T) {
|
|||||||
RoleAttributePath: gitlabAttrPath,
|
RoleAttributePath: gitlabAttrPath,
|
||||||
ExpectedLogin: "root",
|
ExpectedLogin: "root",
|
||||||
ExpectedEmail: "root@example.org",
|
ExpectedEmail: "root@example.org",
|
||||||
ExpectedRole: "Admin",
|
ExpectedRoles: map[int64]org.RoleType{1: "Admin"},
|
||||||
ExpectedGrafanaAdmin: trueBoolPtr(),
|
ExpectedGrafanaAdmin: trueBoolPtr(),
|
||||||
},
|
},
|
||||||
{ // Edge case, user in Viewer Group, Server Admin disabled but attribute path contains a condition for Server Admin => User has the Admin role
|
{ // Edge case, user in Viewer Group, Server Admin disabled but attribute path contains a condition for Server Admin => User has the Admin role
|
||||||
@ -98,7 +98,7 @@ func TestSocialGitlab_UserInfo(t *testing.T) {
|
|||||||
RoleAttributePath: gitlabAttrPath,
|
RoleAttributePath: gitlabAttrPath,
|
||||||
ExpectedLogin: "root",
|
ExpectedLogin: "root",
|
||||||
ExpectedEmail: "root@example.org",
|
ExpectedEmail: "root@example.org",
|
||||||
ExpectedRole: "Admin",
|
ExpectedRoles: map[int64]org.RoleType{1: "Admin"},
|
||||||
ExpectedGrafanaAdmin: nil,
|
ExpectedGrafanaAdmin: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -109,7 +109,7 @@ func TestSocialGitlab_UserInfo(t *testing.T) {
|
|||||||
RoleAttributePath: gitlabAttrPath,
|
RoleAttributePath: gitlabAttrPath,
|
||||||
ExpectedLogin: "gitlab-editor",
|
ExpectedLogin: "gitlab-editor",
|
||||||
ExpectedEmail: "gitlab-editor@example.org",
|
ExpectedEmail: "gitlab-editor@example.org",
|
||||||
ExpectedRole: "Editor",
|
ExpectedRoles: map[int64]org.RoleType{1: "Editor"},
|
||||||
ExpectedGrafanaAdmin: falseBoolPtr(),
|
ExpectedGrafanaAdmin: falseBoolPtr(),
|
||||||
GroupHeaders: map[string]string{
|
GroupHeaders: map[string]string{
|
||||||
// All headers omitted to test that the provider does not make a second request
|
// All headers omitted to test that the provider does not make a second request
|
||||||
@ -123,7 +123,7 @@ func TestSocialGitlab_UserInfo(t *testing.T) {
|
|||||||
RoleAttributePath: gitlabAttrPath,
|
RoleAttributePath: gitlabAttrPath,
|
||||||
ExpectedLogin: "gitlab-editor",
|
ExpectedLogin: "gitlab-editor",
|
||||||
ExpectedEmail: "gitlab-editor@example.org",
|
ExpectedEmail: "gitlab-editor@example.org",
|
||||||
ExpectedRole: "",
|
ExpectedRoles: nil,
|
||||||
ExpectedGrafanaAdmin: nilPointer,
|
ExpectedGrafanaAdmin: nilPointer,
|
||||||
},
|
},
|
||||||
{ // Fallback to autoAssignOrgRole
|
{ // Fallback to autoAssignOrgRole
|
||||||
@ -134,7 +134,7 @@ func TestSocialGitlab_UserInfo(t *testing.T) {
|
|||||||
RoleAttributePath: gitlabAttrPath,
|
RoleAttributePath: gitlabAttrPath,
|
||||||
ExpectedLogin: "gitlab-editor",
|
ExpectedLogin: "gitlab-editor",
|
||||||
ExpectedEmail: "gitlab-editor@example.org",
|
ExpectedEmail: "gitlab-editor@example.org",
|
||||||
ExpectedRole: "Admin",
|
ExpectedRoles: map[int64]org.RoleType{1: "Admin"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Strict mode prevents fallback to default",
|
Name: "Strict mode prevents fallback to default",
|
||||||
@ -146,13 +146,13 @@ func TestSocialGitlab_UserInfo(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{ // Edge case, no match, no strict mode and no fallback => User has the Viewer role (hard coded)
|
{ // Edge case, no match, no strict mode and no fallback => User has the Viewer role (hard coded)
|
||||||
Name: "Fallback with no default will create a user with a default role",
|
Name: "Fallback with no default will create a user with a default role",
|
||||||
Cfg: conf{},
|
Cfg: conf{AutoAssignOrgRole: org.RoleViewer},
|
||||||
UserRespBody: editorUserRespBody,
|
UserRespBody: editorUserRespBody,
|
||||||
GroupsRespBody: "[]",
|
GroupsRespBody: "[]",
|
||||||
RoleAttributePath: gitlabAttrPath,
|
RoleAttributePath: gitlabAttrPath,
|
||||||
ExpectedLogin: "gitlab-editor",
|
ExpectedLogin: "gitlab-editor",
|
||||||
ExpectedEmail: "gitlab-editor@example.org",
|
ExpectedEmail: "gitlab-editor@example.org",
|
||||||
ExpectedRole: "Viewer",
|
ExpectedRoles: map[int64]org.RoleType{1: "Viewer"},
|
||||||
},
|
},
|
||||||
{ // Edge case, no attribute path with strict mode => Error
|
{ // Edge case, no attribute path with strict mode => Error
|
||||||
Name: "Strict mode with no attribute path",
|
Name: "Strict mode with no attribute path",
|
||||||
@ -160,49 +160,88 @@ func TestSocialGitlab_UserInfo(t *testing.T) {
|
|||||||
UserRespBody: editorUserRespBody,
|
UserRespBody: editorUserRespBody,
|
||||||
GroupsRespBody: "[" + strings.Join([]string{editorGroup}, ",") + "]",
|
GroupsRespBody: "[" + strings.Join([]string{editorGroup}, ",") + "]",
|
||||||
RoleAttributePath: "",
|
RoleAttributePath: "",
|
||||||
ExpectedError: errRoleAttributePathNotSet,
|
ExpectedError: errRoleAttributeStrictViolation,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Should map role when only org mapping is set",
|
||||||
|
Cfg: conf{OrgMapping: []string{"editors:Org4:Editor", "*:Org5:Viewer"}},
|
||||||
|
UserRespBody: editorUserRespBody,
|
||||||
|
GroupsRespBody: "[" + strings.Join([]string{editorGroup}, ",") + "]",
|
||||||
|
ExpectedLogin: "gitlab-editor",
|
||||||
|
ExpectedEmail: "gitlab-editor@example.org",
|
||||||
|
ExpectedRoles: map[int64]org.RoleType{4: "Editor", 5: "Viewer"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Should map role when only org mapping is set and role attribute strict is enabled",
|
||||||
|
Cfg: conf{OrgMapping: []string{"editors:Org4:Editor", "*:Org5:Viewer"}, RoleAttributeStrict: true},
|
||||||
|
UserRespBody: editorUserRespBody,
|
||||||
|
GroupsRespBody: "[" + strings.Join([]string{editorGroup}, ",") + "]",
|
||||||
|
ExpectedLogin: "gitlab-editor",
|
||||||
|
ExpectedEmail: "gitlab-editor@example.org",
|
||||||
|
ExpectedRoles: map[int64]org.RoleType{4: "Editor", 5: "Viewer"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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"}},
|
||||||
|
UserRespBody: editorUserRespBody,
|
||||||
|
GroupsRespBody: "[" + strings.Join([]string{editorGroup}, ",") + "]",
|
||||||
|
ExpectedError: errRoleAttributeStrictViolation,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "should return error when neither role attribute path nor org mapping is set and role attribute strict is enabled",
|
||||||
|
Cfg: conf{RoleAttributeStrict: true},
|
||||||
|
UserRespBody: editorUserRespBody,
|
||||||
|
GroupsRespBody: "[" + strings.Join([]string{editorGroup}, ",") + "]",
|
||||||
|
ExpectedError: errRoleAttributeStrictViolation,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, tt := range tests {
|
||||||
provider.info.RoleAttributePath = test.RoleAttributePath
|
t.Run(tt.Name, func(t *testing.T) {
|
||||||
provider.info.AllowAssignGrafanaAdmin = test.Cfg.AllowAssignGrafanaAdmin
|
cfg := setting.NewCfg()
|
||||||
provider.cfg.AutoAssignOrgRole = string(test.Cfg.AutoAssignOrgRole)
|
cfg.AutoAssignOrgRole = string(tt.Cfg.AutoAssignOrgRole)
|
||||||
provider.info.RoleAttributeStrict = test.Cfg.RoleAttributeStrict
|
|
||||||
provider.info.SkipOrgRoleSync = test.Cfg.SkipOrgRoleSync
|
orgMapper := ProvideOrgRoleMapper(cfg, &orgtest.FakeOrgService{ExpectedOrgs: []*org.OrgDTO{{ID: 4, Name: "Org4"}, {ID: 5, Name: "Org5"}}})
|
||||||
|
provider := NewGitLabProvider(&social.OAuthInfo{
|
||||||
|
RoleAttributePath: tt.RoleAttributePath,
|
||||||
|
RoleAttributeStrict: tt.Cfg.RoleAttributeStrict,
|
||||||
|
AllowAssignGrafanaAdmin: tt.Cfg.AllowAssignGrafanaAdmin,
|
||||||
|
SkipOrgRoleSync: tt.Cfg.SkipOrgRoleSync,
|
||||||
|
OrgMapping: tt.Cfg.OrgMapping,
|
||||||
|
// OrgAttributePath: "",
|
||||||
|
}, cfg, orgMapper, &ssosettingstests.MockService{}, featuremgmt.WithFeatures())
|
||||||
|
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
switch r.RequestURI {
|
switch r.RequestURI {
|
||||||
case userURI:
|
case userURI:
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
_, err := w.Write([]byte(test.UserRespBody))
|
_, err := w.Write([]byte(tt.UserRespBody))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
case groupsURI:
|
case groupsURI:
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
for k, v := range test.GroupHeaders {
|
for k, v := range tt.GroupHeaders {
|
||||||
w.Header().Set(k, v)
|
w.Header().Set(k, v)
|
||||||
}
|
}
|
||||||
_, err := w.Write([]byte(test.GroupsRespBody))
|
_, err := w.Write([]byte(tt.GroupsRespBody))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
default:
|
default:
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
require.Fail(t, "unexpected request URI: "+r.RequestURI)
|
require.Fail(t, "unexpected request URI: "+r.RequestURI)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
provider.info.ApiUrl = ts.URL + apiURI
|
provider.info.ApiUrl = ts.URL + apiURI
|
||||||
actualResult, err := provider.UserInfo(context.Background(), ts.Client(), &oauth2.Token{})
|
actualResult, err := provider.UserInfo(context.Background(), ts.Client(), &oauth2.Token{})
|
||||||
if test.ExpectedError != nil {
|
if tt.ExpectedError != nil {
|
||||||
require.ErrorIs(t, err, test.ExpectedError)
|
require.ErrorIs(t, err, tt.ExpectedError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, test.ExpectedEmail, actualResult.Email)
|
require.Equal(t, tt.ExpectedEmail, actualResult.Email)
|
||||||
require.Equal(t, test.ExpectedLogin, actualResult.Login)
|
require.Equal(t, tt.ExpectedLogin, actualResult.Login)
|
||||||
require.Equal(t, test.ExpectedRole, actualResult.Role)
|
require.Equal(t, tt.ExpectedRoles, actualResult.OrgRoles)
|
||||||
require.Equal(t, test.ExpectedGrafanaAdmin, actualResult.IsGrafanaAdmin)
|
require.Equal(t, tt.ExpectedGrafanaAdmin, actualResult.IsGrafanaAdmin)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -267,14 +306,12 @@ func TestSocialGitlab_extractFromToken(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantUser: &userData{
|
wantUser: &userData{
|
||||||
ID: "12345678",
|
ID: "12345678",
|
||||||
Login: "johndoe",
|
Login: "johndoe",
|
||||||
Email: "johndoe@example.com",
|
Email: "johndoe@example.com",
|
||||||
Name: "John Doe",
|
Name: "John Doe",
|
||||||
Groups: []string{"admins", "editors", "viewers"},
|
Groups: []string{"admins", "editors", "viewers"},
|
||||||
EmailVerified: true,
|
EmailVerified: true,
|
||||||
Role: "Viewer",
|
|
||||||
IsGrafanaAdmin: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -334,14 +371,12 @@ func TestSocialGitlab_extractFromToken(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantUser: &userData{
|
wantUser: &userData{
|
||||||
ID: "12345678",
|
ID: "12345678",
|
||||||
Login: "johndoe",
|
Login: "johndoe",
|
||||||
Email: "johndoe@example.com",
|
Email: "johndoe@example.com",
|
||||||
Name: "John Doe",
|
Name: "John Doe",
|
||||||
Groups: []string{"admins"},
|
Groups: []string{"admins"},
|
||||||
EmailVerified: true,
|
EmailVerified: true,
|
||||||
Role: "Viewer",
|
|
||||||
IsGrafanaAdmin: nil,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -168,8 +168,8 @@ 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
|
// This is required to implement OrgRole mapping for OAuth providers step by step
|
||||||
switch c.providerName {
|
switch c.providerName {
|
||||||
case social.GenericOAuthProviderName, social.GitHubProviderName:
|
case social.GenericOAuthProviderName, social.GitHubProviderName, social.GitlabProviderName:
|
||||||
// Do nothing, GenericOAuthProvider and GitHub already supports OrgRole mapping
|
// Do nothing, these providers already supports OrgRole mapping
|
||||||
default:
|
default:
|
||||||
userInfo.OrgRoles, userInfo.IsGrafanaAdmin, _ = getRoles(c.cfg, func() (org.RoleType, *bool, error) {
|
userInfo.OrgRoles, userInfo.IsGrafanaAdmin, _ = getRoles(c.cfg, func() (org.RoleType, *bool, error) {
|
||||||
return userInfo.Role, userInfo.IsGrafanaAdmin, nil
|
return userInfo.Role, userInfo.IsGrafanaAdmin, nil
|
||||||
|
Loading…
Reference in New Issue
Block a user