Skip to content

Commit 4aed97b

Browse files
authored
[PM-26690] Wire VNextSavePolicyCommand behind PolicyValidatorsRefactor feature flag (#6483)
* Add PolicyValidatorsRefactor constant to FeatureFlagKeys in Constants.cs * Add Metadata property and ToSavePolicyModel method to PolicyUpdateRequestModel * Refactor PoliciesController to utilize IVNextSavePolicyCommand based on feature flag - Added IFeatureService and IVNextSavePolicyCommand dependencies to PoliciesController. - Updated PutVNext method to conditionally use VNextSavePolicyCommand or SavePolicyCommand based on the PolicyValidatorsRefactor feature flag. - Enhanced unit tests to verify behavior for both enabled and disabled states of the feature flag. * Update public PoliciesController to to utilize IVNextSavePolicyCommand based on feature flag - Introduced IFeatureService and IVNextSavePolicyCommand to manage policy saving based on the PolicyValidatorsRefactor feature flag. - Updated the Put method to conditionally use the new VNextSavePolicyCommand or the legacy SavePolicyCommand. - Added unit tests to validate the behavior of the Put method for both enabled and disabled states of the feature flag. * Refactor VerifyOrganizationDomainCommand to utilize IVNextSavePolicyCommand based on feature flag - Added IFeatureService and IVNextSavePolicyCommand dependencies to VerifyOrganizationDomainCommand. - Updated EnableSingleOrganizationPolicyAsync method to conditionally use VNextSavePolicyCommand or SavePolicyCommand based on the PolicyValidatorsRefactor feature flag. - Enhanced unit tests to validate the behavior when the feature flag is enabled. * Enhance SsoConfigService to utilize IVNextSavePolicyCommand based on feature flag - Added IFeatureService and IVNextSavePolicyCommand dependencies to SsoConfigService. - Updated SaveAsync method to conditionally use VNextSavePolicyCommand or SavePolicyCommand based on the PolicyValidatorsRefactor feature flag. - Added unit tests to validate the behavior when the feature flag is enabled. * Refactor SavePolicyModel to simplify constructor usage by removing EmptyMetadataModel parameter. Update related usages across the codebase to reflect the new constructor overloads. * Update PolicyUpdateRequestModel to make Metadata property nullable for improved null safety
1 parent 3668a44 commit 4aed97b

File tree

19 files changed

+426
-59
lines changed

19 files changed

+426
-59
lines changed

src/Api/AdminConsole/Controllers/PoliciesController.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Bit.Core.AdminConsole.Enums;
1313
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
1414
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
15+
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
1516
using Bit.Core.AdminConsole.Repositories;
1617
using Bit.Core.Auth.Models.Business.Tokenables;
1718
using Bit.Core.Context;
@@ -41,8 +42,9 @@ public class PoliciesController : Controller
4142
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
4243
private readonly IPolicyRepository _policyRepository;
4344
private readonly IUserService _userService;
44-
45+
private readonly IFeatureService _featureService;
4546
private readonly ISavePolicyCommand _savePolicyCommand;
47+
private readonly IVNextSavePolicyCommand _vNextSavePolicyCommand;
4648

4749
public PoliciesController(IPolicyRepository policyRepository,
4850
IOrganizationUserRepository organizationUserRepository,
@@ -53,7 +55,9 @@ public PoliciesController(IPolicyRepository policyRepository,
5355
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
5456
IOrganizationHasVerifiedDomainsQuery organizationHasVerifiedDomainsQuery,
5557
IOrganizationRepository organizationRepository,
56-
ISavePolicyCommand savePolicyCommand)
58+
IFeatureService featureService,
59+
ISavePolicyCommand savePolicyCommand,
60+
IVNextSavePolicyCommand vNextSavePolicyCommand)
5761
{
5862
_policyRepository = policyRepository;
5963
_organizationUserRepository = organizationUserRepository;
@@ -65,7 +69,9 @@ public PoliciesController(IPolicyRepository policyRepository,
6569
_organizationRepository = organizationRepository;
6670
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
6771
_organizationHasVerifiedDomainsQuery = organizationHasVerifiedDomainsQuery;
72+
_featureService = featureService;
6873
_savePolicyCommand = savePolicyCommand;
74+
_vNextSavePolicyCommand = vNextSavePolicyCommand;
6975
}
7076

7177
[HttpGet("{type}")]
@@ -221,7 +227,9 @@ public async Task<PolicyResponseModel> PutVNext(Guid orgId, [FromBody] SavePolic
221227
{
222228
var savePolicyRequest = await model.ToSavePolicyModelAsync(orgId, _currentContext);
223229

224-
var policy = await _savePolicyCommand.VNextSaveAsync(savePolicyRequest);
230+
var policy = _featureService.IsEnabled(FeatureFlagKeys.PolicyValidatorsRefactor) ?
231+
await _vNextSavePolicyCommand.SaveAsync(savePolicyRequest) :
232+
await _savePolicyCommand.VNextSaveAsync(savePolicyRequest);
225233

226234
return new PolicyResponseModel(policy);
227235
}

src/Api/AdminConsole/Public/Controllers/PoliciesController.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
using Bit.Api.AdminConsole.Public.Models.Request;
66
using Bit.Api.AdminConsole.Public.Models.Response;
77
using Bit.Api.Models.Public.Response;
8+
using Bit.Core;
9+
using Bit.Core.AdminConsole.Entities;
810
using Bit.Core.AdminConsole.Enums;
911
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
12+
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
1013
using Bit.Core.AdminConsole.Repositories;
1114
using Bit.Core.AdminConsole.Services;
1215
using Bit.Core.Context;
16+
using Bit.Core.Services;
1317
using Microsoft.AspNetCore.Authorization;
1418
using Microsoft.AspNetCore.Mvc;
1519

@@ -22,18 +26,24 @@ public class PoliciesController : Controller
2226
private readonly IPolicyRepository _policyRepository;
2327
private readonly IPolicyService _policyService;
2428
private readonly ICurrentContext _currentContext;
29+
private readonly IFeatureService _featureService;
2530
private readonly ISavePolicyCommand _savePolicyCommand;
31+
private readonly IVNextSavePolicyCommand _vNextSavePolicyCommand;
2632

2733
public PoliciesController(
2834
IPolicyRepository policyRepository,
2935
IPolicyService policyService,
3036
ICurrentContext currentContext,
31-
ISavePolicyCommand savePolicyCommand)
37+
IFeatureService featureService,
38+
ISavePolicyCommand savePolicyCommand,
39+
IVNextSavePolicyCommand vNextSavePolicyCommand)
3240
{
3341
_policyRepository = policyRepository;
3442
_policyService = policyService;
3543
_currentContext = currentContext;
44+
_featureService = featureService;
3645
_savePolicyCommand = savePolicyCommand;
46+
_vNextSavePolicyCommand = vNextSavePolicyCommand;
3747
}
3848

3949
/// <summary>
@@ -87,8 +97,17 @@ public async Task<IActionResult> List()
8797
[ProducesResponseType((int)HttpStatusCode.NotFound)]
8898
public async Task<IActionResult> Put(PolicyType type, [FromBody] PolicyUpdateRequestModel model)
8999
{
90-
var policyUpdate = model.ToPolicyUpdate(_currentContext.OrganizationId!.Value, type);
91-
var policy = await _savePolicyCommand.SaveAsync(policyUpdate);
100+
Policy policy;
101+
if (_featureService.IsEnabled(FeatureFlagKeys.PolicyValidatorsRefactor))
102+
{
103+
var savePolicyModel = model.ToSavePolicyModel(_currentContext.OrganizationId!.Value, type);
104+
policy = await _vNextSavePolicyCommand.SaveAsync(savePolicyModel);
105+
}
106+
else
107+
{
108+
var policyUpdate = model.ToPolicyUpdate(_currentContext.OrganizationId!.Value, type);
109+
policy = await _savePolicyCommand.SaveAsync(policyUpdate);
110+
}
92111

93112
var response = new PolicyResponseModel(policy);
94113
return new JsonResult(response);

src/Api/AdminConsole/Public/Models/Request/PolicyUpdateRequestModel.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ namespace Bit.Api.AdminConsole.Public.Models.Request;
88

99
public class PolicyUpdateRequestModel : PolicyBaseModel
1010
{
11+
public Dictionary<string, object>? Metadata { get; set; }
12+
1113
public PolicyUpdate ToPolicyUpdate(Guid organizationId, PolicyType type)
1214
{
1315
var serializedData = PolicyDataValidator.ValidateAndSerialize(Data, type);
@@ -21,4 +23,22 @@ public PolicyUpdate ToPolicyUpdate(Guid organizationId, PolicyType type)
2123
PerformedBy = new SystemUser(EventSystemUser.PublicApi)
2224
};
2325
}
26+
27+
public SavePolicyModel ToSavePolicyModel(Guid organizationId, PolicyType type)
28+
{
29+
var serializedData = PolicyDataValidator.ValidateAndSerialize(Data, type);
30+
31+
var policyUpdate = new PolicyUpdate
32+
{
33+
Type = type,
34+
OrganizationId = organizationId,
35+
Data = serializedData,
36+
Enabled = Enabled.GetValueOrDefault()
37+
};
38+
39+
var performedBy = new SystemUser(EventSystemUser.PublicApi);
40+
var metadata = PolicyDataValidator.ValidateAndDeserializeMetadata(Metadata, type);
41+
42+
return new SavePolicyModel(policyUpdate, performedBy, metadata);
43+
}
2444
}

src/Core/AdminConsole/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
77
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
88
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
9+
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
910
using Bit.Core.Context;
1011
using Bit.Core.Entities;
1112
using Bit.Core.Enums;
@@ -24,7 +25,9 @@ public class VerifyOrganizationDomainCommand(
2425
IEventService eventService,
2526
IGlobalSettings globalSettings,
2627
ICurrentContext currentContext,
28+
IFeatureService featureService,
2729
ISavePolicyCommand savePolicyCommand,
30+
IVNextSavePolicyCommand vNextSavePolicyCommand,
2831
IMailService mailService,
2932
IOrganizationUserRepository organizationUserRepository,
3033
IOrganizationRepository organizationRepository,
@@ -131,15 +134,26 @@ private async Task DomainVerificationSideEffectsAsync(OrganizationDomain domain,
131134
await SendVerifiedDomainUserEmailAsync(domain);
132135
}
133136

134-
private async Task EnableSingleOrganizationPolicyAsync(Guid organizationId, IActingUser actingUser) =>
135-
await savePolicyCommand.SaveAsync(
136-
new PolicyUpdate
137-
{
138-
OrganizationId = organizationId,
139-
Type = PolicyType.SingleOrg,
140-
Enabled = true,
141-
PerformedBy = actingUser
142-
});
137+
private async Task EnableSingleOrganizationPolicyAsync(Guid organizationId, IActingUser actingUser)
138+
{
139+
var policyUpdate = new PolicyUpdate
140+
{
141+
OrganizationId = organizationId,
142+
Type = PolicyType.SingleOrg,
143+
Enabled = true,
144+
PerformedBy = actingUser
145+
};
146+
147+
if (featureService.IsEnabled(FeatureFlagKeys.PolicyValidatorsRefactor))
148+
{
149+
var savePolicyModel = new SavePolicyModel(policyUpdate, actingUser);
150+
await vNextSavePolicyCommand.SaveAsync(savePolicyModel);
151+
}
152+
else
153+
{
154+
await savePolicyCommand.SaveAsync(policyUpdate);
155+
}
156+
}
143157

144158
private async Task SendVerifiedDomainUserEmailAsync(OrganizationDomain domain)
145159
{

src/Core/AdminConsole/OrganizationFeatures/Policies/Models/SavePolicyModel.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,18 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
55

66
public record SavePolicyModel(PolicyUpdate PolicyUpdate, IActingUser? PerformedBy, IPolicyMetadataModel Metadata)
77
{
8+
public SavePolicyModel(PolicyUpdate PolicyUpdate)
9+
: this(PolicyUpdate, null, new EmptyMetadataModel())
10+
{
11+
}
12+
13+
public SavePolicyModel(PolicyUpdate PolicyUpdate, IActingUser performedBy)
14+
: this(PolicyUpdate, performedBy, new EmptyMetadataModel())
15+
{
16+
}
17+
18+
public SavePolicyModel(PolicyUpdate PolicyUpdate, IPolicyMetadataModel metadata)
19+
: this(PolicyUpdate, null, metadata)
20+
{
21+
}
822
}

src/Core/Auth/Services/Implementations/SsoConfigService.cs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33

44
using Bit.Core.AdminConsole.Entities;
55
using Bit.Core.AdminConsole.Enums;
6+
using Bit.Core.AdminConsole.Models.Data;
67
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
78
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
89
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
10+
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
911
using Bit.Core.AdminConsole.Repositories;
1012
using Bit.Core.Auth.Entities;
1113
using Bit.Core.Auth.Enums;
@@ -24,22 +26,28 @@ public class SsoConfigService : ISsoConfigService
2426
private readonly IOrganizationRepository _organizationRepository;
2527
private readonly IOrganizationUserRepository _organizationUserRepository;
2628
private readonly IEventService _eventService;
29+
private readonly IFeatureService _featureService;
2730
private readonly ISavePolicyCommand _savePolicyCommand;
31+
private readonly IVNextSavePolicyCommand _vNextSavePolicyCommand;
2832

2933
public SsoConfigService(
3034
ISsoConfigRepository ssoConfigRepository,
3135
IPolicyRepository policyRepository,
3236
IOrganizationRepository organizationRepository,
3337
IOrganizationUserRepository organizationUserRepository,
3438
IEventService eventService,
35-
ISavePolicyCommand savePolicyCommand)
39+
IFeatureService featureService,
40+
ISavePolicyCommand savePolicyCommand,
41+
IVNextSavePolicyCommand vNextSavePolicyCommand)
3642
{
3743
_ssoConfigRepository = ssoConfigRepository;
3844
_policyRepository = policyRepository;
3945
_organizationRepository = organizationRepository;
4046
_organizationUserRepository = organizationUserRepository;
4147
_eventService = eventService;
48+
_featureService = featureService;
4249
_savePolicyCommand = savePolicyCommand;
50+
_vNextSavePolicyCommand = vNextSavePolicyCommand;
4351
}
4452

4553
public async Task SaveAsync(SsoConfig config, Organization organization)
@@ -67,13 +75,12 @@ public async Task SaveAsync(SsoConfig config, Organization organization)
6775
// Automatically enable account recovery, SSO required, and single org policies if trusted device encryption is selected
6876
if (config.GetData().MemberDecryptionType == MemberDecryptionType.TrustedDeviceEncryption)
6977
{
70-
71-
await _savePolicyCommand.SaveAsync(new()
78+
var singleOrgPolicy = new PolicyUpdate
7279
{
7380
OrganizationId = config.OrganizationId,
7481
Type = PolicyType.SingleOrg,
7582
Enabled = true
76-
});
83+
};
7784

7885
var resetPasswordPolicy = new PolicyUpdate
7986
{
@@ -82,14 +89,27 @@ await _savePolicyCommand.SaveAsync(new()
8289
Enabled = true,
8390
};
8491
resetPasswordPolicy.SetDataModel(new ResetPasswordDataModel { AutoEnrollEnabled = true });
85-
await _savePolicyCommand.SaveAsync(resetPasswordPolicy);
8692

87-
await _savePolicyCommand.SaveAsync(new()
93+
var requireSsoPolicy = new PolicyUpdate
8894
{
8995
OrganizationId = config.OrganizationId,
9096
Type = PolicyType.RequireSso,
9197
Enabled = true
92-
});
98+
};
99+
100+
if (_featureService.IsEnabled(FeatureFlagKeys.PolicyValidatorsRefactor))
101+
{
102+
var performedBy = new SystemUser(EventSystemUser.Unknown);
103+
await _vNextSavePolicyCommand.SaveAsync(new SavePolicyModel(singleOrgPolicy, performedBy));
104+
await _vNextSavePolicyCommand.SaveAsync(new SavePolicyModel(resetPasswordPolicy, performedBy));
105+
await _vNextSavePolicyCommand.SaveAsync(new SavePolicyModel(requireSsoPolicy, performedBy));
106+
}
107+
else
108+
{
109+
await _savePolicyCommand.SaveAsync(singleOrgPolicy);
110+
await _savePolicyCommand.SaveAsync(resetPasswordPolicy);
111+
await _savePolicyCommand.SaveAsync(requireSsoPolicy);
112+
}
93113
}
94114

95115
await LogEventsAsync(config, oldConfig);

src/Core/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ public static class FeatureFlagKeys
143143
public const string AutomaticConfirmUsers = "pm-19934-auto-confirm-organization-users";
144144
public const string PM23845_VNextApplicationCache = "pm-24957-refactor-memory-application-cache";
145145
public const string AccountRecoveryCommand = "pm-25581-prevent-provider-account-recovery";
146+
public const string PolicyValidatorsRefactor = "pm-26423-refactor-policy-side-effects";
146147

147148
/* Auth Team */
148149
public const string TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence";

0 commit comments

Comments
 (0)