diff --git a/bitwarden_license/src/Sso/Controllers/AccountController.cs b/bitwarden_license/src/Sso/Controllers/AccountController.cs index dde2ac7a46..3d998b6a75 100644 --- a/bitwarden_license/src/Sso/Controllers/AccountController.cs +++ b/bitwarden_license/src/Sso/Controllers/AccountController.cs @@ -2,7 +2,7 @@ using Bit.Core; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; -using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.Auth.Entities; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models; @@ -45,7 +45,7 @@ public class AccountController : Controller private readonly ISsoConfigRepository _ssoConfigRepository; private readonly ISsoUserRepository _ssoUserRepository; private readonly IUserRepository _userRepository; - private readonly IPolicyRepository _policyRepository; + private readonly IPolicyQuery _policyQuery; private readonly IUserService _userService; private readonly II18nService _i18nService; private readonly UserManager _userManager; @@ -67,7 +67,7 @@ public class AccountController : Controller ISsoConfigRepository ssoConfigRepository, ISsoUserRepository ssoUserRepository, IUserRepository userRepository, - IPolicyRepository policyRepository, + IPolicyQuery policyQuery, IUserService userService, II18nService i18nService, UserManager userManager, @@ -88,7 +88,7 @@ public class AccountController : Controller _userRepository = userRepository; _ssoConfigRepository = ssoConfigRepository; _ssoUserRepository = ssoUserRepository; - _policyRepository = policyRepository; + _policyQuery = policyQuery; _userService = userService; _i18nService = i18nService; _userManager = userManager; @@ -687,9 +687,8 @@ public class AccountController : Controller await _registerUserCommand.RegisterSSOAutoProvisionedUserAsync(newUser, organization); // If the organization has 2fa policy enabled, make sure to default jit user 2fa to email - var twoFactorPolicy = - await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.TwoFactorAuthentication); - if (twoFactorPolicy != null && twoFactorPolicy.Enabled) + var twoFactorPolicy = await _policyQuery.RunAsync(organization.Id, PolicyType.TwoFactorAuthentication); + if (twoFactorPolicy.Enabled) { newUser.SetTwoFactorProviders(new Dictionary { diff --git a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs index 024c54a48e..37b58bc252 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs @@ -57,7 +57,7 @@ public class OrganizationUsersController : BaseAdminConsoleController private readonly ICollectionRepository _collectionRepository; private readonly IGroupRepository _groupRepository; private readonly IUserService _userService; - private readonly IPolicyRepository _policyRepository; + private readonly IPolicyQuery _policyQuery; private readonly ICurrentContext _currentContext; private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery; private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; @@ -90,7 +90,7 @@ public class OrganizationUsersController : BaseAdminConsoleController ICollectionRepository collectionRepository, IGroupRepository groupRepository, IUserService userService, - IPolicyRepository policyRepository, + IPolicyQuery policyQuery, ICurrentContext currentContext, ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery, IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand, @@ -123,7 +123,7 @@ public class OrganizationUsersController : BaseAdminConsoleController _collectionRepository = collectionRepository; _groupRepository = groupRepository; _userService = userService; - _policyRepository = policyRepository; + _policyQuery = policyQuery; _currentContext = currentContext; _countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery; _updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand; @@ -350,10 +350,9 @@ public class OrganizationUsersController : BaseAdminConsoleController return false; } - var masterPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); - var useMasterPasswordPolicy = masterPasswordPolicy != null && - masterPasswordPolicy.Enabled && - masterPasswordPolicy.GetDataModel().AutoEnrollEnabled; + var masterPasswordPolicy = await _policyQuery.RunAsync(orgId, PolicyType.ResetPassword); + var useMasterPasswordPolicy = masterPasswordPolicy.Enabled && + masterPasswordPolicy.GetDataModel().AutoEnrollEnabled; return useMasterPasswordPolicy; } diff --git a/src/Api/AdminConsole/Controllers/OrganizationsController.cs b/src/Api/AdminConsole/Controllers/OrganizationsController.cs index 100cd7caf6..a6de8c521f 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationsController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationsController.cs @@ -48,7 +48,7 @@ public class OrganizationsController : Controller { private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationUserRepository _organizationUserRepository; - private readonly IPolicyRepository _policyRepository; + private readonly IPolicyQuery _policyQuery; private readonly IOrganizationService _organizationService; private readonly IUserService _userService; private readonly ICurrentContext _currentContext; @@ -74,7 +74,7 @@ public class OrganizationsController : Controller public OrganizationsController( IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, - IPolicyRepository policyRepository, + IPolicyQuery policyQuery, IOrganizationService organizationService, IUserService userService, ICurrentContext currentContext, @@ -99,7 +99,7 @@ public class OrganizationsController : Controller { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; - _policyRepository = policyRepository; + _policyQuery = policyQuery; _organizationService = organizationService; _userService = userService; _currentContext = currentContext; @@ -183,15 +183,14 @@ public class OrganizationsController : Controller return new OrganizationAutoEnrollStatusResponseModel(organization.Id, resetPasswordPolicyRequirement.AutoEnrollEnabled(organization.Id)); } - var resetPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword); - if (resetPasswordPolicy == null || !resetPasswordPolicy.Enabled || resetPasswordPolicy.Data == null) + var resetPasswordPolicy = await _policyQuery.RunAsync(organization.Id, PolicyType.ResetPassword); + if (!resetPasswordPolicy.Enabled || resetPasswordPolicy.Data == null) { return new OrganizationAutoEnrollStatusResponseModel(organization.Id, false); } var data = JsonSerializer.Deserialize(resetPasswordPolicy.Data, JsonHelpers.IgnoreCase); return new OrganizationAutoEnrollStatusResponseModel(organization.Id, data?.AutoEnrollEnabled ?? false); - } [HttpPost("")] diff --git a/src/Api/AdminConsole/Controllers/PoliciesController.cs b/src/Api/AdminConsole/Controllers/PoliciesController.cs index bce0332d67..fe3600c3dd 100644 --- a/src/Api/AdminConsole/Controllers/PoliciesController.cs +++ b/src/Api/AdminConsole/Controllers/PoliciesController.cs @@ -7,7 +7,6 @@ using Bit.Api.AdminConsole.Models.Request; using Bit.Api.AdminConsole.Models.Response.Helpers; using Bit.Api.AdminConsole.Models.Response.Organizations; using Bit.Api.Models.Response; -using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; @@ -43,6 +42,7 @@ public class PoliciesController : Controller private readonly IUserService _userService; private readonly ISavePolicyCommand _savePolicyCommand; private readonly IVNextSavePolicyCommand _vNextSavePolicyCommand; + private readonly IPolicyQuery _policyQuery; public PoliciesController(IPolicyRepository policyRepository, IOrganizationUserRepository organizationUserRepository, @@ -54,7 +54,8 @@ public class PoliciesController : Controller IOrganizationHasVerifiedDomainsQuery organizationHasVerifiedDomainsQuery, IOrganizationRepository organizationRepository, ISavePolicyCommand savePolicyCommand, - IVNextSavePolicyCommand vNextSavePolicyCommand) + IVNextSavePolicyCommand vNextSavePolicyCommand, + IPolicyQuery policyQuery) { _policyRepository = policyRepository; _organizationUserRepository = organizationUserRepository; @@ -68,27 +69,24 @@ public class PoliciesController : Controller _organizationHasVerifiedDomainsQuery = organizationHasVerifiedDomainsQuery; _savePolicyCommand = savePolicyCommand; _vNextSavePolicyCommand = vNextSavePolicyCommand; + _policyQuery = policyQuery; } [HttpGet("{type}")] - public async Task Get(Guid orgId, int type) + public async Task Get(Guid orgId, PolicyType type) { if (!await _currentContext.ManagePolicies(orgId)) { throw new NotFoundException(); } - var policy = await _policyRepository.GetByOrganizationIdTypeAsync(orgId, (PolicyType)type); - if (policy == null) - { - return new PolicyDetailResponseModel(new Policy { Type = (PolicyType)type }); - } + var policy = await _policyQuery.RunAsync(orgId, type); if (policy.Type is PolicyType.SingleOrg) { - return await policy.GetSingleOrgPolicyDetailResponseAsync(_organizationHasVerifiedDomainsQuery); + return await policy.GetSingleOrgPolicyStatusResponseAsync(_organizationHasVerifiedDomainsQuery); } - return new PolicyDetailResponseModel(policy); + return new PolicyStatusResponseModel(policy); } [HttpGet("")] diff --git a/src/Api/AdminConsole/Models/Response/Helpers/PolicyDetailResponses.cs b/src/Api/AdminConsole/Models/Response/Helpers/PolicyStatusResponses.cs similarity index 66% rename from src/Api/AdminConsole/Models/Response/Helpers/PolicyDetailResponses.cs rename to src/Api/AdminConsole/Models/Response/Helpers/PolicyStatusResponses.cs index dded6a4c89..da08cdef0f 100644 --- a/src/Api/AdminConsole/Models/Response/Helpers/PolicyDetailResponses.cs +++ b/src/Api/AdminConsole/Models/Response/Helpers/PolicyStatusResponses.cs @@ -1,19 +1,21 @@ using Bit.Api.AdminConsole.Models.Response.Organizations; -using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces; namespace Bit.Api.AdminConsole.Models.Response.Helpers; -public static class PolicyDetailResponses +public static class PolicyStatusResponses { - public static async Task GetSingleOrgPolicyDetailResponseAsync(this Policy policy, IOrganizationHasVerifiedDomainsQuery hasVerifiedDomainsQuery) + public static async Task GetSingleOrgPolicyStatusResponseAsync( + this PolicyStatus policy, IOrganizationHasVerifiedDomainsQuery hasVerifiedDomainsQuery) { if (policy.Type is not PolicyType.SingleOrg) { throw new ArgumentException($"'{nameof(policy)}' must be of type '{nameof(PolicyType.SingleOrg)}'.", nameof(policy)); } - return new PolicyDetailResponseModel(policy, await CanToggleState()); + + return new PolicyStatusResponseModel(policy, await CanToggleState()); async Task CanToggleState() { @@ -25,5 +27,4 @@ public static class PolicyDetailResponses return !policy.Enabled; } } - } diff --git a/src/Api/AdminConsole/Models/Response/Organizations/PolicyDetailResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/PolicyDetailResponseModel.cs deleted file mode 100644 index cb5560e689..0000000000 --- a/src/Api/AdminConsole/Models/Response/Organizations/PolicyDetailResponseModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Bit.Core.AdminConsole.Entities; - -namespace Bit.Api.AdminConsole.Models.Response.Organizations; - -public class PolicyDetailResponseModel : PolicyResponseModel -{ - public PolicyDetailResponseModel(Policy policy, string obj = "policy") : base(policy, obj) - { - } - - public PolicyDetailResponseModel(Policy policy, bool canToggleState) : base(policy) - { - CanToggleState = canToggleState; - } - - /// - /// Indicates whether the Policy can be enabled/disabled - /// - public bool CanToggleState { get; set; } = true; -} diff --git a/src/Api/AdminConsole/Models/Response/Organizations/PolicyStatusResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/PolicyStatusResponseModel.cs new file mode 100644 index 0000000000..8c93302a17 --- /dev/null +++ b/src/Api/AdminConsole/Models/Response/Organizations/PolicyStatusResponseModel.cs @@ -0,0 +1,33 @@ +using System.Text.Json; +using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; +using Bit.Core.Models.Api; + +namespace Bit.Api.AdminConsole.Models.Response.Organizations; + +public class PolicyStatusResponseModel : ResponseModel +{ + public PolicyStatusResponseModel(PolicyStatus policy, bool canToggleState = true) : base("policy") + { + OrganizationId = policy.OrganizationId; + Type = policy.Type; + + if (!string.IsNullOrWhiteSpace(policy.Data)) + { + Data = JsonSerializer.Deserialize>(policy.Data) ?? new(); + } + + Enabled = policy.Enabled; + CanToggleState = canToggleState; + } + + public Guid OrganizationId { get; init; } + public PolicyType Type { get; init; } + public Dictionary Data { get; init; } = new(); + public bool Enabled { get; init; } + + /// + /// Indicates whether the Policy can be enabled/disabled + /// + public bool CanToggleState { get; init; } +} diff --git a/src/Api/Billing/Controllers/OrganizationSponsorshipsController.cs b/src/Api/Billing/Controllers/OrganizationSponsorshipsController.cs index 7ca85d52a8..8a1467dfa2 100644 --- a/src/Api/Billing/Controllers/OrganizationSponsorshipsController.cs +++ b/src/Api/Billing/Controllers/OrganizationSponsorshipsController.cs @@ -6,7 +6,7 @@ using Bit.Api.Models.Response; using Bit.Api.Models.Response.Organizations; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationConnections.Interfaces; -using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Exceptions; @@ -38,7 +38,7 @@ public class OrganizationSponsorshipsController : Controller private readonly ICloudSyncSponsorshipsCommand _syncSponsorshipsCommand; private readonly ICurrentContext _currentContext; private readonly IUserService _userService; - private readonly IPolicyRepository _policyRepository; + private readonly IPolicyQuery _policyQuery; private readonly IFeatureService _featureService; public OrganizationSponsorshipsController( @@ -55,7 +55,7 @@ public class OrganizationSponsorshipsController : Controller ICloudSyncSponsorshipsCommand syncSponsorshipsCommand, IUserService userService, ICurrentContext currentContext, - IPolicyRepository policyRepository, + IPolicyQuery policyQuery, IFeatureService featureService) { _organizationSponsorshipRepository = organizationSponsorshipRepository; @@ -71,7 +71,7 @@ public class OrganizationSponsorshipsController : Controller _syncSponsorshipsCommand = syncSponsorshipsCommand; _userService = userService; _currentContext = currentContext; - _policyRepository = policyRepository; + _policyQuery = policyQuery; _featureService = featureService; } @@ -81,10 +81,10 @@ public class OrganizationSponsorshipsController : Controller public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model) { var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId); - var freeFamiliesSponsorshipPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(sponsoringOrgId, + var freeFamiliesSponsorshipPolicy = await _policyQuery.RunAsync(sponsoringOrgId, PolicyType.FreeFamiliesSponsorshipPolicy); - if (freeFamiliesSponsorshipPolicy?.Enabled == true) + if (freeFamiliesSponsorshipPolicy.Enabled) { throw new BadRequestException("Free Bitwarden Families sponsorship has been disabled by your organization administrator."); } @@ -108,10 +108,10 @@ public class OrganizationSponsorshipsController : Controller [SelfHosted(NotSelfHostedOnly = true)] public async Task ResendSponsorshipOffer(Guid sponsoringOrgId, [FromQuery] string sponsoredFriendlyName) { - var freeFamiliesSponsorshipPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(sponsoringOrgId, + var freeFamiliesSponsorshipPolicy = await _policyQuery.RunAsync(sponsoringOrgId, PolicyType.FreeFamiliesSponsorshipPolicy); - if (freeFamiliesSponsorshipPolicy?.Enabled == true) + if (freeFamiliesSponsorshipPolicy.Enabled) { throw new BadRequestException("Free Bitwarden Families sponsorship has been disabled by your organization administrator."); } @@ -138,9 +138,9 @@ public class OrganizationSponsorshipsController : Controller var (isValid, sponsorship) = await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email); if (isValid && sponsorship.SponsoringOrganizationId.HasValue) { - var policy = await _policyRepository.GetByOrganizationIdTypeAsync(sponsorship.SponsoringOrganizationId.Value, + var policy = await _policyQuery.RunAsync(sponsorship.SponsoringOrganizationId.Value, PolicyType.FreeFamiliesSponsorshipPolicy); - isFreeFamilyPolicyEnabled = policy?.Enabled ?? false; + isFreeFamilyPolicyEnabled = policy.Enabled; } var response = PreValidateSponsorshipResponseModel.From(isValid, isFreeFamilyPolicyEnabled); @@ -165,10 +165,10 @@ public class OrganizationSponsorshipsController : Controller throw new BadRequestException("Can only redeem sponsorship for an organization you own."); } - var freeFamiliesSponsorshipPolicy = await _policyRepository.GetByOrganizationIdTypeAsync( + var freeFamiliesSponsorshipPolicy = await _policyQuery.RunAsync( model.SponsoredOrganizationId, PolicyType.FreeFamiliesSponsorshipPolicy); - if (freeFamiliesSponsorshipPolicy?.Enabled == true) + if (freeFamiliesSponsorshipPolicy.Enabled) { throw new BadRequestException("Free Bitwarden Families sponsorship has been disabled by your organization administrator."); } diff --git a/src/Core/AdminConsole/Models/Data/Organizations/Policies/PolicyStatus.cs b/src/Core/AdminConsole/Models/Data/Organizations/Policies/PolicyStatus.cs new file mode 100644 index 0000000000..68c754f6ba --- /dev/null +++ b/src/Core/AdminConsole/Models/Data/Organizations/Policies/PolicyStatus.cs @@ -0,0 +1,26 @@ +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Enums; +using Bit.Core.Utilities; + +namespace Bit.Core.AdminConsole.Models.Data.Organizations.Policies; + +public class PolicyStatus +{ + public PolicyStatus(Guid organizationId, PolicyType policyType, Policy? policy = null) + { + OrganizationId = policy?.OrganizationId ?? organizationId; + Data = policy?.Data; + Type = policy?.Type ?? policyType; + Enabled = policy?.Enabled ?? false; + } + + public Guid OrganizationId { get; set; } + public PolicyType Type { get; set; } + public bool Enabled { get; set; } + public string? Data { get; set; } + + public T GetDataModel() where T : IPolicyDataModel, new() + { + return CoreHelpers.LoadClassFromJsonData(Data); + } +} diff --git a/src/Core/AdminConsole/OrganizationFeatures/AccountRecovery/AdminRecoverAccountCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/AccountRecovery/AdminRecoverAccountCommand.cs index 5783301a0b..bd30112945 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/AccountRecovery/AdminRecoverAccountCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/AccountRecovery/AdminRecoverAccountCommand.cs @@ -1,5 +1,5 @@ using Bit.Core.AdminConsole.Enums; -using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Identity; namespace Bit.Core.AdminConsole.OrganizationFeatures.AccountRecovery; public class AdminRecoverAccountCommand(IOrganizationRepository organizationRepository, - IPolicyRepository policyRepository, + IPolicyQuery policyQuery, IUserRepository userRepository, IMailService mailService, IEventService eventService, @@ -30,9 +30,8 @@ public class AdminRecoverAccountCommand(IOrganizationRepository organizationRepo } // Enterprise policy must be enabled - var resetPasswordPolicy = - await policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); - if (resetPasswordPolicy == null || !resetPasswordPolicy.Enabled) + var resetPasswordPolicy = await policyQuery.RunAsync(orgId, PolicyType.ResetPassword); + if (!resetPasswordPolicy.Enabled) { throw new BadRequestException("Organization does not have the password reset policy enabled."); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUser/AutomaticallyConfirmOrganizationUsersValidator.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUser/AutomaticallyConfirmOrganizationUsersValidator.cs index 3375120516..f067f529ea 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUser/AutomaticallyConfirmOrganizationUsersValidator.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUser/AutomaticallyConfirmOrganizationUsersValidator.cs @@ -3,7 +3,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimed using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; -using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Utilities.v2; using Bit.Core.AdminConsole.Utilities.v2.Validation; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; @@ -20,7 +19,7 @@ public class AutomaticallyConfirmOrganizationUsersValidator( IPolicyRequirementQuery policyRequirementQuery, IAutomaticUserConfirmationPolicyEnforcementValidator automaticUserConfirmationPolicyEnforcementValidator, IUserService userService, - IPolicyRepository policyRepository) : IAutomaticallyConfirmOrganizationUsersValidator + IPolicyQuery policyQuery) : IAutomaticallyConfirmOrganizationUsersValidator { public async Task> ValidateAsync( AutomaticallyConfirmOrganizationUserValidationRequest request) @@ -74,7 +73,7 @@ public class AutomaticallyConfirmOrganizationUsersValidator( } private async Task OrganizationHasAutomaticallyConfirmUsersPolicyEnabledAsync(AutomaticallyConfirmOrganizationUserValidationRequest request) => - await policyRepository.GetByOrganizationIdTypeAsync(request.OrganizationId, PolicyType.AutomaticUserConfirmation) is { Enabled: true } + (await policyQuery.RunAsync(request.OrganizationId, PolicyType.AutomaticUserConfirmation)).Enabled && request.Organization is { UseAutomaticUserConfirmation: true }; private async Task OrganizationUserConformsToTwoFactorRequiredPolicyAsync(AutomaticallyConfirmOrganizationUserValidationRequest request) diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/SendOrganizationInvitesCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/SendOrganizationInvitesCommand.cs index cd5066d11b..61f428414f 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/SendOrganizationInvitesCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/SendOrganizationInvitesCommand.cs @@ -4,7 +4,7 @@ using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; -using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.Auth.Models.Business; using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Repositories; @@ -19,7 +19,7 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUse public class SendOrganizationInvitesCommand( IUserRepository userRepository, ISsoConfigRepository ssoConfigurationRepository, - IPolicyRepository policyRepository, + IPolicyQuery policyQuery, IOrgUserInviteTokenableFactory orgUserInviteTokenableFactory, IDataProtectorTokenFactory dataProtectorTokenFactory, IMailService mailService) : ISendOrganizationInvitesCommand @@ -58,7 +58,7 @@ public class SendOrganizationInvitesCommand( // need to check the policy if the org has SSO enabled. var orgSsoLoginRequiredPolicyEnabled = orgSsoEnabled && organization.UsePolicies && - (await policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.RequireSso))?.Enabled == true; + (await policyQuery.RunAsync(organization.Id, PolicyType.RequireSso)).Enabled; // Generate the list of org users and expiring tokens // create helper function to create expiring tokens diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/IPolicyQuery.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/IPolicyQuery.cs new file mode 100644 index 0000000000..02eeeaa847 --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/IPolicyQuery.cs @@ -0,0 +1,17 @@ +using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; + +namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies; + +public interface IPolicyQuery +{ + /// + /// Retrieves a summary view of an organization's usage of a policy specified by the . + /// + /// + /// This query is the entrypoint for consumers interested in understanding how a particular + /// has been applied to an organization; the resultant is not indicative of explicit + /// policy configuration. + /// + Task RunAsync(Guid organizationId, PolicyType policyType); +} diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/PolicyQuery.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/PolicyQuery.cs new file mode 100644 index 0000000000..0ee6f9ab06 --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/PolicyQuery.cs @@ -0,0 +1,14 @@ +using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; +using Bit.Core.AdminConsole.Repositories; + +namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations; + +public class PolicyQuery(IPolicyRepository policyRepository) : IPolicyQuery +{ + public async Task RunAsync(Guid organizationId, PolicyType policyType) + { + var dbPolicy = await policyRepository.GetByOrganizationIdTypeAsync(organizationId, policyType); + return new PolicyStatus(organizationId, policyType, dbPolicy); + } +} diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyServiceCollectionExtensions.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyServiceCollectionExtensions.cs index f69935715d..6e0c3aa8d9 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyServiceCollectionExtensions.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyServiceCollectionExtensions.cs @@ -18,6 +18,7 @@ public static class PolicyServiceCollectionExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddPolicyValidators(); diff --git a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs index b51842398d..d87bc65042 100644 --- a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs +++ b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs @@ -48,7 +48,7 @@ public class OrganizationService : IOrganizationService private readonly IEventService _eventService; private readonly IApplicationCacheService _applicationCacheService; private readonly IStripePaymentService _paymentService; - private readonly IPolicyRepository _policyRepository; + private readonly IPolicyQuery _policyQuery; private readonly IPolicyService _policyService; private readonly ISsoUserRepository _ssoUserRepository; private readonly IGlobalSettings _globalSettings; @@ -75,7 +75,7 @@ public class OrganizationService : IOrganizationService IEventService eventService, IApplicationCacheService applicationCacheService, IStripePaymentService paymentService, - IPolicyRepository policyRepository, + IPolicyQuery policyQuery, IPolicyService policyService, ISsoUserRepository ssoUserRepository, IGlobalSettings globalSettings, @@ -102,7 +102,7 @@ public class OrganizationService : IOrganizationService _eventService = eventService; _applicationCacheService = applicationCacheService; _paymentService = paymentService; - _policyRepository = policyRepository; + _policyQuery = policyQuery; _policyService = policyService; _ssoUserRepository = ssoUserRepository; _globalSettings = globalSettings; @@ -835,9 +835,8 @@ public class OrganizationService : IOrganizationService } // Make sure the organization has the policy enabled - var resetPasswordPolicy = - await _policyRepository.GetByOrganizationIdTypeAsync(organizationId, PolicyType.ResetPassword); - if (resetPasswordPolicy == null || !resetPasswordPolicy.Enabled) + var resetPasswordPolicy = await _policyQuery.RunAsync(organizationId, PolicyType.ResetPassword); + if (!resetPasswordPolicy.Enabled) { throw new BadRequestException("Organization does not have the password reset policy enabled."); } diff --git a/src/Core/Auth/Services/Implementations/SsoConfigService.cs b/src/Core/Auth/Services/Implementations/SsoConfigService.cs index 0cb8b68042..3c4f1ef85d 100644 --- a/src/Core/Auth/Services/Implementations/SsoConfigService.cs +++ b/src/Core/Auth/Services/Implementations/SsoConfigService.cs @@ -5,9 +5,9 @@ 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; using Bit.Core.Auth.Repositories; @@ -21,7 +21,7 @@ namespace Bit.Core.Auth.Services; public class SsoConfigService : ISsoConfigService { private readonly ISsoConfigRepository _ssoConfigRepository; - private readonly IPolicyRepository _policyRepository; + private readonly IPolicyQuery _policyQuery; private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IEventService _eventService; @@ -29,14 +29,14 @@ public class SsoConfigService : ISsoConfigService public SsoConfigService( ISsoConfigRepository ssoConfigRepository, - IPolicyRepository policyRepository, + IPolicyQuery policyQuery, IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, IEventService eventService, IVNextSavePolicyCommand vNextSavePolicyCommand) { _ssoConfigRepository = ssoConfigRepository; - _policyRepository = policyRepository; + _policyQuery = policyQuery; _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; _eventService = eventService; @@ -114,14 +114,14 @@ public class SsoConfigService : ISsoConfigService throw new BadRequestException("Organization cannot use Key Connector."); } - var singleOrgPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.SingleOrg); - if (singleOrgPolicy is not { Enabled: true }) + var singleOrgPolicy = await _policyQuery.RunAsync(config.OrganizationId, PolicyType.SingleOrg); + if (!singleOrgPolicy.Enabled) { throw new BadRequestException("Key Connector requires the Single Organization policy to be enabled."); } - var ssoPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.RequireSso); - if (ssoPolicy is not { Enabled: true }) + var ssoPolicy = await _policyQuery.RunAsync(config.OrganizationId, PolicyType.RequireSso); + if (!ssoPolicy.Enabled) { throw new BadRequestException("Key Connector requires the Single Sign-On Authentication policy to be enabled."); } diff --git a/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs b/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs index 4a0e9c2cf5..ba63afb54c 100644 --- a/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs +++ b/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs @@ -1,6 +1,6 @@ using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; -using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models; using Bit.Core.Auth.Models.Business.Tokenables; @@ -27,7 +27,7 @@ public class RegisterUserCommand : IRegisterUserCommand private readonly IGlobalSettings _globalSettings; private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationRepository _organizationRepository; - private readonly IPolicyRepository _policyRepository; + private readonly IPolicyQuery _policyQuery; private readonly IOrganizationDomainRepository _organizationDomainRepository; private readonly IFeatureService _featureService; @@ -50,7 +50,7 @@ public class RegisterUserCommand : IRegisterUserCommand IGlobalSettings globalSettings, IOrganizationUserRepository organizationUserRepository, IOrganizationRepository organizationRepository, - IPolicyRepository policyRepository, + IPolicyQuery policyQuery, IOrganizationDomainRepository organizationDomainRepository, IFeatureService featureService, IDataProtectionProvider dataProtectionProvider, @@ -65,7 +65,7 @@ public class RegisterUserCommand : IRegisterUserCommand _globalSettings = globalSettings; _organizationUserRepository = organizationUserRepository; _organizationRepository = organizationRepository; - _policyRepository = policyRepository; + _policyQuery = policyQuery; _organizationDomainRepository = organizationDomainRepository; _featureService = featureService; @@ -246,9 +246,9 @@ public class RegisterUserCommand : IRegisterUserCommand var orgUser = await _organizationUserRepository.GetByIdAsync(orgUserId.Value); if (orgUser != null) { - var twoFactorPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgUser.OrganizationId, + var twoFactorPolicy = await _policyQuery.RunAsync(orgUser.OrganizationId, PolicyType.TwoFactorAuthentication); - if (twoFactorPolicy != null && twoFactorPolicy.Enabled) + if (twoFactorPolicy.Enabled) { user.SetTwoFactorProviders(new Dictionary { diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs index 4ad63bd8d7..9c06ce1709 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs @@ -5,6 +5,7 @@ using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Models.OrganizationConnectionConfigs; using Bit.Core.AdminConsole.OrganizationFeatures.Organizations; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Repositories; @@ -30,6 +31,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand private readonly IGroupRepository _groupRepository; private readonly IStripePaymentService _paymentService; private readonly IPolicyRepository _policyRepository; + private readonly IPolicyQuery _policyQuery; private readonly ISsoConfigRepository _ssoConfigRepository; private readonly IOrganizationConnectionRepository _organizationConnectionRepository; private readonly IServiceAccountRepository _serviceAccountRepository; @@ -45,6 +47,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand IGroupRepository groupRepository, IStripePaymentService paymentService, IPolicyRepository policyRepository, + IPolicyQuery policyQuery, ISsoConfigRepository ssoConfigRepository, IOrganizationConnectionRepository organizationConnectionRepository, IServiceAccountRepository serviceAccountRepository, @@ -59,6 +62,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand _groupRepository = groupRepository; _paymentService = paymentService; _policyRepository = policyRepository; + _policyQuery = policyQuery; _ssoConfigRepository = ssoConfigRepository; _organizationConnectionRepository = organizationConnectionRepository; _serviceAccountRepository = serviceAccountRepository; @@ -184,9 +188,8 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand if (!newPlan.HasResetPassword && organization.UseResetPassword) { - var resetPasswordPolicy = - await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword); - if (resetPasswordPolicy != null && resetPasswordPolicy.Enabled) + var resetPasswordPolicy = await _policyQuery.RunAsync(organization.Id, PolicyType.ResetPassword); + if (resetPasswordPolicy.Enabled) { throw new BadRequestException("Your new plan does not allow the Password Reset feature. " + "Disable your Password Reset policy."); diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index 64caf1d462..5f87ee85d2 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -61,7 +61,7 @@ public class UserService : UserManager, IUserService private readonly IEventService _eventService; private readonly IApplicationCacheService _applicationCacheService; private readonly IStripePaymentService _paymentService; - private readonly IPolicyRepository _policyRepository; + private readonly IPolicyQuery _policyQuery; private readonly IPolicyService _policyService; private readonly IFido2 _fido2; private readonly ICurrentContext _currentContext; @@ -98,7 +98,7 @@ public class UserService : UserManager, IUserService IEventService eventService, IApplicationCacheService applicationCacheService, IStripePaymentService paymentService, - IPolicyRepository policyRepository, + IPolicyQuery policyQuery, IPolicyService policyService, IFido2 fido2, ICurrentContext currentContext, @@ -139,7 +139,7 @@ public class UserService : UserManager, IUserService _eventService = eventService; _applicationCacheService = applicationCacheService; _paymentService = paymentService; - _policyRepository = policyRepository; + _policyQuery = policyQuery; _policyService = policyService; _fido2 = fido2; _currentContext = currentContext; @@ -722,9 +722,8 @@ public class UserService : UserManager, IUserService } // Enterprise policy must be enabled - var resetPasswordPolicy = - await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); - if (resetPasswordPolicy == null || !resetPasswordPolicy.Enabled) + var resetPasswordPolicy = await _policyQuery.RunAsync(orgId, PolicyType.ResetPassword); + if (!resetPasswordPolicy.Enabled) { throw new BadRequestException("Organization does not have the password reset policy enabled."); } diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs index d97a1be793..68a63bf579 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs @@ -14,7 +14,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; -using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Utilities.v2.Results; using Bit.Core.Auth.Entities; using Bit.Core.Auth.Repositories; @@ -30,6 +29,7 @@ using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; +using Bit.Core.Test.AdminConsole.AutoFixture; using Bit.Core.Utilities; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -137,23 +137,20 @@ public class OrganizationUsersControllerTests [Theory] [BitAutoData] public async Task Accept_WhenOrganizationUsePoliciesIsEnabledAndResetPolicyIsEnabled_ShouldHandleResetPassword(Guid orgId, Guid orgUserId, - OrganizationUserAcceptRequestModel model, User user, SutProvider sutProvider) + OrganizationUserAcceptRequestModel model, User user, + [Policy(PolicyType.ResetPassword, true)] PolicyStatus policy, + SutProvider sutProvider) { // Arrange var applicationCacheService = sutProvider.GetDependency(); applicationCacheService.GetOrganizationAbilityAsync(orgId).Returns(new OrganizationAbility { UsePolicies = true }); - var policy = new Policy - { - Enabled = true, - Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, }), - }; + policy.Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, }); var userService = sutProvider.GetDependency(); userService.GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); - - var policyRepository = sutProvider.GetDependency(); - policyRepository.GetByOrganizationIdTypeAsync(orgId, + var policyQuery = sutProvider.GetDependency(); + policyQuery.RunAsync(orgId, PolicyType.ResetPassword).Returns(policy); // Act @@ -167,29 +164,27 @@ public class OrganizationUsersControllerTests await userService.Received(1).GetUserByPrincipalAsync(default); await applicationCacheService.Received(1).GetOrganizationAbilityAsync(orgId); - await policyRepository.Received(1).GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); + await policyQuery.Received(1).RunAsync(orgId, PolicyType.ResetPassword); } [Theory] [BitAutoData] public async Task Accept_WhenOrganizationUsePoliciesIsDisabled_ShouldNotHandleResetPassword(Guid orgId, Guid orgUserId, - OrganizationUserAcceptRequestModel model, User user, SutProvider sutProvider) + OrganizationUserAcceptRequestModel model, User user, + [Policy(PolicyType.ResetPassword, true)] PolicyStatus policy, + SutProvider sutProvider) { // Arrange var applicationCacheService = sutProvider.GetDependency(); applicationCacheService.GetOrganizationAbilityAsync(orgId).Returns(new OrganizationAbility { UsePolicies = false }); - var policy = new Policy - { - Enabled = true, - Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, }), - }; + policy.Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, }); var userService = sutProvider.GetDependency(); userService.GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); - var policyRepository = sutProvider.GetDependency(); - policyRepository.GetByOrganizationIdTypeAsync(orgId, + var policyQuery = sutProvider.GetDependency(); + policyQuery.RunAsync(orgId, PolicyType.ResetPassword).Returns(policy); // Act @@ -202,7 +197,7 @@ public class OrganizationUsersControllerTests await sutProvider.GetDependency().Received(0) .UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id); - await policyRepository.Received(0).GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); + await policyQuery.Received(0).RunAsync(orgId, PolicyType.ResetPassword); await applicationCacheService.Received(1).GetOrganizationAbilityAsync(orgId); } @@ -383,7 +378,7 @@ public class OrganizationUsersControllerTests var policyRequirementQuery = sutProvider.GetDependency(); - var policyRepository = sutProvider.GetDependency(); + var policyQuery = sutProvider.GetDependency(); var policyRequirement = new ResetPasswordPolicyRequirement { AutoEnrollOrganizations = [orgId] }; @@ -400,7 +395,7 @@ public class OrganizationUsersControllerTests await userService.Received(1).GetUserByPrincipalAsync(default); await applicationCacheService.Received(0).GetOrganizationAbilityAsync(orgId); - await policyRepository.Received(0).GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); + await policyQuery.Received(0).RunAsync(orgId, PolicyType.ResetPassword); await policyRequirementQuery.Received(1).GetAsync(user.Id); Assert.True(policyRequirement.AutoEnrollEnabled(orgId)); } @@ -425,7 +420,7 @@ public class OrganizationUsersControllerTests var userService = sutProvider.GetDependency(); userService.GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); - var policyRepository = sutProvider.GetDependency(); + var policyQuery = sutProvider.GetDependency(); var policyRequirementQuery = sutProvider.GetDependency(); @@ -445,7 +440,7 @@ public class OrganizationUsersControllerTests await userService.Received(1).GetUserByPrincipalAsync(default); await applicationCacheService.Received(0).GetOrganizationAbilityAsync(orgId); - await policyRepository.Received(0).GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); + await policyQuery.Received(0).RunAsync(orgId, PolicyType.ResetPassword); await policyRequirementQuery.Received(1).GetAsync(user.Id); Assert.Equal("Master Password reset is required, but not provided.", exception.Message); diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs index d87f035a13..cc09e9e0a0 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs @@ -7,6 +7,7 @@ using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Models.Business; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; @@ -25,6 +26,7 @@ using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Repositories; using Bit.Core.Services; +using Bit.Core.Test.AdminConsole.AutoFixture; using Bit.Core.Test.Billing.Mocks; using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider; using Bit.Test.Common.AutoFixture; @@ -200,28 +202,21 @@ public class OrganizationsControllerTests SutProvider sutProvider, User user, Organization organization, - OrganizationUser organizationUser) + OrganizationUser organizationUser, + [Policy(PolicyType.ResetPassword, data: "{\"AutoEnrollEnabled\": true}")] PolicyStatus policy) { - var policy = new Policy - { - Type = PolicyType.ResetPassword, - Enabled = true, - Data = "{\"AutoEnrollEnabled\": true}", - OrganizationId = organization.Id - }; - sutProvider.GetDependency().GetUserByPrincipalAsync(Arg.Any()).Returns(user); sutProvider.GetDependency().GetByIdentifierAsync(organization.Id.ToString()).Returns(organization); sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.PolicyRequirements).Returns(false); sutProvider.GetDependency().GetByOrganizationAsync(organization.Id, user.Id).Returns(organizationUser); - sutProvider.GetDependency().GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword).Returns(policy); + sutProvider.GetDependency().RunAsync(organization.Id, PolicyType.ResetPassword).Returns(policy); var result = await sutProvider.Sut.GetAutoEnrollStatus(organization.Id.ToString()); await sutProvider.GetDependency().Received(1).GetUserByPrincipalAsync(Arg.Any()); await sutProvider.GetDependency().Received(1).GetByIdentifierAsync(organization.Id.ToString()); await sutProvider.GetDependency().Received(0).GetAsync(user.Id); - await sutProvider.GetDependency().Received(1).GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword); + await sutProvider.GetDependency().Received(1).RunAsync(organization.Id, PolicyType.ResetPassword); Assert.True(result.ResetPasswordEnabled); } diff --git a/test/Api.Test/AdminConsole/Models/Response/Helpers/PolicyDetailResponsesTests.cs b/test/Api.Test/AdminConsole/Models/Response/Helpers/PolicyStatusResponsesTests.cs similarity index 62% rename from test/Api.Test/AdminConsole/Models/Response/Helpers/PolicyDetailResponsesTests.cs rename to test/Api.Test/AdminConsole/Models/Response/Helpers/PolicyStatusResponsesTests.cs index 9b863091db..46c6d64bdd 100644 --- a/test/Api.Test/AdminConsole/Models/Response/Helpers/PolicyDetailResponsesTests.cs +++ b/test/Api.Test/AdminConsole/Models/Response/Helpers/PolicyStatusResponsesTests.cs @@ -1,14 +1,13 @@ -using AutoFixture; -using Bit.Api.AdminConsole.Models.Response.Helpers; -using Bit.Core.AdminConsole.Entities; +using Bit.Api.AdminConsole.Models.Response.Helpers; using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces; using NSubstitute; using Xunit; namespace Bit.Api.Test.AdminConsole.Models.Response.Helpers; -public class PolicyDetailResponsesTests +public class PolicyStatusResponsesTests { [Theory] [InlineData(true, false)] @@ -17,19 +16,13 @@ public class PolicyDetailResponsesTests bool policyEnabled, bool expectedCanToggle) { - var fixture = new Fixture(); - - var policy = fixture.Build() - .Without(p => p.Data) - .With(p => p.Type, PolicyType.SingleOrg) - .With(p => p.Enabled, policyEnabled) - .Create(); + var policy = new PolicyStatus(Guid.NewGuid(), PolicyType.SingleOrg) { Enabled = policyEnabled }; var querySub = Substitute.For(); querySub.HasVerifiedDomainsAsync(policy.OrganizationId) .Returns(true); - var result = await policy.GetSingleOrgPolicyDetailResponseAsync(querySub); + var result = await policy.GetSingleOrgPolicyStatusResponseAsync(querySub); Assert.Equal(expectedCanToggle, result.CanToggleState); } @@ -37,18 +30,13 @@ public class PolicyDetailResponsesTests [Fact] public async Task GetSingleOrgPolicyDetailResponseAsync_WhenIsNotSingleOrgType_ThenShouldThrowArgumentException() { - var fixture = new Fixture(); - - var policy = fixture.Build() - .Without(p => p.Data) - .With(p => p.Type, PolicyType.TwoFactorAuthentication) - .Create(); + var policy = new PolicyStatus(Guid.NewGuid(), PolicyType.TwoFactorAuthentication); var querySub = Substitute.For(); querySub.HasVerifiedDomainsAsync(policy.OrganizationId) .Returns(true); - var action = async () => await policy.GetSingleOrgPolicyDetailResponseAsync(querySub); + var action = async () => await policy.GetSingleOrgPolicyStatusResponseAsync(querySub); await Assert.ThrowsAsync("policy", action); } @@ -56,18 +44,13 @@ public class PolicyDetailResponsesTests [Fact] public async Task GetSingleOrgPolicyDetailResponseAsync_WhenIsSingleOrgTypeAndDoesNotHaveVerifiedDomains_ThenShouldBeAbleToToggle() { - var fixture = new Fixture(); - - var policy = fixture.Build() - .Without(p => p.Data) - .With(p => p.Type, PolicyType.SingleOrg) - .Create(); + var policy = new PolicyStatus(Guid.NewGuid(), PolicyType.SingleOrg); var querySub = Substitute.For(); querySub.HasVerifiedDomainsAsync(policy.OrganizationId) .Returns(false); - var result = await policy.GetSingleOrgPolicyDetailResponseAsync(querySub); + var result = await policy.GetSingleOrgPolicyStatusResponseAsync(querySub); Assert.True(result.CanToggleState); } diff --git a/test/Api.Test/Billing/Controllers/OrganizationSponsorshipsControllerTests.cs b/test/Api.Test/Billing/Controllers/OrganizationSponsorshipsControllerTests.cs index 87334dc085..a7eb4dda5e 100644 --- a/test/Api.Test/Billing/Controllers/OrganizationSponsorshipsControllerTests.cs +++ b/test/Api.Test/Billing/Controllers/OrganizationSponsorshipsControllerTests.cs @@ -1,6 +1,9 @@ using Bit.Api.Billing.Controllers; using Bit.Api.Models.Request.Organizations; using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.Billing.Enums; using Bit.Core.Context; using Bit.Core.Entities; @@ -10,6 +13,7 @@ using Bit.Core.Models.Data; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; +using Bit.Core.Test.AdminConsole.AutoFixture; using Bit.Core.Test.Billing.Mocks; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -82,7 +86,9 @@ public class OrganizationSponsorshipsControllerTests [BitAutoData] public async Task RedeemSponsorship_NotSponsoredOrgOwner_Success(string sponsorshipToken, User user, OrganizationSponsorship sponsorship, Organization sponsoringOrganization, - OrganizationSponsorshipRedeemRequestModel model, SutProvider sutProvider) + OrganizationSponsorshipRedeemRequestModel model, + [Policy(PolicyType.FreeFamiliesSponsorshipPolicy, false)] PolicyStatus policy, + SutProvider sutProvider) { sutProvider.GetDependency().UserId.Returns(user.Id); sutProvider.GetDependency().GetUserByIdAsync(user.Id) @@ -91,6 +97,9 @@ public class OrganizationSponsorshipsControllerTests user.Email).Returns((true, sponsorship)); sutProvider.GetDependency().OrganizationOwner(model.SponsoredOrganizationId).Returns(true); sutProvider.GetDependency().GetByIdAsync(model.SponsoredOrganizationId).Returns(sponsoringOrganization); + sutProvider.GetDependency() + .RunAsync(Arg.Any(), PolicyType.FreeFamiliesSponsorshipPolicy) + .Returns(policy); await sutProvider.Sut.RedeemSponsorship(sponsorshipToken, model); @@ -101,14 +110,18 @@ public class OrganizationSponsorshipsControllerTests [Theory] [BitAutoData] public async Task PreValidateSponsorshipToken_ValidatesToken_Success(string sponsorshipToken, User user, - OrganizationSponsorship sponsorship, SutProvider sutProvider) + OrganizationSponsorship sponsorship, + [Policy(PolicyType.FreeFamiliesSponsorshipPolicy, false)] PolicyStatus policy, + SutProvider sutProvider) { sutProvider.GetDependency().UserId.Returns(user.Id); sutProvider.GetDependency().GetUserByIdAsync(user.Id) .Returns(user); sutProvider.GetDependency() .ValidateRedemptionTokenAsync(sponsorshipToken, user.Email).Returns((true, sponsorship)); - + sutProvider.GetDependency() + .RunAsync(Arg.Any(), PolicyType.FreeFamiliesSponsorshipPolicy) + .Returns(policy); await sutProvider.Sut.PreValidateSponsorshipToken(sponsorshipToken); await sutProvider.GetDependency().Received(1) diff --git a/test/Api.Test/Controllers/PoliciesControllerTests.cs b/test/Api.Test/Controllers/PoliciesControllerTests.cs index efb9f7aaa9..03ab20ec28 100644 --- a/test/Api.Test/Controllers/PoliciesControllerTests.cs +++ b/test/Api.Test/Controllers/PoliciesControllerTests.cs @@ -49,7 +49,7 @@ public class PoliciesControllerTests sutProvider.GetDependency() .GetProperUserId(Arg.Any()) - .Returns((Guid?)userId); + .Returns(userId); sutProvider.GetDependency() .GetByOrganizationAsync(orgId, userId) @@ -95,7 +95,7 @@ public class PoliciesControllerTests // Arrange sutProvider.GetDependency() .GetProperUserId(Arg.Any()) - .Returns((Guid?)userId); + .Returns(userId); sutProvider.GetDependency() .GetByOrganizationAsync(orgId, userId) @@ -113,7 +113,7 @@ public class PoliciesControllerTests // Arrange sutProvider.GetDependency() .GetProperUserId(Arg.Any()) - .Returns((Guid?)userId); + .Returns(userId); sutProvider.GetDependency() .GetByOrganizationAsync(orgId, userId) @@ -135,7 +135,7 @@ public class PoliciesControllerTests // Arrange sutProvider.GetDependency() .GetProperUserId(Arg.Any()) - .Returns((Guid?)userId); + .Returns(userId); sutProvider.GetDependency() .GetByOrganizationAsync(orgId, userId) @@ -186,59 +186,35 @@ public class PoliciesControllerTests [Theory] [BitAutoData] public async Task Get_WhenUserCanManagePolicies_WithExistingType_ReturnsExistingPolicy( - SutProvider sutProvider, Guid orgId, Policy policy, int type) + SutProvider sutProvider, Guid orgId, PolicyStatus policy, PolicyType type) { // Arrange sutProvider.GetDependency() .ManagePolicies(orgId) .Returns(true); - policy.Type = (PolicyType)type; + policy.Type = type; policy.Enabled = true; policy.Data = null; - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(orgId, (PolicyType)type) + sutProvider.GetDependency() + .RunAsync(orgId, type) .Returns(policy); // Act var result = await sutProvider.Sut.Get(orgId, type); // Assert - Assert.IsType(result); - Assert.Equal(policy.Id, result.Id); + Assert.IsType(result); Assert.Equal(policy.Type, result.Type); Assert.Equal(policy.Enabled, result.Enabled); Assert.Equal(policy.OrganizationId, result.OrganizationId); } - [Theory] - [BitAutoData] - public async Task Get_WhenUserCanManagePolicies_WithNonExistingType_ReturnsDefaultPolicy( - SutProvider sutProvider, Guid orgId, int type) - { - // Arrange - sutProvider.GetDependency() - .ManagePolicies(orgId) - .Returns(true); - - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(orgId, (PolicyType)type) - .Returns((Policy)null); - - // Act - var result = await sutProvider.Sut.Get(orgId, type); - - // Assert - Assert.IsType(result); - Assert.Equal(result.Type, (PolicyType)type); - Assert.False(result.Enabled); - } - [Theory] [BitAutoData] public async Task Get_WhenUserCannotManagePolicies_ThrowsNotFoundException( - SutProvider sutProvider, Guid orgId, int type) + SutProvider sutProvider, Guid orgId, PolicyType type) { // Arrange sutProvider.GetDependency() diff --git a/test/Core.Test/AdminConsole/AutoFixture/PolicyFixtures.cs b/test/Core.Test/AdminConsole/AutoFixture/PolicyFixtures.cs index 09b112c43c..01ffb86a7d 100644 --- a/test/Core.Test/AdminConsole/AutoFixture/PolicyFixtures.cs +++ b/test/Core.Test/AdminConsole/AutoFixture/PolicyFixtures.cs @@ -3,6 +3,7 @@ using AutoFixture; using AutoFixture.Xunit2; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; namespace Bit.Core.Test.AdminConsole.AutoFixture; @@ -10,19 +11,30 @@ internal class PolicyCustomization : ICustomization { public PolicyType Type { get; set; } public bool Enabled { get; set; } + public string? Data { get; set; } - public PolicyCustomization(PolicyType type, bool enabled) + public PolicyCustomization(PolicyType type, bool enabled, string? data) { Type = type; Enabled = enabled; + Data = data; } public void Customize(IFixture fixture) { + var orgId = Guid.NewGuid(); + fixture.Customize(composer => composer - .With(o => o.OrganizationId, Guid.NewGuid()) + .With(o => o.OrganizationId, orgId) .With(o => o.Type, Type) - .With(o => o.Enabled, Enabled)); + .With(o => o.Enabled, Enabled) + .With(o => o.Data, Data)); + + fixture.Customize(composer => composer + .With(o => o.OrganizationId, orgId) + .With(o => o.Type, Type) + .With(o => o.Enabled, Enabled) + .With(o => o.Data, Data)); } } @@ -30,15 +42,17 @@ public class PolicyAttribute : CustomizeAttribute { private readonly PolicyType _type; private readonly bool _enabled; + private readonly string? _data; - public PolicyAttribute(PolicyType type, bool enabled = true) + public PolicyAttribute(PolicyType type, bool enabled = true, string? data = null) { _type = type; _enabled = enabled; + _data = data; } public override ICustomization GetCustomization(ParameterInfo parameter) { - return new PolicyCustomization(_type, _enabled); + return new PolicyCustomization(_type, _enabled, _data); } } diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/AccountRecovery/AdminRecoverAccountCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/AccountRecovery/AdminRecoverAccountCommandTests.cs index 88025301b6..3095907a22 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/AccountRecovery/AdminRecoverAccountCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/AccountRecovery/AdminRecoverAccountCommandTests.cs @@ -1,14 +1,16 @@ using AutoFixture; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.AccountRecovery; -using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Platform.Push; using Bit.Core.Repositories; using Bit.Core.Services; +using Bit.Core.Test.AdminConsole.AutoFixture; using Bit.Core.Test.AutoFixture.OrganizationUserFixtures; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -29,11 +31,12 @@ public class AdminRecoverAccountCommandTests Organization organization, OrganizationUser organizationUser, User user, + [Policy(PolicyType.ResetPassword, true)] PolicyStatus policy, SutProvider sutProvider) { // Arrange SetupValidOrganization(sutProvider, organization); - SetupValidPolicy(sutProvider, organization); + SetupValidPolicy(sutProvider, organization, policy); SetupValidOrganizationUser(organizationUser, organization.Id); SetupValidUser(sutProvider, user, organizationUser); SetupSuccessfulPasswordUpdate(sutProvider, user, newMasterPassword); @@ -87,25 +90,18 @@ public class AdminRecoverAccountCommandTests Assert.Equal("Organization does not allow password reset.", exception.Message); } - public static IEnumerable InvalidPolicies => new object[][] - { - [new Policy { Type = PolicyType.ResetPassword, Enabled = false }], [null] - }; - [Theory] - [BitMemberAutoData(nameof(InvalidPolicies))] + [BitAutoData] public async Task RecoverAccountAsync_InvalidPolicy_ThrowsBadRequest( - Policy resetPasswordPolicy, string newMasterPassword, string key, Organization organization, + [Policy(PolicyType.ResetPassword, false)] PolicyStatus policy, SutProvider sutProvider) { // Arrange SetupValidOrganization(sutProvider, organization); - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword) - .Returns(resetPasswordPolicy); + SetupValidPolicy(sutProvider, organization, policy); // Act & Assert var exception = await Assert.ThrowsAsync(() => @@ -171,11 +167,12 @@ public class AdminRecoverAccountCommandTests Organization organization, string newMasterPassword, string key, + [Policy(PolicyType.ResetPassword, true)] PolicyStatus policy, SutProvider sutProvider) { // Arrange SetupValidOrganization(sutProvider, organization); - SetupValidPolicy(sutProvider, organization); + SetupValidPolicy(sutProvider, organization, policy); // Act & Assert var exception = await Assert.ThrowsAsync(() => @@ -190,11 +187,12 @@ public class AdminRecoverAccountCommandTests string key, Organization organization, OrganizationUser organizationUser, + [Policy(PolicyType.ResetPassword, true)] PolicyStatus policy, SutProvider sutProvider) { // Arrange SetupValidOrganization(sutProvider, organization); - SetupValidPolicy(sutProvider, organization); + SetupValidPolicy(sutProvider, organization, policy); SetupValidOrganizationUser(organizationUser, organization.Id); sutProvider.GetDependency() .GetUserByIdAsync(organizationUser.UserId!.Value) @@ -213,11 +211,12 @@ public class AdminRecoverAccountCommandTests Organization organization, OrganizationUser organizationUser, User user, + [Policy(PolicyType.ResetPassword, true)] PolicyStatus policy, SutProvider sutProvider) { // Arrange SetupValidOrganization(sutProvider, organization); - SetupValidPolicy(sutProvider, organization); + SetupValidPolicy(sutProvider, organization, policy); SetupValidOrganizationUser(organizationUser, organization.Id); user.UsesKeyConnector = true; sutProvider.GetDependency() @@ -238,11 +237,10 @@ public class AdminRecoverAccountCommandTests .Returns(organization); } - private static void SetupValidPolicy(SutProvider sutProvider, Organization organization) + private static void SetupValidPolicy(SutProvider sutProvider, Organization organization, PolicyStatus policy) { - var policy = new Policy { Type = PolicyType.ResetPassword, Enabled = true }; - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword) + sutProvider.GetDependency() + .RunAsync(organization.Id, PolicyType.ResetPassword) .Returns(policy); } diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUsers/AutomaticallyConfirmOrganizationUsersValidatorTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUsers/AutomaticallyConfirmOrganizationUsersValidatorTests.cs index c3fb52ecbe..50e40b9803 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUsers/AutomaticallyConfirmOrganizationUsersValidatorTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/AutoConfirmUsers/AutomaticallyConfirmOrganizationUsersValidatorTests.cs @@ -7,7 +7,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimed using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; -using Bit.Core.AdminConsole.Repositories; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Billing.Enums; using Bit.Core.Entities; @@ -120,7 +119,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests [Organization(useAutomaticUserConfirmation: true, planType: PlanType.EnterpriseAnnually)] Organization organization, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, User user, - [Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) + [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy) { // Arrange organizationUser.UserId = user.Id; @@ -137,8 +136,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests Key = "test-key" }; - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) + sutProvider.GetDependency() + .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .Returns(autoConfirmPolicy); sutProvider.GetDependency() @@ -280,7 +279,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests [Organization(useAutomaticUserConfirmation: true)] Organization organization, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, Guid userId, - [Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) + [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy) { // Arrange organizationUser.UserId = userId; @@ -303,8 +302,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests PolicyType = PolicyType.TwoFactorAuthentication }; - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) + sutProvider.GetDependency() + .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .Returns(autoConfirmPolicy); sutProvider.GetDependency() @@ -334,7 +333,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests [Organization(useAutomaticUserConfirmation: true)] Organization organization, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, User user, - [Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) + [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy) { // Arrange organizationUser.UserId = user.Id; @@ -351,8 +350,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests Key = "test-key" }; - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) + sutProvider.GetDependency() + .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .Returns(autoConfirmPolicy); sutProvider.GetDependency() @@ -389,7 +388,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests [Organization(useAutomaticUserConfirmation: true)] Organization organization, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, User user, - [Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) + [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy) { // Arrange organizationUser.UserId = user.Id; @@ -406,8 +405,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests Key = "test-key" }; - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) + sutProvider.GetDependency() + .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .Returns(autoConfirmPolicy); sutProvider.GetDependency() @@ -448,7 +447,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests [Organization(useAutomaticUserConfirmation: true)] Organization organization, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, User user, - [Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) + [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy) { // Arrange organizationUser.UserId = user.Id; @@ -465,8 +464,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests Key = "test-key" }; - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) + sutProvider.GetDependency() + .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .Returns(autoConfirmPolicy); sutProvider.GetDependency() @@ -501,7 +500,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests SutProvider sutProvider, Organization organization, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, - Guid userId) + Guid userId, + [Policy(PolicyType.AutomaticUserConfirmation, false)] PolicyStatus policy) { // Arrange organizationUser.UserId = userId; @@ -518,9 +518,9 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests Key = "test-key" }; - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) - .Returns((Policy)null); + sutProvider.GetDependency() + .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation) + .Returns(policy); sutProvider.GetDependency() .TwoFactorIsEnabledAsync(Arg.Any>()) @@ -545,7 +545,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests [Organization(useAutomaticUserConfirmation: false)] Organization organization, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, Guid userId, - [Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) + [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy) { // Arrange organizationUser.UserId = userId; @@ -562,8 +562,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests Key = "test-key" }; - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) + sutProvider.GetDependency() + .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .Returns(autoConfirmPolicy); sutProvider.GetDependency() @@ -589,7 +589,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests [Organization(useAutomaticUserConfirmation: true)] Organization organization, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, User user, - [Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) + [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy) { // Arrange organizationUser.UserId = user.Id; @@ -606,8 +606,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests Key = "test-key" }; - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) + sutProvider.GetDependency() + .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .Returns(autoConfirmPolicy); sutProvider.GetDependency() diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/SendOrganizationInvitesCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/SendOrganizationInvitesCommandTests.cs index 23c1a32c03..ddede2d191 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/SendOrganizationInvitesCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/SendOrganizationInvitesCommandTests.cs @@ -1,7 +1,9 @@ using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; -using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.Auth.Entities; using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Repositories; @@ -9,6 +11,7 @@ using Bit.Core.Billing.Enums; using Bit.Core.Entities; using Bit.Core.Models.Mail; using Bit.Core.Services; +using Bit.Core.Test.AdminConsole.AutoFixture; using Bit.Core.Test.AutoFixture.OrganizationFixtures; using Bit.Core.Tokens; using Bit.Test.Common.AutoFixture; @@ -31,6 +34,7 @@ public class SendOrganizationInvitesCommandTests Organization organization, SsoConfig ssoConfig, OrganizationUser invite, + [Policy(PolicyType.RequireSso, false)] PolicyStatus policy, SutProvider sutProvider) { // Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks @@ -45,7 +49,9 @@ public class SendOrganizationInvitesCommandTests sutProvider.GetDependency().GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig); // Return null policy to mimic new org that's never turned on the require sso policy - sutProvider.GetDependency().GetManyByOrganizationIdAsync(organization.Id).ReturnsNull(); + sutProvider.GetDependency() + .RunAsync(organization.Id, PolicyType.RequireSso) + .Returns(policy); // Mock tokenable factory to return a token that expires in 5 days sutProvider.GetDependency() diff --git a/test/Core.Test/Auth/Services/SsoConfigServiceTests.cs b/test/Core.Test/Auth/Services/SsoConfigServiceTests.cs index 2f4d00a7fa..ca4378e6ec 100644 --- a/test/Core.Test/Auth/Services/SsoConfigServiceTests.cs +++ b/test/Core.Test/Auth/Services/SsoConfigServiceTests.cs @@ -2,9 +2,9 @@ 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; using Bit.Core.Auth.Models.Data; @@ -13,6 +13,7 @@ using Bit.Core.Auth.Services; using Bit.Core.Exceptions; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Repositories; +using Bit.Core.Test.AdminConsole.AutoFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; @@ -163,7 +164,8 @@ public class SsoConfigServiceTests [Theory, BitAutoData] public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled_Throws(SutProvider sutProvider, - Organization organization) + Organization organization, + [Policy(PolicyType.SingleOrg, false)] PolicyStatus policy) { var utcNow = DateTime.UtcNow; @@ -180,6 +182,9 @@ public class SsoConfigServiceTests RevisionDate = utcNow.AddDays(-10), }; + sutProvider.GetDependency().RunAsync( + Arg.Any(), PolicyType.SingleOrg).Returns(policy); + var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.SaveAsync(ssoConfig, organization)); @@ -191,7 +196,9 @@ public class SsoConfigServiceTests [Theory, BitAutoData] public async Task SaveAsync_KeyConnector_SsoPolicyNotEnabled_Throws(SutProvider sutProvider, - Organization organization) + Organization organization, + [Policy(PolicyType.SingleOrg, true)] PolicyStatus singleOrgPolicy, + [Policy(PolicyType.RequireSso, false)] PolicyStatus requireSsoPolicy) { var utcNow = DateTime.UtcNow; @@ -208,11 +215,10 @@ public class SsoConfigServiceTests RevisionDate = utcNow.AddDays(-10), }; - sutProvider.GetDependency().GetByOrganizationIdTypeAsync( - Arg.Any(), PolicyType.SingleOrg).Returns(new Policy - { - Enabled = true - }); + sutProvider.GetDependency().RunAsync( + Arg.Any(), PolicyType.SingleOrg).Returns(singleOrgPolicy); + sutProvider.GetDependency().RunAsync( + Arg.Any(), PolicyType.RequireSso).Returns(requireSsoPolicy); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.SaveAsync(ssoConfig, organization)); @@ -225,7 +231,8 @@ public class SsoConfigServiceTests [Theory, BitAutoData] public async Task SaveAsync_KeyConnector_SsoConfigNotEnabled_Throws(SutProvider sutProvider, - Organization organization) + Organization organization, + [Policy(PolicyType.SingleOrg, true)] PolicyStatus policy) { var utcNow = DateTime.UtcNow; @@ -242,11 +249,8 @@ public class SsoConfigServiceTests RevisionDate = utcNow.AddDays(-10), }; - sutProvider.GetDependency().GetByOrganizationIdTypeAsync( - Arg.Any(), Arg.Any()).Returns(new Policy - { - Enabled = true - }); + sutProvider.GetDependency().RunAsync( + Arg.Any(), Arg.Any()).Returns(policy); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.SaveAsync(ssoConfig, organization)); @@ -259,7 +263,8 @@ public class SsoConfigServiceTests [Theory, BitAutoData] public async Task SaveAsync_KeyConnector_KeyConnectorAbilityNotEnabled_Throws(SutProvider sutProvider, - Organization organization) + Organization organization, + [Policy(PolicyType.SingleOrg, true)] PolicyStatus policy) { var utcNow = DateTime.UtcNow; @@ -277,11 +282,8 @@ public class SsoConfigServiceTests RevisionDate = utcNow.AddDays(-10), }; - sutProvider.GetDependency().GetByOrganizationIdTypeAsync( - Arg.Any(), Arg.Any()).Returns(new Policy - { - Enabled = true, - }); + sutProvider.GetDependency().RunAsync( + Arg.Any(), Arg.Any()).Returns(policy); var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.SaveAsync(ssoConfig, organization)); @@ -294,7 +296,8 @@ public class SsoConfigServiceTests [Theory, BitAutoData] public async Task SaveAsync_KeyConnector_Success(SutProvider sutProvider, - Organization organization) + Organization organization, + [Policy(PolicyType.SingleOrg, true)] PolicyStatus policy) { var utcNow = DateTime.UtcNow; @@ -312,11 +315,8 @@ public class SsoConfigServiceTests RevisionDate = utcNow.AddDays(-10), }; - sutProvider.GetDependency().GetByOrganizationIdTypeAsync( - Arg.Any(), Arg.Any()).Returns(new Policy - { - Enabled = true, - }); + sutProvider.GetDependency().RunAsync( + Arg.Any(), Arg.Any()).Returns(policy); await sutProvider.Sut.SaveAsync(ssoConfig, organization); diff --git a/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs b/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs index b67bfaa131..29193bacbc 100644 --- a/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs +++ b/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs @@ -1,7 +1,8 @@ using System.Text; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums; -using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models; using Bit.Core.Auth.Models.Business.Tokenables; @@ -13,6 +14,7 @@ using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterpri using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; +using Bit.Core.Test.AdminConsole.AutoFixture; using Bit.Core.Tokens; using Bit.Core.Utilities; using Bit.Test.Common.AutoFixture; @@ -241,7 +243,8 @@ public class RegisterUserCommandTests [BitAutoData(true, "sampleInitiationPath")] [BitAutoData(true, "Secrets Manager trial")] public async Task RegisterUserViaOrganizationInviteToken_ComplexHappyPath_Succeeds(bool addUserReferenceData, string initiationPath, - SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId, Policy twoFactorPolicy) + SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId, + [Policy(PolicyType.TwoFactorAuthentication, true)] PolicyStatus policy) { // Arrange sutProvider.GetDependency() @@ -267,10 +270,9 @@ public class RegisterUserCommandTests .GetByIdAsync(orgUserId) .Returns(orgUser); - twoFactorPolicy.Enabled = true; - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(orgUser.OrganizationId, PolicyType.TwoFactorAuthentication) - .Returns(twoFactorPolicy); + sutProvider.GetDependency() + .RunAsync(orgUser.OrganizationId, PolicyType.TwoFactorAuthentication) + .Returns(policy); sutProvider.GetDependency() .CreateUserAsync(user, masterPasswordHash) @@ -286,9 +288,9 @@ public class RegisterUserCommandTests .Received(1) .GetByIdAsync(orgUserId); - await sutProvider.GetDependency() + await sutProvider.GetDependency() .Received(1) - .GetByOrganizationIdTypeAsync(orgUser.OrganizationId, PolicyType.TwoFactorAuthentication); + .RunAsync(orgUser.OrganizationId, PolicyType.TwoFactorAuthentication); sutProvider.GetDependency() .Received(1) @@ -431,7 +433,8 @@ public class RegisterUserCommandTests [Theory] [BitAutoData] public async Task RegisterUserViaOrganizationInviteToken_BlockedDomainFromDifferentOrg_ThrowsBadRequestException( - SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId) + SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId, + [Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy) { // Arrange user.Email = "user@blocked-domain.com"; @@ -463,6 +466,10 @@ public class RegisterUserCommandTests .HasVerifiedDomainWithBlockClaimedDomainPolicyAsync("blocked-domain.com", orgUser.OrganizationId) .Returns(true); + sutProvider.GetDependency() + .RunAsync(Arg.Any(), PolicyType.TwoFactorAuthentication) + .Returns(policy); + // Act & Assert var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId)); @@ -472,7 +479,8 @@ public class RegisterUserCommandTests [Theory] [BitAutoData] public async Task RegisterUserViaOrganizationInviteToken_BlockedDomainFromSameOrg_Succeeds( - SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId) + SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId, + [Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy) { // Arrange user.Email = "user@company-domain.com"; @@ -509,6 +517,10 @@ public class RegisterUserCommandTests .CreateUserAsync(user, masterPasswordHash) .Returns(IdentityResult.Success); + sutProvider.GetDependency() + .RunAsync(Arg.Any(), PolicyType.TwoFactorAuthentication) + .Returns(policy); + // Act var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId); @@ -1245,6 +1257,7 @@ public class RegisterUserCommandTests OrganizationUser orgUser, string orgInviteToken, string masterPasswordHash, + [Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy, SutProvider sutProvider) { // Arrange @@ -1259,9 +1272,9 @@ public class RegisterUserCommandTests .GetByIdAsync(orgUser.Id) .Returns(orgUser); - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(Arg.Any(), PolicyType.TwoFactorAuthentication) - .Returns((Policy)null); + sutProvider.GetDependency() + .RunAsync(Arg.Any(), PolicyType.TwoFactorAuthentication) + .Returns(policy); sutProvider.GetDependency() .GetByIdAsync(orgUser.OrganizationId) @@ -1331,6 +1344,7 @@ public class RegisterUserCommandTests OrganizationUser orgUser, string masterPasswordHash, string orgInviteToken, + [Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy, SutProvider sutProvider) { // Arrange @@ -1346,9 +1360,9 @@ public class RegisterUserCommandTests .GetByIdAsync(orgUser.Id) .Returns(orgUser); - sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(Arg.Any(), PolicyType.TwoFactorAuthentication) - .Returns((Policy)null); + sutProvider.GetDependency() + .RunAsync(Arg.Any(), PolicyType.TwoFactorAuthentication) + .Returns(policy); sutProvider.GetDependency() .GetByIdAsync(orgUser.OrganizationId) diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs index 223047ee07..b4f1fe2d98 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs @@ -1,4 +1,7 @@ -using Bit.Core.Billing.Enums; +using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies; +using Bit.Core.Billing.Enums; using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Services; using Bit.Core.Exceptions; @@ -9,6 +12,7 @@ using Bit.Core.OrganizationFeatures.OrganizationSubscriptions; using Bit.Core.Repositories; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; +using Bit.Core.Test.AdminConsole.AutoFixture; using Bit.Core.Test.AutoFixture.OrganizationFixtures; using Bit.Core.Test.Billing.Mocks; using Bit.Test.Common.AutoFixture; @@ -72,8 +76,12 @@ public class UpgradeOrganizationPlanCommandTests [Theory] [FreeOrganizationUpgradeCustomize, BitAutoData] public async Task UpgradePlan_Passes(Organization organization, OrganizationUpgrade upgrade, + [Policy(PolicyType.ResetPassword, false)] PolicyStatus policy, SutProvider sutProvider) { + sutProvider.GetDependency() + .RunAsync(Arg.Any(), Arg.Any()) + .Returns(policy); sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); sutProvider.GetDependency().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType)); upgrade.AdditionalSmSeats = 10; @@ -100,6 +108,7 @@ public class UpgradeOrganizationPlanCommandTests PlanType planType, Organization organization, OrganizationUpgrade organizationUpgrade, + [Policy(PolicyType.ResetPassword, false)] PolicyStatus policy, SutProvider sutProvider) { sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); @@ -116,6 +125,9 @@ public class UpgradeOrganizationPlanCommandTests organizationUpgrade.Plan = planType; sutProvider.GetDependency().GetPlanOrThrow(organizationUpgrade.Plan).Returns(MockPlans.Get(organizationUpgrade.Plan)); + sutProvider.GetDependency() + .RunAsync(Arg.Any(), Arg.Any()) + .Returns(policy); sutProvider.GetDependency() .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts { @@ -141,15 +153,20 @@ public class UpgradeOrganizationPlanCommandTests [BitAutoData(PlanType.TeamsAnnually)] [BitAutoData(PlanType.TeamsStarter)] public async Task UpgradePlan_SM_Passes(PlanType planType, Organization organization, OrganizationUpgrade upgrade, + [Policy(PolicyType.ResetPassword, false)] PolicyStatus policy, SutProvider sutProvider) { - sutProvider.GetDependency().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType)); upgrade.Plan = planType; sutProvider.GetDependency().GetPlanOrThrow(upgrade.Plan).Returns(MockPlans.Get(upgrade.Plan)); var plan = MockPlans.Get(upgrade.Plan); + sutProvider.GetDependency() + .RunAsync(Arg.Any(), Arg.Any()) + .Returns(policy); + + sutProvider.GetDependency().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType)); sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); upgrade.AdditionalSeats = 15; @@ -180,6 +197,7 @@ public class UpgradeOrganizationPlanCommandTests [BitAutoData(PlanType.TeamsAnnually)] [BitAutoData(PlanType.TeamsStarter)] public async Task UpgradePlan_SM_NotEnoughSmSeats_Throws(PlanType planType, Organization organization, OrganizationUpgrade upgrade, + [Policy(PolicyType.ResetPassword, false)] PolicyStatus policy, SutProvider sutProvider) { upgrade.Plan = planType; @@ -191,6 +209,10 @@ public class UpgradeOrganizationPlanCommandTests organization.SmSeats = 2; sutProvider.GetDependency().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType)); + sutProvider.GetDependency() + .RunAsync(Arg.Any(), Arg.Any()) + .Returns(policy); + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); sutProvider.GetDependency() .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts @@ -214,7 +236,9 @@ public class UpgradeOrganizationPlanCommandTests [BitAutoData(PlanType.TeamsAnnually, 51)] [BitAutoData(PlanType.TeamsStarter, 51)] public async Task UpgradePlan_SM_NotEnoughServiceAccounts_Throws(PlanType planType, int currentServiceAccounts, - Organization organization, OrganizationUpgrade upgrade, SutProvider sutProvider) + Organization organization, OrganizationUpgrade upgrade, + [Policy(PolicyType.ResetPassword, false)] PolicyStatus policy, + SutProvider sutProvider) { upgrade.Plan = planType; upgrade.AdditionalSeats = 15; @@ -226,6 +250,10 @@ public class UpgradeOrganizationPlanCommandTests organization.SmServiceAccounts = currentServiceAccounts; sutProvider.GetDependency().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType)); + sutProvider.GetDependency() + .RunAsync(Arg.Any(), Arg.Any()) + .Returns(policy); + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); sutProvider.GetDependency() .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts @@ -251,6 +279,7 @@ public class UpgradeOrganizationPlanCommandTests OrganizationUpgrade upgrade, string newPublicKey, string newPrivateKey, + [Policy(PolicyType.ResetPassword, false)] PolicyStatus policy, SutProvider sutProvider) { organization.PublicKey = null; @@ -262,6 +291,9 @@ public class UpgradeOrganizationPlanCommandTests publicKey: newPublicKey); upgrade.AdditionalSeats = 10; + sutProvider.GetDependency() + .RunAsync(Arg.Any(), Arg.Any()) + .Returns(policy); sutProvider.GetDependency() .GetByIdAsync(organization.Id) .Returns(organization); @@ -291,6 +323,7 @@ public class UpgradeOrganizationPlanCommandTests public async Task UpgradePlan_WhenOrganizationAlreadyHasPublicAndPrivateKeys_DoesNotOverwriteWithNull( Organization organization, OrganizationUpgrade upgrade, + [Policy(PolicyType.ResetPassword, false)] PolicyStatus policy, SutProvider sutProvider) { // Arrange @@ -304,6 +337,9 @@ public class UpgradeOrganizationPlanCommandTests upgrade.Keys = null; upgrade.AdditionalSeats = 10; + sutProvider.GetDependency() + .RunAsync(Arg.Any(), Arg.Any()) + .Returns(policy); sutProvider.GetDependency() .GetByIdAsync(organization.Id) .Returns(organization); @@ -333,6 +369,7 @@ public class UpgradeOrganizationPlanCommandTests public async Task UpgradePlan_WhenOrganizationAlreadyHasPublicAndPrivateKeys_DoesNotBackfillWithNewKeys( Organization organization, OrganizationUpgrade upgrade, + [Policy(PolicyType.ResetPassword, false)] PolicyStatus policy, SutProvider sutProvider) { // Arrange @@ -343,6 +380,9 @@ public class UpgradeOrganizationPlanCommandTests organization.PublicKey = existingPublicKey; organization.PrivateKey = existingPrivateKey; + sutProvider.GetDependency() + .RunAsync(Arg.Any(), Arg.Any()) + .Returns(policy); upgrade.Plan = PlanType.TeamsAnnually; upgrade.Keys = new PublicKeyEncryptionKeyPairData( diff --git a/test/Core.Test/OrganizationFeatures/Policies/PolicyQueryTests.cs b/test/Core.Test/OrganizationFeatures/Policies/PolicyQueryTests.cs new file mode 100644 index 0000000000..ac33a5e5a6 --- /dev/null +++ b/test/Core.Test/OrganizationFeatures/Policies/PolicyQueryTests.cs @@ -0,0 +1,55 @@ +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations; +using Bit.Core.AdminConsole.Repositories; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using NSubstitute.ReturnsExtensions; +using Xunit; + +namespace Bit.Core.Test.OrganizationFeatures.Policies; + +[SutProviderCustomize] +public class PolicyQueryTests +{ + [Theory, BitAutoData] + public async Task RunAsync_WithExistingPolicy_ReturnsPolicy(SutProvider sutProvider, + Policy policy) + { + // Arrange + sutProvider.GetDependency() + .GetByOrganizationIdTypeAsync(policy.OrganizationId, policy.Type) + .Returns(policy); + + // Act + var policyData = await sutProvider.Sut.RunAsync(policy.OrganizationId, policy.Type); + + // Assert + Assert.Equal(policy.Data, policyData.Data); + Assert.Equal(policy.Type, policyData.Type); + Assert.Equal(policy.Enabled, policyData.Enabled); + Assert.Equal(policy.OrganizationId, policyData.OrganizationId); + } + + [Theory, BitAutoData] + public async Task RunAsync_WithNonExistentPolicy_ReturnsDefaultDisabledPolicy( + SutProvider sutProvider, + Guid organizationId, + PolicyType policyType) + { + // Arrange + sutProvider.GetDependency() + .GetByOrganizationIdTypeAsync(organizationId, policyType) + .ReturnsNull(); + + // Act + var policyData = await sutProvider.Sut.RunAsync(organizationId, policyType); + + // Assert + Assert.Equal(organizationId, policyData.OrganizationId); + Assert.Equal(policyType, policyData.Type); + Assert.False(policyData.Enabled); + Assert.Null(policyData.Data); + } +}