mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 11:52:28 +08:00
Auth: Restore legacy behavior and add deprecation notice for empty org role in oauth (#55118)
* Auth: Add deprecation notice for empty org role Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> * fix recasts * fix azure tests missing logger * Adding test to gitlab oauth * Covering more cases * Cover more options * Add role attributestrict check fail * Adding one more edge case test * Using legacy for gitlab * Yet another edge case YAEC * Reverting github oauth to legacy Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Not using token Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Nit. * Adding warning in docs Co-authored-by: Jguer <joao.guerreiro@grafana.com> * add warning to generic oauth Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Be more precise Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Adding warning to github oauth Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Adding warning to gitlab oauth Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Adding warning to okta oauth Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Add docs about mapping to AzureAD Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Clarify oauth_skip_org_role_update_sync Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Nit. * Nit on Azure AD Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Reorder docs index Co-authored-by: Jguer <joao.guerreiro@grafana.com> * Fix typo Co-authored-by: Jguer <joao.guerreiro@grafana.com> Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> Co-authored-by: gamab <gabi.mabs@gmail.com>
This commit is contained in:
@ -792,8 +792,13 @@ Administrators can increase this if they experience OAuth login state mismatch e
|
|||||||
### oauth_skip_org_role_update_sync
|
### oauth_skip_org_role_update_sync
|
||||||
|
|
||||||
Skip forced assignment of OrgID `1` or `auto_assign_org_id` for external logins. Default is `false`.
|
Skip forced assignment of OrgID `1` or `auto_assign_org_id` for external logins. Default is `false`.
|
||||||
Use this setting to distribute users with external login to multiple organizations.
|
Use this setting to allow users with external login to be manually assigned to multiple organizations.
|
||||||
Otherwise, the users' organization would get reset on every new login, for example, via AzureAD.
|
|
||||||
|
By default, the users' organization and role is reset on every new login.
|
||||||
|
|
||||||
|
> **Warning**: Currently if no organization role mapping is found for a user, Grafana doesn't update the user's organization role.
|
||||||
|
> With Grafana 10, if `oauth_skip_org_role_update_sync` option is set to `false`, users with no mapping will be
|
||||||
|
> reset to the default organization role on every login. [See `auto_assign_org_role` option]({{< relref ".#auto_assign_org_role" >}}).
|
||||||
|
|
||||||
### api_key_max_seconds_to_live
|
### api_key_max_seconds_to_live
|
||||||
|
|
||||||
|
@ -100,6 +100,22 @@ To enable the Azure AD OAuth2, register your application with Azure AD.
|
|||||||
|
|
||||||
1. Click on **Users and Groups** and add Users/Groups to the Grafana roles by using **Add User**.
|
1. Click on **Users and Groups** and add Users/Groups to the Grafana roles by using **Add User**.
|
||||||
|
|
||||||
|
### Map roles
|
||||||
|
|
||||||
|
By default, Azure AD authentication will map users to organization roles based on the most privileged application role assigned to the user in AzureAD.
|
||||||
|
|
||||||
|
If no application 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`.
|
||||||
|
It denies user access if no role or an invalid role is returned.
|
||||||
|
|
||||||
|
**On every login** the user organization role will be reset to match AzureAD's application role and
|
||||||
|
their organization membership will be reset to the default organization.
|
||||||
|
|
||||||
|
If Azure AD authentication is not intended to sync user roles and organization membership,
|
||||||
|
`oauth_skip_org_role_update_sync` should be enabled.
|
||||||
|
See [configure-grafana]({{< relref "../../configure-grafana#oauth_skip_org_role_update_sync" >}}) for more details.
|
||||||
|
|
||||||
### Assign server administrator privileges
|
### Assign server administrator privileges
|
||||||
|
|
||||||
> Available in Grafana v9.2 and later versions.
|
> Available in Grafana v9.2 and later versions.
|
||||||
|
@ -21,9 +21,8 @@ You can configure many different OAuth2 authentication services with Grafana usi
|
|||||||
- [Set up OAuth2 with Bitbucket](#set-up-oauth2-with-bitbucket)
|
- [Set up OAuth2 with Bitbucket](#set-up-oauth2-with-bitbucket)
|
||||||
- [Set up OAuth2 with Centrify](#set-up-oauth2-with-centrify)
|
- [Set up OAuth2 with Centrify](#set-up-oauth2-with-centrify)
|
||||||
- [Set up OAuth2 with OneLogin](#set-up-oauth2-with-onelogin)
|
- [Set up OAuth2 with OneLogin](#set-up-oauth2-with-onelogin)
|
||||||
- [JMESPath examples](#jmespath-examples)
|
- [Role mapping](#role-mapping)
|
||||||
- [Role mapping](#role-mapping)
|
- [Team synchronization](#team-synchronization)
|
||||||
- [Groups mapping](#groups-mapping)
|
|
||||||
|
|
||||||
This callback URL must match the full HTTP address that you use in your browser to access Grafana, but with the suffixed path of `/login/generic_oauth`.
|
This callback URL must match the full HTTP address that you use in your browser to access Grafana, but with the suffixed path of `/login/generic_oauth`.
|
||||||
|
|
||||||
@ -80,12 +79,6 @@ Grafana determines a user's email address by querying the OAuth provider until i
|
|||||||
1. Query the `/emails` endpoint of the OAuth provider's API (configured with `api_url`), then check for the presence of an email address marked as a primary address.
|
1. Query the `/emails` endpoint of the OAuth provider's API (configured with `api_url`), then check for the presence of an email address marked as a primary address.
|
||||||
1. If no email address is found in steps (1-4), then the email address of the user is set to an empty string.
|
1. If no email address is found in steps (1-4), then the email address of the user is set to an empty string.
|
||||||
|
|
||||||
### Roles
|
|
||||||
|
|
||||||
Grafana checks for the presence of a role using the [JMESPath](http://jmespath.org/examples.html) specified via the `role_attribute_path` configuration option. The JMESPath is applied to the `id_token` first. If there is no match, then the UserInfo endpoint specified via the `api_url` configuration option is tried next. The result after evaluation of the `role_attribute_path` JMESPath expression should be a valid Grafana role, for example, `Viewer`, `Editor` or `Admin`.
|
|
||||||
|
|
||||||
For more information, refer to the [JMESPath examples](#jmespath-examples).
|
|
||||||
|
|
||||||
### Groups / Teams
|
### Groups / Teams
|
||||||
|
|
||||||
Similarly, group mappings are made using [JMESPath](http://jmespath.org/examples.html) with the `groups_attribute_path` configuration option. The `id_token` is attempted first, followed by the UserInfo from the `api_url`. The result of the JMESPath expression should be a string array of groups.
|
Similarly, group mappings are made using [JMESPath](http://jmespath.org/examples.html) with the `groups_attribute_path` configuration option. The `id_token` is attempted first, followed by the UserInfo from the `api_url`. The result of the JMESPath expression should be a string array of groups.
|
||||||
@ -241,14 +234,32 @@ allowed_organizations =
|
|||||||
allowed_organizations =
|
allowed_organizations =
|
||||||
```
|
```
|
||||||
|
|
||||||
## JMESPath examples
|
## Role 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. The JMESPath is applied to the `id_token` first. If there is no match, then the UserInfo endpoint specified via the `api_url` configuration option is tried next. The result after evaluation of the `role_attribute_path` JMESPath expression should be a valid Grafana role, for example, `Viewer`, `Editor` or `Admin`.
|
||||||
|
|
||||||
|
For more information, refer to the [JMESPath examples](#jmespath-examples).
|
||||||
|
|
||||||
|
> **Warning**: Currently if no organization role mapping is found for a user, Grafana doesn't
|
||||||
|
> update the user's organization role. This is going to change in Grafana 10. To avoid overriding manually set roles,
|
||||||
|
> enable the `oauth_skip_org_role_update_sync` option.
|
||||||
|
> See [configure-grafana]({{< relref "../../configure-grafana#oauth_skip_org_role_update_sync" >}}) for more information.
|
||||||
|
|
||||||
|
On first login, if the`role_attribute_path` property does not return a role, then 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`.
|
||||||
|
It denies user access if no role or an invalid role is returned.
|
||||||
|
|
||||||
|
> **Warning**: With Grafana 10, **on every login**, if the`role_attribute_path` property does not return a role,
|
||||||
|
> then the user is assigned the role specified by
|
||||||
|
> [the `auto_assign_org_role` option]({{< relref "../../configure-grafana#auto_assign_org_role" >}}).
|
||||||
|
|
||||||
|
### JMESPath examples
|
||||||
|
|
||||||
|
#### Map user organization role
|
||||||
|
|
||||||
To ease configuration of a proper JMESPath expression, you can test/evaluate expressions with custom payloads at http://jmespath.org/.
|
To ease configuration of a proper JMESPath expression, you can test/evaluate expressions with custom payloads at http://jmespath.org/.
|
||||||
|
|
||||||
### Role mapping
|
|
||||||
|
|
||||||
If the`role_attribute_path` property does not return a role, then the user is assigned the `Viewer` role by default. You can disable the role assignment by setting `role_attribute_strict = true`. It denies user access if no role or an invalid role is returned.
|
|
||||||
|
|
||||||
**Basic example:**
|
**Basic example:**
|
||||||
|
|
||||||
In the following example user will get `Editor` as role when authenticating. The value of the property `role` will be the resulting role if the role is a proper Grafana role, i.e. `Viewer`, `Editor` or `Admin`.
|
In the following example user will get `Editor` as role when authenticating. The value of the property `role` will be the resulting role if the role is a proper Grafana role, i.e. `Viewer`, `Editor` or `Admin`.
|
||||||
@ -317,7 +328,7 @@ Example:
|
|||||||
role_attribute_path = contains(info.roles[*], 'admin') && 'GrafanaAdmin' || contains(info.roles[*], 'editor') && 'Editor' || 'Viewer'
|
role_attribute_path = contains(info.roles[*], 'admin') && 'GrafanaAdmin' || contains(info.roles[*], 'editor') && 'Editor' || 'Viewer'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Groups mapping
|
## Team synchronization
|
||||||
|
|
||||||
> Available in Grafana Enterprise v8.1 and later versions.
|
> Available in Grafana Enterprise v8.1 and later versions.
|
||||||
|
|
||||||
|
@ -109,6 +109,20 @@ For the path lookup, Grafana uses JSON obtained from querying GitHub's API [`/ap
|
|||||||
|
|
||||||
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 [Roles and permissions]({{< relref "../../../administration/roles-and-permissions/" >}}).
|
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 [Roles and permissions]({{< relref "../../../administration/roles-and-permissions/" >}}).
|
||||||
|
|
||||||
|
> **Warning**: Currently if no organization role mapping is found for a user, Grafana doesn't
|
||||||
|
> update the user's organization role. This is going to change in Grafana 10. To avoid overriding manually set roles,
|
||||||
|
> enable the `oauth_skip_org_role_update_sync` option.
|
||||||
|
> See [configure-grafana]({{< relref "../../configure-grafana#oauth_skip_org_role_update_sync" >}}) for more information.
|
||||||
|
|
||||||
|
On first login, if the`role_attribute_path` property does not return a role, then 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`.
|
||||||
|
It denies user access if no role or an invalid role is returned.
|
||||||
|
|
||||||
|
> **Warning**: With Grafana 10, **on every login**, if the`role_attribute_path` property does not return a role,
|
||||||
|
> then the user is assigned the role specified by
|
||||||
|
> [the `auto_assign_org_role` option]({{< relref "../../configure-grafana#auto_assign_org_role" >}}).
|
||||||
|
|
||||||
An example Query could look like the following:
|
An example Query could look like the following:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -129,6 +129,20 @@ You can use GitLab OAuth to map roles. During mapping, Grafana checks for the pr
|
|||||||
|
|
||||||
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 and a `groups` key containing all of the user's teams. 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 [Roles and permissions]({{< relref "../../../administration/roles-and-permissions/" >}}).
|
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 and a `groups` key containing all of the user's teams. 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 [Roles and permissions]({{< relref "../../../administration/roles-and-permissions/" >}}).
|
||||||
|
|
||||||
|
> **Warning**: Currently if no organization role mapping is found for a user, Grafana doesn't
|
||||||
|
> update the user's organization role. This is going to change in Grafana 10. To avoid overriding manually set roles,
|
||||||
|
> enable the `oauth_skip_org_role_update_sync` option.
|
||||||
|
> See [configure-grafana]({{< relref "../../configure-grafana#oauth_skip_org_role_update_sync" >}}) for more information.
|
||||||
|
|
||||||
|
On first login, if the`role_attribute_path` property does not return a role, then 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`.
|
||||||
|
It denies user access if no role or an invalid role is returned.
|
||||||
|
|
||||||
|
> **Warning**: With Grafana 10, **on every login**, if the`role_attribute_path` property does not return a role,
|
||||||
|
> then the user is assigned the role specified by
|
||||||
|
> [the `auto_assign_org_role` option]({{< relref "../../configure-grafana#auto_assign_org_role" >}}).
|
||||||
|
|
||||||
An example Query could look like the following:
|
An example Query could look like the following:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
|
@ -75,6 +75,20 @@ Grafana can attempt to do role mapping through Okta OAuth. In order to achieve t
|
|||||||
|
|
||||||
Grafana uses JSON obtained from querying the `/userinfo` endpoint for the path lookup. The result after evaluating the `role_attribute_path` JMESPath expression needs to be a valid Grafana role, i.e. `Viewer`, `Editor` or `Admin`. For more information about roles and permissions in Grafana, refer to [Roles and permissions]({{< relref "../../../administration/roles-and-permissions/" >}}).
|
Grafana uses JSON obtained from querying the `/userinfo` endpoint for the path lookup. The result after evaluating the `role_attribute_path` JMESPath expression needs to be a valid Grafana role, i.e. `Viewer`, `Editor` or `Admin`. For more information about roles and permissions in Grafana, refer to [Roles and permissions]({{< relref "../../../administration/roles-and-permissions/" >}}).
|
||||||
|
|
||||||
|
> **Warning**: Currently if no organization role mapping is found for a user, Grafana doesn't
|
||||||
|
> update the user's organization role. This is going to change in Grafana 10. To avoid overriding manually set roles,
|
||||||
|
> enable the `oauth_skip_org_role_update_sync` option.
|
||||||
|
> See [configure-grafana]({{< relref "../../configure-grafana#oauth_skip_org_role_update_sync" >}}) for more information.
|
||||||
|
|
||||||
|
On first login, if the`role_attribute_path` property does not return a role, then 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`.
|
||||||
|
It denies user access if no role or an invalid role is returned.
|
||||||
|
|
||||||
|
> **Warning**: With Grafana 10, **on every login**, if the`role_attribute_path` property does not return a role,
|
||||||
|
> then the user is assigned the role specified by
|
||||||
|
> [the `auto_assign_org_role` option]({{< relref "../../configure-grafana#auto_assign_org_role" >}}).
|
||||||
|
|
||||||
Read about how to [add custom claims](https://developer.okta.com/docs/guides/customize-tokens-returned-from-okta/add-custom-claim/) to the user info in Okta. Also, check Generic OAuth page for [JMESPath examples]({{< relref "generic-oauth/#jmespath-examples" >}}).
|
Read about how to [add custom claims](https://developer.okta.com/docs/guides/customize-tokens-returned-from-okta/add-custom-claim/) to the user info in Okta. Also, check Generic OAuth page for [JMESPath examples]({{< relref "generic-oauth/#jmespath-examples" >}}).
|
||||||
|
|
||||||
#### Map server administrator privileges
|
#### Map server administrator privileges
|
||||||
|
@ -275,7 +275,7 @@ func (hs *HTTPServer) buildExternalUserInfo(token *oauth2.Token, userInfo *socia
|
|||||||
}
|
}
|
||||||
|
|
||||||
if userInfo.Role != "" && !hs.Cfg.OAuthSkipOrgRoleUpdateSync {
|
if userInfo.Role != "" && !hs.Cfg.OAuthSkipOrgRoleUpdateSync {
|
||||||
rt := org.RoleType(userInfo.Role)
|
rt := userInfo.Role
|
||||||
if rt.IsValid() {
|
if rt.IsValid() {
|
||||||
// The user will be assigned a role in either the auto-assigned organization or in the default one
|
// The user will be assigned a role in either the auto-assigned organization or in the default one
|
||||||
var orgID int64
|
var orgID int64
|
||||||
|
@ -68,10 +68,11 @@ func (s *SocialAzureAD) UserInfo(client *http.Client, token *oauth2.Token) (*Bas
|
|||||||
return nil, ErrEmailNotFound
|
return nil, ErrEmailNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
role, grafanaAdmin := claims.extractRoleAndAdmin(s.autoAssignOrgRole, s.roleAttributeStrict)
|
role, grafanaAdmin := s.extractRoleAndAdmin(&claims)
|
||||||
if role == "" {
|
if s.roleAttributeStrict && !role.IsValid() {
|
||||||
return nil, ErrInvalidBasicRole
|
return nil, ErrInvalidBasicRole
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug("AzureAD OAuth: extracted role", "email", email, "role", role)
|
logger.Debug("AzureAD OAuth: extracted role", "email", email, "role", role)
|
||||||
|
|
||||||
groups, err := extractGroups(client, claims, token)
|
groups, err := extractGroups(client, claims, token)
|
||||||
@ -94,7 +95,7 @@ func (s *SocialAzureAD) UserInfo(client *http.Client, token *oauth2.Token) (*Bas
|
|||||||
Name: claims.Name,
|
Name: claims.Name,
|
||||||
Email: email,
|
Email: email,
|
||||||
Login: email,
|
Login: email,
|
||||||
Role: string(role),
|
Role: role,
|
||||||
IsGrafanaAdmin: isGrafanaAdmin,
|
IsGrafanaAdmin: isGrafanaAdmin,
|
||||||
Groups: groups,
|
Groups: groups,
|
||||||
}, nil
|
}, nil
|
||||||
@ -127,22 +128,12 @@ func (claims *azureClaims) extractEmail() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// extractRoleAndAdmin extracts the role from the claims and returns the role and whether the user is a Grafana admin.
|
// extractRoleAndAdmin extracts the role from the claims and returns the role and whether the user is a Grafana admin.
|
||||||
func (claims *azureClaims) extractRoleAndAdmin(autoAssignRole string, strictMode bool) (org.RoleType, bool) {
|
func (s *SocialAzureAD) extractRoleAndAdmin(claims *azureClaims) (org.RoleType, bool) {
|
||||||
if len(claims.Roles) == 0 {
|
if len(claims.Roles) == 0 {
|
||||||
if strictMode {
|
return s.defaultRole(false), false
|
||||||
return org.RoleType(""), false
|
|
||||||
}
|
|
||||||
|
|
||||||
return org.RoleType(autoAssignRole), false
|
|
||||||
}
|
|
||||||
|
|
||||||
roleOrder := []org.RoleType{
|
|
||||||
RoleGrafanaAdmin,
|
|
||||||
org.RoleAdmin,
|
|
||||||
org.RoleEditor,
|
|
||||||
org.RoleViewer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
roleOrder := []org.RoleType{RoleGrafanaAdmin, org.RoleAdmin, org.RoleEditor, org.RoleViewer}
|
||||||
for _, role := range roleOrder {
|
for _, role := range roleOrder {
|
||||||
if found := hasRole(claims.Roles, role); found {
|
if found := hasRole(claims.Roles, role); found {
|
||||||
if role == RoleGrafanaAdmin {
|
if role == RoleGrafanaAdmin {
|
||||||
@ -153,11 +144,7 @@ func (claims *azureClaims) extractRoleAndAdmin(autoAssignRole string, strictMode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if strictMode {
|
return s.defaultRole(false), false
|
||||||
return org.RoleType(""), false
|
|
||||||
}
|
|
||||||
|
|
||||||
return org.RoleViewer, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasRole(roles []string, role org.RoleType) bool {
|
func hasRole(roles []string, role org.RoleType) bool {
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -54,7 +53,7 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
|||||||
ID: "1234",
|
ID: "1234",
|
||||||
},
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
SocialBase: &SocialBase{autoAssignOrgRole: "Viewer"},
|
SocialBase: newSocialBase("azuread", &oauth2.Config{}, &OAuthInfo{}, "Viewer"),
|
||||||
},
|
},
|
||||||
want: &BasicUserInfo{
|
want: &BasicUserInfo{
|
||||||
Id: "1234",
|
Id: "1234",
|
||||||
@ -93,7 +92,7 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
|||||||
ID: "1234",
|
ID: "1234",
|
||||||
},
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
SocialBase: &SocialBase{autoAssignOrgRole: "Viewer"},
|
SocialBase: newSocialBase("azuread", &oauth2.Config{}, &OAuthInfo{}, "Viewer"),
|
||||||
},
|
},
|
||||||
want: &BasicUserInfo{
|
want: &BasicUserInfo{
|
||||||
Id: "1234",
|
Id: "1234",
|
||||||
@ -142,6 +141,9 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Only other roles",
|
name: "Only other roles",
|
||||||
|
fields: fields{
|
||||||
|
SocialBase: newSocialBase("azuread", &oauth2.Config{}, &OAuthInfo{}, "Viewer"),
|
||||||
|
},
|
||||||
claims: &azureClaims{
|
claims: &azureClaims{
|
||||||
Email: "me@example.com",
|
Email: "me@example.com",
|
||||||
PreferredUsername: "",
|
PreferredUsername: "",
|
||||||
@ -168,7 +170,7 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
|||||||
ID: "1234",
|
ID: "1234",
|
||||||
},
|
},
|
||||||
fields: fields{
|
fields: fields{
|
||||||
SocialBase: &SocialBase{autoAssignOrgRole: "Editor"},
|
SocialBase: newSocialBase("azuread", &oauth2.Config{}, &OAuthInfo{}, "Editor"),
|
||||||
},
|
},
|
||||||
want: &BasicUserInfo{
|
want: &BasicUserInfo{
|
||||||
Id: "1234",
|
Id: "1234",
|
||||||
@ -217,7 +219,7 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Grafana Admin but setting is disabled",
|
name: "Grafana Admin but setting is disabled",
|
||||||
fields: fields{SocialBase: &SocialBase{allowAssignGrafanaAdmin: false}},
|
fields: fields{SocialBase: newSocialBase("azuread", &oauth2.Config{}, &OAuthInfo{AllowAssignGrafanaAdmin: false}, "Editor")},
|
||||||
claims: &azureClaims{
|
claims: &azureClaims{
|
||||||
Email: "me@example.com",
|
Email: "me@example.com",
|
||||||
PreferredUsername: "",
|
PreferredUsername: "",
|
||||||
@ -258,8 +260,9 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Grafana Admin and Editor roles in claim",
|
name: "Grafana Admin and Editor roles in claim",
|
||||||
fields: fields{SocialBase: &SocialBase{allowAssignGrafanaAdmin: true}},
|
fields: fields{SocialBase: newSocialBase("azuread",
|
||||||
|
&oauth2.Config{}, &OAuthInfo{AllowAssignGrafanaAdmin: true}, "")},
|
||||||
claims: &azureClaims{
|
claims: &azureClaims{
|
||||||
Email: "me@example.com",
|
Email: "me@example.com",
|
||||||
PreferredUsername: "",
|
PreferredUsername: "",
|
||||||
@ -297,7 +300,8 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
|||||||
name: "Error if user is a member of allowed_groups",
|
name: "Error if user is a member of allowed_groups",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
allowedGroups: []string{"foo", "bar"},
|
allowedGroups: []string{"foo", "bar"},
|
||||||
SocialBase: &SocialBase{autoAssignOrgRole: "Viewer"},
|
SocialBase: newSocialBase("azuread",
|
||||||
|
&oauth2.Config{}, &OAuthInfo{AllowAssignGrafanaAdmin: false}, "Viewer"),
|
||||||
},
|
},
|
||||||
claims: &azureClaims{
|
claims: &azureClaims{
|
||||||
Email: "me@example.com",
|
Email: "me@example.com",
|
||||||
@ -443,9 +447,8 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
|||||||
t.Errorf("UserInfo() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("UserInfo() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("UserInfo() got = %v, want %v", got, tt.want)
|
require.EqualValues(t, tt.want, got)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,15 +144,11 @@ func (s *SocialGenericOAuth) UserInfo(client *http.Client, token *oauth2.Token)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if userInfo.Role == "" {
|
if userInfo.Role == "" {
|
||||||
role, grafanaAdmin := s.extractRoleAndAdmin(data.rawJSON, []string{})
|
role, grafanaAdmin := s.extractRoleAndAdmin(data.rawJSON, []string{}, true)
|
||||||
if role != "" {
|
if role != "" {
|
||||||
if s.roleAttributeStrict && !role.IsValid() {
|
|
||||||
return nil, ErrInvalidBasicRole
|
|
||||||
}
|
|
||||||
|
|
||||||
s.log.Debug("Setting user info role from extracted role")
|
s.log.Debug("Setting user info role from extracted role")
|
||||||
|
|
||||||
userInfo.Role = string(role)
|
userInfo.Role = role
|
||||||
if s.allowAssignGrafanaAdmin {
|
if s.allowAssignGrafanaAdmin {
|
||||||
userInfo.IsGrafanaAdmin = &grafanaAdmin
|
userInfo.IsGrafanaAdmin = &grafanaAdmin
|
||||||
}
|
}
|
||||||
@ -170,6 +166,10 @@ func (s *SocialGenericOAuth) UserInfo(client *http.Client, token *oauth2.Token)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.roleAttributeStrict && !userInfo.Role.IsValid() {
|
||||||
|
return nil, ErrInvalidBasicRole
|
||||||
|
}
|
||||||
|
|
||||||
if userInfo.Email == "" {
|
if userInfo.Email == "" {
|
||||||
var err error
|
var err error
|
||||||
userInfo.Email, err = s.FetchPrivateEmail(client)
|
userInfo.Email, err = s.FetchPrivateEmail(client)
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/go-kit/log/level"
|
"github.com/go-kit/log/level"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newLogger(name string, lev string) log.Logger {
|
func newLogger(name string, lev string) log.Logger {
|
||||||
@ -251,7 +252,7 @@ func TestUserInfoSearchesForEmailAndRole(t *testing.T) {
|
|||||||
OAuth2Extra interface{}
|
OAuth2Extra interface{}
|
||||||
RoleAttributePath string
|
RoleAttributePath string
|
||||||
ExpectedEmail string
|
ExpectedEmail string
|
||||||
ExpectedRole string
|
ExpectedRole org.RoleType
|
||||||
ExpectedGrafanaAdmin *bool
|
ExpectedGrafanaAdmin *bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -201,7 +201,7 @@ func (s *SocialGithub) UserInfo(client *http.Client, token *oauth2.Token) (*Basi
|
|||||||
|
|
||||||
teams := convertToGroupList(teamMemberships)
|
teams := convertToGroupList(teamMemberships)
|
||||||
|
|
||||||
role, grafanaAdmin := s.extractRoleAndAdmin(response.Body, teams)
|
role, grafanaAdmin := s.extractRoleAndAdmin(response.Body, teams, true)
|
||||||
if s.roleAttributeStrict && !role.IsValid() {
|
if s.roleAttributeStrict && !role.IsValid() {
|
||||||
return nil, ErrInvalidBasicRole
|
return nil, ErrInvalidBasicRole
|
||||||
}
|
}
|
||||||
@ -216,7 +216,7 @@ func (s *SocialGithub) UserInfo(client *http.Client, token *oauth2.Token) (*Basi
|
|||||||
Login: data.Login,
|
Login: data.Login,
|
||||||
Id: fmt.Sprintf("%d", data.Id),
|
Id: fmt.Sprintf("%d", data.Id),
|
||||||
Email: data.Email,
|
Email: data.Email,
|
||||||
Role: string(role),
|
Role: role,
|
||||||
Groups: teams,
|
Groups: teams,
|
||||||
IsGrafanaAdmin: isGrafanaAdmin,
|
IsGrafanaAdmin: isGrafanaAdmin,
|
||||||
}
|
}
|
||||||
|
@ -165,8 +165,8 @@ func TestSocialGitHub_UserInfo(t *testing.T) {
|
|||||||
Groups: []string{"https://github.com/orgs/github/teams/justice-league", "@github/justice-league"},
|
Groups: []string{"https://github.com/orgs/github/teams/justice-league", "@github/justice-league"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{ // Case that's going to change with Grafana 10
|
||||||
name: "auto assign org role",
|
name: "No fallback to default org role (will change in Grafana 10)",
|
||||||
roleAttributePath: "",
|
roleAttributePath: "",
|
||||||
userRawJSON: testGHUserJSON,
|
userRawJSON: testGHUserJSON,
|
||||||
autoAssignOrgRole: "Editor",
|
autoAssignOrgRole: "Editor",
|
||||||
@ -176,7 +176,7 @@ func TestSocialGitHub_UserInfo(t *testing.T) {
|
|||||||
Name: "monalisa octocat",
|
Name: "monalisa octocat",
|
||||||
Email: "octocat@github.com",
|
Email: "octocat@github.com",
|
||||||
Login: "octocat",
|
Login: "octocat",
|
||||||
Role: "Editor",
|
Role: "",
|
||||||
Groups: []string{"https://github.com/orgs/github/teams/justice-league", "@github/justice-league"},
|
Groups: []string{"https://github.com/orgs/github/teams/justice-league", "@github/justice-league"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -89,7 +89,7 @@ func (s *SocialGitlab) GetGroupsPage(client *http.Client, url string) ([]string,
|
|||||||
return fullPaths, next
|
return fullPaths, next
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SocialGitlab) UserInfo(client *http.Client, token *oauth2.Token) (*BasicUserInfo, error) {
|
func (s *SocialGitlab) UserInfo(client *http.Client, _ *oauth2.Token) (*BasicUserInfo, error) {
|
||||||
var data struct {
|
var data struct {
|
||||||
Id int
|
Id int
|
||||||
Username string
|
Username string
|
||||||
@ -113,7 +113,7 @@ func (s *SocialGitlab) UserInfo(client *http.Client, token *oauth2.Token) (*Basi
|
|||||||
|
|
||||||
groups := s.GetGroups(client)
|
groups := s.GetGroups(client)
|
||||||
|
|
||||||
role, grafanaAdmin := s.extractRoleAndAdmin(response.Body, groups)
|
role, grafanaAdmin := s.extractRoleAndAdmin(response.Body, groups, true)
|
||||||
if s.roleAttributeStrict && !role.IsValid() {
|
if s.roleAttributeStrict && !role.IsValid() {
|
||||||
return nil, ErrInvalidBasicRole
|
return nil, ErrInvalidBasicRole
|
||||||
}
|
}
|
||||||
@ -129,7 +129,7 @@ func (s *SocialGitlab) UserInfo(client *http.Client, token *oauth2.Token) (*Basi
|
|||||||
Login: data.Username,
|
Login: data.Username,
|
||||||
Email: data.Email,
|
Email: data.Email,
|
||||||
Groups: groups,
|
Groups: groups,
|
||||||
Role: string(role),
|
Role: role,
|
||||||
IsGrafanaAdmin: isGrafanaAdmin,
|
IsGrafanaAdmin: isGrafanaAdmin,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
159
pkg/login/social/gitlab_oauth_test.go
Normal file
159
pkg/login/social/gitlab_oauth_test.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package social
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
apiURI = "/api/v4"
|
||||||
|
userURI = "/api/v4/user"
|
||||||
|
groupsURI = "/api/v4/groups"
|
||||||
|
|
||||||
|
gitlabAttrPath = `is_admin && 'GrafanaAdmin' || contains(groups[*], 'admins') && 'Admin' || contains(groups[*], 'editors') && 'Editor' || contains(groups[*], 'viewers') && 'Viewer'`
|
||||||
|
|
||||||
|
rootUserRespBody = `{"id":1,"username":"root","name":"Administrator","state":"active","email":"root@example.org","is_admin":true,"namespace_id":1}`
|
||||||
|
editorUserRespBody = `{"id":3,"username":"gitlab-editor","name":"Gitlab Editor","state":"active","email":"gitlab-editor@example.org","is_admin":false,"namespace_id":1}`
|
||||||
|
|
||||||
|
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"}`
|
||||||
|
// serverAdminGroup = `{"id":7,"web_url":"http://grafana-gitlab.local/groups/serveradmins","name":"ServerAdmins","path":"serveradmins","project_creation_level":"developer","full_name":"ServerAdmins","full_path":"serveradmins","created_at":"2022-09-13T19:38:36.227Z"}`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSocialGitlab_UserInfo(t *testing.T) {
|
||||||
|
provider := SocialGitlab{
|
||||||
|
SocialBase: &SocialBase{
|
||||||
|
log: newLogger("gitlab_oauth_test", "debug"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type conf struct {
|
||||||
|
AllowAssignGrafanaAdmin bool
|
||||||
|
RoleAttributeStrict bool
|
||||||
|
AutoAssignOrgRole org.RoleType
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
Name string
|
||||||
|
Cfg conf
|
||||||
|
UserRespBody string
|
||||||
|
GroupsRespBody string
|
||||||
|
RoleAttributePath string
|
||||||
|
ExpectedLogin string
|
||||||
|
ExpectedEmail string
|
||||||
|
ExpectedRole org.RoleType
|
||||||
|
ExpectedGrafanaAdmin *bool
|
||||||
|
ExpectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "Server Admin Allowed",
|
||||||
|
Cfg: conf{AllowAssignGrafanaAdmin: true},
|
||||||
|
UserRespBody: rootUserRespBody,
|
||||||
|
GroupsRespBody: "[" + strings.Join([]string{adminGroup, editorGroup, viewerGroup}, ",") + "]",
|
||||||
|
RoleAttributePath: gitlabAttrPath,
|
||||||
|
ExpectedLogin: "root",
|
||||||
|
ExpectedEmail: "root@example.org",
|
||||||
|
ExpectedRole: "Admin",
|
||||||
|
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
|
||||||
|
Name: "Server Admin Disabled",
|
||||||
|
Cfg: conf{AllowAssignGrafanaAdmin: false},
|
||||||
|
UserRespBody: rootUserRespBody,
|
||||||
|
GroupsRespBody: "[" + strings.Join([]string{viewerGroup}, ",") + "]",
|
||||||
|
RoleAttributePath: gitlabAttrPath,
|
||||||
|
ExpectedLogin: "root",
|
||||||
|
ExpectedEmail: "root@example.org",
|
||||||
|
ExpectedRole: "Admin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Editor",
|
||||||
|
Cfg: conf{AllowAssignGrafanaAdmin: true},
|
||||||
|
UserRespBody: editorUserRespBody,
|
||||||
|
GroupsRespBody: "[" + strings.Join([]string{viewerGroup, editorGroup}, ",") + "]",
|
||||||
|
RoleAttributePath: gitlabAttrPath,
|
||||||
|
ExpectedLogin: "gitlab-editor",
|
||||||
|
ExpectedEmail: "gitlab-editor@example.org",
|
||||||
|
ExpectedRole: "Editor",
|
||||||
|
ExpectedGrafanaAdmin: falseBoolPtr(),
|
||||||
|
},
|
||||||
|
{ // Case that's going to change with Grafana 10
|
||||||
|
Name: "No fallback to default org role (will change in Grafana 10)",
|
||||||
|
Cfg: conf{AutoAssignOrgRole: org.RoleViewer},
|
||||||
|
UserRespBody: editorUserRespBody,
|
||||||
|
GroupsRespBody: "[" + strings.Join([]string{}, ",") + "]",
|
||||||
|
RoleAttributePath: gitlabAttrPath,
|
||||||
|
ExpectedLogin: "gitlab-editor",
|
||||||
|
ExpectedEmail: "gitlab-editor@example.org",
|
||||||
|
ExpectedRole: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Strict mode prevents fallback to default",
|
||||||
|
Cfg: conf{RoleAttributeStrict: true, AutoAssignOrgRole: org.RoleViewer},
|
||||||
|
UserRespBody: editorUserRespBody,
|
||||||
|
GroupsRespBody: "[" + strings.Join([]string{}, ",") + "]",
|
||||||
|
RoleAttributePath: gitlabAttrPath,
|
||||||
|
ExpectedError: ErrInvalidBasicRole,
|
||||||
|
},
|
||||||
|
{ // Edge case, no match, no strict mode and no fallback => User has an empty role
|
||||||
|
Name: "Fallback with no default will create a user with an empty role",
|
||||||
|
Cfg: conf{},
|
||||||
|
UserRespBody: editorUserRespBody,
|
||||||
|
GroupsRespBody: "[" + strings.Join([]string{}, ",") + "]",
|
||||||
|
RoleAttributePath: gitlabAttrPath,
|
||||||
|
ExpectedLogin: "gitlab-editor",
|
||||||
|
ExpectedEmail: "gitlab-editor@example.org",
|
||||||
|
ExpectedRole: "",
|
||||||
|
},
|
||||||
|
{ // Edge case, no attribute path with strict mode => User has an empty role
|
||||||
|
Name: "Strict mode with no attribute path",
|
||||||
|
Cfg: conf{RoleAttributeStrict: true, AutoAssignOrgRole: org.RoleViewer},
|
||||||
|
UserRespBody: editorUserRespBody,
|
||||||
|
GroupsRespBody: "[" + strings.Join([]string{editorGroup}, ",") + "]",
|
||||||
|
RoleAttributePath: "",
|
||||||
|
ExpectedError: ErrInvalidBasicRole,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
provider.roleAttributePath = test.RoleAttributePath
|
||||||
|
provider.allowAssignGrafanaAdmin = test.Cfg.AllowAssignGrafanaAdmin
|
||||||
|
provider.autoAssignOrgRole = string(test.Cfg.AutoAssignOrgRole)
|
||||||
|
provider.roleAttributeStrict = test.Cfg.RoleAttributeStrict
|
||||||
|
|
||||||
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
switch r.RequestURI {
|
||||||
|
case userURI:
|
||||||
|
_, err := w.Write([]byte(test.UserRespBody))
|
||||||
|
require.NoError(t, err)
|
||||||
|
case groupsURI:
|
||||||
|
_, err := w.Write([]byte(test.GroupsRespBody))
|
||||||
|
require.NoError(t, err)
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
provider.apiUrl = ts.URL + apiURI
|
||||||
|
actualResult, err := provider.UserInfo(ts.Client(), nil)
|
||||||
|
if test.ExpectedError != nil {
|
||||||
|
require.Equal(t, err, test.ExpectedError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, test.ExpectedEmail, actualResult.Email)
|
||||||
|
require.Equal(t, test.ExpectedLogin, actualResult.Login)
|
||||||
|
require.Equal(t, test.ExpectedRole, actualResult.Role)
|
||||||
|
require.Equal(t, test.ExpectedGrafanaAdmin, actualResult.IsGrafanaAdmin)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
@ -44,7 +45,7 @@ func (s *SocialGrafanaCom) IsOrganizationMember(organizations []OrgRecord) bool
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SocialGrafanaCom) UserInfo(client *http.Client, token *oauth2.Token) (*BasicUserInfo, error) {
|
func (s *SocialGrafanaCom) UserInfo(client *http.Client, _ *oauth2.Token) (*BasicUserInfo, error) {
|
||||||
var data struct {
|
var data struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@ -69,7 +70,7 @@ func (s *SocialGrafanaCom) UserInfo(client *http.Client, token *oauth2.Token) (*
|
|||||||
Name: data.Name,
|
Name: data.Name,
|
||||||
Login: data.Login,
|
Login: data.Login,
|
||||||
Email: data.Email,
|
Email: data.Email,
|
||||||
Role: data.Role,
|
Role: org.RoleType(data.Role),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.IsOrganizationMember(data.Orgs) {
|
if !s.IsOrganizationMember(data.Orgs) {
|
||||||
|
@ -80,7 +80,7 @@ func (s *SocialOkta) UserInfo(client *http.Client, token *oauth2.Token) (*BasicU
|
|||||||
return nil, errMissingGroupMembership
|
return nil, errMissingGroupMembership
|
||||||
}
|
}
|
||||||
|
|
||||||
role, grafanaAdmin := s.extractRoleAndAdmin(data.rawJSON, groups)
|
role, grafanaAdmin := s.extractRoleAndAdmin(data.rawJSON, groups, true)
|
||||||
if s.roleAttributeStrict && !role.IsValid() {
|
if s.roleAttributeStrict && !role.IsValid() {
|
||||||
return nil, ErrInvalidBasicRole
|
return nil, ErrInvalidBasicRole
|
||||||
}
|
}
|
||||||
@ -95,7 +95,7 @@ func (s *SocialOkta) UserInfo(client *http.Client, token *oauth2.Token) (*BasicU
|
|||||||
Name: claims.Name,
|
Name: claims.Name,
|
||||||
Email: email,
|
Email: email,
|
||||||
Login: email,
|
Login: email,
|
||||||
Role: string(role),
|
Role: role,
|
||||||
IsGrafanaAdmin: isGrafanaAdmin,
|
IsGrafanaAdmin: isGrafanaAdmin,
|
||||||
Groups: groups,
|
Groups: groups,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -228,7 +228,7 @@ type BasicUserInfo struct {
|
|||||||
Name string
|
Name string
|
||||||
Email string
|
Email string
|
||||||
Login string
|
Login string
|
||||||
Role string
|
Role org.RoleType
|
||||||
IsGrafanaAdmin *bool // nil will avoid overriding user's set server admin setting
|
IsGrafanaAdmin *bool // nil will avoid overriding user's set server admin setting
|
||||||
Groups []string
|
Groups []string
|
||||||
}
|
}
|
||||||
@ -312,13 +312,9 @@ type groupStruct struct {
|
|||||||
Groups []string `json:"groups"`
|
Groups []string `json:"groups"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SocialBase) extractRoleAndAdmin(rawJSON []byte, groups []string) (org.RoleType, bool) {
|
func (s *SocialBase) extractRoleAndAdmin(rawJSON []byte, groups []string, legacy bool) (org.RoleType, bool) {
|
||||||
if s.roleAttributePath == "" {
|
if s.roleAttributePath == "" {
|
||||||
if s.autoAssignOrgRole != "" {
|
return s.defaultRole(legacy), false
|
||||||
return org.RoleType(s.autoAssignOrgRole), false
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
role, err := s.searchJSONForStringAttr(s.roleAttributePath, rawJSON)
|
role, err := s.searchJSONForStringAttr(s.roleAttributePath, rawJSON)
|
||||||
@ -333,7 +329,29 @@ func (s *SocialBase) extractRoleAndAdmin(rawJSON []byte, groups []string) (org.R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", false
|
return s.defaultRole(legacy), false
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultRole returns the default role for the user based on the autoAssignOrgRole setting
|
||||||
|
// if legacy is enabled "" is returned indicating the previous role assignment is used.
|
||||||
|
func (s *SocialBase) defaultRole(legacy bool) org.RoleType {
|
||||||
|
if s.roleAttributeStrict {
|
||||||
|
s.log.Debug("RoleAttributeStrict is set, returning no role.")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.autoAssignOrgRole != "" && !legacy {
|
||||||
|
s.log.Debug("No role found, returning default.")
|
||||||
|
return org.RoleType(s.autoAssignOrgRole)
|
||||||
|
}
|
||||||
|
|
||||||
|
if legacy {
|
||||||
|
s.log.Warn("No valid role found. Skipping role sync. " +
|
||||||
|
"In Grafana 10, this will result in the user being assigned the default role and overriding manual assignment. " +
|
||||||
|
"If role sync is not desired, set oauth_skip_org_role_update_sync to false")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// match grafana admin role and translate to org role and bool.
|
// match grafana admin role and translate to org role and bool.
|
||||||
|
Reference in New Issue
Block a user