From f9b34baa357a6364b5449ef204bc091410c2f010 Mon Sep 17 00:00:00 2001 From: Mihai Doarna Date: Mon, 28 Jul 2025 11:31:33 +0300 Subject: [PATCH] SCIM: Add flag for rejecting non provisioned users from logging in (#108568) add flag for rejecting non provisioned users from logging in --- .../authn/authnimpl/sync/user_sync.go | 36 +++-- .../authn/authnimpl/sync/user_sync_test.go | 147 ++++++++++++------ pkg/services/scimutil/scim_util.go | 34 ++-- pkg/services/scimutil/scim_util_test.go | 92 +++++------ 4 files changed, 181 insertions(+), 128 deletions(-) diff --git a/pkg/services/authn/authnimpl/sync/user_sync.go b/pkg/services/authn/authnimpl/sync/user_sync.go index 7516c840ccc..a82b1434152 100644 --- a/pkg/services/authn/authnimpl/sync/user_sync.go +++ b/pkg/services/authn/authnimpl/sync/user_sync.go @@ -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") diff --git a/pkg/services/authn/authnimpl/sync/user_sync_test.go b/pkg/services/authn/authnimpl/sync/user_sync_test.go index 04be22dac89..49e50f55fdc 100644 --- a/pkg/services/authn/authnimpl/sync/user_sync_test.go +++ b/pkg/services/authn/authnimpl/sync/user_sync_test.go @@ -1092,11 +1092,24 @@ func TestUserSync_ValidateUserProvisioningHook(t *testing.T) { }, }, { - desc: "it should skip validation if allowedNonProvisionedUsers is enabled", + desc: "it should skip validation if rejectNonProvisionedUsers is disabled", userSyncServiceSetup: func() *UserSync { userSyncService := initUserSyncService() - userSyncService.allowNonProvisionedUsers = true + userSyncService.rejectNonProvisionedUsers = false userSyncService.isUserProvisioningEnabled = true + userSyncService.userService = &usertest.FakeUserService{ + ExpectedUser: &user.User{ + ID: 1, + IsProvisioned: false, + }, + } + userSyncService.authInfoService = &authinfotest.FakeService{ + ExpectedUserAuth: &login.UserAuth{ + UserId: 1, + AuthModule: login.GenericOAuthModule, + AuthId: "1", + }, + } return userSyncService }, identity: &authn.Identity{ @@ -1111,7 +1124,7 @@ func TestUserSync_ValidateUserProvisioningHook(t *testing.T) { desc: "it should skip validation if the user is authenticated via GrafanaComAuthModule", userSyncServiceSetup: func() *UserSync { userSyncService := initUserSyncService() - userSyncService.allowNonProvisionedUsers = false + userSyncService.rejectNonProvisionedUsers = true userSyncService.isUserProvisioningEnabled = true return userSyncService }, @@ -1127,7 +1140,7 @@ func TestUserSync_ValidateUserProvisioningHook(t *testing.T) { desc: "it should fail to validate the identity with the provisioned user, unexpected error", userSyncServiceSetup: func() *UserSync { userSyncService := initUserSyncService() - userSyncService.allowNonProvisionedUsers = false + userSyncService.rejectNonProvisionedUsers = true userSyncService.isUserProvisioningEnabled = true userSyncService.userService = &usertest.FakeUserService{ ExpectedError: errors.New("random error"), @@ -1148,7 +1161,7 @@ func TestUserSync_ValidateUserProvisioningHook(t *testing.T) { desc: "it should fail to validate the identity with the provisioned user, no user found", userSyncServiceSetup: func() *UserSync { userSyncService := initUserSyncService() - userSyncService.allowNonProvisionedUsers = false + userSyncService.rejectNonProvisionedUsers = true userSyncService.isUserProvisioningEnabled = true userSyncService.userService = &usertest.FakeUserService{} return userSyncService @@ -1167,7 +1180,7 @@ func TestUserSync_ValidateUserProvisioningHook(t *testing.T) { desc: "it should fail to validate the provisioned user.ExternalUID with the identity.ExternalUID - empty ExternalUID", userSyncServiceSetup: func() *UserSync { userSyncService := initUserSyncService() - userSyncService.allowNonProvisionedUsers = false + userSyncService.rejectNonProvisionedUsers = true userSyncService.isUserProvisioningEnabled = true userSyncService.userService = &usertest.FakeUserService{ ExpectedUser: &user.User{ @@ -1198,7 +1211,7 @@ func TestUserSync_ValidateUserProvisioningHook(t *testing.T) { desc: "it should fail to validate the provisioned user.ExternalUID with the identity.ExternalUID - different ExternalUID", userSyncServiceSetup: func() *UserSync { userSyncService := initUserSyncService() - userSyncService.allowNonProvisionedUsers = false + userSyncService.rejectNonProvisionedUsers = true userSyncService.isUserProvisioningEnabled = true userSyncService.userService = &usertest.FakeUserService{ ExpectedUser: &user.User{ @@ -1230,7 +1243,7 @@ func TestUserSync_ValidateUserProvisioningHook(t *testing.T) { desc: "it should successfully validate the provisioned user.ExternalUID with the identity.ExternalUID", userSyncServiceSetup: func() *UserSync { userSyncService := initUserSyncService() - userSyncService.allowNonProvisionedUsers = false + userSyncService.rejectNonProvisionedUsers = true userSyncService.isUserProvisioningEnabled = true userSyncService.userService = &usertest.FakeUserService{ ExpectedUser: &user.User{ @@ -1259,10 +1272,10 @@ func TestUserSync_ValidateUserProvisioningHook(t *testing.T) { expectedErr: nil, }, { - desc: "it should failed to validate a non provisioned user when retrieved from the database", + desc: "it should fail to validate a non provisioned user when configured to reject non provisioned users", userSyncServiceSetup: func() *UserSync { userSyncService := initUserSyncService() - userSyncService.allowNonProvisionedUsers = false + userSyncService.rejectNonProvisionedUsers = true userSyncService.isUserProvisioningEnabled = true userSyncService.userService = &usertest.FakeUserService{ ExpectedUser: &user.User{ @@ -1290,6 +1303,38 @@ func TestUserSync_ValidateUserProvisioningHook(t *testing.T) { }, expectedErr: errUserNotProvisioned.Errorf("user is not provisioned"), }, + { + desc: "it should skip to validate a non provisioned user when configured to allow non provisioned users", + userSyncServiceSetup: func() *UserSync { + userSyncService := initUserSyncService() + userSyncService.rejectNonProvisionedUsers = false + userSyncService.isUserProvisioningEnabled = true + userSyncService.userService = &usertest.FakeUserService{ + ExpectedUser: &user.User{ + ID: 1, + IsProvisioned: false, + }, + } + userSyncService.authInfoService = &authinfotest.FakeService{ + ExpectedUserAuth: &login.UserAuth{ + UserId: 1, + AuthModule: login.SAMLAuthModule, + AuthId: "1", + ExternalUID: "random-external-uid", + }, + } + return userSyncService + }, + identity: &authn.Identity{ + AuthenticatedBy: login.SAMLAuthModule, + AuthID: "1", + ExternalUID: "different-external-uid", + ClientParams: authn.ClientParams{ + SyncUser: true, + }, + }, + expectedErr: nil, + }, { desc: "ValidateProvisioning: DB ExternalUID is empty, Incoming ExternalUID is empty - expect mismatch (stricter logic)", userSyncServiceSetup: func() *UserSync { @@ -1351,7 +1396,7 @@ func TestUserSync_ValidateUserProvisioningHook(t *testing.T) { desc: "it should skip ExternalUID validation for a SAML-provisioned user accessed by a non-SAML method with an empty incoming ExternalUID", userSyncServiceSetup: func() *UserSync { userSyncService := initUserSyncService() - userSyncService.allowNonProvisionedUsers = false + userSyncService.rejectNonProvisionedUsers = false userSyncService.isUserProvisioningEnabled = true userSyncService.userService = &usertest.FakeUserService{ ExpectedUser: &user.User{ @@ -1380,7 +1425,7 @@ func TestUserSync_ValidateUserProvisioningHook(t *testing.T) { desc: "it should fail validation when a provisioned user is accessed by SAML with an empty incoming ExternalUID", userSyncServiceSetup: func() *UserSync { userSyncService := initUserSyncService() - userSyncService.allowNonProvisionedUsers = false + userSyncService.rejectNonProvisionedUsers = true userSyncService.isUserProvisioningEnabled = true userSyncService.userService = &usertest.FakeUserService{ ExpectedUser: &user.User{ @@ -1425,10 +1470,10 @@ func TestUserSync_SCIMUtilIntegration(t *testing.T) { // Mock SCIM utility for testing type mockSCIMUtil struct { - userSyncEnabled bool - nonProvisionedUsersAllowed bool - shouldUseDynamicConfig bool - shouldReturnError bool + userSyncEnabled bool + nonProvisionedUsersRejected bool + shouldUseDynamicConfig bool + shouldReturnError bool } createMockSCIMUtil := func(mockCfg *mockSCIMUtil) *scimutil.SCIMUtil { @@ -1453,9 +1498,9 @@ func TestUserSync_SCIMUtilIntegration(t *testing.T) { "namespace": "default", }, "spec": map[string]interface{}{ - "enableUserSync": mockCfg.userSyncEnabled, - "enableGroupSync": false, // Not used for this test - "allowNonProvisionedUsers": mockCfg.nonProvisionedUsersAllowed, + "enableUserSync": mockCfg.userSyncEnabled, + "enableGroupSync": false, // Not used for this test + "rejectNonProvisionedUsers": mockCfg.nonProvisionedUsersRejected, }, }, } @@ -1467,13 +1512,13 @@ func TestUserSync_SCIMUtilIntegration(t *testing.T) { } tests := []struct { - name string - identity *authn.Identity - staticConfig *StaticSCIMConfig - mockSCIMUtil *mockSCIMUtil - expectedUserSyncEnabled bool - expectedNonProvisionedAllowed bool - expectedError error + name string + identity *authn.Identity + staticConfig *StaticSCIMConfig + mockSCIMUtil *mockSCIMUtil + expectedUserSyncEnabled bool + expectedNonProvisionedRejected bool + expectedError error }{ { name: "SCIM util nil - uses static config", @@ -1483,11 +1528,11 @@ func TestUserSync_SCIMUtilIntegration(t *testing.T) { }, staticConfig: &StaticSCIMConfig{ IsUserProvisioningEnabled: true, - AllowNonProvisionedUsers: false, + RejectNonProvisionedUsers: false, }, - mockSCIMUtil: nil, // No SCIM util - expectedUserSyncEnabled: true, - expectedNonProvisionedAllowed: false, + mockSCIMUtil: nil, // No SCIM util + expectedUserSyncEnabled: true, + expectedNonProvisionedRejected: false, }, { name: "SCIM util with dynamic config - user sync enabled", @@ -1497,15 +1542,15 @@ func TestUserSync_SCIMUtilIntegration(t *testing.T) { }, staticConfig: &StaticSCIMConfig{ IsUserProvisioningEnabled: false, // Static disabled - AllowNonProvisionedUsers: false, + RejectNonProvisionedUsers: true, }, mockSCIMUtil: &mockSCIMUtil{ - userSyncEnabled: true, // Dynamic enabled - nonProvisionedUsersAllowed: true, - shouldUseDynamicConfig: true, + userSyncEnabled: true, // Dynamic enabled + nonProvisionedUsersRejected: true, + shouldUseDynamicConfig: true, }, - expectedUserSyncEnabled: true, - expectedNonProvisionedAllowed: true, + expectedUserSyncEnabled: true, + expectedNonProvisionedRejected: true, }, { name: "SCIM util with dynamic config - user sync disabled", @@ -1515,15 +1560,15 @@ func TestUserSync_SCIMUtilIntegration(t *testing.T) { }, staticConfig: &StaticSCIMConfig{ IsUserProvisioningEnabled: true, // Static enabled - AllowNonProvisionedUsers: true, + RejectNonProvisionedUsers: true, }, mockSCIMUtil: &mockSCIMUtil{ - userSyncEnabled: false, // Dynamic disabled - nonProvisionedUsersAllowed: false, - shouldUseDynamicConfig: true, + userSyncEnabled: false, // Dynamic disabled + nonProvisionedUsersRejected: false, + shouldUseDynamicConfig: true, }, - expectedUserSyncEnabled: false, - expectedNonProvisionedAllowed: false, + expectedUserSyncEnabled: false, + expectedNonProvisionedRejected: false, }, { name: "SCIM util with error - falls back to static config", @@ -1533,13 +1578,13 @@ func TestUserSync_SCIMUtilIntegration(t *testing.T) { }, staticConfig: &StaticSCIMConfig{ IsUserProvisioningEnabled: true, - AllowNonProvisionedUsers: false, + RejectNonProvisionedUsers: false, }, mockSCIMUtil: &mockSCIMUtil{ shouldReturnError: true, }, - expectedUserSyncEnabled: true, - expectedNonProvisionedAllowed: false, + expectedUserSyncEnabled: true, + expectedNonProvisionedRejected: false, }, } @@ -1559,14 +1604,14 @@ func TestUserSync_SCIMUtilIntegration(t *testing.T) { } assert.Equal(t, tt.expectedUserSyncEnabled, userSyncEnabled, "User sync enabled mismatch") - // Test non-provisioned users allowed check - var nonProvisionedAllowed bool + // Test non-provisioned users rejected check + var nonProvisionedReject bool if userSync.scimUtil != nil { - nonProvisionedAllowed = userSync.scimUtil.AreNonProvisionedUsersAllowed(ctx, orgID, tt.staticConfig.AllowNonProvisionedUsers) + nonProvisionedReject = userSync.scimUtil.AreNonProvisionedUsersRejected(ctx, orgID, tt.staticConfig.RejectNonProvisionedUsers) } else { - nonProvisionedAllowed = tt.staticConfig.AllowNonProvisionedUsers + nonProvisionedReject = tt.staticConfig.RejectNonProvisionedUsers } - assert.Equal(t, tt.expectedNonProvisionedAllowed, nonProvisionedAllowed, "Non-provisioned users allowed mismatch") + assert.Equal(t, tt.expectedNonProvisionedRejected, nonProvisionedReject, "Non-provisioned users rejected mismatch") }) } } @@ -1765,7 +1810,7 @@ func TestUserSync_GetUsageStats(t *testing.T) { func TestUserSync_SCIMLoginUsageStatSet(t *testing.T) { userSync := initUserSyncService() - userSync.allowNonProvisionedUsers = false + userSync.rejectNonProvisionedUsers = false userSync.isUserProvisioningEnabled = true userSync.userService = &usertest.FakeUserService{ ExpectedUser: &user.User{ diff --git a/pkg/services/scimutil/scim_util.go b/pkg/services/scimutil/scim_util.go index 92d0ac74270..30998d4f5d9 100644 --- a/pkg/services/scimutil/scim_util.go +++ b/pkg/services/scimutil/scim_util.go @@ -44,23 +44,23 @@ func (s *SCIMUtil) IsUserSyncEnabled(ctx context.Context, orgID int64, staticEna return staticEnabled } -// AreNonProvisionedUsersAllowed checks if non-provisioned users are allowed using dynamic configuration with static fallback -func (s *SCIMUtil) AreNonProvisionedUsersAllowed(ctx context.Context, orgID int64, staticAllowed bool) bool { +// AreNonProvisionedUsersRejected checks if non-provisioned users are rejected using dynamic configuration with static fallback +func (s *SCIMUtil) AreNonProvisionedUsersRejected(ctx context.Context, orgID int64, staticRejected bool) bool { if s.k8sClient == nil { s.logger.Debug("K8s client not configured, using static SCIM config for non-provisioned users") - return staticAllowed + return staticRejected } - dynamicAllowed, dynamicConfigFetched := s.fetchDynamicSCIMSetting(ctx, orgID, "allowNonProvisionedUsers") + dynamicRejected, dynamicConfigFetched := s.fetchDynamicSCIMSetting(ctx, orgID, "rejectNonProvisionedUsers") if dynamicConfigFetched { - s.logger.Debug("Using dynamic SCIM config for user sync", "orgID", orgID, "enabled", dynamicAllowed) - return dynamicAllowed + s.logger.Debug("Using dynamic SCIM config for user sync", "orgID", orgID, "enabled", dynamicRejected) + return dynamicRejected } // Fallback to static config if dynamic config wasn't fetched successfully - s.logger.Debug("Using static SCIM config for user sync", "orgID", orgID, "enabled", staticAllowed) - return staticAllowed + s.logger.Debug("Using static SCIM config for user sync", "orgID", orgID, "enabled", staticRejected) + return staticRejected } // fetchDynamicSCIMSetting attempts to retrieve a specific dynamic SCIM configuration setting @@ -82,8 +82,8 @@ func (s *SCIMUtil) fetchDynamicSCIMSetting(ctx context.Context, orgID int64, set enabled = scimConfig.EnableUserSync case "group": enabled = scimConfig.EnableGroupSync - case "allowNonProvisionedUsers": - enabled = scimConfig.AllowNonProvisionedUsers + case "rejectNonProvisionedUsers": + enabled = scimConfig.RejectNonProvisionedUsers default: s.logger.Error("Invalid setting type provided to fetchDynamicSCIMSetting", "settingType", settingType) return false, false @@ -108,9 +108,9 @@ func (s *SCIMUtil) getOrgSCIMConfig(ctx context.Context, orgID int64) (*SCIMConf // SCIMConfigSpec represents the spec part of a SCIMConfig resource type SCIMConfigSpec struct { - EnableUserSync bool `json:"enableUserSync"` - EnableGroupSync bool `json:"enableGroupSync"` - AllowNonProvisionedUsers bool `json:"allowNonProvisionedUsers"` + EnableUserSync bool `json:"enableUserSync"` + EnableGroupSync bool `json:"enableGroupSync"` + RejectNonProvisionedUsers bool `json:"rejectNonProvisionedUsers"` } // unstructuredToSCIMConfig converts an unstructured object to a SCIMConfigSpec @@ -130,11 +130,11 @@ func (s *SCIMUtil) unstructuredToSCIMConfig(obj *unstructured.Unstructured) (*SC enableUserSync, _, _ := unstructured.NestedBool(spec, "enableUserSync") enableGroupSync, _, _ := unstructured.NestedBool(spec, "enableGroupSync") - allowNonProvisionedUsers, _, _ := unstructured.NestedBool(spec, "allowNonProvisionedUsers") + rejectNonProvisionedUsers, _, _ := unstructured.NestedBool(spec, "rejectNonProvisionedUsers") return &SCIMConfigSpec{ - EnableUserSync: enableUserSync, - EnableGroupSync: enableGroupSync, - AllowNonProvisionedUsers: allowNonProvisionedUsers, + EnableUserSync: enableUserSync, + EnableGroupSync: enableGroupSync, + RejectNonProvisionedUsers: rejectNonProvisionedUsers, }, nil } diff --git a/pkg/services/scimutil/scim_util_test.go b/pkg/services/scimutil/scim_util_test.go index 0e9d3b39ee5..f1350402e85 100644 --- a/pkg/services/scimutil/scim_util_test.go +++ b/pkg/services/scimutil/scim_util_test.go @@ -213,33 +213,33 @@ func TestSCIMUtil_IsUserSyncEnabled(t *testing.T) { } } -func TestSCIMUtil_AreNonProvisionedUsersAllowed(t *testing.T) { +func TestSCIMUtil_AreNonProvisionedUsersRejected(t *testing.T) { ctx := context.Background() orgID := int64(1) tests := []struct { name string k8sClient client.K8sHandler - staticAllowed bool + staticRejected bool expectedResult bool setupMock func(*MockK8sHandler) }{ { name: "k8s client nil - returns static config", k8sClient: nil, - staticAllowed: true, + staticRejected: true, expectedResult: true, }, { name: "k8s client nil - returns static config false", k8sClient: nil, - staticAllowed: false, + staticRejected: false, expectedResult: false, }, { - name: "k8s client error - falls back to static config", - k8sClient: &MockK8sHandler{}, - staticAllowed: true, + name: "k8s client error - falls back to static config", + k8sClient: &MockK8sHandler{}, + staticRejected: true, setupMock: func(mockHandler *MockK8sHandler) { mockHandler.On("Get", ctx, "default", orgID, metav1.GetOptions{}, mock.Anything). Return(nil, errors.New("k8s error")) @@ -247,9 +247,9 @@ func TestSCIMUtil_AreNonProvisionedUsersAllowed(t *testing.T) { expectedResult: true, }, { - name: "dynamic config user sync enabled - non-provisioned users allowed", - k8sClient: &MockK8sHandler{}, - staticAllowed: false, + name: "dynamic config user sync enabled - non-provisioned users rejected", + k8sClient: &MockK8sHandler{}, + staticRejected: false, setupMock: func(mockHandler *MockK8sHandler) { obj := createMockSCIMConfigWithNonProvisioned(true, false, true) mockHandler.On("Get", ctx, "default", orgID, metav1.GetOptions{}, mock.Anything). @@ -258,9 +258,9 @@ func TestSCIMUtil_AreNonProvisionedUsersAllowed(t *testing.T) { expectedResult: true, }, { - name: "dynamic config user sync disabled - non-provisioned users not allowed", - k8sClient: &MockK8sHandler{}, - staticAllowed: true, + name: "dynamic config user sync disabled - non-provisioned users allowed", + k8sClient: &MockK8sHandler{}, + staticRejected: true, setupMock: func(mockHandler *MockK8sHandler) { obj := createMockSCIMConfigWithNonProvisioned(false, true, false) mockHandler.On("Get", ctx, "default", orgID, metav1.GetOptions{}, mock.Anything). @@ -269,9 +269,9 @@ func TestSCIMUtil_AreNonProvisionedUsersAllowed(t *testing.T) { expectedResult: false, }, { - name: "dynamic config both settings disabled - non-provisioned users not allowed", - k8sClient: &MockK8sHandler{}, - staticAllowed: true, + name: "dynamic config both settings disabled - non-provisioned users allowed", + k8sClient: &MockK8sHandler{}, + staticRejected: true, setupMock: func(mockHandler *MockK8sHandler) { obj := createMockSCIMConfigWithNonProvisioned(false, false, false) mockHandler.On("Get", ctx, "default", orgID, metav1.GetOptions{}, mock.Anything). @@ -280,9 +280,9 @@ func TestSCIMUtil_AreNonProvisionedUsersAllowed(t *testing.T) { expectedResult: false, }, { - name: "dynamic config both settings enabled - non-provisioned users allowed", - k8sClient: &MockK8sHandler{}, - staticAllowed: false, + name: "dynamic config both settings enabled - non-provisioned users rejected", + k8sClient: &MockK8sHandler{}, + staticRejected: false, setupMock: func(mockHandler *MockK8sHandler) { obj := createMockSCIMConfigWithNonProvisioned(true, true, true) mockHandler.On("Get", ctx, "default", orgID, metav1.GetOptions{}, mock.Anything). @@ -299,7 +299,7 @@ func TestSCIMUtil_AreNonProvisionedUsersAllowed(t *testing.T) { } util := NewSCIMUtil(tt.k8sClient) - result := util.AreNonProvisionedUsersAllowed(ctx, orgID, tt.staticAllowed) + result := util.AreNonProvisionedUsersRejected(ctx, orgID, tt.staticRejected) assert.Equal(t, tt.expectedResult, result) @@ -449,9 +449,9 @@ func TestSCIMUtil_fetchDynamicSCIMSetting(t *testing.T) { }, }, { - name: "allowNonProvisionedUsers setting enabled", + name: "rejectNonProvisionedUsers setting enabled", k8sClient: &MockK8sHandler{}, - settingType: "allowNonProvisionedUsers", + settingType: "rejectNonProvisionedUsers", expectedEnabled: true, expectedDynamicFetched: true, setupMock: func(mockHandler *MockK8sHandler) { @@ -461,9 +461,9 @@ func TestSCIMUtil_fetchDynamicSCIMSetting(t *testing.T) { }, }, { - name: "allowNonProvisionedUsers setting disabled", + name: "rejectNonProvisionedUsers setting disabled", k8sClient: &MockK8sHandler{}, - settingType: "allowNonProvisionedUsers", + settingType: "rejectNonProvisionedUsers", expectedEnabled: false, expectedDynamicFetched: true, setupMock: func(mockHandler *MockK8sHandler) { @@ -570,36 +570,36 @@ func TestSCIMUtil_unstructuredToSCIMConfig(t *testing.T) { name: "valid object with both settings enabled", obj: createMockSCIMConfig(true, true), expectedSpec: SCIMConfigSpec{ - EnableUserSync: true, - EnableGroupSync: true, - AllowNonProvisionedUsers: false, + EnableUserSync: true, + EnableGroupSync: true, + RejectNonProvisionedUsers: false, }, }, { name: "valid object with both settings disabled", obj: createMockSCIMConfig(false, false), expectedSpec: SCIMConfigSpec{ - EnableUserSync: false, - EnableGroupSync: false, - AllowNonProvisionedUsers: false, + EnableUserSync: false, + EnableGroupSync: false, + RejectNonProvisionedUsers: false, }, }, { name: "valid object with mixed settings", obj: createMockSCIMConfig(true, false), expectedSpec: SCIMConfigSpec{ - EnableUserSync: true, - EnableGroupSync: false, - AllowNonProvisionedUsers: false, + EnableUserSync: true, + EnableGroupSync: false, + RejectNonProvisionedUsers: false, }, }, { - name: "valid object with allowNonProvisionedUsers enabled", + name: "valid object with rejectNonProvisionedUsers enabled", obj: createMockSCIMConfigWithNonProvisioned(false, false, true), expectedSpec: SCIMConfigSpec{ - EnableUserSync: false, - EnableGroupSync: false, - AllowNonProvisionedUsers: true, + EnableUserSync: false, + EnableGroupSync: false, + RejectNonProvisionedUsers: true, }, }, { @@ -641,7 +641,7 @@ func createMockSCIMConfig(userSyncEnabled, groupSyncEnabled bool) *unstructured. } // Helper function to create a mock SCIMConfig unstructured object with non-provisioned users setting -func createMockSCIMConfigWithNonProvisioned(userSyncEnabled, groupSyncEnabled, allowNonProvisionedUsers bool) *unstructured.Unstructured { +func createMockSCIMConfigWithNonProvisioned(userSyncEnabled, groupSyncEnabled, rejectNonProvisionedUsers bool) *unstructured.Unstructured { return &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "scim.grafana.com/v0alpha1", @@ -651,9 +651,9 @@ func createMockSCIMConfigWithNonProvisioned(userSyncEnabled, groupSyncEnabled, a "namespace": "default", }, "spec": map[string]interface{}{ - "enableUserSync": userSyncEnabled, - "enableGroupSync": groupSyncEnabled, - "allowNonProvisionedUsers": allowNonProvisionedUsers, + "enableUserSync": userSyncEnabled, + "enableGroupSync": groupSyncEnabled, + "rejectNonProvisionedUsers": rejectNonProvisionedUsers, }, }, } @@ -676,9 +676,9 @@ func TestSCIMUtil_Integration(t *testing.T) { userSyncEnabled := util.IsUserSyncEnabled(ctx, orgID, false) assert.True(t, userSyncEnabled) - // Test non-provisioned users allowed - nonProvisionedAllowed := util.AreNonProvisionedUsersAllowed(ctx, orgID, false) - assert.True(t, nonProvisionedAllowed) + // Test non-provisioned users rejected + nonProvisionedRejected := util.AreNonProvisionedUsersRejected(ctx, orgID, false) + assert.True(t, nonProvisionedRejected) mockClient.AssertExpectations(t) }) @@ -695,8 +695,8 @@ func TestSCIMUtil_Integration(t *testing.T) { assert.True(t, userSyncEnabled) // Test non-provisioned users falls back to static config - nonProvisionedAllowed := util.AreNonProvisionedUsersAllowed(ctx, orgID, true) - assert.True(t, nonProvisionedAllowed) + nonProvisionedRejected := util.AreNonProvisionedUsersRejected(ctx, orgID, true) + assert.True(t, nonProvisionedRejected) mockClient.AssertExpectations(t) })