Auth: Add skip_org_role_sync for Okta (#62106)

* WIP

* Update pkg/services/login/authinfo.go

* fix: merge

* change order to internal last

* adds: docs

* add: configuration for defaults and sample

* Update docs/sources/setup-grafana/configure-grafana/_index.md

Co-authored-by: Jo <joao.guerreiro@grafana.com>

* Update docs/sources/setup-grafana/configure-grafana/_index.md

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

---------

Co-authored-by: Jo <joao.guerreiro@grafana.com>
Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
This commit is contained in:
Eric Leijonmarck 2023-01-30 10:54:14 +00:00 committed by GitHub
parent d7026900bd
commit a232e7ceca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 68 additions and 12 deletions

View File

@ -621,6 +621,7 @@ allowed_groups =
role_attribute_path = role_attribute_path =
role_attribute_strict = false role_attribute_strict = false
allow_assign_grafana_admin = false allow_assign_grafana_admin = false
skip_org_role_sync = false
#################################### Generic OAuth ####################### #################################### Generic OAuth #######################
[auth.generic_oauth] [auth.generic_oauth]

View File

@ -616,6 +616,7 @@
;role_attribute_path = ;role_attribute_path =
;role_attribute_strict = false ;role_attribute_strict = false
;allow_assign_grafana_admin = false ;allow_assign_grafana_admin = false
;skip_org_role_sync = false
#################################### Generic OAuth ########################## #################################### Generic OAuth ##########################
[auth.generic_oauth] [auth.generic_oauth]

View File

@ -940,6 +940,21 @@ The following table shows the OAuth provider's setting with the default value an
| GitLab | false | true | User organization roles are set with `defaultRole`, and the organization role can be changed for GitLab synced users. | | GitLab | false | true | User organization roles are set with `defaultRole`, and the organization role can be changed for GitLab synced users. |
| GitLab | true | true | User organization roles are set with `defaultRole` for GitLab. For other providers, the synchronization is skipped, and the org role can be changed, along with other OAuth provider users' org roles. | | GitLab | true | true | User organization roles are set with `defaultRole` for GitLab. For other providers, the synchronization is skipped, and the org role can be changed, along with other OAuth provider users' org roles. |
### [auth.okta] skip_org_role_sync
When a user logs in the first time, Grafana sets the organization role based on the value specified in `AutoAssignOrgRole`. If you want to manage organization roles through Grafana's UI, set the `skip_org_role_sync` option to `true`.
This also impacts `allow_assign_grafana_admin` setting, by not syncing the grafana admin role from GitLab.
> **Note:** There is a separate setting called `oauth_skip_org_role_update_sync` which has a different scope. While `skip_org_role_sync` only applies to the specific OAuth provider, `oauth_skip_org_role_update_sync` is a generic setting that affects all configured OAuth providers.
The following table shows the OAuth provider's setting with the default value and the skip org role sync setting.
| OAuth Provider | `oauth_skip_org_role_sync_update` | `skip_org_role_sync` | Behavior |
| --- | --- | --- | --- |
| Okta | false | false | User organization roles are set with `defaultRole` and cannot be changed. |
| Github | true | false | User organization roles are set with `defaultRole` for Okta, and Grafana Admins are set. For other providers, the synchronization is skipped, and the org role can be changed, along with other OAuth provider users' org roles. |
| Okta | false | true | User organization roles are set with `defaultRole`, and the organization role can be changed for Okta synced users. |
| Okta | true | true | User organization roles are set with `defaultRole` for Okta. For other providers, the synchronization is skipped, and the org role can be changed, along with other OAuth provider users' org roles. |
### api_key_max_seconds_to_live ### api_key_max_seconds_to_live
Limit of API key seconds to live before expiration. Default is -1 (unlimited). Limit of API key seconds to live before expiration. Default is -1 (unlimited).

View File

@ -130,6 +130,17 @@ Example:
role_attribute_path = contains(groups[*], 'admin') && 'GrafanaAdmin' || contains(groups[*], 'editor') && 'Editor' || 'Viewer' role_attribute_path = contains(groups[*], 'admin') && 'GrafanaAdmin' || contains(groups[*], 'editor') && 'Editor' || 'Viewer'
``` ```
## Skip organization role sync
To prevent the sync of org roles from Okta, set `skip_org_role_sync` to `true`. This is useful if you want to manage the organization roles for your users from within Grafana.
```ini
[auth.okta]
# ..
# prevents the sync of org roles from Okta
skip_org_role_sync = true
```
### Team Sync (Enterprise only) ### Team Sync (Enterprise only)
Map your Okta groups to teams in Grafana so that your users will automatically be added to Map your Okta groups to teams in Grafana so that your users will automatically be added to

View File

@ -232,6 +232,7 @@ export interface AuthSettings {
GrafanaComSkipOrgRoleSync?: boolean; GrafanaComSkipOrgRoleSync?: boolean;
GithubSkipOrgRoleSync?: boolean; GithubSkipOrgRoleSync?: boolean;
GitLabSkipOrgRoleSync?: boolean; GitLabSkipOrgRoleSync?: boolean;
OktaSkipOrgRoleSync?: boolean;
AzureADSkipOrgRoleSync?: boolean; AzureADSkipOrgRoleSync?: boolean;
GoogleSkipOrgRoleSync?: boolean; GoogleSkipOrgRoleSync?: boolean;
DisableSyncLock?: boolean; DisableSyncLock?: boolean;

View File

@ -155,6 +155,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *contextmodel.ReqContext) (map[st
"GrafanaComSkipOrgRoleSync": hs.Cfg.GrafanaComSkipOrgRoleSync, "GrafanaComSkipOrgRoleSync": hs.Cfg.GrafanaComSkipOrgRoleSync,
"GitLabSkipOrgRoleSync": hs.Cfg.GitLabSkipOrgRoleSync, "GitLabSkipOrgRoleSync": hs.Cfg.GitLabSkipOrgRoleSync,
"AzureADSkipOrgRoleSync": hs.Cfg.AzureADSkipOrgRoleSync, "AzureADSkipOrgRoleSync": hs.Cfg.AzureADSkipOrgRoleSync,
"OktaSkipOrgRoleSync": hs.Cfg.OktaSkipOrgRoleSync,
"DisableSyncLock": hs.Cfg.DisableSyncLock, "DisableSyncLock": hs.Cfg.DisableSyncLock,
}, },
"buildInfo": map[string]interface{}{ "buildInfo": map[string]interface{}{

View File

@ -8,12 +8,15 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
"gopkg.in/square/go-jose.v2/jwt" "gopkg.in/square/go-jose.v2/jwt"
"github.com/grafana/grafana/pkg/models/roletype"
) )
type SocialOkta struct { type SocialOkta struct {
*SocialBase *SocialBase
apiUrl string apiUrl string
allowedGroups []string allowedGroups []string
skipOrgRoleSync bool
} }
type OktaUserInfoJson struct { type OktaUserInfoJson struct {
@ -75,14 +78,20 @@ func (s *SocialOkta) UserInfo(client *http.Client, token *oauth2.Token) (*BasicU
return nil, errMissingGroupMembership return nil, errMissingGroupMembership
} }
role, grafanaAdmin := s.extractRoleAndAdmin(data.rawJSON, groups, true) var role roletype.RoleType
if s.roleAttributeStrict && !role.IsValid() { var isGrafanaAdmin *bool
return nil, &InvalidBasicRoleError{idP: "Okta", assignedRole: string(role)} if !s.skipOrgRoleSync {
var grafanaAdmin bool
role, grafanaAdmin = s.extractRoleAndAdmin(data.rawJSON, groups, true)
if s.roleAttributeStrict && !role.IsValid() {
return nil, &InvalidBasicRoleError{idP: "Okta", assignedRole: string(role)}
}
if s.allowAssignGrafanaAdmin {
isGrafanaAdmin = &grafanaAdmin
}
} }
if s.allowAssignGrafanaAdmin && s.skipOrgRoleSync {
var isGrafanaAdmin *bool = nil s.log.Debug("allowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other")
if s.allowAssignGrafanaAdmin {
isGrafanaAdmin = &grafanaAdmin
} }
return &BasicUserInfo{ return &BasicUserInfo{

View File

@ -183,9 +183,10 @@ func ProvideService(cfg *setting.Cfg,
// Okta // Okta
if name == "okta" { if name == "okta" {
ss.socialMap["okta"] = &SocialOkta{ ss.socialMap["okta"] = &SocialOkta{
SocialBase: newSocialBase(name, &config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features), SocialBase: newSocialBase(name, &config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
apiUrl: info.ApiUrl, apiUrl: info.ApiUrl,
allowedGroups: util.SplitString(sec.Key("allowed_groups").String()), allowedGroups: util.SplitString(sec.Key("allowed_groups").String()),
skipOrgRoleSync: cfg.OktaSkipOrgRoleSync,
} }
} }

View File

@ -34,6 +34,8 @@ func GetAuthProviderLabel(authModule string) string {
return "AzureAD" return "AzureAD"
case "oauth_gitlab": case "oauth_gitlab":
return "GitLab" return "GitLab"
case "oauth_okta":
return "Okta"
case "oauth_grafana_com", "oauth_grafananet": case "oauth_grafana_com", "oauth_grafananet":
return "grafana.com" return "grafana.com"
case SAMLAuthModule: case SAMLAuthModule:

View File

@ -500,6 +500,9 @@ type Cfg struct {
SecureSocksDSProxy SecureSocksDSProxySettings SecureSocksDSProxy SecureSocksDSProxySettings
// Okta OAuth
OktaSkipOrgRoleSync bool
// Access Control // Access Control
RBACEnabled bool RBACEnabled bool
RBACPermissionCache bool RBACPermissionCache bool
@ -1394,6 +1397,11 @@ func readAuthGitlabSettings(iniFile *ini.File, cfg *Cfg) {
cfg.GitLabSkipOrgRoleSync = sec.Key("skip_org_role_sync").MustBool(false) cfg.GitLabSkipOrgRoleSync = sec.Key("skip_org_role_sync").MustBool(false)
} }
func readAuthOktaSettings(iniFile *ini.File, cfg *Cfg) {
sec := iniFile.Section("auth.okta")
cfg.OktaSkipOrgRoleSync = sec.Key("skip_org_role_sync").MustBool(false)
}
func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) { func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
auth := iniFile.Section("auth") auth := iniFile.Section("auth")
@ -1454,6 +1462,9 @@ func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
// GitLab Auth // GitLab Auth
readAuthGitlabSettings(iniFile, cfg) readAuthGitlabSettings(iniFile, cfg)
// Okta Auth
readAuthOktaSettings(iniFile, cfg)
// anonymous access // anonymous access
AnonymousEnabled = iniFile.Section("auth.anonymous").Key("enabled").MustBool(false) AnonymousEnabled = iniFile.Section("auth.anonymous").Key("enabled").MustBool(false)
cfg.AnonymousEnabled = AnonymousEnabled cfg.AnonymousEnabled = AnonymousEnabled

View File

@ -117,6 +117,7 @@ export class UserAdminPage extends PureComponent<Props> {
const isGitLabUser = user?.isExternal && user?.authLabels?.includes('GitLab'); const isGitLabUser = user?.isExternal && user?.authLabels?.includes('GitLab');
const isAuthProxyUser = user?.isExternal && user?.authLabels?.includes('Auth Proxy'); const isAuthProxyUser = user?.isExternal && user?.authLabels?.includes('Auth Proxy');
const isAzureADUser = user?.isExternal && user?.authLabels?.includes('AzureAD'); const isAzureADUser = user?.isExternal && user?.authLabels?.includes('AzureAD');
const isOktaUser = user?.isExternal && user?.authLabels?.includes('Okta');
const isGrafanaComUser = user?.isExternal && user?.authLabels?.includes('grafana.com'); const isGrafanaComUser = user?.isExternal && user?.authLabels?.includes('grafana.com');
const isUserSynced = const isUserSynced =
!config.auth.DisableSyncLock && !config.auth.DisableSyncLock &&
@ -127,6 +128,7 @@ export class UserAdminPage extends PureComponent<Props> {
isGitLabUser || isGitLabUser ||
isOAuthUserWithSkippableSync || isOAuthUserWithSkippableSync ||
isSAMLUser || isSAMLUser ||
isOktaUser ||
isLDAPUser || isLDAPUser ||
isGithubUser || isGithubUser ||
isAzureADUser || isAzureADUser ||
@ -139,6 +141,7 @@ export class UserAdminPage extends PureComponent<Props> {
(!config.auth.JWTAuthSkipOrgRoleSync && isJWTUser) || (!config.auth.JWTAuthSkipOrgRoleSync && isJWTUser) ||
// both OAuthSkipOrgRoleUpdateSync and specific provider settings needs to be false for a user to be synced // both OAuthSkipOrgRoleUpdateSync and specific provider settings needs to be false for a user to be synced
(!config.auth.OAuthSkipOrgRoleUpdateSync && !config.auth.GrafanaComSkipOrgRoleSync && isGrafanaComUser) || (!config.auth.OAuthSkipOrgRoleUpdateSync && !config.auth.GrafanaComSkipOrgRoleSync && isGrafanaComUser) ||
(!config.auth.OAuthSkipOrgRoleUpdateSync && !config.auth.OktaSkipOrgRoleSync && isOktaUser) ||
(!config.auth.OAuthSkipOrgRoleUpdateSync && !config.auth.GithubSkipOrgRoleSync && isGithubUser) || (!config.auth.OAuthSkipOrgRoleUpdateSync && !config.auth.GithubSkipOrgRoleSync && isGithubUser) ||
(!config.auth.OAuthSkipOrgRoleUpdateSync && !config.auth.AzureADSkipOrgRoleSync && isAzureADUser) || (!config.auth.OAuthSkipOrgRoleUpdateSync && !config.auth.AzureADSkipOrgRoleSync && isAzureADUser) ||
(!config.auth.OAuthSkipOrgRoleUpdateSync && !config.auth.GitLabSkipOrgRoleSync && isGitLabUser) || (!config.auth.OAuthSkipOrgRoleUpdateSync && !config.auth.GitLabSkipOrgRoleSync && isGitLabUser) ||