OAuth: Support role mapping for GitLab OAuth (#30025)

* Support `role_attribute_path` for GitLab OAuth

Allow role mapping for GitLab accounts.

Example:

  [auth.gitlab]
  role_attribute_path = is_admin && 'Admin' || 'Viewer'

* Support `role_attribute_path` for GitLab OAuth

Allow role mapping for GitLab accounts.

Example:

  [auth.gitlab]
  role_attribute_path = is_admin && 'Admin' || 'Viewer'

* docs: add docs for role_attribute_path

* Apply suggestions from code review

Co-authored-by: Peter Leitzen <splattael@users.noreply.github.com>

* docs: update example

example should suggest a full configuration

* Apply suggestions from code review

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>

* Apply suggestions from code review

Co-authored-by: Fiona Artiaga <89225282+GrafanaWriter@users.noreply.github.com>

* docs: add suggestions from tech writers

Co-authored-by: Henry Sachs <Henry.Sachs@deutschebahn.com>
Co-authored-by: Henry Sachs <henrysachs@gmail.com>
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Fiona Artiaga <89225282+GrafanaWriter@users.noreply.github.com>
This commit is contained in:
Peter Leitzen 2021-09-13 18:44:37 +02:00 committed by GitHub
parent 89878dae1b
commit 4f70113ea0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 47 additions and 11 deletions

View File

@ -26,10 +26,10 @@ instance, if you access Grafana at `http://203.0.113.31:3000`, you should use
http://203.0.113.31:3000/login/gitlab
```
Finally, select _read_api_ as the _Scope_ and submit the form. Note that if you're
Finally, select _read_api_as the_Scope_and submit the form. Note that if you're
not going to use GitLab groups for authorization (i.e. not setting
`allowed_groups`, see below), you can select _read_user_ instead of _read_api_ as
the _Scope_, thus giving a more restricted access to your GitLab API.
`allowed_groups`, see below), you can select_read_user_ instead of _read_api_as
the_Scope_, thus giving a more restricted access to your GitLab API.
You'll get an _Application Id_ and a _Secret_ in return; we'll call them
`GITLAB_APPLICATION_ID` and `GITLAB_SECRET` respectively for the rest of this
@ -94,8 +94,8 @@ display name, especially if the display name contains spaces or special
characters. Make sure you always use the group or subgroup name as it appears
in the URL of the group or subgroup.
Here's a complete example with `allow_sign_up` enabled, and access limited to
the `example` and `foo/bar` groups:
Here's a complete example with `allow_sign_up` enabled, with access limited to
the `example` and `foo/bar` groups. The example also promotes all GitLab Admins to Grafana Admins:
```ini
[auth.gitlab]
@ -103,13 +103,28 @@ enabled = true
allow_sign_up = true
client_id = GITLAB_APPLICATION_ID
client_secret = GITLAB_SECRET
scopes = api
scopes = read_api
auth_url = https://gitlab.com/oauth/authorize
token_url = https://gitlab.com/oauth/token
api_url = https://gitlab.com/api/v4
allowed_groups = example, foo/bar
role_attribute_path = is_admin && 'Admin' || 'Viewer'
```
### Map roles
You can use GitLab OAuth to map roles. During mapping, Grafana checks for the presence of a role using the [JMESPath](http://jmespath.org/examples.html) specified via the `role_attribute_path` configuration option.
For the path lookup, Grafana uses JSON obtained from querying GitLab's API [`/api/v4/user`](https://docs.gitlab.com/ee/api/users.html#list-current-user-for-normal-users) endpoint. The result of evaluating the `role_attribute_path` JMESPath expression must be a valid Grafana role, for example, `Viewer`, `Editor` or `Admin`. For more information about roles and permissions in Grafana, refer to [Organization roles]({{< relref "../permissions/organization_roles.md" >}}).
An example Query could look like the following:
```bash
role_attribute_path = is_admin && 'Admin' || 'Viewer'
```
This allows every GitLab Admin to be an Admin in Grafana.
### Team Sync (Enterprise only)
> Only available in Grafana Enterprise v6.4+

View File

@ -13,8 +13,9 @@ import (
type SocialGitlab struct {
*SocialBase
allowedGroups []string
apiUrl string
allowedGroups []string
apiUrl string
roleAttributePath string
}
func (s *SocialGitlab) Type() int {
@ -114,12 +115,18 @@ func (s *SocialGitlab) UserInfo(client *http.Client, token *oauth2.Token) (*Basi
groups := s.GetGroups(client)
role, err := s.extractRole(response.Body)
if err != nil {
s.log.Error("Failed to extract role", "error", err)
}
userInfo := &BasicUserInfo{
Id: fmt.Sprintf("%d", data.Id),
Name: data.Name,
Login: data.Username,
Email: data.Email,
Groups: groups,
Role: role,
}
if !s.IsGroupMember(groups) {
@ -128,3 +135,16 @@ func (s *SocialGitlab) UserInfo(client *http.Client, token *oauth2.Token) (*Basi
return userInfo, nil
}
func (s *SocialGitlab) extractRole(rawJSON []byte) (string, error) {
if s.roleAttributePath == "" {
return "", nil
}
role, err := s.searchJSONForStringAttr(s.roleAttributePath, rawJSON)
if err != nil {
return "", err
}
return role, nil
}

View File

@ -126,9 +126,10 @@ func ProvideService(cfg *setting.Cfg) *SocialService {
// GitLab.
if name == "gitlab" {
ss.socialMap["gitlab"] = &SocialGitlab{
SocialBase: newSocialBase(name, &config, info),
apiUrl: info.ApiUrl,
allowedGroups: util.SplitString(sec.Key("allowed_groups").String()),
SocialBase: newSocialBase(name, &config, info),
apiUrl: info.ApiUrl,
allowedGroups: util.SplitString(sec.Key("allowed_groups").String()),
roleAttributePath: info.RoleAttributePath,
}
}