mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Auth: Load ini/env vars settings in the fallback strategy (#78495)
* Return data in camelCase from the OAuth fb strategy * changes * wip * Add defaults for oauth fb strategy * revert other changes * Add tests * Add Defaults to cfg and use it in OAuthStrategy * Return *OAuthInfo from OAuthStrategy * lint * Remove unnecessary Defaults * Introduce const for fields, fix import order * Align failing tests * clean up * Changes requested by @gamab * Update pkg/services/ssosettings/strategies/oauth_strategy_test.go Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> * Load data on startup * Rename + simplify --------- Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
@@ -22,11 +22,15 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
var (
|
||||
errAzureADMissingGroups = &Error{"either the user does not have any group membership or the groups claim is missing from the token."}
|
||||
const (
|
||||
AzureADProviderName = "azuread"
|
||||
forceUseGraphAPIKey = "force_use_graph_api" // #nosec G101 not a hardcoded credential
|
||||
)
|
||||
|
||||
const azureADProviderName = "azuread"
|
||||
var (
|
||||
ExtraAzureADSettingKeys = []string{forceUseGraphAPIKey, allowedOrganizationsKey}
|
||||
errAzureADMissingGroups = &Error{"either the user does not have any group membership or the groups claim is missing from the token."}
|
||||
)
|
||||
|
||||
var _ SocialConnector = (*SocialAzureAD)(nil)
|
||||
|
||||
@@ -74,12 +78,12 @@ func NewAzureADProvider(settings map[string]any, cfg *setting.Cfg, features *fea
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := createOAuthConfig(info, cfg, azureADProviderName)
|
||||
config := createOAuthConfig(info, cfg, AzureADProviderName)
|
||||
provider := &SocialAzureAD{
|
||||
SocialBase: newSocialBase(azureADProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
SocialBase: newSocialBase(AzureADProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
cache: cache,
|
||||
allowedOrganizations: util.SplitString(info.Extra["allowed_organizations"]),
|
||||
forceUseGraphAPI: mustBool(info.Extra["force_use_graph_api"], false),
|
||||
allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]),
|
||||
forceUseGraphAPI: MustBool(info.Extra[forceUseGraphAPIKey], false),
|
||||
skipOrgRoleSync: cfg.AzureADSkipOrgRoleSync,
|
||||
// FIXME: Move skipOrgRoleSync to OAuthInfo
|
||||
// skipOrgRoleSync: info.SkipOrgRoleSync
|
||||
|
||||
@@ -19,6 +19,13 @@ import (
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
// consider moving this to OAuthInfo
|
||||
teamIdsKey = "team_ids"
|
||||
// consider moving this to OAuthInfo
|
||||
allowedOrganizationsKey = "allowed_organizations"
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingGroupMembership = &Error{"user not a member of one of the required groups"}
|
||||
)
|
||||
@@ -166,7 +173,7 @@ func createOAuthConfig(info *OAuthInfo, cfg *setting.Cfg, defaultName string) *o
|
||||
return &config
|
||||
}
|
||||
|
||||
func mustBool(value any, defaultValue bool) bool {
|
||||
func MustBool(value any, defaultValue bool) bool {
|
||||
if value == nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ signout_redirect_url = https://oauth.com/signout?post_logout_redirect_uri=https:
|
||||
AuthStyle: "",
|
||||
AllowAssignGrafanaAdmin: true,
|
||||
UseRefreshToken: true,
|
||||
SkipOrgRoleSync: true,
|
||||
HostedDomain: "test_hosted_domain",
|
||||
SignoutRedirectUrl: "https://oauth.com/signout?post_logout_redirect_uri=https://grafana.com",
|
||||
Extra: map[string]string{
|
||||
@@ -90,7 +91,6 @@ signout_redirect_url = https://oauth.com/signout?post_logout_redirect_uri=https:
|
||||
"id_token_attribute_name": "id_token",
|
||||
"login_attribute_path": "login",
|
||||
"name_attribute_path": "name",
|
||||
"skip_org_role_sync": "true",
|
||||
"team_ids": "first, second",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -17,7 +17,15 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
const genericOAuthProviderName = "generic_oauth"
|
||||
const (
|
||||
GenericOAuthProviderName = "generic_oauth"
|
||||
|
||||
nameAttributePathKey = "name_attribute_path"
|
||||
loginAttributePathKey = "login_attribute_path"
|
||||
idTokenAttributeNameKey = "id_token_attribute_name" // #nosec G101 not a hardcoded credential
|
||||
)
|
||||
|
||||
var ExtraGenericOAuthSettingKeys = []string{nameAttributePathKey, loginAttributePathKey, idTokenAttributeNameKey, teamIdsKey, allowedOrganizationsKey}
|
||||
|
||||
type SocialGenericOAuth struct {
|
||||
*SocialBase
|
||||
@@ -42,20 +50,20 @@ func NewGenericOAuthProvider(settings map[string]any, cfg *setting.Cfg, features
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := createOAuthConfig(info, cfg, genericOAuthProviderName)
|
||||
config := createOAuthConfig(info, cfg, GenericOAuthProviderName)
|
||||
provider := &SocialGenericOAuth{
|
||||
SocialBase: newSocialBase(genericOAuthProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
SocialBase: newSocialBase(GenericOAuthProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
apiUrl: info.ApiUrl,
|
||||
teamsUrl: info.TeamsUrl,
|
||||
emailAttributeName: info.EmailAttributeName,
|
||||
emailAttributePath: info.EmailAttributePath,
|
||||
nameAttributePath: info.Extra["name_attribute_path"],
|
||||
nameAttributePath: info.Extra[nameAttributePathKey],
|
||||
groupsAttributePath: info.GroupsAttributePath,
|
||||
loginAttributePath: info.Extra["login_attribute_path"],
|
||||
idTokenAttributeName: info.Extra["id_token_attribute_name"],
|
||||
loginAttributePath: info.Extra[loginAttributePathKey],
|
||||
idTokenAttributeName: info.Extra[idTokenAttributeNameKey],
|
||||
teamIdsAttributePath: info.TeamIdsAttributePath,
|
||||
teamIds: util.SplitString(info.Extra["team_ids"]),
|
||||
allowedOrganizations: util.SplitString(info.Extra["allowed_organizations"]),
|
||||
teamIds: util.SplitString(info.Extra[teamIdsKey]),
|
||||
allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]),
|
||||
allowedGroups: info.AllowedGroups,
|
||||
skipOrgRoleSync: cfg.GenericOAuthSkipOrgRoleSync,
|
||||
// FIXME: Move skipOrgRoleSync to OAuthInfo
|
||||
|
||||
@@ -19,7 +19,9 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
const gitHubProviderName = "github"
|
||||
const GitHubProviderName = "github"
|
||||
|
||||
var ExtraGithubSettingKeys = []string{allowedOrganizationsKey, teamIdsKey}
|
||||
|
||||
type SocialGithub struct {
|
||||
*SocialBase
|
||||
@@ -55,17 +57,17 @@ func NewGitHubProvider(settings map[string]any, cfg *setting.Cfg, features *feat
|
||||
return nil, err
|
||||
}
|
||||
|
||||
teamIds, err := mustInts(util.SplitString(info.Extra["team_ids"]))
|
||||
teamIds, err := mustInts(util.SplitString(info.Extra[teamIdsKey]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := createOAuthConfig(info, cfg, gitHubProviderName)
|
||||
config := createOAuthConfig(info, cfg, GitHubProviderName)
|
||||
provider := &SocialGithub{
|
||||
SocialBase: newSocialBase(gitHubProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
SocialBase: newSocialBase(GitHubProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
apiUrl: info.ApiUrl,
|
||||
teamIds: teamIds,
|
||||
allowedOrganizations: util.SplitString(info.Extra["allowed_organizations"]),
|
||||
allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]),
|
||||
skipOrgRoleSync: cfg.GitHubSkipOrgRoleSync,
|
||||
// FIXME: Move skipOrgRoleSync to OAuthInfo
|
||||
// skipOrgRoleSync: info.SkipOrgRoleSync
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
const (
|
||||
groupPerPage = 50
|
||||
accessLevelGuest = "10"
|
||||
gitlabProviderName = "gitlab"
|
||||
GitlabProviderName = "gitlab"
|
||||
)
|
||||
|
||||
type SocialGitlab struct {
|
||||
@@ -55,9 +55,9 @@ func NewGitLabProvider(settings map[string]any, cfg *setting.Cfg, features *feat
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := createOAuthConfig(info, cfg, gitlabProviderName)
|
||||
config := createOAuthConfig(info, cfg, GitlabProviderName)
|
||||
provider := &SocialGitlab{
|
||||
SocialBase: newSocialBase(gitlabProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
SocialBase: newSocialBase(GitlabProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
apiUrl: info.ApiUrl,
|
||||
skipOrgRoleSync: cfg.GitLabSkipOrgRoleSync,
|
||||
// FIXME: Move skipOrgRoleSync to OAuthInfo
|
||||
|
||||
@@ -18,7 +18,7 @@ const (
|
||||
legacyAPIURL = "https://www.googleapis.com/oauth2/v1/userinfo"
|
||||
googleIAMGroupsEndpoint = "https://content-cloudidentity.googleapis.com/v1/groups/-/memberships:searchDirectGroups"
|
||||
googleIAMScope = "https://www.googleapis.com/auth/cloud-identity.groups.readonly"
|
||||
googleProviderName = "google"
|
||||
GoogleProviderName = "google"
|
||||
)
|
||||
|
||||
type SocialGoogle struct {
|
||||
@@ -42,9 +42,9 @@ func NewGoogleProvider(settings map[string]any, cfg *setting.Cfg, features *feat
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := createOAuthConfig(info, cfg, googleProviderName)
|
||||
config := createOAuthConfig(info, cfg, GoogleProviderName)
|
||||
provider := &SocialGoogle{
|
||||
SocialBase: newSocialBase(googleProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
SocialBase: newSocialBase(GoogleProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
hostedDomain: info.HostedDomain,
|
||||
apiUrl: info.ApiUrl,
|
||||
skipOrgRoleSync: cfg.GoogleSkipOrgRoleSync,
|
||||
|
||||
@@ -15,7 +15,13 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
const grafanaComProviderName = "grafana_com"
|
||||
const (
|
||||
GrafanaComProviderName = "grafana_com"
|
||||
// legacy/old settings for the provider
|
||||
GrafanaNetProviderName = "grafananet"
|
||||
)
|
||||
|
||||
var ExtraGrafanaComSettingKeys = []string{allowedOrganizationsKey}
|
||||
|
||||
type SocialGrafanaCom struct {
|
||||
*SocialBase
|
||||
@@ -39,11 +45,11 @@ func NewGrafanaComProvider(settings map[string]any, cfg *setting.Cfg, features *
|
||||
info.TokenUrl = cfg.GrafanaComURL + "/api/oauth2/token"
|
||||
info.AuthStyle = "inheader"
|
||||
|
||||
config := createOAuthConfig(info, cfg, grafanaComProviderName)
|
||||
config := createOAuthConfig(info, cfg, GrafanaComProviderName)
|
||||
provider := &SocialGrafanaCom{
|
||||
SocialBase: newSocialBase(grafanaComProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
SocialBase: newSocialBase(GrafanaComProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
url: cfg.GrafanaComURL,
|
||||
allowedOrganizations: util.SplitString(info.Extra["allowed_organizations"]),
|
||||
allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]),
|
||||
skipOrgRoleSync: cfg.GrafanaComSkipOrgRoleSync,
|
||||
// FIXME: Move skipOrgRoleSync to OAuthInfo
|
||||
// skipOrgRoleSync: info.SkipOrgRoleSync
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
const oktaProviderName = "okta"
|
||||
const OktaProviderName = "okta"
|
||||
|
||||
type SocialOkta struct {
|
||||
*SocialBase
|
||||
@@ -49,9 +49,9 @@ func NewOktaProvider(settings map[string]any, cfg *setting.Cfg, features *featur
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := createOAuthConfig(info, cfg, oktaProviderName)
|
||||
config := createOAuthConfig(info, cfg, OktaProviderName)
|
||||
provider := &SocialOkta{
|
||||
SocialBase: newSocialBase(oktaProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
SocialBase: newSocialBase(OktaProviderName, config, info, cfg.AutoAssignOrgRole, cfg.OAuthSkipOrgRoleUpdateSync, *features),
|
||||
apiUrl: info.ApiUrl,
|
||||
allowedGroups: info.AllowedGroups,
|
||||
// FIXME: Move skipOrgRoleSync to OAuthInfo
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
|
||||
const (
|
||||
OfflineAccessScope = "offline_access"
|
||||
RoleGrafanaAdmin = "GrafanaAdmin" // For AzureAD for example this value cannot contain spaces
|
||||
)
|
||||
|
||||
type SocialService struct {
|
||||
@@ -43,40 +44,50 @@ type SocialService struct {
|
||||
}
|
||||
|
||||
type OAuthInfo struct {
|
||||
AllowAssignGrafanaAdmin bool `mapstructure:"allow_assign_grafana_admin" toml:"allow_assign_grafana_admin" json:"allowAssignGrafanaAdmin"`
|
||||
AllowSignup bool `mapstructure:"allow_sign_up" toml:"allow_sign_up" json:"allowSignup"`
|
||||
AllowedDomains []string `mapstructure:"allowed_domains" toml:"allowed_domains" json:"allowedDomains"`
|
||||
AllowedGroups []string `mapstructure:"allowed_groups" toml:"allowed_groups" json:"allowedGroups"`
|
||||
ApiUrl string `mapstructure:"api_url" toml:"api_url" json:"apiUrl"`
|
||||
AuthUrl string `mapstructure:"auth_url" toml:"auth_url" json:"authUrl"`
|
||||
AuthStyle string `mapstructure:"auth_style" toml:"auth_style" json:"authStyle"`
|
||||
AuthUrl string `mapstructure:"auth_url" toml:"auth_url" json:"authUrl"`
|
||||
AutoLogin bool `mapstructure:"auto_login" toml:"auto_login" json:"autoLogin"`
|
||||
ClientId string `mapstructure:"client_id" toml:"client_id" json:"clientId"`
|
||||
ClientSecret string `mapstructure:"client_secret" toml:"-" json:"clientSecret"`
|
||||
EmailAttributeName string `mapstructure:"email_attribute_name" toml:"email_attribute_name" json:"emailAttributeName"`
|
||||
EmailAttributePath string `mapstructure:"email_attribute_path" toml:"email_attribute_path" json:"emailAttributePath"`
|
||||
EmptyScopes bool `mapstructure:"empty_scopes" toml:"empty_scopes" json:"emptyScopes"`
|
||||
Enabled bool `mapstructure:"enabled" toml:"enabled" json:"enabled"`
|
||||
GroupsAttributePath string `mapstructure:"groups_attribute_path" toml:"groups_attribute_path" json:"groupsAttributePath"`
|
||||
HostedDomain string `mapstructure:"hosted_domain" toml:"hosted_domain" json:"hostedDomain"`
|
||||
Icon string `mapstructure:"icon" toml:"icon" json:"icon"`
|
||||
Name string `mapstructure:"name" toml:"name" json:"name"`
|
||||
RoleAttributePath string `mapstructure:"role_attribute_path" toml:"role_attribute_path" json:"roleAttributePath"`
|
||||
RoleAttributeStrict bool `mapstructure:"role_attribute_strict" toml:"role_attribute_strict" json:"roleAttributeStrict"`
|
||||
Scopes []string `mapstructure:"scopes" toml:"scopes" json:"scopes"`
|
||||
SignoutRedirectUrl string `mapstructure:"signout_redirect_url" toml:"signout_redirect_url" json:"signoutRedirectUrl"`
|
||||
SkipOrgRoleSync bool `mapstructure:"skip_org_role_sync" toml:"skip_org_role_sync" json:"skipOrgRoleSync"`
|
||||
TeamIdsAttributePath string `mapstructure:"team_ids_attribute_path" toml:"team_ids_attribute_path" json:"teamIdsAttributePath"`
|
||||
TeamsUrl string `mapstructure:"teams_url" toml:"teams_url" json:"teamsUrl"`
|
||||
TlsClientCa string `mapstructure:"tls_client_ca" toml:"tls_client_ca" json:"tlsClientCa"`
|
||||
TlsClientCert string `mapstructure:"tls_client_cert" toml:"tls_client_cert" json:"tlsClientCert"`
|
||||
TlsClientKey string `mapstructure:"tls_client_key" toml:"tls_client_key" json:"tlsClientKey"`
|
||||
TokenUrl string `mapstructure:"token_url" toml:"token_url" json:"tokenUrl"`
|
||||
AllowedDomains []string `mapstructure:"allowed_domains" toml:"allowed_domains" json:"allowedDomains"`
|
||||
AllowedGroups []string `mapstructure:"allowed_groups" toml:"allowed_groups" json:"allowedGroups"`
|
||||
Scopes []string `mapstructure:"scopes" toml:"scopes" json:"scopes"`
|
||||
AllowAssignGrafanaAdmin bool `mapstructure:"allow_assign_grafana_admin" toml:"allow_assign_grafana_admin" json:"allowAssignGrafanaAdmin"`
|
||||
AllowSignup bool `mapstructure:"allow_sign_up" toml:"allow_sign_up" json:"allowSignup"`
|
||||
AutoLogin bool `mapstructure:"auto_login" toml:"auto_login" json:"autoLogin"`
|
||||
Enabled bool `mapstructure:"enabled" toml:"enabled" json:"enabled"`
|
||||
RoleAttributeStrict bool `mapstructure:"role_attribute_strict" toml:"role_attribute_strict" json:"roleAttributeStrict"`
|
||||
TlsSkipVerify bool `mapstructure:"tls_skip_verify_insecure" toml:"tls_skip_verify_insecure" json:"tlsSkipVerify"`
|
||||
TokenUrl string `mapstructure:"token_url" toml:"token_url" json:"tokenUrl"`
|
||||
UsePKCE bool `mapstructure:"use_pkce" toml:"use_pkce" json:"usePKCE"`
|
||||
UseRefreshToken bool `mapstructure:"use_refresh_token" toml:"use_refresh_token" json:"useRefreshToken"`
|
||||
SignoutRedirectUrl string `mapstructure:"signout_redirect_url" toml:"signout_redirect_url" json:"signoutRedirectUrl"`
|
||||
Extra map[string]string `mapstructure:",remain" toml:"extra,omitempty" json:"extra"`
|
||||
}
|
||||
|
||||
func NewOAuthInfo() *OAuthInfo {
|
||||
return &OAuthInfo{
|
||||
Scopes: []string{},
|
||||
AllowedDomains: []string{},
|
||||
AllowedGroups: []string{},
|
||||
Extra: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
func ProvideService(cfg *setting.Cfg,
|
||||
features *featuremgmt.FeatureManager,
|
||||
usageStats usagestats.Service,
|
||||
@@ -105,8 +116,8 @@ func ProvideService(cfg *setting.Cfg,
|
||||
continue
|
||||
}
|
||||
|
||||
if name == "grafananet" {
|
||||
name = grafanaCom
|
||||
if name == GrafanaNetProviderName {
|
||||
name = GrafanaComProviderName
|
||||
}
|
||||
|
||||
conn, err := ss.createOAuthConnector(name, settingsKVs, cfg, features, cache)
|
||||
@@ -177,15 +188,11 @@ func (e Error) Error() string {
|
||||
return e.s
|
||||
}
|
||||
|
||||
const (
|
||||
grafanaCom = "grafana_com"
|
||||
RoleGrafanaAdmin = "GrafanaAdmin" // For AzureAD for example this value cannot contain spaces
|
||||
)
|
||||
|
||||
var (
|
||||
SocialBaseUrl = "/login/"
|
||||
SocialMap = make(map[string]SocialConnector)
|
||||
allOauthes = []string{"github", "gitlab", "google", "generic_oauth", "grafananet", grafanaCom, "azuread", "okta"}
|
||||
allOauthes = []string{GitHubProviderName, GitlabProviderName, GoogleProviderName, GenericOAuthProviderName, GrafanaNetProviderName,
|
||||
GrafanaComProviderName, AzureADProviderName, OktaProviderName}
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
@@ -507,19 +514,19 @@ func (s *SocialBase) retrieveRawIDToken(idToken any) ([]byte, error) {
|
||||
|
||||
func (ss *SocialService) createOAuthConnector(name string, settings map[string]any, cfg *setting.Cfg, features *featuremgmt.FeatureManager, cache remotecache.CacheStorage) (SocialConnector, error) {
|
||||
switch name {
|
||||
case azureADProviderName:
|
||||
case AzureADProviderName:
|
||||
return NewAzureADProvider(settings, cfg, features, cache)
|
||||
case genericOAuthProviderName:
|
||||
case GenericOAuthProviderName:
|
||||
return NewGenericOAuthProvider(settings, cfg, features)
|
||||
case gitHubProviderName:
|
||||
case GitHubProviderName:
|
||||
return NewGitHubProvider(settings, cfg, features)
|
||||
case gitlabProviderName:
|
||||
case GitlabProviderName:
|
||||
return NewGitLabProvider(settings, cfg, features)
|
||||
case googleProviderName:
|
||||
case GoogleProviderName:
|
||||
return NewGoogleProvider(settings, cfg, features)
|
||||
case grafanaComProviderName:
|
||||
case GrafanaComProviderName:
|
||||
return NewGrafanaComProvider(settings, cfg, features)
|
||||
case oktaProviderName:
|
||||
case OktaProviderName:
|
||||
return NewOktaProvider(settings, cfg, features)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown oauth provider: %s", name)
|
||||
|
||||
@@ -37,13 +37,13 @@ type SSOSettings struct {
|
||||
}
|
||||
|
||||
type SSOSettingsDTO struct {
|
||||
ID string `xorm:"id pk" json:"id"`
|
||||
Provider string `xorm:"provider" json:"provider"`
|
||||
Settings map[string]interface{} `xorm:"settings" json:"settings"`
|
||||
Created time.Time `xorm:"created" json:"-"`
|
||||
Updated time.Time `xorm:"updated" json:"-"`
|
||||
IsDeleted bool `xorm:"is_deleted" json:"-"`
|
||||
Source SettingsSource `xorm:"-" json:"source"`
|
||||
ID string `xorm:"id pk" json:"id"`
|
||||
Provider string `xorm:"provider" json:"provider"`
|
||||
Settings map[string]any `xorm:"settings" json:"settings"`
|
||||
Created time.Time `xorm:"created" json:"-"`
|
||||
Updated time.Time `xorm:"updated" json:"-"`
|
||||
IsDeleted bool `xorm:"is_deleted" json:"-"`
|
||||
Source SettingsSource `xorm:"-" json:"source"`
|
||||
}
|
||||
|
||||
// TableName returns the table name (needed for Xorm)
|
||||
@@ -79,7 +79,7 @@ func (s SSOSettings) ToSSOSettingsDTO() (*SSOSettingsDTO, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var settings map[string]interface{}
|
||||
var settings map[string]any
|
||||
err = json.Unmarshal(settingsEncoded, &settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -3,6 +3,7 @@ package ssosettings
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/login/social"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings/models"
|
||||
)
|
||||
@@ -12,7 +13,7 @@ var (
|
||||
// TODO: make it configurable
|
||||
ConfigurableOAuthProviders = []string{"github", "gitlab", "google", "generic_oauth", "azuread", "okta"}
|
||||
|
||||
AllOAuthProviders = []string{"github", "gitlab", "google", "generic_oauth", "grafana_com", "azuread", "okta"}
|
||||
AllOAuthProviders = []string{social.GitHubProviderName, social.GitlabProviderName, social.GoogleProviderName, social.GenericOAuthProviderName, social.GrafanaComProviderName, social.AzureADProviderName, social.OktaProviderName}
|
||||
)
|
||||
|
||||
// Service is a SSO settings service
|
||||
@@ -28,7 +29,7 @@ type Service interface {
|
||||
// Delete deletes the SSO settings for a given provider (soft delete)
|
||||
Delete(ctx context.Context, provider string) error
|
||||
// Patch updates the specified SSO settings (key-value pairs) for a given provider
|
||||
Patch(ctx context.Context, provider string, data map[string]interface{}) error
|
||||
Patch(ctx context.Context, provider string, data map[string]any) error
|
||||
// RegisterReloadable registers a reloadable provider
|
||||
RegisterReloadable(ctx context.Context, provider string, reloadable Reloadable)
|
||||
// Reload implements ssosettings.Reloadable interface
|
||||
@@ -45,7 +46,7 @@ type Reloadable interface {
|
||||
// using the config file and/or environment variables. Used mostly for backwards compatibility.
|
||||
type FallbackStrategy interface {
|
||||
IsMatch(provider string) bool
|
||||
ParseConfigFromSystem(ctx context.Context) (map[string]interface{}, error)
|
||||
GetProviderConfig(ctx context.Context, provider string) (any, error)
|
||||
}
|
||||
|
||||
// Store is a SSO settings store
|
||||
@@ -55,6 +56,6 @@ type Store interface {
|
||||
Get(ctx context.Context, provider string) (*models.SSOSettings, error)
|
||||
List(ctx context.Context) ([]*models.SSOSettings, error)
|
||||
Upsert(ctx context.Context, settings models.SSOSettings) error
|
||||
Patch(ctx context.Context, provider string, data map[string]interface{}) error
|
||||
Patch(ctx context.Context, provider string, data map[string]any) error
|
||||
Delete(ctx context.Context, provider string) error
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ func (s *SSOSettingsService) Upsert(ctx context.Context, settings models.SSOSett
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SSOSettingsService) Patch(ctx context.Context, provider string, data map[string]interface{}) error {
|
||||
func (s *SSOSettingsService) Patch(ctx context.Context, provider string, data map[string]any) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
@@ -147,21 +147,21 @@ func (s *SSOSettingsService) loadSettingsUsingFallbackStrategy(ctx context.Conte
|
||||
return nil, errors.New("no fallback strategy found for provider: " + provider)
|
||||
}
|
||||
|
||||
settingsFromSystem, err := loadStrategy.ParseConfigFromSystem(ctx)
|
||||
settingsFromSystem, err := loadStrategy.GetProviderConfig(ctx, provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oAuthInfo, err := social.CreateOAuthInfoFromKeyValues(settingsFromSystem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
switch settingsFromSystem := settingsFromSystem.(type) {
|
||||
case *social.OAuthInfo:
|
||||
return &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Source: models.System,
|
||||
OAuthSettings: settingsFromSystem,
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.New("could not parse settings from system")
|
||||
}
|
||||
|
||||
return &models.SSOSettings{
|
||||
Provider: provider,
|
||||
Source: models.System,
|
||||
OAuthSettings: oAuthInfo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getSettingsByProvider(provider string, settings []*models.SSOSettings) []*models.SSOSettings {
|
||||
|
||||
@@ -52,9 +52,7 @@ func TestSSOSettingsService_GetForProvider(t *testing.T) {
|
||||
setup: func(env testEnv) {
|
||||
env.store.ExpectedError = ssosettings.ErrNotFound
|
||||
env.fallbackStrategy.ExpectedIsMatch = true
|
||||
env.fallbackStrategy.ExpectedConfig = map[string]interface{}{
|
||||
"enabled": true,
|
||||
}
|
||||
env.fallbackStrategy.ExpectedConfig = &social.OAuthInfo{Enabled: true}
|
||||
},
|
||||
want: &models.SSOSettings{
|
||||
Provider: "github",
|
||||
@@ -150,9 +148,7 @@ func TestSSOSettingsService_List(t *testing.T) {
|
||||
},
|
||||
}
|
||||
env.fallbackStrategy.ExpectedIsMatch = true
|
||||
env.fallbackStrategy.ExpectedConfig = map[string]interface{}{
|
||||
"enabled": false,
|
||||
}
|
||||
env.fallbackStrategy.ExpectedConfig = &social.OAuthInfo{Enabled: false}
|
||||
},
|
||||
identity: defaultIdentity,
|
||||
want: []*models.SSOSettings{
|
||||
@@ -210,9 +206,7 @@ func TestSSOSettingsService_List(t *testing.T) {
|
||||
},
|
||||
}
|
||||
env.fallbackStrategy.ExpectedIsMatch = true
|
||||
env.fallbackStrategy.ExpectedConfig = map[string]interface{}{
|
||||
"enabled": false,
|
||||
}
|
||||
env.fallbackStrategy.ExpectedConfig = &social.OAuthInfo{Enabled: false}
|
||||
},
|
||||
identity: scopedIdentity,
|
||||
want: []*models.SSOSettings{
|
||||
@@ -241,9 +235,7 @@ func TestSSOSettingsService_List(t *testing.T) {
|
||||
setup: func(env testEnv) {
|
||||
env.store.ExpectedSSOSettings = []*models.SSOSettings{}
|
||||
env.fallbackStrategy.ExpectedIsMatch = true
|
||||
env.fallbackStrategy.ExpectedConfig = map[string]interface{}{
|
||||
"enabled": false,
|
||||
}
|
||||
env.fallbackStrategy.ExpectedConfig = &social.OAuthInfo{Enabled: false}
|
||||
},
|
||||
identity: defaultIdentity,
|
||||
want: []*models.SSOSettings{
|
||||
|
||||
@@ -4,7 +4,7 @@ import context "context"
|
||||
|
||||
type FakeFallbackStrategy struct {
|
||||
ExpectedIsMatch bool
|
||||
ExpectedConfig map[string]interface{}
|
||||
ExpectedConfig any
|
||||
|
||||
ExpectedError error
|
||||
}
|
||||
@@ -17,6 +17,6 @@ func (f *FakeFallbackStrategy) IsMatch(provider string) bool {
|
||||
return f.ExpectedIsMatch
|
||||
}
|
||||
|
||||
func (f *FakeFallbackStrategy) ParseConfigFromSystem(ctx context.Context) (map[string]interface{}, error) {
|
||||
func (f *FakeFallbackStrategy) GetProviderConfig(ctx context.Context, provider string) (any, error) {
|
||||
return f.ExpectedConfig, f.ExpectedError
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func (f *FakeStore) Upsert(ctx context.Context, settings models.SSOSettings) err
|
||||
return f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakeStore) Patch(ctx context.Context, provider string, data map[string]interface{}) error {
|
||||
func (f *FakeStore) Patch(ctx context.Context, provider string, data map[string]any) error {
|
||||
return f.ExpectedError
|
||||
}
|
||||
|
||||
|
||||
@@ -2,73 +2,101 @@ package strategies
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/login/social"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type OAuthStrategy struct {
|
||||
provider string
|
||||
cfg *setting.Cfg
|
||||
supportedProvidersRegex *regexp.Regexp
|
||||
cfg *setting.Cfg
|
||||
settingsByProvider map[string]*social.OAuthInfo
|
||||
}
|
||||
|
||||
var extraKeysByProvider = map[string][]string{
|
||||
social.AzureADProviderName: social.ExtraAzureADSettingKeys,
|
||||
social.GenericOAuthProviderName: social.ExtraGenericOAuthSettingKeys,
|
||||
social.GitHubProviderName: social.ExtraGithubSettingKeys,
|
||||
social.GrafanaComProviderName: social.ExtraGrafanaComSettingKeys,
|
||||
social.GrafanaNetProviderName: social.ExtraGrafanaComSettingKeys,
|
||||
}
|
||||
|
||||
var _ ssosettings.FallbackStrategy = (*OAuthStrategy)(nil)
|
||||
|
||||
func NewOAuthStrategy(cfg *setting.Cfg) *OAuthStrategy {
|
||||
compiledRegex := regexp.MustCompile(`^` + strings.Join(ssosettings.AllOAuthProviders, "|") + `$`)
|
||||
return &OAuthStrategy{
|
||||
cfg: cfg,
|
||||
supportedProvidersRegex: compiledRegex,
|
||||
oauthStrategy := &OAuthStrategy{
|
||||
cfg: cfg,
|
||||
settingsByProvider: make(map[string]*social.OAuthInfo),
|
||||
}
|
||||
|
||||
oauthStrategy.loadAllSettings()
|
||||
return oauthStrategy
|
||||
}
|
||||
|
||||
func (s *OAuthStrategy) IsMatch(provider string) bool {
|
||||
return s.supportedProvidersRegex.MatchString(provider)
|
||||
_, ok := s.settingsByProvider[provider]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (s *OAuthStrategy) ParseConfigFromSystem(_ context.Context) (map[string]any, error) {
|
||||
section := s.cfg.SectionWithEnvOverrides("auth." + s.provider)
|
||||
|
||||
// TODO: load the provider specific keys separately
|
||||
result := map[string]any{
|
||||
"client_id": section.Key("client_id").Value(),
|
||||
"client_secret": section.Key("client_secret").Value(),
|
||||
"scopes": section.Key("scopes").Value(),
|
||||
"auth_url": section.Key("auth_url").Value(),
|
||||
"token_url": section.Key("token_url").Value(),
|
||||
"api_url": section.Key("api_url").Value(),
|
||||
"teams_url": section.Key("teams_url").Value(),
|
||||
"enabled": section.Key("enabled").MustBool(false),
|
||||
"email_attribute_name": section.Key("email_attribute_name").Value(),
|
||||
"email_attribute_path": section.Key("email_attribute_path").Value(),
|
||||
"role_attribute_path": section.Key("role_attribute_path").Value(),
|
||||
"role_attribute_strict": section.Key("role_attribute_strict").MustBool(false),
|
||||
"groups_attribute_path": section.Key("groups_attribute_path").Value(),
|
||||
"team_ids_attribute_path": section.Key("team_ids_attribute_path").Value(),
|
||||
"allowed_domains": section.Key("allowed_domains").Value(),
|
||||
"hosted_domain": section.Key("hosted_domain").Value(),
|
||||
"allow_sign_up": section.Key("allow_sign_up").MustBool(true),
|
||||
"name": section.Key("name").MustString("default name"), // TODO: change this default value
|
||||
"icon": section.Key("icon").Value(),
|
||||
// TODO: @mgyongyosi move skipOrgRoleSync here in a separate PR
|
||||
// "skip_org_role_sync": section.Key("skip_org_role_sync").MustBool(false),
|
||||
"tls_client_cert": section.Key("tls_client_cert").Value(),
|
||||
"tls_client_key": section.Key("tls_client_key").Value(),
|
||||
"tls_client_ca": section.Key("tls_client_ca").Value(),
|
||||
"tls_skip_verify_insecure": section.Key("tls_skip_verify_insecure").MustBool(false),
|
||||
"use_pkce": section.Key("use_pkce").MustBool(true),
|
||||
"use_refresh_token": section.Key("use_refresh_token").MustBool(false),
|
||||
"allow_assign_grafana_admin": section.Key("allow_assign_grafana_admin").MustBool(false),
|
||||
"auto_login": section.Key("auto_login").MustBool(false),
|
||||
"allowed_groups": section.Key("allowed_groups").Value(),
|
||||
}
|
||||
|
||||
// when empty_scopes parameter exists and is true, overwrite scope with empty value
|
||||
if section.Key("empty_scopes").MustBool(false) {
|
||||
result["scopes"] = []string{}
|
||||
}
|
||||
return result, nil
|
||||
func (s *OAuthStrategy) GetProviderConfig(_ context.Context, provider string) (any, error) {
|
||||
return s.settingsByProvider[provider], nil
|
||||
}
|
||||
|
||||
func (s *OAuthStrategy) loadAllSettings() {
|
||||
allProviders := append(ssosettings.AllOAuthProviders, social.GrafanaNetProviderName)
|
||||
for _, provider := range allProviders {
|
||||
settings := s.loadSettingsForProvider(provider)
|
||||
if provider == social.GrafanaNetProviderName {
|
||||
provider = social.GrafanaComProviderName
|
||||
}
|
||||
s.settingsByProvider[provider] = settings
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OAuthStrategy) loadSettingsForProvider(provider string) *social.OAuthInfo {
|
||||
section := s.cfg.SectionWithEnvOverrides("auth." + provider)
|
||||
|
||||
result := &social.OAuthInfo{
|
||||
AllowAssignGrafanaAdmin: section.Key("allow_assign_grafana_admin").MustBool(false),
|
||||
AllowSignup: section.Key("allow_sign_up").MustBool(false),
|
||||
AllowedDomains: util.SplitString(section.Key("allowed_domains").Value()),
|
||||
AllowedGroups: util.SplitString(section.Key("allowed_groups").Value()),
|
||||
ApiUrl: section.Key("api_url").Value(),
|
||||
AuthStyle: section.Key("auth_style").Value(),
|
||||
AuthUrl: section.Key("auth_url").Value(),
|
||||
AutoLogin: section.Key("auto_login").MustBool(false),
|
||||
ClientId: section.Key("client_id").Value(),
|
||||
ClientSecret: section.Key("client_secret").Value(),
|
||||
EmailAttributeName: section.Key("email_attribute_name").Value(),
|
||||
EmailAttributePath: section.Key("email_attribute_path").Value(),
|
||||
EmptyScopes: section.Key("empty_scopes").MustBool(false),
|
||||
Enabled: section.Key("enabled").MustBool(false),
|
||||
GroupsAttributePath: section.Key("groups_attribute_path").Value(),
|
||||
HostedDomain: section.Key("hosted_domain").Value(),
|
||||
Icon: section.Key("icon").Value(),
|
||||
Name: section.Key("name").Value(),
|
||||
RoleAttributePath: section.Key("role_attribute_path").Value(),
|
||||
RoleAttributeStrict: section.Key("role_attribute_strict").MustBool(false),
|
||||
Scopes: util.SplitString(section.Key("scopes").Value()),
|
||||
SignoutRedirectUrl: section.Key("signout_redirect_url").Value(),
|
||||
SkipOrgRoleSync: section.Key("skip_org_role_sync").MustBool(false),
|
||||
TeamIdsAttributePath: section.Key("team_ids_attribute_path").Value(),
|
||||
TeamsUrl: section.Key("teams_url").Value(),
|
||||
TlsClientCa: section.Key("tls_client_ca").Value(),
|
||||
TlsClientCert: section.Key("tls_client_cert").Value(),
|
||||
TlsClientKey: section.Key("tls_client_key").Value(),
|
||||
TlsSkipVerify: section.Key("tls_skip_verify_insecure").MustBool(false),
|
||||
TokenUrl: section.Key("token_url").Value(),
|
||||
UsePKCE: section.Key("use_pkce").MustBool(false),
|
||||
UseRefreshToken: section.Key("use_refresh_token").MustBool(false),
|
||||
Extra: map[string]string{},
|
||||
}
|
||||
|
||||
extraFields := extraKeysByProvider[provider]
|
||||
for _, key := range extraFields {
|
||||
result.Extra[key] = section.Key(key).Value()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
196
pkg/services/ssosettings/strategies/oauth_strategy_test.go
Normal file
196
pkg/services/ssosettings/strategies/oauth_strategy_test.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package strategies
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/login/social"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
iniContent = `
|
||||
[auth.generic_oauth]
|
||||
name = OAuth
|
||||
icon = signin
|
||||
enabled = true
|
||||
allow_sign_up = false
|
||||
auto_login = true
|
||||
client_id = test_client_id
|
||||
client_secret = test_client_secret
|
||||
scopes = ["openid", "profile", "email"]
|
||||
empty_scopes = false
|
||||
email_attribute_name = email:primary
|
||||
email_attribute_path = email
|
||||
login_attribute_path = login
|
||||
name_attribute_path = name
|
||||
role_attribute_path = role
|
||||
role_attribute_strict = true
|
||||
groups_attribute_path = groups
|
||||
id_token_attribute_name = id_token
|
||||
team_ids_attribute_path = team_ids
|
||||
auth_style = inheader
|
||||
auth_url = test_auth_url
|
||||
token_url = test_token_url
|
||||
api_url = test_api_url
|
||||
teams_url = test_teams_url
|
||||
allowed_domains = domain1.com
|
||||
allowed_groups =
|
||||
team_ids = first, second
|
||||
allowed_organizations = org1, org2
|
||||
tls_skip_verify_insecure = true
|
||||
tls_client_cert =
|
||||
tls_client_key =
|
||||
tls_client_ca =
|
||||
use_pkce = false
|
||||
allow_assign_grafana_admin = true
|
||||
skip_org_role_sync = true
|
||||
use_refresh_token = true
|
||||
empty_scopes =
|
||||
hosted_domain = test_hosted_domain
|
||||
signout_redirect_url = test_signout_redirect_url
|
||||
`
|
||||
|
||||
expectedOAuthInfo = &social.OAuthInfo{
|
||||
Name: "OAuth",
|
||||
Icon: "signin",
|
||||
Enabled: true,
|
||||
AllowSignup: false,
|
||||
AutoLogin: true,
|
||||
ClientId: "test_client_id",
|
||||
ClientSecret: "test_client_secret",
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
EmptyScopes: false,
|
||||
EmailAttributeName: "email:primary",
|
||||
EmailAttributePath: "email",
|
||||
RoleAttributePath: "role",
|
||||
RoleAttributeStrict: true,
|
||||
GroupsAttributePath: "groups",
|
||||
TeamIdsAttributePath: "team_ids",
|
||||
AuthUrl: "test_auth_url",
|
||||
TokenUrl: "test_token_url",
|
||||
ApiUrl: "test_api_url",
|
||||
TeamsUrl: "test_teams_url",
|
||||
AllowedDomains: []string{"domain1.com"},
|
||||
AllowedGroups: []string{},
|
||||
TlsSkipVerify: true,
|
||||
TlsClientCert: "",
|
||||
TlsClientKey: "",
|
||||
TlsClientCa: "",
|
||||
UsePKCE: false,
|
||||
AuthStyle: "inheader",
|
||||
AllowAssignGrafanaAdmin: true,
|
||||
UseRefreshToken: true,
|
||||
HostedDomain: "test_hosted_domain",
|
||||
SkipOrgRoleSync: true,
|
||||
SignoutRedirectUrl: "test_signout_redirect_url",
|
||||
Extra: map[string]string{
|
||||
"allowed_organizations": "org1, org2",
|
||||
"id_token_attribute_name": "id_token",
|
||||
"login_attribute_path": "login",
|
||||
"name_attribute_path": "name",
|
||||
"team_ids": "first, second",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestGetProviderConfig_EnvVarsOnly(t *testing.T) {
|
||||
setupEnvVars(t)
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
strategy := NewOAuthStrategy(cfg)
|
||||
|
||||
result, err := strategy.GetProviderConfig(context.Background(), "generic_oauth")
|
||||
require.NoError(t, err)
|
||||
|
||||
oauthInfo, ok := result.(*social.OAuthInfo)
|
||||
require.True(t, ok)
|
||||
|
||||
require.Equal(t, expectedOAuthInfo, oauthInfo)
|
||||
}
|
||||
|
||||
func TestGetProviderConfig_IniFileOnly(t *testing.T) {
|
||||
iniFile, err := ini.Load([]byte(iniContent))
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.Raw = iniFile
|
||||
|
||||
strategy := NewOAuthStrategy(cfg)
|
||||
|
||||
result, err := strategy.GetProviderConfig(context.Background(), "generic_oauth")
|
||||
require.NoError(t, err)
|
||||
|
||||
oauthInfo, ok := result.(*social.OAuthInfo)
|
||||
require.True(t, ok)
|
||||
|
||||
require.Equal(t, expectedOAuthInfo, oauthInfo)
|
||||
}
|
||||
|
||||
func TestGetProviderConfig_EnvVarsOverrideIniFileSettings(t *testing.T) {
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_ENABLED", "false")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_SKIP_ORG_ROLE_SYNC", "false")
|
||||
|
||||
iniFile, err := ini.Load([]byte(iniContent))
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.Raw = iniFile
|
||||
|
||||
strategy := NewOAuthStrategy(cfg)
|
||||
|
||||
result, err := strategy.GetProviderConfig(context.Background(), "generic_oauth")
|
||||
require.NoError(t, err)
|
||||
|
||||
oauthInfo, ok := result.(*social.OAuthInfo)
|
||||
require.True(t, ok)
|
||||
|
||||
expectedOAuthInfoWithOverrides := *expectedOAuthInfo
|
||||
expectedOAuthInfoWithOverrides.Enabled = false
|
||||
expectedOAuthInfoWithOverrides.SkipOrgRoleSync = false
|
||||
|
||||
require.Equal(t, expectedOAuthInfoWithOverrides, *oauthInfo)
|
||||
}
|
||||
|
||||
func setupEnvVars(t *testing.T) {
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_NAME", "OAuth")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_ICON", "signin")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_ENABLED", "true")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_ALLOW_SIGN_UP", "false")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_AUTO_LOGIN", "true")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_CLIENT_ID", "test_client_id")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET", "test_client_secret")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_SCOPES", `["openid", "profile", "email"]`)
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_EMPTY_SCOPES", "")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_EMAIL_ATTRIBUTE_NAME", "email:primary")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_EMAIL_ATTRIBUTE_PATH", "email")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH", "role")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_STRICT", "true")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_GROUPS_ATTRIBUTE_PATH", "groups")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_TEAM_IDS_ATTRIBUTE_PATH", "team_ids")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_AUTH_URL", "test_auth_url")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_TOKEN_URL", "test_token_url")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_API_URL", "test_api_url")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_TEAMS_URL", "test_teams_url")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_ALLOWED_DOMAINS", "domain1.com")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_ALLOWED_GROUPS", "")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_TLS_SKIP_VERIFY_INSECURE", "true")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_TLS_CLIENT_CERT", "")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_TLS_CLIENT_KEY", "")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_TLS_CLIENT_CA", "")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_USE_PKCE", "false")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_AUTH_STYLE", "inheader")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_ALLOW_ASSIGN_GRAFANA_ADMIN", "true")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_SKIP_ORG_ROLE_SYNC", "true")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_USE_REFRESH_TOKEN", "true")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_HOSTED_DOMAIN", "test_hosted_domain")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_ALLOWED_ORGANIZATIONS", "org1, org2")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_ID_TOKEN_ATTRIBUTE_NAME", "id_token")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_LOGIN_ATTRIBUTE_PATH", "login")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_NAME_ATTRIBUTE_PATH", "name")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_TEAM_IDS", "first, second")
|
||||
t.Setenv("GF_AUTH_GENERIC_OAUTH_SIGNOUT_REDIRECT_URL", "test_signout_redirect_url")
|
||||
}
|
||||
Reference in New Issue
Block a user