SCIM: Add flag for rejecting non provisioned users from logging in (#108568)

add flag for rejecting non provisioned users from logging in
This commit is contained in:
Mihai Doarna
2025-07-28 11:31:33 +03:00
committed by GitHub
parent 3a285d9b16
commit f9b34baa35
4 changed files with 181 additions and 128 deletions
+22 -14
View File
@@ -84,8 +84,8 @@ var (
// StaticSCIMConfig represents the static SCIM configuration from config.ini
type StaticSCIMConfig struct {
AllowNonProvisionedUsers bool
IsUserProvisioningEnabled bool
RejectNonProvisionedUsers bool
}
func ProvideUserSync(userService user.Service, userProtectionService login.UserProtectionService, authInfoService login.AuthInfoService,
@@ -94,13 +94,13 @@ func ProvideUserSync(userService user.Service, userProtectionService login.UserP
) *UserSync {
scimSection := cfg.Raw.Section("auth.scim")
staticConfig := &StaticSCIMConfig{
AllowNonProvisionedUsers: scimSection.Key("allow_non_provisioned_users").MustBool(false),
IsUserProvisioningEnabled: scimSection.Key("user_sync_enabled").MustBool(false),
RejectNonProvisionedUsers: scimSection.Key("reject_non_provisioned_users").MustBool(false),
}
return &UserSync{
allowNonProvisionedUsers: staticConfig.AllowNonProvisionedUsers,
isUserProvisioningEnabled: staticConfig.IsUserProvisioningEnabled,
rejectNonProvisionedUsers: staticConfig.RejectNonProvisionedUsers,
userService: userService,
authInfoService: authInfoService,
userProtectionService: userProtectionService,
@@ -115,8 +115,8 @@ func ProvideUserSync(userService user.Service, userProtectionService login.UserP
}
type UserSync struct {
allowNonProvisionedUsers bool
isUserProvisioningEnabled bool
rejectNonProvisionedUsers bool
userService user.Service
authInfoService login.AuthInfoService
userProtectionService login.UserProtectionService
@@ -187,9 +187,13 @@ func (s *UserSync) ValidateUserProvisioningHook(ctx context.Context, currentIden
return nil
}
// Reject non-provisioned users
log.Error("Failed to access user, user is not provisioned")
return errUserNotProvisioned.Errorf("user is not provisioned")
// Reject non-provisioned users if configured to do so
if s.shouldRejectNonProvisionedUsers(ctx, currentIdentity) {
log.Error("Failed to authenticate user, user is not provisioned")
return errUserNotProvisioned.Errorf("user is not provisioned")
}
return nil
}
func (s *UserSync) skipProvisioningValidation(ctx context.Context, currentIdentity *authn.Identity) bool {
@@ -197,12 +201,10 @@ func (s *UserSync) skipProvisioningValidation(ctx context.Context, currentIdenti
// Use dynamic SCIM settings if available, otherwise fall back to static config
effectiveUserSyncEnabled := s.isUserProvisioningEnabled
effectiveAllowNonProvisionedUsers := s.allowNonProvisionedUsers
if s.scimUtil != nil {
orgID := currentIdentity.GetOrgID()
effectiveUserSyncEnabled = s.scimUtil.IsUserSyncEnabled(ctx, orgID, s.staticConfig.IsUserProvisioningEnabled)
effectiveAllowNonProvisionedUsers = s.scimUtil.AreNonProvisionedUsersAllowed(ctx, orgID, s.staticConfig.AllowNonProvisionedUsers)
}
if !effectiveUserSyncEnabled {
@@ -210,11 +212,6 @@ func (s *UserSync) skipProvisioningValidation(ctx context.Context, currentIdenti
return true
}
if effectiveAllowNonProvisionedUsers {
log.Debug("Non-provisioned users are allowed, skipping validation")
return true
}
if currentIdentity.AuthenticatedBy == login.GrafanaComAuthModule {
log.Debug("User is authenticated via GrafanaComAuthModule, skipping validation")
return true
@@ -223,6 +220,17 @@ func (s *UserSync) skipProvisioningValidation(ctx context.Context, currentIdenti
return false
}
func (s *UserSync) shouldRejectNonProvisionedUsers(ctx context.Context, currentIdentity *authn.Identity) bool {
effectiveRejectNonProvisionedUsers := s.rejectNonProvisionedUsers
if s.scimUtil != nil {
orgID := currentIdentity.GetOrgID()
effectiveRejectNonProvisionedUsers = s.scimUtil.AreNonProvisionedUsersRejected(ctx, orgID, s.staticConfig.RejectNonProvisionedUsers)
}
return effectiveRejectNonProvisionedUsers
}
// SyncUserHook syncs a user with the database
func (s *UserSync) SyncUserHook(ctx context.Context, id *authn.Identity, _ *authn.Request) error {
ctx, span := s.tracer.Start(ctx, "user.sync.SyncUserHook")