[MM-69528] Enable feature flags for ranked attributes, permission policies and masking by default (#37265)

* [MM-69528] Enable feature flags for ranked attributes, permission policies and masking

Change the default value to true for AttributeValueMasking,
PermissionPolicies, ChannelPermissionPolicies, PolicySimulation and
PropertyFieldRank, and update the unit tests and a stale comment that
pinned the old defaults.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Fix access control tests for feature flags enabled by default

Tests that assumed AttributeValueMasking, PermissionPolicies, and
PolicySimulation were off by default now explicitly disable those flags
in setup or use SetReadOnlyFF(false) before runtime UpdateConfig calls.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Add regression test for PropertyFieldRank default

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Disable masking in store-mock tests that assume flag off

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Fix store-mock masking helper without UpdateConfig

UpdateConfig triggers config persistence listeners that call
Store.Post() on the mock store. Mutate feature flags in place instead.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Fix masking-off deactivation test for new flag default

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Fix Setup/InitBasic compile errors in access control tests

Pass testing.TB to Setup and InitBasic after removing redundant
feature-flag SetupConfig wrappers.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* Restore ABAC feature-flag defaults in test cleanup

Defer blocks in access_control_test.go were resetting PermissionPolicies,
ChannelPermissionPolicies, and PolicySimulation to false after subtests,
leaving the shared TestHelper in a partially-disabled state. With these
flags now defaulting to true, cleanup should restore the default-enabled
baseline instead.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
This commit is contained in:
cursor[bot]
2026-06-30 16:23:32 -04:00
committed by GitHub
co-authored by mattermost-code Cursor Agent
parent 98a9c89514
commit 939afca46f
5 changed files with 101 additions and 96 deletions
+60 -61
View File
@@ -16,8 +16,32 @@ import (
"github.com/stretchr/testify/require"
)
// maskingOffTestConfig disables attribute-value masking for policy-endpoint
// tests that do not cover masking. ABAC and other ABAC sub-flags default on.
func maskingOffTestConfig(cfg *model.Config) {
cfg.FeatureFlags.AttributeValueMasking = false
}
func allowTestFeatureFlagUpdates(t *testing.T, th *TestHelper) {
t.Helper()
th.ConfigStore.SetReadOnlyFF(false)
}
func updateTestFeatureFlags(t *testing.T, th *TestHelper, fn func(cfg *model.Config)) {
t.Helper()
allowTestFeatureFlagUpdates(t, th)
th.App.UpdateConfig(fn)
}
func restoreABACFeatureFlagDefaults(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = true
cfg.FeatureFlags.ChannelPermissionPolicies = true
cfg.FeatureFlags.PolicySimulation = true
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = model.NewPointer(true)
}
func TestCreateAccessControlPolicy(t *testing.T) {
th := SetupConfig(t, func(cfg *model.Config) { cfg.FeatureFlags.AttributeBasedAccessControl = true }).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
samplePolicy := &model.AccessControlPolicy{
ID: th.BasicChannel.Id,
@@ -273,7 +297,7 @@ func TestCreateAccessControlPolicy(t *testing.T) {
ok := th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
require.True(t, ok, "SetLicense should return true")
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = false
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = new(true)
})
@@ -319,7 +343,7 @@ func TestCreateAccessControlPolicy(t *testing.T) {
th.App.Srv().Channels().AccessControl = mockAccessControlService
mockAccessControlService.On("SavePolicy", mock.AnythingOfType("*request.Context"), mock.AnythingOfType("*model.AccessControlPolicy")).Return(permissionPolicy, nil).Times(1)
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = true
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = new(true)
})
@@ -341,14 +365,12 @@ func TestCreateAccessControlPolicy(t *testing.T) {
ok := th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
require.True(t, ok, "SetLicense should return true")
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = true
cfg.FeatureFlags.ChannelPermissionPolicies = false
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = new(true)
})
defer th.App.UpdateConfig(func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = false
})
defer updateTestFeatureFlags(t, th, restoreABACFeatureFlagDefaults)
channelPolicy := &model.AccessControlPolicy{
ID: model.NewId(),
@@ -381,14 +403,12 @@ func TestCreateAccessControlPolicy(t *testing.T) {
ok := th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
require.True(t, ok, "SetLicense should return true")
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = false
cfg.FeatureFlags.ChannelPermissionPolicies = true
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = new(true)
})
defer th.App.UpdateConfig(func(cfg *model.Config) {
cfg.FeatureFlags.ChannelPermissionPolicies = false
})
defer updateTestFeatureFlags(t, th, restoreABACFeatureFlagDefaults)
channelPolicy := &model.AccessControlPolicy{
ID: model.NewId(),
@@ -443,15 +463,12 @@ func TestCreateAccessControlPolicy(t *testing.T) {
// other tests, so the mock returns success straight away.
mockAccessControlService.On("SavePolicy", mock.AnythingOfType("*request.Context"), mock.AnythingOfType("*model.AccessControlPolicy")).Return(channelPolicy, nil).Times(1)
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = true
cfg.FeatureFlags.ChannelPermissionPolicies = true
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = new(true)
})
defer th.App.UpdateConfig(func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = false
cfg.FeatureFlags.ChannelPermissionPolicies = false
})
defer updateTestFeatureFlags(t, th, restoreABACFeatureFlagDefaults)
_, resp, err := th.SystemAdminClient.CreateAccessControlPolicy(context.Background(), channelPolicy)
require.NoError(t, err)
@@ -498,7 +515,7 @@ func TestCreateAccessControlPolicy(t *testing.T) {
}
func TestGetAccessControlPolicy(t *testing.T) {
th := SetupConfig(t, func(cfg *model.Config) { cfg.FeatureFlags.AttributeBasedAccessControl = true }).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
samplePolicy := &model.AccessControlPolicy{
ID: model.NewId(),
@@ -626,7 +643,7 @@ func TestGetAccessControlPolicy(t *testing.T) {
}
func TestDeleteAccessControlPolicy(t *testing.T) {
th := SetupConfig(t, func(cfg *model.Config) { cfg.FeatureFlags.AttributeBasedAccessControl = true }).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
samplePolicyID := model.NewId()
@@ -702,7 +719,7 @@ func TestDeleteAccessControlPolicy(t *testing.T) {
}
func TestCheckExpression(t *testing.T) {
th := SetupConfig(t, func(cfg *model.Config) { cfg.FeatureFlags.AttributeBasedAccessControl = true }).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
t.Run("CheckExpression without license", func(t *testing.T) {
_, resp, err := th.SystemAdminClient.CheckExpression(context.Background(), "true")
@@ -837,7 +854,7 @@ func TestCheckExpression(t *testing.T) {
}
func TestTestExpression(t *testing.T) {
th := SetupConfig(t, func(cfg *model.Config) { cfg.FeatureFlags.AttributeBasedAccessControl = true }).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
t.Run("TestExpression without license", func(t *testing.T) {
_, resp, err := th.SystemAdminClient.TestExpression(context.Background(), model.QueryExpressionParams{})
@@ -884,7 +901,7 @@ func TestTestExpression(t *testing.T) {
}
func TestSearchAccessControlPolicies(t *testing.T) {
th := SetupConfig(t, func(cfg *model.Config) { cfg.FeatureFlags.AttributeBasedAccessControl = true }).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
t.Run("SearchAccessControlPolicies without license", func(t *testing.T) {
_, resp, err := th.SystemAdminClient.SearchAccessControlPolicies(context.Background(), model.AccessControlPolicySearch{})
@@ -935,7 +952,7 @@ func TestSearchAccessControlPolicies(t *testing.T) {
ok := th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
require.True(t, ok, "SetLicense should return true")
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = false
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = new(true)
})
@@ -959,7 +976,7 @@ func TestSearchAccessControlPolicies(t *testing.T) {
Type: model.AccessControlPolicyTypePermission,
}).Return([]*model.AccessControlPolicy{}, int64(0), nil).Times(1)
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = true
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = new(true)
})
@@ -1058,7 +1075,7 @@ func TestSearchTeamAccessControlPolicies(t *testing.T) {
}
func TestAssignAccessPolicy(t *testing.T) {
th := SetupConfig(t, func(cfg *model.Config) { cfg.FeatureFlags.AttributeBasedAccessControl = true }).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
samplePolicy := &model.AccessControlPolicy{
ID: model.NewId(),
@@ -1135,7 +1152,7 @@ func TestAssignAccessPolicy(t *testing.T) {
}
func TestUnassignAccessPolicy(t *testing.T) {
th := SetupConfig(t, func(cfg *model.Config) { cfg.FeatureFlags.AttributeBasedAccessControl = true }).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
samplePolicy := &model.AccessControlPolicy{
ID: model.NewId(),
@@ -1208,7 +1225,7 @@ func TestUnassignAccessPolicy(t *testing.T) {
}
func TestGetChannelsForAccessControlPolicy(t *testing.T) {
th := SetupConfig(t, func(cfg *model.Config) { cfg.FeatureFlags.AttributeBasedAccessControl = true }).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
samplePolicy := &model.AccessControlPolicy{
ID: model.NewId(),
@@ -1266,7 +1283,7 @@ func TestGetChannelsForAccessControlPolicy(t *testing.T) {
}
func TestSearchChannelsForAccessControlPolicy(t *testing.T) {
th := SetupConfig(t, func(cfg *model.Config) { cfg.FeatureFlags.AttributeBasedAccessControl = true }).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
newSamplePolicy := func() *model.AccessControlPolicy {
return &model.AccessControlPolicy{
@@ -1480,7 +1497,7 @@ func TestSearchChannelsForAccessControlPolicy(t *testing.T) {
}
func TestSetActiveStatus(t *testing.T) {
th := Setup(t).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
samplePolicy := &model.AccessControlPolicy{
ID: th.BasicChannel.Id,
@@ -1669,8 +1686,9 @@ func setupTeamAdminABAC(t *testing.T, th *TestHelper) *mocks.AccessControlServic
mockACS := &mocks.AccessControlServiceInterface{}
th.App.Srv().Channels().AccessControl = mockACS
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = new(true)
cfg.FeatureFlags.AttributeValueMasking = false
})
th.AddPermissionToRole(t, model.PermissionManageTeamAccessRules.Id, model.TeamAdminRoleId)
@@ -1713,12 +1731,7 @@ func newParentPolicy(teamID string) *model.AccessControlPolicy {
// fail-closed branch (unknown property field) so the masking always produces the
// "--------" sentinel without requiring a real CPA setup.
func TestResponseMaskingOnPolicyEndpoints(t *testing.T) {
// SetupConfig sets FFs before route init via SetReadOnlyFF(false). Avoids
// os.Setenv which isn't parallel-safe.
th := SetupConfig(t, func(cfg *model.Config) {
cfg.FeatureFlags.AttributeBasedAccessControl = true
cfg.FeatureFlags.AttributeValueMasking = true
}).InitBasic(t)
th := Setup(t).InitBasic(t)
ok := th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
require.True(t, ok, "SetLicense should return true")
@@ -2652,7 +2665,7 @@ func TestScopeReconciliationCrossTeam(t *testing.T) {
// proxies to the access-control service which we mock here so the test
// stays focused on the API surface (auth + payload validation).
func TestSimulatePolicyForUsers(t *testing.T) {
th := SetupConfig(t, func(cfg *model.Config) { cfg.FeatureFlags.AttributeBasedAccessControl = true }).InitBasic(t)
th := SetupConfig(t, maskingOffTestConfig).InitBasic(t)
t.Run("returns 501 when umbrella PermissionPolicies flag is disabled", func(t *testing.T) {
// Set the Enterprise Advanced license up-front so any future
@@ -2667,7 +2680,7 @@ func TestSimulatePolicyForUsers(t *testing.T) {
ok := th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
require.True(t, ok)
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = false
cfg.FeatureFlags.PolicySimulation = true // sub-flag alone must not be enough
})
@@ -2695,13 +2708,11 @@ func TestSimulatePolicyForUsers(t *testing.T) {
ok := th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
require.True(t, ok)
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = true
cfg.FeatureFlags.PolicySimulation = false
})
defer th.App.UpdateConfig(func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = false
})
defer updateTestFeatureFlags(t, th, restoreABACFeatureFlagDefaults)
body := mustMarshal(t, model.PolicySimulationByUsersParams{
Policy: &model.AccessControlPolicy{ID: model.NewId(), Type: model.AccessControlPolicyTypeChannel},
@@ -2723,14 +2734,11 @@ func TestSimulatePolicyForUsers(t *testing.T) {
require.True(t, ok)
defer th.App.Srv().SetLicense(nil)
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = true
cfg.FeatureFlags.PolicySimulation = true
})
defer th.App.UpdateConfig(func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = false
cfg.FeatureFlags.PolicySimulation = false
})
defer updateTestFeatureFlags(t, th, restoreABACFeatureFlagDefaults)
body := mustMarshal(t, model.PolicySimulationByUsersParams{
Policy: &model.AccessControlPolicy{ID: model.NewId(), Type: model.AccessControlPolicyTypeChannel},
@@ -2747,15 +2755,12 @@ func TestSimulatePolicyForUsers(t *testing.T) {
ok := th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
require.True(t, ok)
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = true
cfg.FeatureFlags.PolicySimulation = true
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = model.NewPointer(true)
})
defer th.App.UpdateConfig(func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = false
cfg.FeatureFlags.PolicySimulation = false
})
defer updateTestFeatureFlags(t, th, restoreABACFeatureFlagDefaults)
mockACS := &mocks.AccessControlServiceInterface{}
th.App.Srv().Channels().AccessControl = mockACS
@@ -2774,15 +2779,12 @@ func TestSimulatePolicyForUsers(t *testing.T) {
ok := th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
require.True(t, ok)
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = true
cfg.FeatureFlags.PolicySimulation = true
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = model.NewPointer(true)
})
defer th.App.UpdateConfig(func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = false
cfg.FeatureFlags.PolicySimulation = false
})
defer updateTestFeatureFlags(t, th, restoreABACFeatureFlagDefaults)
mockACS := &mocks.AccessControlServiceInterface{}
mockACS.On("SimulatePolicyForUsers", mock.Anything, mock.Anything).Return(
@@ -2807,15 +2809,12 @@ func TestSimulatePolicyForUsers(t *testing.T) {
ok := th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
require.True(t, ok)
th.App.UpdateConfig(func(cfg *model.Config) {
updateTestFeatureFlags(t, th, func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = true
cfg.FeatureFlags.PolicySimulation = true
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = model.NewPointer(true)
})
defer th.App.UpdateConfig(func(cfg *model.Config) {
cfg.FeatureFlags.PermissionPolicies = false
cfg.FeatureFlags.PolicySimulation = false
})
defer updateTestFeatureFlags(t, th, restoreABACFeatureFlagDefaults)
mockACS := &mocks.AccessControlServiceInterface{}
th.App.Srv().Channels().AccessControl = mockACS
+22 -24
View File
@@ -25,8 +25,19 @@ func celSafeName() string {
return "f_" + model.NewId()
}
func storeMockWithMaskingOff(tb testing.TB) *TestHelper {
tb.Helper()
th := SetupWithStoreMock(tb)
// Mutate in place — UpdateConfig persists config and triggers
// listeners that call Store.Post(), which the mock store lacks.
th.App.Config().FeatureFlags.AttributeValueMasking = false
return th
}
func TestCreateOrUpdateAccessControlPolicy(t *testing.T) {
th := Setup(t).InitBasic(t)
th := SetupConfig(t, func(cfg *model.Config) {
cfg.FeatureFlags.AttributeValueMasking = false
}).InitBasic(t)
t.Run("Feature not enabled", func(t *testing.T) {
th.App.Srv().ch.AccessControl = nil
@@ -115,7 +126,7 @@ func TestCreateOrUpdateAccessControlPolicy(t *testing.T) {
})
t.Run("Channel-type policy broadcasts policy enforced update", func(t *testing.T) {
thMock := SetupWithStoreMock(t)
thMock := storeMockWithMaskingOff(t)
channelID := model.NewId()
channelPolicy := &model.AccessControlPolicy{
@@ -157,7 +168,7 @@ func TestCreateOrUpdateAccessControlPolicy(t *testing.T) {
})
t.Run("Parent-type policy does not broadcast channel-only update", func(t *testing.T) {
thMock := SetupWithStoreMock(t)
thMock := storeMockWithMaskingOff(t)
parentID := model.NewId()
parentPolicy := &model.AccessControlPolicy{
@@ -341,10 +352,7 @@ func TestDeleteAccessControlPolicy(t *testing.T) {
// could not audit. The canonical walker's HasMaskedValuesForCaller is mocked
// to return true, simulating a hidden-value field without requiring a full
// CPA setup for the test.
th := SetupConfig(t, func(cfg *model.Config) {
cfg.FeatureFlags.AttributeBasedAccessControl = true
cfg.FeatureFlags.AttributeValueMasking = true
}).InitBasic(t)
th := Setup(t).InitBasic(t)
callerID := model.NewId()
th.Context = th.Context.WithSession(&model.Session{UserId: callerID, Id: model.NewId()}).(*request.Context)
@@ -378,9 +386,7 @@ func TestDeleteAccessControlPolicy(t *testing.T) {
// Belt-and-braces: with AttributeValueMasking off, the masking guard must not
// fire — the policy deletes normally even if the caller wouldn't have seen all
// values. Guards against accidentally inverting the flag condition.
thMock := SetupWithStoreMock(t)
// Note: SetupWithStoreMock doesn't take a config callback. Feature flags
// default to false, which is exactly the state this test wants.
thMock := storeMockWithMaskingOff(t)
thMock.Context = thMock.Context.WithSession(&model.Session{UserId: model.NewId(), Id: model.NewId()}).(*request.Context)
@@ -2149,6 +2155,7 @@ func TestHasPermissionToFileAction(t *testing.T) {
mockAccessControl := &mocks.AccessControlServiceInterface{}
th.App.Srv().ch.AccessControl = mockAccessControl
th.ConfigStore.SetReadOnlyFF(false)
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = new(false)
cfg.FeatureFlags.PermissionPolicies = true
@@ -2162,6 +2169,7 @@ func TestHasPermissionToFileAction(t *testing.T) {
mockAccessControl := &mocks.AccessControlServiceInterface{}
th.App.Srv().ch.AccessControl = mockAccessControl
th.ConfigStore.SetReadOnlyFF(false)
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.AccessControlSettings.EnableAttributeBasedAccessControl = new(true)
cfg.FeatureFlags.PermissionPolicies = false
@@ -4535,7 +4543,7 @@ func TestPublishChannelPolicyEnforcedUpdateHydratesBroadcastPayload(t *testing.T
// broadcast payload so connected clients can react to action-set
// changes without a follow-up REST round-trip. The hydration happens
// after GetChannel reloads the (now-policy-enforced) channel post-save.
thMock := SetupWithStoreMock(t)
thMock := storeMockWithMaskingOff(t)
channelID := model.NewId()
channelPolicy := &model.AccessControlPolicy{
@@ -4980,10 +4988,7 @@ func TestUpdateAccessControlPoliciesActive_MaskingGuard(t *testing.T) {
t.Run("deactivation blocked when caller has masked values", func(t *testing.T) {
// policyHasMaskedValuesForCaller resolves the property group from the store,
// so this subtest uses SetupConfig + InitBasic rather than a mock store.
th := SetupConfig(t, func(cfg *model.Config) {
cfg.FeatureFlags.AttributeBasedAccessControl = true
cfg.FeatureFlags.AttributeValueMasking = true
}).InitBasic(t)
th := Setup(t).InitBasic(t)
callerID := model.NewId()
th.Context = th.Context.WithSession(&model.Session{UserId: callerID, Id: model.NewId()}).(*request.Context)
@@ -5016,10 +5021,6 @@ func TestUpdateAccessControlPoliciesActive_MaskingGuard(t *testing.T) {
t.Run("activation always allowed even when caller has masked values", func(t *testing.T) {
// The guard skips Active=true updates, so no property store access is needed.
thMock := SetupWithStoreMock(t)
thMock.App.UpdateConfig(func(cfg *model.Config) {
cfg.FeatureFlags.AttributeBasedAccessControl = true
cfg.FeatureFlags.AttributeValueMasking = true
})
callerID := model.NewId()
thMock.Context = thMock.Context.WithSession(&model.Session{UserId: callerID, Id: model.NewId()}).(*request.Context)
@@ -5057,7 +5058,7 @@ func TestUpdateAccessControlPoliciesActive_MaskingGuard(t *testing.T) {
})
t.Run("deactivation allowed when masking flag is off", func(t *testing.T) {
thMock := SetupWithStoreMock(t)
thMock := storeMockWithMaskingOff(t)
thMock.Context = thMock.Context.WithSession(&model.Session{UserId: model.NewId(), Id: model.NewId()}).(*request.Context)
channelID := model.NewId()
@@ -5190,10 +5191,7 @@ func TestMaskPolicyExpressions_FailClosedUsesDenyAllSentinel(t *testing.T) {
callerID := model.NewId()
t.Run("MaskExpressionForCaller failure masks rule to deny-all sentinel", func(t *testing.T) {
th2 := SetupConfig(t, func(cfg *model.Config) {
cfg.FeatureFlags.AttributeBasedAccessControl = true
cfg.FeatureFlags.AttributeValueMasking = true
}).InitBasic(t)
th2 := Setup(t).InitBasic(t)
policy := &model.AccessControlPolicy{
Rules: []model.AccessControlPolicyRule{
+1 -1
View File
@@ -76,7 +76,7 @@ func (a *App) publishPropertyFieldEvent(rctx request.CTX, eventType model.Websoc
// (template/system/channel in the access_control group) legitimately use the
// rank type and ship behind the separate, GA-by-default ClassificationMarkings
// flag. Gating those here would break the classification admin panel (create
// and edit alike) whenever PropertyFieldRank is off, which is the default.
// and edit alike) whenever PropertyFieldRank is off.
func (a *App) rankPropertyFieldGate(where string, field *model.PropertyField) *model.AppError {
if field == nil || field.Type != model.PropertyFieldTypeRank {
return nil
+5 -5
View File
@@ -178,11 +178,11 @@ func (f *FeatureFlags) SetDefaults() {
f.ExperimentalAuditSettingsSystemConsoleUI = true
f.CustomProfileAttributes = true
f.AttributeBasedAccessControl = true
f.AttributeValueMasking = false
f.PermissionPolicies = false
f.AttributeValueMasking = true
f.PermissionPolicies = true
f.TeamMembershipAccessControl = false
f.ChannelPermissionPolicies = false
f.PolicySimulation = false
f.ChannelPermissionPolicies = true
f.PolicySimulation = true
f.ContentFlagging = true
f.EnableMattermostEntry = true
@@ -215,7 +215,7 @@ func (f *FeatureFlags) SetDefaults() {
f.MobileEphemeralMode = false
f.PropertyFieldRank = false
f.PropertyFieldRank = true
f.MmBlocksEnabled = true
}
+13 -5
View File
@@ -60,8 +60,16 @@ func TestFeatureFlagsSetDefaults_AttributeValueMasking(t *testing.T) {
var flags FeatureFlags
flags.SetDefaults()
require.False(t, flags.AttributeValueMasking, "AttributeValueMasking should default to false")
require.Equal(t, "false", flags.ToMap()["AttributeValueMasking"])
require.True(t, flags.AttributeValueMasking, "AttributeValueMasking should default to true")
require.Equal(t, "true", flags.ToMap()["AttributeValueMasking"])
}
func TestFeatureFlagsSetDefaults_PropertyFieldRank(t *testing.T) {
var flags FeatureFlags
flags.SetDefaults()
require.True(t, flags.PropertyFieldRank, "PropertyFieldRank should default to true")
require.Equal(t, "true", flags.ToMap()["PropertyFieldRank"])
}
// TestFeatureFlagsPermissionPoliciesDependencies pins down the
@@ -71,12 +79,12 @@ func TestFeatureFlagsSetDefaults_AttributeValueMasking(t *testing.T) {
// dependency (additional gates, new sub-flags) only have to update
// one place and existing call sites stay correct.
func TestFeatureFlagsPermissionPoliciesDependencies(t *testing.T) {
t.Run("both helpers are off when defaults are applied", func(t *testing.T) {
t.Run("both helpers are on when defaults are applied", func(t *testing.T) {
var f FeatureFlags
f.SetDefaults()
require.False(t, f.IsChannelPermissionPoliciesEnabled())
require.False(t, f.IsPolicySimulationEnabled())
require.True(t, f.IsChannelPermissionPoliciesEnabled())
require.True(t, f.IsPolicySimulationEnabled())
})
t.Run("sub-flag alone is not enough — the umbrella must be on too", func(t *testing.T) {