mirror of
https://github.com/bitwarden/server
synced 2025-12-16 00:03:54 +00:00
[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
This commit is contained in:
@@ -12,6 +12,7 @@ using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Context;
|
||||
@@ -41,8 +42,9 @@ public class PoliciesController : Controller
|
||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly ISavePolicyCommand _savePolicyCommand;
|
||||
private readonly IVNextSavePolicyCommand _vNextSavePolicyCommand;
|
||||
|
||||
public PoliciesController(IPolicyRepository policyRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
@@ -53,7 +55,9 @@ public class PoliciesController : Controller
|
||||
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
||||
IOrganizationHasVerifiedDomainsQuery organizationHasVerifiedDomainsQuery,
|
||||
IOrganizationRepository organizationRepository,
|
||||
ISavePolicyCommand savePolicyCommand)
|
||||
IFeatureService featureService,
|
||||
ISavePolicyCommand savePolicyCommand,
|
||||
IVNextSavePolicyCommand vNextSavePolicyCommand)
|
||||
{
|
||||
_policyRepository = policyRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
@@ -65,7 +69,9 @@ public class PoliciesController : Controller
|
||||
_organizationRepository = organizationRepository;
|
||||
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
||||
_organizationHasVerifiedDomainsQuery = organizationHasVerifiedDomainsQuery;
|
||||
_featureService = featureService;
|
||||
_savePolicyCommand = savePolicyCommand;
|
||||
_vNextSavePolicyCommand = vNextSavePolicyCommand;
|
||||
}
|
||||
|
||||
[HttpGet("{type}")]
|
||||
@@ -221,7 +227,9 @@ public class PoliciesController : Controller
|
||||
{
|
||||
var savePolicyRequest = await model.ToSavePolicyModelAsync(orgId, _currentContext);
|
||||
|
||||
var policy = await _savePolicyCommand.VNextSaveAsync(savePolicyRequest);
|
||||
var policy = _featureService.IsEnabled(FeatureFlagKeys.PolicyValidatorsRefactor) ?
|
||||
await _vNextSavePolicyCommand.SaveAsync(savePolicyRequest) :
|
||||
await _savePolicyCommand.VNextSaveAsync(savePolicyRequest);
|
||||
|
||||
return new PolicyResponseModel(policy);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,15 @@ using System.Net;
|
||||
using Bit.Api.AdminConsole.Public.Models.Request;
|
||||
using Bit.Api.AdminConsole.Public.Models.Response;
|
||||
using Bit.Api.Models.Public.Response;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -22,18 +26,24 @@ public class PoliciesController : Controller
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly ISavePolicyCommand _savePolicyCommand;
|
||||
private readonly IVNextSavePolicyCommand _vNextSavePolicyCommand;
|
||||
|
||||
public PoliciesController(
|
||||
IPolicyRepository policyRepository,
|
||||
IPolicyService policyService,
|
||||
ICurrentContext currentContext,
|
||||
ISavePolicyCommand savePolicyCommand)
|
||||
IFeatureService featureService,
|
||||
ISavePolicyCommand savePolicyCommand,
|
||||
IVNextSavePolicyCommand vNextSavePolicyCommand)
|
||||
{
|
||||
_policyRepository = policyRepository;
|
||||
_policyService = policyService;
|
||||
_currentContext = currentContext;
|
||||
_featureService = featureService;
|
||||
_savePolicyCommand = savePolicyCommand;
|
||||
_vNextSavePolicyCommand = vNextSavePolicyCommand;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -87,8 +97,17 @@ public class PoliciesController : Controller
|
||||
[ProducesResponseType((int)HttpStatusCode.NotFound)]
|
||||
public async Task<IActionResult> Put(PolicyType type, [FromBody] PolicyUpdateRequestModel model)
|
||||
{
|
||||
var policyUpdate = model.ToPolicyUpdate(_currentContext.OrganizationId!.Value, type);
|
||||
var policy = await _savePolicyCommand.SaveAsync(policyUpdate);
|
||||
Policy policy;
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.PolicyValidatorsRefactor))
|
||||
{
|
||||
var savePolicyModel = model.ToSavePolicyModel(_currentContext.OrganizationId!.Value, type);
|
||||
policy = await _vNextSavePolicyCommand.SaveAsync(savePolicyModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
var policyUpdate = model.ToPolicyUpdate(_currentContext.OrganizationId!.Value, type);
|
||||
policy = await _savePolicyCommand.SaveAsync(policyUpdate);
|
||||
}
|
||||
|
||||
var response = new PolicyResponseModel(policy);
|
||||
return new JsonResult(response);
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Bit.Api.AdminConsole.Public.Models.Request;
|
||||
|
||||
public class PolicyUpdateRequestModel : PolicyBaseModel
|
||||
{
|
||||
public Dictionary<string, object>? Metadata { get; set; }
|
||||
|
||||
public PolicyUpdate ToPolicyUpdate(Guid organizationId, PolicyType type)
|
||||
{
|
||||
var serializedData = PolicyDataValidator.ValidateAndSerialize(Data, type);
|
||||
@@ -21,4 +23,22 @@ public class PolicyUpdateRequestModel : PolicyBaseModel
|
||||
PerformedBy = new SystemUser(EventSystemUser.PublicApi)
|
||||
};
|
||||
}
|
||||
|
||||
public SavePolicyModel ToSavePolicyModel(Guid organizationId, PolicyType type)
|
||||
{
|
||||
var serializedData = PolicyDataValidator.ValidateAndSerialize(Data, type);
|
||||
|
||||
var policyUpdate = new PolicyUpdate
|
||||
{
|
||||
Type = type,
|
||||
OrganizationId = organizationId,
|
||||
Data = serializedData,
|
||||
Enabled = Enabled.GetValueOrDefault()
|
||||
};
|
||||
|
||||
var performedBy = new SystemUser(EventSystemUser.PublicApi);
|
||||
var metadata = PolicyDataValidator.ValidateAndDeserializeMetadata(Metadata, type);
|
||||
|
||||
return new SavePolicyModel(policyUpdate, performedBy, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Bit.Core.AdminConsole.Models.Data;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
@@ -24,7 +25,9 @@ public class VerifyOrganizationDomainCommand(
|
||||
IEventService eventService,
|
||||
IGlobalSettings globalSettings,
|
||||
ICurrentContext currentContext,
|
||||
IFeatureService featureService,
|
||||
ISavePolicyCommand savePolicyCommand,
|
||||
IVNextSavePolicyCommand vNextSavePolicyCommand,
|
||||
IMailService mailService,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
@@ -131,15 +134,26 @@ public class VerifyOrganizationDomainCommand(
|
||||
await SendVerifiedDomainUserEmailAsync(domain);
|
||||
}
|
||||
|
||||
private async Task EnableSingleOrganizationPolicyAsync(Guid organizationId, IActingUser actingUser) =>
|
||||
await savePolicyCommand.SaveAsync(
|
||||
new PolicyUpdate
|
||||
{
|
||||
OrganizationId = organizationId,
|
||||
Type = PolicyType.SingleOrg,
|
||||
Enabled = true,
|
||||
PerformedBy = actingUser
|
||||
});
|
||||
private async Task EnableSingleOrganizationPolicyAsync(Guid organizationId, IActingUser actingUser)
|
||||
{
|
||||
var policyUpdate = new PolicyUpdate
|
||||
{
|
||||
OrganizationId = organizationId,
|
||||
Type = PolicyType.SingleOrg,
|
||||
Enabled = true,
|
||||
PerformedBy = actingUser
|
||||
};
|
||||
|
||||
if (featureService.IsEnabled(FeatureFlagKeys.PolicyValidatorsRefactor))
|
||||
{
|
||||
var savePolicyModel = new SavePolicyModel(policyUpdate, actingUser);
|
||||
await vNextSavePolicyCommand.SaveAsync(savePolicyModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
await savePolicyCommand.SaveAsync(policyUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendVerifiedDomainUserEmailAsync(OrganizationDomain domain)
|
||||
{
|
||||
|
||||
@@ -5,4 +5,18 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||
|
||||
public record SavePolicyModel(PolicyUpdate PolicyUpdate, IActingUser? PerformedBy, IPolicyMetadataModel Metadata)
|
||||
{
|
||||
public SavePolicyModel(PolicyUpdate PolicyUpdate)
|
||||
: this(PolicyUpdate, null, new EmptyMetadataModel())
|
||||
{
|
||||
}
|
||||
|
||||
public SavePolicyModel(PolicyUpdate PolicyUpdate, IActingUser performedBy)
|
||||
: this(PolicyUpdate, performedBy, new EmptyMetadataModel())
|
||||
{
|
||||
}
|
||||
|
||||
public SavePolicyModel(PolicyUpdate PolicyUpdate, IPolicyMetadataModel metadata)
|
||||
: this(PolicyUpdate, null, metadata)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Enums;
|
||||
@@ -24,7 +26,9 @@ public class SsoConfigService : ISsoConfigService
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly ISavePolicyCommand _savePolicyCommand;
|
||||
private readonly IVNextSavePolicyCommand _vNextSavePolicyCommand;
|
||||
|
||||
public SsoConfigService(
|
||||
ISsoConfigRepository ssoConfigRepository,
|
||||
@@ -32,14 +36,18 @@ public class SsoConfigService : ISsoConfigService
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IEventService eventService,
|
||||
ISavePolicyCommand savePolicyCommand)
|
||||
IFeatureService featureService,
|
||||
ISavePolicyCommand savePolicyCommand,
|
||||
IVNextSavePolicyCommand vNextSavePolicyCommand)
|
||||
{
|
||||
_ssoConfigRepository = ssoConfigRepository;
|
||||
_policyRepository = policyRepository;
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_eventService = eventService;
|
||||
_featureService = featureService;
|
||||
_savePolicyCommand = savePolicyCommand;
|
||||
_vNextSavePolicyCommand = vNextSavePolicyCommand;
|
||||
}
|
||||
|
||||
public async Task SaveAsync(SsoConfig config, Organization organization)
|
||||
@@ -67,13 +75,12 @@ public class SsoConfigService : ISsoConfigService
|
||||
// Automatically enable account recovery, SSO required, and single org policies if trusted device encryption is selected
|
||||
if (config.GetData().MemberDecryptionType == MemberDecryptionType.TrustedDeviceEncryption)
|
||||
{
|
||||
|
||||
await _savePolicyCommand.SaveAsync(new()
|
||||
var singleOrgPolicy = new PolicyUpdate
|
||||
{
|
||||
OrganizationId = config.OrganizationId,
|
||||
Type = PolicyType.SingleOrg,
|
||||
Enabled = true
|
||||
});
|
||||
};
|
||||
|
||||
var resetPasswordPolicy = new PolicyUpdate
|
||||
{
|
||||
@@ -82,14 +89,27 @@ public class SsoConfigService : ISsoConfigService
|
||||
Enabled = true,
|
||||
};
|
||||
resetPasswordPolicy.SetDataModel(new ResetPasswordDataModel { AutoEnrollEnabled = true });
|
||||
await _savePolicyCommand.SaveAsync(resetPasswordPolicy);
|
||||
|
||||
await _savePolicyCommand.SaveAsync(new()
|
||||
var requireSsoPolicy = new PolicyUpdate
|
||||
{
|
||||
OrganizationId = config.OrganizationId,
|
||||
Type = PolicyType.RequireSso,
|
||||
Enabled = true
|
||||
});
|
||||
};
|
||||
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.PolicyValidatorsRefactor))
|
||||
{
|
||||
var performedBy = new SystemUser(EventSystemUser.Unknown);
|
||||
await _vNextSavePolicyCommand.SaveAsync(new SavePolicyModel(singleOrgPolicy, performedBy));
|
||||
await _vNextSavePolicyCommand.SaveAsync(new SavePolicyModel(resetPasswordPolicy, performedBy));
|
||||
await _vNextSavePolicyCommand.SaveAsync(new SavePolicyModel(requireSsoPolicy, performedBy));
|
||||
}
|
||||
else
|
||||
{
|
||||
await _savePolicyCommand.SaveAsync(singleOrgPolicy);
|
||||
await _savePolicyCommand.SaveAsync(resetPasswordPolicy);
|
||||
await _savePolicyCommand.SaveAsync(requireSsoPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
await LogEventsAsync(config, oldConfig);
|
||||
|
||||
@@ -143,6 +143,7 @@ public static class FeatureFlagKeys
|
||||
public const string AutomaticConfirmUsers = "pm-19934-auto-confirm-organization-users";
|
||||
public const string PM23845_VNextApplicationCache = "pm-24957-refactor-memory-application-cache";
|
||||
public const string AccountRecoveryCommand = "pm-25581-prevent-provider-account-recovery";
|
||||
public const string PolicyValidatorsRefactor = "pm-26423-refactor-policy-side-effects";
|
||||
|
||||
/* Auth Team */
|
||||
public const string TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence";
|
||||
|
||||
Reference in New Issue
Block a user