Auth: Fix orgrole picker disabled if isSynced user (#64033)
* fix: disable orgrolepicker if externaluser is synced * add disable to role picker * just took me 2 hours to center the icon * wip * fix: check externallySyncedUser for API call * remove check from store * add: tests * refactor authproxy and made tests run * add: feature toggle * set feature toggle for tests * add: IsProviderEnabled * refactor: featuretoggle name * IsProviderEnabled tests * add specific tests for isProviderEnabled * fix: org_user tests * add: owner to featuretoggle * add missing authlabels * remove fmt * feature toggle * change config * add test for a different authmodule * test refactor * gen feature toggle again * fix basic auth user able to change the org role * test for basic auth role * make err.base to error * lowered lvl of log and input mesg
This commit is contained in:
@@ -90,7 +90,7 @@ func ProvideService(
|
||||
|
||||
var proxyClients []authn.ProxyClient
|
||||
var passwordClients []authn.PasswordClient
|
||||
if s.cfg.LDAPEnabled {
|
||||
if s.cfg.LDAPAuthEnabled {
|
||||
ldap := clients.ProvideLDAP(cfg, ldapService)
|
||||
proxyClients = append(proxyClients, ldap)
|
||||
passwordClients = append(passwordClients, ldap)
|
||||
|
||||
@@ -12,7 +12,7 @@ func (s *Service) getUsageStats(ctx context.Context) (map[string]interface{}, er
|
||||
// Add stats about auth configuration
|
||||
authTypes := map[string]bool{}
|
||||
authTypes["basic_auth"] = s.cfg.BasicAuthEnabled
|
||||
authTypes["ldap"] = s.cfg.LDAPEnabled
|
||||
authTypes["ldap"] = s.cfg.LDAPAuthEnabled
|
||||
authTypes["auth_proxy"] = s.cfg.AuthProxyEnabled
|
||||
authTypes["anonymous"] = s.cfg.AnonymousEnabled
|
||||
authTypes["jwt"] = s.cfg.JWTAuthEnabled
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestService_getUsageStats(t *testing.T) {
|
||||
svc.cfg.BasicAuthEnabled = true
|
||||
svc.cfg.AuthProxyEnabled = true
|
||||
svc.cfg.JWTAuthEnabled = true
|
||||
svc.cfg.LDAPEnabled = true
|
||||
svc.cfg.LDAPAuthEnabled = true
|
||||
svc.cfg.EditorsCanAdmin = true
|
||||
svc.cfg.ViewersCanEdit = true
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ func (auth *AuthProxy) Login(reqCtx *contextmodel.ReqContext, ignoreCache bool)
|
||||
}
|
||||
}
|
||||
|
||||
if auth.cfg.LDAPEnabled {
|
||||
if auth.cfg.LDAPAuthEnabled {
|
||||
id, err := auth.LoginViaLDAP(reqCtx)
|
||||
if err != nil {
|
||||
if errors.Is(err, ldap.ErrInvalidCredentials) {
|
||||
|
||||
@@ -107,7 +107,7 @@ func TestMiddlewareContext_ldap(t *testing.T) {
|
||||
cache := remotecache.NewFakeStore(t)
|
||||
|
||||
auth, reqCtx := prepareMiddleware(t, cache, nil)
|
||||
auth.cfg.LDAPEnabled = true
|
||||
auth.cfg.LDAPAuthEnabled = true
|
||||
ldapFake := &service.LDAPFakeService{
|
||||
ExpectedUser: &login.ExternalUserInfo{UserId: id},
|
||||
}
|
||||
@@ -126,7 +126,7 @@ func TestMiddlewareContext_ldap(t *testing.T) {
|
||||
cache := remotecache.NewFakeStore(t)
|
||||
|
||||
auth, reqCtx := prepareMiddleware(t, cache, nil)
|
||||
auth.cfg.LDAPEnabled = true
|
||||
auth.cfg.LDAPAuthEnabled = true
|
||||
ldapFake := &service.LDAPFakeService{
|
||||
ExpectedUser: nil,
|
||||
ExpectedError: service.ErrUnableToCreateLDAPClient,
|
||||
|
||||
@@ -393,6 +393,12 @@ var (
|
||||
State: FeatureStateAlpha,
|
||||
Owner: grafanaBackendPlatformSquad,
|
||||
},
|
||||
{
|
||||
Name: "onlyExternalOrgRoleSync",
|
||||
Description: "Prohibits a user from changing organization roles synced with external auth providers",
|
||||
State: FeatureStateAlpha,
|
||||
Owner: grafanaAuthnzSquad,
|
||||
},
|
||||
{
|
||||
Name: "drawerDataSourcePicker",
|
||||
Description: "Changes the user experience for data source selection to a drawer.",
|
||||
|
||||
@@ -58,6 +58,7 @@ logsContextDatasourceUi,alpha,@grafana/observability-logs,false,false,false,true
|
||||
lokiQuerySplitting,alpha,@grafana/observability-logs,false,false,false,true
|
||||
lokiQuerySplittingConfig,alpha,@grafana/observability-logs,false,false,false,true
|
||||
individualCookiePreferences,alpha,@grafana/backend-platform,false,false,false,false
|
||||
onlyExternalOrgRoleSync,alpha,@grafana/grafana-authnz-team,false,false,false,false
|
||||
drawerDataSourcePicker,alpha,@grafana/grafana-bi-squad,false,false,false,true
|
||||
traceqlSearch,alpha,@grafana/observability-traces-and-profiling,false,false,false,true
|
||||
prometheusMetricEncyclopedia,alpha,@grafana/observability-metrics,false,false,false,true
|
||||
|
||||
|
@@ -243,6 +243,10 @@ const (
|
||||
// Support overriding cookie preferences per user
|
||||
FlagIndividualCookiePreferences = "individualCookiePreferences"
|
||||
|
||||
// FlagOnlyExternalOrgRoleSync
|
||||
// Prohibits a user from changing organization roles synced with external auth providers
|
||||
FlagOnlyExternalOrgRoleSync = "onlyExternalOrgRoleSync"
|
||||
|
||||
// FlagDrawerDataSourcePicker
|
||||
// Changes the user experience for data source selection to a drawer.
|
||||
FlagDrawerDataSourcePicker = "drawerDataSourcePicker"
|
||||
|
||||
@@ -65,7 +65,7 @@ func ProvideService(cfg *setting.Cfg, router routing.RouteRegister, accessContro
|
||||
adminRoute.Get("/ldap/status", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPStatusRead)), routing.Wrap(s.GetLDAPStatus))
|
||||
}, middleware.ReqSignedIn)
|
||||
|
||||
if cfg.LDAPEnabled {
|
||||
if cfg.LDAPAuthEnabled {
|
||||
bundleRegistry.RegisterSupportItemCollector(supportbundles.Collector{
|
||||
UID: "auth-ldap",
|
||||
DisplayName: "LDAP",
|
||||
@@ -94,7 +94,7 @@ func ProvideService(cfg *setting.Cfg, router routing.RouteRegister, accessContro
|
||||
// 403: forbiddenError
|
||||
// 500: internalServerError
|
||||
func (s *Service) ReloadLDAPCfg(c *contextmodel.ReqContext) response.Response {
|
||||
if !s.cfg.LDAPEnabled {
|
||||
if !s.cfg.LDAPAuthEnabled {
|
||||
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ func (s *Service) ReloadLDAPCfg(c *contextmodel.ReqContext) response.Response {
|
||||
// 403: forbiddenError
|
||||
// 500: internalServerError
|
||||
func (s *Service) GetLDAPStatus(c *contextmodel.ReqContext) response.Response {
|
||||
if !s.cfg.LDAPEnabled {
|
||||
if !s.cfg.LDAPAuthEnabled {
|
||||
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ func (s *Service) GetLDAPStatus(c *contextmodel.ReqContext) response.Response {
|
||||
// 403: forbiddenError
|
||||
// 500: internalServerError
|
||||
func (s *Service) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.Response {
|
||||
if !s.cfg.LDAPEnabled {
|
||||
if !s.cfg.LDAPAuthEnabled {
|
||||
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ func (s *Service) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.Resp
|
||||
// 403: forbiddenError
|
||||
// 500: internalServerError
|
||||
func (s *Service) GetUserFromLDAP(c *contextmodel.ReqContext) response.Response {
|
||||
if !s.cfg.LDAPEnabled {
|
||||
if !s.cfg.LDAPAuthEnabled {
|
||||
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ func setupAPITest(t *testing.T, opts ...func(a *Service)) (*Service, *webtest.Se
|
||||
t.Helper()
|
||||
router := routing.NewRouteRegister()
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
a := ProvideService(cfg,
|
||||
router,
|
||||
|
||||
@@ -72,7 +72,7 @@ func (s *Service) supportBundleCollector(context.Context) (*supportbundles.Suppo
|
||||
|
||||
bWriter.WriteString("```ini\n")
|
||||
|
||||
bWriter.WriteString(fmt.Sprintf("enabled = %v\n", s.cfg.LDAPEnabled))
|
||||
bWriter.WriteString(fmt.Sprintf("enabled = %v\n", s.cfg.LDAPAuthEnabled))
|
||||
bWriter.WriteString(fmt.Sprintf("config_file = %s\n", s.cfg.LDAPConfigFilePath))
|
||||
bWriter.WriteString(fmt.Sprintf("allow_sign_up = %v\n", s.cfg.LDAPAllowSignup))
|
||||
bWriter.WriteString(fmt.Sprintf("sync_cron = %s\n", s.cfg.LDAPSyncCron))
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestServer_Login_UserBind_Fail(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
Config: &ServerConfig{
|
||||
@@ -106,7 +106,7 @@ func TestServer_Login_ValidCredentials(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
@@ -143,7 +143,7 @@ func TestServer_Login_UnauthenticatedBind(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
@@ -190,7 +190,7 @@ func TestServer_Login_AuthenticatedBind(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
@@ -233,7 +233,7 @@ func TestServer_Login_UserWildcardBind(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
|
||||
@@ -54,7 +54,7 @@ func TestServer_getSearchRequest(t *testing.T) {
|
||||
func TestSerializeUsers(t *testing.T) {
|
||||
t.Run("simple case", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
@@ -93,7 +93,7 @@ func TestSerializeUsers(t *testing.T) {
|
||||
|
||||
t.Run("without lastname", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
@@ -130,7 +130,7 @@ func TestSerializeUsers(t *testing.T) {
|
||||
|
||||
t.Run("mark user without matching group as disabled", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
@@ -164,7 +164,7 @@ func TestSerializeUsers(t *testing.T) {
|
||||
func TestServer_validateGrafanaUser(t *testing.T) {
|
||||
t.Run("no group config", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
@@ -184,7 +184,7 @@ func TestServer_validateGrafanaUser(t *testing.T) {
|
||||
|
||||
t.Run("user in group", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
@@ -211,7 +211,7 @@ func TestServer_validateGrafanaUser(t *testing.T) {
|
||||
|
||||
t.Run("user not in group", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
|
||||
@@ -68,7 +68,7 @@ func TestServer_Users(t *testing.T) {
|
||||
|
||||
// Set up attribute map without surname and email
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
@@ -212,7 +212,7 @@ func TestServer_Users(t *testing.T) {
|
||||
})
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
@@ -289,7 +289,7 @@ func TestServer_Users(t *testing.T) {
|
||||
})
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LDAPEnabled = true
|
||||
cfg.LDAPAuthEnabled = true
|
||||
|
||||
server := &Server{
|
||||
cfg: cfg,
|
||||
|
||||
@@ -47,7 +47,7 @@ func ProvideService(cfg *setting.Cfg) *LDAPImpl {
|
||||
loadingMutex: &sync.Mutex{},
|
||||
}
|
||||
|
||||
if !cfg.LDAPEnabled {
|
||||
if !cfg.LDAPAuthEnabled {
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ func ProvideService(cfg *setting.Cfg) *LDAPImpl {
|
||||
}
|
||||
|
||||
func (s *LDAPImpl) ReloadConfig() error {
|
||||
if !s.cfg.LDAPEnabled {
|
||||
if !s.cfg.LDAPAuthEnabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ func (s *LDAPImpl) Config() *ldap.Config {
|
||||
}
|
||||
|
||||
func (s *LDAPImpl) Login(query *login.LoginUserQuery) (*login.ExternalUserInfo, error) {
|
||||
if !s.cfg.LDAPEnabled {
|
||||
if !s.cfg.LDAPAuthEnabled {
|
||||
return nil, ErrLDAPNotEnabled
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ func (s *LDAPImpl) Login(query *login.LoginUserQuery) (*login.ExternalUserInfo,
|
||||
}
|
||||
|
||||
func (s *LDAPImpl) User(username string) (*login.ExternalUserInfo, error) {
|
||||
if !s.cfg.LDAPEnabled {
|
||||
if !s.cfg.LDAPAuthEnabled {
|
||||
return nil, ErrLDAPNotEnabled
|
||||
}
|
||||
|
||||
|
||||
@@ -87,10 +87,10 @@ var config *Config
|
||||
// the config or it reads it and caches it first.
|
||||
func GetConfig(cfg *setting.Cfg) (*Config, error) {
|
||||
if cfg != nil {
|
||||
if !cfg.LDAPEnabled {
|
||||
if !cfg.LDAPAuthEnabled {
|
||||
return nil, nil
|
||||
}
|
||||
} else if !cfg.LDAPEnabled {
|
||||
} else if !cfg.LDAPAuthEnabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ const (
|
||||
LDAPLabel = "LDAP"
|
||||
JWTLabel = "JWT"
|
||||
// OAuth provider labels
|
||||
AuthProxtLabel = "Auth Proxy"
|
||||
AuthProxyLabel = "Auth Proxy"
|
||||
AzureADLabel = "AzureAD"
|
||||
GoogleLabel = "Google"
|
||||
GenericOAuthLabel = "Generic OAuth"
|
||||
@@ -72,14 +72,18 @@ const (
|
||||
// https://github.com/grafana/grafana/blob/4181acec72f76df7ad02badce13769bae4a1f840/pkg/services/login/authinfoservice/database/database.go#L61
|
||||
// this means that if the user has multiple auth providers and one of them is set to sync org roles
|
||||
// then IsExternallySynced will be true for this one provider and false for the others
|
||||
func IsExternallySynced(cfg *setting.Cfg, autoProviderLabel string) bool {
|
||||
func IsExternallySynced(cfg *setting.Cfg, authModule string) bool {
|
||||
// provider enabled in config
|
||||
if !IsProviderEnabled(cfg, authModule) {
|
||||
return false
|
||||
}
|
||||
// first check SAML, LDAP and JWT
|
||||
switch autoProviderLabel {
|
||||
case SAMLLabel:
|
||||
switch authModule {
|
||||
case SAMLAuthModule:
|
||||
return !cfg.SAMLSkipOrgRoleSync
|
||||
case LDAPLabel:
|
||||
case LDAPAuthModule:
|
||||
return !cfg.LDAPSkipOrgRoleSync
|
||||
case JWTLabel:
|
||||
case JWTModule:
|
||||
return !cfg.JWTAuthSkipOrgRoleSync
|
||||
}
|
||||
// then check the rest of the oauth providers
|
||||
@@ -88,25 +92,52 @@ func IsExternallySynced(cfg *setting.Cfg, autoProviderLabel string) bool {
|
||||
if cfg.OAuthSkipOrgRoleUpdateSync {
|
||||
return false
|
||||
}
|
||||
switch autoProviderLabel {
|
||||
case GoogleLabel:
|
||||
switch authModule {
|
||||
case GoogleAuthModule:
|
||||
return !cfg.GoogleSkipOrgRoleSync
|
||||
case OktaLabel:
|
||||
case OktaAuthModule:
|
||||
return !cfg.OktaSkipOrgRoleSync
|
||||
case AzureADLabel:
|
||||
case AzureADAuthModule:
|
||||
return !cfg.AzureADSkipOrgRoleSync
|
||||
case GitLabLabel:
|
||||
case GitLabAuthModule:
|
||||
return !cfg.GitLabSkipOrgRoleSync
|
||||
case GithubLabel:
|
||||
return !cfg.GithubSkipOrgRoleSync
|
||||
case GrafanaComLabel:
|
||||
case GithubAuthModule:
|
||||
return !cfg.GitHubSkipOrgRoleSync
|
||||
case GrafanaComAuthModule:
|
||||
return !cfg.GrafanaComSkipOrgRoleSync
|
||||
case GenericOAuthLabel:
|
||||
case GenericOAuthModule:
|
||||
return !cfg.GenericOAuthSkipOrgRoleSync
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func IsProviderEnabled(cfg *setting.Cfg, authModule string) bool {
|
||||
switch authModule {
|
||||
case SAMLAuthModule:
|
||||
return cfg.SAMLAuthEnabled
|
||||
case LDAPAuthModule:
|
||||
return cfg.LDAPAuthEnabled
|
||||
case JWTModule:
|
||||
return cfg.JWTAuthEnabled
|
||||
case GoogleAuthModule:
|
||||
return cfg.GoogleAuthEnabled
|
||||
case OktaAuthModule:
|
||||
return cfg.OktaAuthEnabled
|
||||
case AzureADAuthModule:
|
||||
return cfg.AzureADEnabled
|
||||
case GitLabAuthModule:
|
||||
return cfg.GitLabAuthEnabled
|
||||
case GithubAuthModule:
|
||||
return cfg.GitHubAuthEnabled
|
||||
case GrafanaComAuthModule:
|
||||
return cfg.GrafanaComAuthEnabled
|
||||
case GenericOAuthModule:
|
||||
return cfg.GenericOAuthAuthEnabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// used for frontend to display a more user friendly label
|
||||
func GetAuthProviderLabel(authModule string) string {
|
||||
switch authModule {
|
||||
case GithubAuthModule:
|
||||
@@ -128,7 +159,7 @@ func GetAuthProviderLabel(authModule string) string {
|
||||
case JWTModule:
|
||||
return JWTLabel
|
||||
case AuthProxyAuthModule:
|
||||
return AuthProxtLabel
|
||||
return AuthProxyLabel
|
||||
case GenericOAuthModule:
|
||||
return GenericOAuthLabel
|
||||
default:
|
||||
|
||||
@@ -14,182 +14,199 @@ func TestIsExternallySynced(t *testing.T) {
|
||||
provider string
|
||||
expected bool
|
||||
}{
|
||||
// azure
|
||||
{
|
||||
name: "AzureAD synced user should return that it is externally synced",
|
||||
cfg: &setting.Cfg{AzureADSkipOrgRoleSync: false},
|
||||
provider: AzureADLabel,
|
||||
cfg: &setting.Cfg{AzureADEnabled: true, AzureADSkipOrgRoleSync: false},
|
||||
provider: AzureADAuthModule,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "AzureAD synced user should return that it is not externally synced when org role sync is set",
|
||||
cfg: &setting.Cfg{AzureADSkipOrgRoleSync: true},
|
||||
provider: AzureADLabel,
|
||||
cfg: &setting.Cfg{AzureADEnabled: true, AzureADSkipOrgRoleSync: true},
|
||||
provider: AzureADAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// FIXME: remove this test as soon as we remove the deprecated setting for skipping org role sync for all external oauth providers
|
||||
{
|
||||
name: "azuread external user should return that it is not externally synced when oauth org role sync is set",
|
||||
cfg: &setting.Cfg{AzureADSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: AzureADLabel,
|
||||
cfg: &setting.Cfg{AzureADEnabled: true, AzureADSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: AzureADAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// google
|
||||
{
|
||||
name: "Google synced user should return that it is externally synced",
|
||||
cfg: &setting.Cfg{GoogleSkipOrgRoleSync: false},
|
||||
provider: GoogleLabel,
|
||||
cfg: &setting.Cfg{GoogleAuthEnabled: true, GoogleSkipOrgRoleSync: false},
|
||||
provider: GoogleAuthModule,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Google synced user should return that it is not externally synced when org role sync is set",
|
||||
cfg: &setting.Cfg{GoogleSkipOrgRoleSync: true},
|
||||
provider: GoogleLabel,
|
||||
cfg: &setting.Cfg{GoogleAuthEnabled: true, GoogleSkipOrgRoleSync: true},
|
||||
provider: GoogleAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// FIXME: remove this test as soon as we remove the deprecated setting for skipping org role sync for all external oauth providers
|
||||
{
|
||||
name: "google external user should return that it is not externally synced when oauth org role sync is set",
|
||||
cfg: &setting.Cfg{GoogleSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GoogleLabel,
|
||||
cfg: &setting.Cfg{GoogleAuthEnabled: true, GoogleSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GoogleAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "external user should return that it is not externally synced when oauth org role sync is set and google skip org role sync set",
|
||||
cfg: &setting.Cfg{GoogleSkipOrgRoleSync: true, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GoogleLabel,
|
||||
cfg: &setting.Cfg{GoogleAuthEnabled: true, GoogleSkipOrgRoleSync: true, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GoogleAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// okta
|
||||
{
|
||||
name: "Okta synced user should return that it is externally synced",
|
||||
cfg: &setting.Cfg{OktaSkipOrgRoleSync: false},
|
||||
provider: OktaLabel,
|
||||
cfg: &setting.Cfg{OktaAuthEnabled: true, OktaSkipOrgRoleSync: false},
|
||||
provider: OktaAuthModule,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Okta synced user should return that it is not externally synced when org role sync is set",
|
||||
cfg: &setting.Cfg{OktaSkipOrgRoleSync: true},
|
||||
cfg: &setting.Cfg{OktaAuthEnabled: true, OktaSkipOrgRoleSync: true},
|
||||
|
||||
provider: OktaLabel,
|
||||
provider: OktaAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// FIXME: remove this test as soon as we remove the deprecated setting for skipping org role sync for all external oauth providers
|
||||
{
|
||||
name: "okta external user should return that it is not externally synced when oauth org role sync is set",
|
||||
cfg: &setting.Cfg{OktaSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: OktaLabel,
|
||||
cfg: &setting.Cfg{OktaAuthEnabled: true, OktaSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: OktaAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// github
|
||||
{
|
||||
name: "Github synced user should return that it is externally synced",
|
||||
cfg: &setting.Cfg{GithubSkipOrgRoleSync: false},
|
||||
provider: GithubLabel,
|
||||
cfg: &setting.Cfg{GitHubAuthEnabled: true, GitHubSkipOrgRoleSync: false},
|
||||
provider: GithubAuthModule,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Github synced user should return that it is not externally synced when org role sync is set",
|
||||
cfg: &setting.Cfg{GithubSkipOrgRoleSync: true},
|
||||
provider: GithubLabel,
|
||||
cfg: &setting.Cfg{GitHubAuthEnabled: true, GitHubSkipOrgRoleSync: true},
|
||||
provider: GithubAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// FIXME: remove this test as soon as we remove the deprecated setting for skipping org role sync for all external oauth providers
|
||||
{
|
||||
name: "github external user should return that it is not externally synced when oauth org role sync is set",
|
||||
cfg: &setting.Cfg{GithubSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GithubLabel,
|
||||
cfg: &setting.Cfg{GitHubAuthEnabled: true, GitHubSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GithubAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// gitlab
|
||||
{
|
||||
name: "Gitlab synced user should return that it is externally synced",
|
||||
cfg: &setting.Cfg{GitLabSkipOrgRoleSync: false},
|
||||
provider: GitLabLabel,
|
||||
cfg: &setting.Cfg{GitLabAuthEnabled: true, GitLabSkipOrgRoleSync: false},
|
||||
provider: GitLabAuthModule,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Gitlab synced user should return that it is not externally synced when org role sync is set",
|
||||
cfg: &setting.Cfg{GitLabSkipOrgRoleSync: true},
|
||||
provider: GitLabLabel,
|
||||
cfg: &setting.Cfg{GitLabAuthEnabled: true, GitLabSkipOrgRoleSync: true},
|
||||
provider: GitLabAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// FIXME: remove this test as soon as we remove the deprecated setting for skipping org role sync for all external oauth providers
|
||||
{
|
||||
name: "gitlab external user should return that it is not externally synced when oauth org role sync is set",
|
||||
cfg: &setting.Cfg{GitLabSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GitLabLabel,
|
||||
cfg: &setting.Cfg{GitLabAuthEnabled: true, GitLabSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GitLabAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// grafana.com
|
||||
{
|
||||
name: "Grafana.com synced user should return that it is externally synced",
|
||||
cfg: &setting.Cfg{GrafanaComSkipOrgRoleSync: false},
|
||||
provider: GrafanaComLabel,
|
||||
cfg: &setting.Cfg{GrafanaComAuthEnabled: true, GrafanaComSkipOrgRoleSync: false},
|
||||
provider: GrafanaComAuthModule,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Grafana.com synced user should return that it is not externally synced when org role sync is set",
|
||||
cfg: &setting.Cfg{GrafanaComSkipOrgRoleSync: true},
|
||||
provider: GrafanaComLabel,
|
||||
cfg: &setting.Cfg{GrafanaComAuthEnabled: true, GrafanaComSkipOrgRoleSync: true},
|
||||
provider: GrafanaComAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// FIXME: remove this test as soon as we remove the deprecated setting for skipping org role sync for all external oauth providers
|
||||
{
|
||||
name: "grafanacom external user should return that it is not externally synced when oauth org role sync is set",
|
||||
cfg: &setting.Cfg{GrafanaComSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GrafanaComLabel,
|
||||
cfg: &setting.Cfg{GrafanaComAuthEnabled: true, GrafanaComSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GrafanaComAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// generic oauth
|
||||
{
|
||||
name: "Generic OAuth synced user should return that it is externally synced",
|
||||
cfg: &setting.Cfg{OAuthSkipOrgRoleUpdateSync: false},
|
||||
provider: GenericOAuthLabel,
|
||||
name: "OAuth synced user should return that it is externally synced",
|
||||
cfg: &setting.Cfg{GenericOAuthAuthEnabled: true, OAuthSkipOrgRoleUpdateSync: false},
|
||||
// this could be any of the external oauth providers
|
||||
provider: GenericOAuthModule,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Generic OAuth synced user should return that it is not externally synced when org role sync is set",
|
||||
cfg: &setting.Cfg{OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GenericOAuthLabel,
|
||||
name: "OAuth synced user should return that it is not externally synced when org role sync is set",
|
||||
cfg: &setting.Cfg{GenericOAuthAuthEnabled: true, OAuthSkipOrgRoleUpdateSync: true},
|
||||
// this could be any of the external oauth providers
|
||||
provider: GenericOAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// FIXME: remove this test as soon as we remove the deprecated setting for skipping org role sync for all external oauth providers
|
||||
{
|
||||
name: "generic oauth external user should return that it is not externally synced when oauth org role sync is set",
|
||||
cfg: &setting.Cfg{GenericOAuthSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GenericOAuthLabel,
|
||||
cfg: &setting.Cfg{GenericOAuthAuthEnabled: true, GenericOAuthSkipOrgRoleSync: false, OAuthSkipOrgRoleUpdateSync: true},
|
||||
provider: GenericOAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// saml
|
||||
{
|
||||
name: "SAML synced user should return that it is externally synced",
|
||||
cfg: &setting.Cfg{SAMLSkipOrgRoleSync: false},
|
||||
provider: SAMLLabel,
|
||||
cfg: &setting.Cfg{SAMLAuthEnabled: true, SAMLSkipOrgRoleSync: false},
|
||||
provider: SAMLAuthModule,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "SAML synced user should return that it is not externally synced when org role sync is set",
|
||||
cfg: &setting.Cfg{SAMLSkipOrgRoleSync: true},
|
||||
provider: SAMLLabel,
|
||||
cfg: &setting.Cfg{SAMLAuthEnabled: true, SAMLSkipOrgRoleSync: true},
|
||||
provider: SAMLAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// ldap
|
||||
{
|
||||
name: "LDAP synced user should return that it is externally synced",
|
||||
cfg: &setting.Cfg{LDAPSkipOrgRoleSync: false},
|
||||
provider: LDAPLabel,
|
||||
cfg: &setting.Cfg{LDAPAuthEnabled: true, LDAPSkipOrgRoleSync: false},
|
||||
provider: LDAPAuthModule,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "LDAP synced user should return that it is not externally synced when org role sync is set",
|
||||
cfg: &setting.Cfg{LDAPSkipOrgRoleSync: true},
|
||||
provider: LDAPLabel,
|
||||
cfg: &setting.Cfg{LDAPAuthEnabled: true, LDAPSkipOrgRoleSync: true},
|
||||
provider: LDAPAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
// jwt
|
||||
{
|
||||
name: "JWT synced user should return that it is externally synced",
|
||||
cfg: &setting.Cfg{JWTAuthSkipOrgRoleSync: false},
|
||||
provider: JWTLabel,
|
||||
cfg: &setting.Cfg{JWTAuthEnabled: true, JWTAuthSkipOrgRoleSync: false},
|
||||
provider: JWTModule,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "JWT synced user should return that it is not externally synced when org role sync is set",
|
||||
cfg: &setting.Cfg{JWTAuthEnabled: true, JWTAuthSkipOrgRoleSync: true},
|
||||
provider: JWTModule,
|
||||
expected: false,
|
||||
},
|
||||
// IsProvider test
|
||||
{
|
||||
name: "If no provider enabled should return false",
|
||||
cfg: &setting.Cfg{JWTAuthSkipOrgRoleSync: true},
|
||||
provider: JWTLabel,
|
||||
provider: JWTModule,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
@@ -200,3 +217,32 @@ func TestIsExternallySynced(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsProviderEnabled(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
cfg *setting.Cfg
|
||||
provider string
|
||||
expected bool
|
||||
}{
|
||||
// github
|
||||
{
|
||||
name: "Github should return true if enabled",
|
||||
cfg: &setting.Cfg{GitHubAuthEnabled: true},
|
||||
provider: GithubAuthModule,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Github should return false if not enabled",
|
||||
cfg: &setting.Cfg{},
|
||||
provider: GithubAuthModule,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.expected, IsProviderEnabled(tc.cfg, tc.provider))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ func (s *ServiceImpl) getServerAdminNode(c *contextmodel.ReqContext) *navtree.Na
|
||||
adminNavLinks = append(adminNavLinks, storage)
|
||||
}
|
||||
|
||||
if s.cfg.LDAPEnabled && hasAccess(ac.ReqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPStatusRead)) {
|
||||
if s.cfg.LDAPAuthEnabled && hasAccess(ac.ReqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPStatusRead)) {
|
||||
adminNavLinks = append(adminNavLinks, &navtree.NavLink{
|
||||
Text: "LDAP", Id: "ldap", Url: s.cfg.AppSubURL + "/admin/ldap", Icon: "book",
|
||||
})
|
||||
|
||||
+21
-19
@@ -12,11 +12,12 @@ import (
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrOrgNameTaken = errors.New("organization name is taken")
|
||||
ErrLastOrgAdmin = errors.New("cannot remove last organization admin")
|
||||
ErrOrgUserNotFound = errors.New("cannot find the organization user")
|
||||
ErrOrgUserAlreadyAdded = errors.New("user is already added to organization")
|
||||
ErrOrgNotFound = errutil.NewBase(errutil.StatusNotFound, "org.notFound", errutil.WithPublicMessage("organization not found"))
|
||||
ErrOrgNameTaken = errors.New("organization name is taken")
|
||||
ErrLastOrgAdmin = errors.New("cannot remove last organization admin")
|
||||
ErrOrgUserNotFound = errors.New("cannot find the organization user")
|
||||
ErrOrgUserAlreadyAdded = errors.New("user is already added to organization")
|
||||
ErrOrgNotFound = errutil.NewBase(errutil.StatusNotFound, "org.notFound", errutil.WithPublicMessage("organization not found"))
|
||||
ErrCannotChangeRoleForExternallySyncedUser = errutil.NewBase(errutil.StatusForbidden, "org.externallySynced", errutil.WithPublicMessage("cannot change role for externally synced user"))
|
||||
)
|
||||
|
||||
type Org struct {
|
||||
@@ -139,20 +140,21 @@ type UpdateOrgUserCommand struct {
|
||||
}
|
||||
|
||||
type OrgUserDTO struct {
|
||||
OrgID int64 `json:"orgId" xorm:"org_id"`
|
||||
UserID int64 `json:"userId" xorm:"user_id"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
AvatarURL string `json:"avatarUrl" xorm:"avatar_url"`
|
||||
Login string `json:"login"`
|
||||
Role string `json:"role"`
|
||||
LastSeenAt time.Time `json:"lastSeenAt"`
|
||||
Updated time.Time `json:"-"`
|
||||
Created time.Time `json:"-"`
|
||||
LastSeenAtAge string `json:"lastSeenAtAge"`
|
||||
AccessControl map[string]bool `json:"accessControl,omitempty"`
|
||||
IsDisabled bool `json:"isDisabled"`
|
||||
AuthLabels []string `json:"authLabels" xorm:"-"`
|
||||
OrgID int64 `json:"orgId" xorm:"org_id"`
|
||||
UserID int64 `json:"userId" xorm:"user_id"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
AvatarURL string `json:"avatarUrl" xorm:"avatar_url"`
|
||||
Login string `json:"login"`
|
||||
Role string `json:"role"`
|
||||
LastSeenAt time.Time `json:"lastSeenAt"`
|
||||
Updated time.Time `json:"-"`
|
||||
Created time.Time `json:"-"`
|
||||
LastSeenAtAge string `json:"lastSeenAtAge"`
|
||||
AccessControl map[string]bool `json:"accessControl,omitempty"`
|
||||
IsDisabled bool `json:"isDisabled"`
|
||||
AuthLabels []string `json:"authLabels" xorm:"-"`
|
||||
IsExternallySynced bool `json:"isExternallySynced"`
|
||||
}
|
||||
|
||||
type RemoveOrgUserCommand struct {
|
||||
|
||||
Reference in New Issue
Block a user