Merge branch 'main' into billing/pm-31360/remove-reversion-related-code

This commit is contained in:
Stephon Brown
2026-01-29 16:28:59 -05:00
39 changed files with 498 additions and 315 deletions

View File

@@ -2,7 +2,7 @@
using Bit.Core; using Bit.Core;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; 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.Entities;
using Bit.Core.Auth.Enums; using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models; using Bit.Core.Auth.Models;
@@ -45,7 +45,7 @@ public class AccountController : Controller
private readonly ISsoConfigRepository _ssoConfigRepository; private readonly ISsoConfigRepository _ssoConfigRepository;
private readonly ISsoUserRepository _ssoUserRepository; private readonly ISsoUserRepository _ssoUserRepository;
private readonly IUserRepository _userRepository; private readonly IUserRepository _userRepository;
private readonly IPolicyRepository _policyRepository; private readonly IPolicyQuery _policyQuery;
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly II18nService _i18nService; private readonly II18nService _i18nService;
private readonly UserManager<User> _userManager; private readonly UserManager<User> _userManager;
@@ -67,7 +67,7 @@ public class AccountController : Controller
ISsoConfigRepository ssoConfigRepository, ISsoConfigRepository ssoConfigRepository,
ISsoUserRepository ssoUserRepository, ISsoUserRepository ssoUserRepository,
IUserRepository userRepository, IUserRepository userRepository,
IPolicyRepository policyRepository, IPolicyQuery policyQuery,
IUserService userService, IUserService userService,
II18nService i18nService, II18nService i18nService,
UserManager<User> userManager, UserManager<User> userManager,
@@ -88,7 +88,7 @@ public class AccountController : Controller
_userRepository = userRepository; _userRepository = userRepository;
_ssoConfigRepository = ssoConfigRepository; _ssoConfigRepository = ssoConfigRepository;
_ssoUserRepository = ssoUserRepository; _ssoUserRepository = ssoUserRepository;
_policyRepository = policyRepository; _policyQuery = policyQuery;
_userService = userService; _userService = userService;
_i18nService = i18nService; _i18nService = i18nService;
_userManager = userManager; _userManager = userManager;
@@ -687,9 +687,8 @@ public class AccountController : Controller
await _registerUserCommand.RegisterSSOAutoProvisionedUserAsync(newUser, organization); await _registerUserCommand.RegisterSSOAutoProvisionedUserAsync(newUser, organization);
// If the organization has 2fa policy enabled, make sure to default jit user 2fa to email // If the organization has 2fa policy enabled, make sure to default jit user 2fa to email
var twoFactorPolicy = var twoFactorPolicy = await _policyQuery.RunAsync(organization.Id, PolicyType.TwoFactorAuthentication);
await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.TwoFactorAuthentication); if (twoFactorPolicy.Enabled)
if (twoFactorPolicy != null && twoFactorPolicy.Enabled)
{ {
newUser.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider> newUser.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{ {

View File

@@ -57,7 +57,7 @@ public class OrganizationUsersController : BaseAdminConsoleController
private readonly ICollectionRepository _collectionRepository; private readonly ICollectionRepository _collectionRepository;
private readonly IGroupRepository _groupRepository; private readonly IGroupRepository _groupRepository;
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly IPolicyRepository _policyRepository; private readonly IPolicyQuery _policyQuery;
private readonly ICurrentContext _currentContext; private readonly ICurrentContext _currentContext;
private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery; private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery;
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
@@ -90,7 +90,7 @@ public class OrganizationUsersController : BaseAdminConsoleController
ICollectionRepository collectionRepository, ICollectionRepository collectionRepository,
IGroupRepository groupRepository, IGroupRepository groupRepository,
IUserService userService, IUserService userService,
IPolicyRepository policyRepository, IPolicyQuery policyQuery,
ICurrentContext currentContext, ICurrentContext currentContext,
ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery, ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery,
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand, IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
@@ -123,7 +123,7 @@ public class OrganizationUsersController : BaseAdminConsoleController
_collectionRepository = collectionRepository; _collectionRepository = collectionRepository;
_groupRepository = groupRepository; _groupRepository = groupRepository;
_userService = userService; _userService = userService;
_policyRepository = policyRepository; _policyQuery = policyQuery;
_currentContext = currentContext; _currentContext = currentContext;
_countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery; _countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery;
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand; _updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
@@ -350,10 +350,9 @@ public class OrganizationUsersController : BaseAdminConsoleController
return false; return false;
} }
var masterPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); var masterPasswordPolicy = await _policyQuery.RunAsync(orgId, PolicyType.ResetPassword);
var useMasterPasswordPolicy = masterPasswordPolicy != null && var useMasterPasswordPolicy = masterPasswordPolicy.Enabled &&
masterPasswordPolicy.Enabled && masterPasswordPolicy.GetDataModel<ResetPasswordDataModel>().AutoEnrollEnabled;
masterPasswordPolicy.GetDataModel<ResetPasswordDataModel>().AutoEnrollEnabled;
return useMasterPasswordPolicy; return useMasterPasswordPolicy;
} }

View File

@@ -48,7 +48,7 @@ public class OrganizationsController : Controller
{ {
private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IPolicyRepository _policyRepository; private readonly IPolicyQuery _policyQuery;
private readonly IOrganizationService _organizationService; private readonly IOrganizationService _organizationService;
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly ICurrentContext _currentContext; private readonly ICurrentContext _currentContext;
@@ -74,7 +74,7 @@ public class OrganizationsController : Controller
public OrganizationsController( public OrganizationsController(
IOrganizationRepository organizationRepository, IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository, IOrganizationUserRepository organizationUserRepository,
IPolicyRepository policyRepository, IPolicyQuery policyQuery,
IOrganizationService organizationService, IOrganizationService organizationService,
IUserService userService, IUserService userService,
ICurrentContext currentContext, ICurrentContext currentContext,
@@ -99,7 +99,7 @@ public class OrganizationsController : Controller
{ {
_organizationRepository = organizationRepository; _organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository; _organizationUserRepository = organizationUserRepository;
_policyRepository = policyRepository; _policyQuery = policyQuery;
_organizationService = organizationService; _organizationService = organizationService;
_userService = userService; _userService = userService;
_currentContext = currentContext; _currentContext = currentContext;
@@ -183,15 +183,14 @@ public class OrganizationsController : Controller
return new OrganizationAutoEnrollStatusResponseModel(organization.Id, resetPasswordPolicyRequirement.AutoEnrollEnabled(organization.Id)); return new OrganizationAutoEnrollStatusResponseModel(organization.Id, resetPasswordPolicyRequirement.AutoEnrollEnabled(organization.Id));
} }
var resetPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword); var resetPasswordPolicy = await _policyQuery.RunAsync(organization.Id, PolicyType.ResetPassword);
if (resetPasswordPolicy == null || !resetPasswordPolicy.Enabled || resetPasswordPolicy.Data == null) if (!resetPasswordPolicy.Enabled || resetPasswordPolicy.Data == null)
{ {
return new OrganizationAutoEnrollStatusResponseModel(organization.Id, false); return new OrganizationAutoEnrollStatusResponseModel(organization.Id, false);
} }
var data = JsonSerializer.Deserialize<ResetPasswordDataModel>(resetPasswordPolicy.Data, JsonHelpers.IgnoreCase); var data = JsonSerializer.Deserialize<ResetPasswordDataModel>(resetPasswordPolicy.Data, JsonHelpers.IgnoreCase);
return new OrganizationAutoEnrollStatusResponseModel(organization.Id, data?.AutoEnrollEnabled ?? false); return new OrganizationAutoEnrollStatusResponseModel(organization.Id, data?.AutoEnrollEnabled ?? false);
} }
[HttpPost("")] [HttpPost("")]

View File

@@ -7,7 +7,6 @@ using Bit.Api.AdminConsole.Models.Request;
using Bit.Api.AdminConsole.Models.Response.Helpers; using Bit.Api.AdminConsole.Models.Response.Helpers;
using Bit.Api.AdminConsole.Models.Response.Organizations; using Bit.Api.AdminConsole.Models.Response.Organizations;
using Bit.Api.Models.Response; using Bit.Api.Models.Response;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
@@ -43,6 +42,7 @@ public class PoliciesController : Controller
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly ISavePolicyCommand _savePolicyCommand; private readonly ISavePolicyCommand _savePolicyCommand;
private readonly IVNextSavePolicyCommand _vNextSavePolicyCommand; private readonly IVNextSavePolicyCommand _vNextSavePolicyCommand;
private readonly IPolicyQuery _policyQuery;
public PoliciesController(IPolicyRepository policyRepository, public PoliciesController(IPolicyRepository policyRepository,
IOrganizationUserRepository organizationUserRepository, IOrganizationUserRepository organizationUserRepository,
@@ -54,7 +54,8 @@ public class PoliciesController : Controller
IOrganizationHasVerifiedDomainsQuery organizationHasVerifiedDomainsQuery, IOrganizationHasVerifiedDomainsQuery organizationHasVerifiedDomainsQuery,
IOrganizationRepository organizationRepository, IOrganizationRepository organizationRepository,
ISavePolicyCommand savePolicyCommand, ISavePolicyCommand savePolicyCommand,
IVNextSavePolicyCommand vNextSavePolicyCommand) IVNextSavePolicyCommand vNextSavePolicyCommand,
IPolicyQuery policyQuery)
{ {
_policyRepository = policyRepository; _policyRepository = policyRepository;
_organizationUserRepository = organizationUserRepository; _organizationUserRepository = organizationUserRepository;
@@ -68,27 +69,24 @@ public class PoliciesController : Controller
_organizationHasVerifiedDomainsQuery = organizationHasVerifiedDomainsQuery; _organizationHasVerifiedDomainsQuery = organizationHasVerifiedDomainsQuery;
_savePolicyCommand = savePolicyCommand; _savePolicyCommand = savePolicyCommand;
_vNextSavePolicyCommand = vNextSavePolicyCommand; _vNextSavePolicyCommand = vNextSavePolicyCommand;
_policyQuery = policyQuery;
} }
[HttpGet("{type}")] [HttpGet("{type}")]
public async Task<PolicyDetailResponseModel> Get(Guid orgId, int type) public async Task<PolicyStatusResponseModel> Get(Guid orgId, PolicyType type)
{ {
if (!await _currentContext.ManagePolicies(orgId)) if (!await _currentContext.ManagePolicies(orgId))
{ {
throw new NotFoundException(); 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) 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("")] [HttpGet("")]

View File

@@ -1,19 +1,21 @@
using Bit.Api.AdminConsole.Models.Response.Organizations; using Bit.Api.AdminConsole.Models.Response.Organizations;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
namespace Bit.Api.AdminConsole.Models.Response.Helpers; namespace Bit.Api.AdminConsole.Models.Response.Helpers;
public static class PolicyDetailResponses public static class PolicyStatusResponses
{ {
public static async Task<PolicyDetailResponseModel> GetSingleOrgPolicyDetailResponseAsync(this Policy policy, IOrganizationHasVerifiedDomainsQuery hasVerifiedDomainsQuery) public static async Task<PolicyStatusResponseModel> GetSingleOrgPolicyStatusResponseAsync(
this PolicyStatus policy, IOrganizationHasVerifiedDomainsQuery hasVerifiedDomainsQuery)
{ {
if (policy.Type is not PolicyType.SingleOrg) if (policy.Type is not PolicyType.SingleOrg)
{ {
throw new ArgumentException($"'{nameof(policy)}' must be of type '{nameof(PolicyType.SingleOrg)}'.", nameof(policy)); 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<bool> CanToggleState() async Task<bool> CanToggleState()
{ {
@@ -25,5 +27,4 @@ public static class PolicyDetailResponses
return !policy.Enabled; return !policy.Enabled;
} }
} }
} }

View File

@@ -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;
}
/// <summary>
/// Indicates whether the Policy can be enabled/disabled
/// </summary>
public bool CanToggleState { get; set; } = true;
}

View File

@@ -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<Dictionary<string, object>>(policy.Data) ?? new();
}
Enabled = policy.Enabled;
CanToggleState = canToggleState;
}
public Guid OrganizationId { get; init; }
public PolicyType Type { get; init; }
public Dictionary<string, object> Data { get; init; } = new();
public bool Enabled { get; init; }
/// <summary>
/// Indicates whether the Policy can be enabled/disabled
/// </summary>
public bool CanToggleState { get; init; }
}

View File

@@ -6,7 +6,7 @@ using Bit.Api.Models.Response;
using Bit.Api.Models.Response.Organizations; using Bit.Api.Models.Response.Organizations;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationConnections.Interfaces; 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.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
@@ -38,7 +38,7 @@ public class OrganizationSponsorshipsController : Controller
private readonly ICloudSyncSponsorshipsCommand _syncSponsorshipsCommand; private readonly ICloudSyncSponsorshipsCommand _syncSponsorshipsCommand;
private readonly ICurrentContext _currentContext; private readonly ICurrentContext _currentContext;
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly IPolicyRepository _policyRepository; private readonly IPolicyQuery _policyQuery;
private readonly IFeatureService _featureService; private readonly IFeatureService _featureService;
public OrganizationSponsorshipsController( public OrganizationSponsorshipsController(
@@ -55,7 +55,7 @@ public class OrganizationSponsorshipsController : Controller
ICloudSyncSponsorshipsCommand syncSponsorshipsCommand, ICloudSyncSponsorshipsCommand syncSponsorshipsCommand,
IUserService userService, IUserService userService,
ICurrentContext currentContext, ICurrentContext currentContext,
IPolicyRepository policyRepository, IPolicyQuery policyQuery,
IFeatureService featureService) IFeatureService featureService)
{ {
_organizationSponsorshipRepository = organizationSponsorshipRepository; _organizationSponsorshipRepository = organizationSponsorshipRepository;
@@ -71,7 +71,7 @@ public class OrganizationSponsorshipsController : Controller
_syncSponsorshipsCommand = syncSponsorshipsCommand; _syncSponsorshipsCommand = syncSponsorshipsCommand;
_userService = userService; _userService = userService;
_currentContext = currentContext; _currentContext = currentContext;
_policyRepository = policyRepository; _policyQuery = policyQuery;
_featureService = featureService; _featureService = featureService;
} }
@@ -81,10 +81,10 @@ public class OrganizationSponsorshipsController : Controller
public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model) public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model)
{ {
var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId); var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId);
var freeFamiliesSponsorshipPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(sponsoringOrgId, var freeFamiliesSponsorshipPolicy = await _policyQuery.RunAsync(sponsoringOrgId,
PolicyType.FreeFamiliesSponsorshipPolicy); PolicyType.FreeFamiliesSponsorshipPolicy);
if (freeFamiliesSponsorshipPolicy?.Enabled == true) if (freeFamiliesSponsorshipPolicy.Enabled)
{ {
throw new BadRequestException("Free Bitwarden Families sponsorship has been disabled by your organization administrator."); 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)] [SelfHosted(NotSelfHostedOnly = true)]
public async Task ResendSponsorshipOffer(Guid sponsoringOrgId, [FromQuery] string sponsoredFriendlyName) public async Task ResendSponsorshipOffer(Guid sponsoringOrgId, [FromQuery] string sponsoredFriendlyName)
{ {
var freeFamiliesSponsorshipPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(sponsoringOrgId, var freeFamiliesSponsorshipPolicy = await _policyQuery.RunAsync(sponsoringOrgId,
PolicyType.FreeFamiliesSponsorshipPolicy); PolicyType.FreeFamiliesSponsorshipPolicy);
if (freeFamiliesSponsorshipPolicy?.Enabled == true) if (freeFamiliesSponsorshipPolicy.Enabled)
{ {
throw new BadRequestException("Free Bitwarden Families sponsorship has been disabled by your organization administrator."); 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); var (isValid, sponsorship) = await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email);
if (isValid && sponsorship.SponsoringOrganizationId.HasValue) if (isValid && sponsorship.SponsoringOrganizationId.HasValue)
{ {
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(sponsorship.SponsoringOrganizationId.Value, var policy = await _policyQuery.RunAsync(sponsorship.SponsoringOrganizationId.Value,
PolicyType.FreeFamiliesSponsorshipPolicy); PolicyType.FreeFamiliesSponsorshipPolicy);
isFreeFamilyPolicyEnabled = policy?.Enabled ?? false; isFreeFamilyPolicyEnabled = policy.Enabled;
} }
var response = PreValidateSponsorshipResponseModel.From(isValid, isFreeFamilyPolicyEnabled); 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."); 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); 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."); throw new BadRequestException("Free Bitwarden Families sponsorship has been disabled by your organization administrator.");
} }

View File

@@ -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<T>() where T : IPolicyDataModel, new()
{
return CoreHelpers.LoadClassFromJsonData<T>(Data);
}
}

View File

@@ -1,5 +1,5 @@
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
@@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Identity;
namespace Bit.Core.AdminConsole.OrganizationFeatures.AccountRecovery; namespace Bit.Core.AdminConsole.OrganizationFeatures.AccountRecovery;
public class AdminRecoverAccountCommand(IOrganizationRepository organizationRepository, public class AdminRecoverAccountCommand(IOrganizationRepository organizationRepository,
IPolicyRepository policyRepository, IPolicyQuery policyQuery,
IUserRepository userRepository, IUserRepository userRepository,
IMailService mailService, IMailService mailService,
IEventService eventService, IEventService eventService,
@@ -30,9 +30,8 @@ public class AdminRecoverAccountCommand(IOrganizationRepository organizationRepo
} }
// Enterprise policy must be enabled // Enterprise policy must be enabled
var resetPasswordPolicy = var resetPasswordPolicy = await policyQuery.RunAsync(orgId, PolicyType.ResetPassword);
await policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); if (!resetPasswordPolicy.Enabled)
if (resetPasswordPolicy == null || !resetPasswordPolicy.Enabled)
{ {
throw new BadRequestException("Organization does not have the password reset policy enabled."); throw new BadRequestException("Organization does not have the password reset policy enabled.");
} }

View File

@@ -3,7 +3,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimed
using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Utilities.v2; using Bit.Core.AdminConsole.Utilities.v2;
using Bit.Core.AdminConsole.Utilities.v2.Validation; using Bit.Core.AdminConsole.Utilities.v2.Validation;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
@@ -20,7 +19,7 @@ public class AutomaticallyConfirmOrganizationUsersValidator(
IPolicyRequirementQuery policyRequirementQuery, IPolicyRequirementQuery policyRequirementQuery,
IAutomaticUserConfirmationPolicyEnforcementValidator automaticUserConfirmationPolicyEnforcementValidator, IAutomaticUserConfirmationPolicyEnforcementValidator automaticUserConfirmationPolicyEnforcementValidator,
IUserService userService, IUserService userService,
IPolicyRepository policyRepository) : IAutomaticallyConfirmOrganizationUsersValidator IPolicyQuery policyQuery) : IAutomaticallyConfirmOrganizationUsersValidator
{ {
public async Task<ValidationResult<AutomaticallyConfirmOrganizationUserValidationRequest>> ValidateAsync( public async Task<ValidationResult<AutomaticallyConfirmOrganizationUserValidationRequest>> ValidateAsync(
AutomaticallyConfirmOrganizationUserValidationRequest request) AutomaticallyConfirmOrganizationUserValidationRequest request)
@@ -74,7 +73,7 @@ public class AutomaticallyConfirmOrganizationUsersValidator(
} }
private async Task<bool> OrganizationHasAutomaticallyConfirmUsersPolicyEnabledAsync(AutomaticallyConfirmOrganizationUserValidationRequest request) => private async Task<bool> 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 }; && request.Organization is { UseAutomaticUserConfirmation: true };
private async Task<bool> OrganizationUserConformsToTwoFactorRequiredPolicyAsync(AutomaticallyConfirmOrganizationUserValidationRequest request) private async Task<bool> OrganizationUserConformsToTwoFactorRequiredPolicyAsync(AutomaticallyConfirmOrganizationUserValidationRequest request)

View File

@@ -4,7 +4,7 @@
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; 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;
using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Repositories; using Bit.Core.Auth.Repositories;
@@ -19,7 +19,7 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUse
public class SendOrganizationInvitesCommand( public class SendOrganizationInvitesCommand(
IUserRepository userRepository, IUserRepository userRepository,
ISsoConfigRepository ssoConfigurationRepository, ISsoConfigRepository ssoConfigurationRepository,
IPolicyRepository policyRepository, IPolicyQuery policyQuery,
IOrgUserInviteTokenableFactory orgUserInviteTokenableFactory, IOrgUserInviteTokenableFactory orgUserInviteTokenableFactory,
IDataProtectorTokenFactory<OrgUserInviteTokenable> dataProtectorTokenFactory, IDataProtectorTokenFactory<OrgUserInviteTokenable> dataProtectorTokenFactory,
IMailService mailService) : ISendOrganizationInvitesCommand IMailService mailService) : ISendOrganizationInvitesCommand
@@ -58,7 +58,7 @@ public class SendOrganizationInvitesCommand(
// need to check the policy if the org has SSO enabled. // need to check the policy if the org has SSO enabled.
var orgSsoLoginRequiredPolicyEnabled = orgSsoEnabled && var orgSsoLoginRequiredPolicyEnabled = orgSsoEnabled &&
organization.UsePolicies && 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 // Generate the list of org users and expiring tokens
// create helper function to create expiring tokens // create helper function to create expiring tokens

View File

@@ -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
{
/// <summary>
/// Retrieves a summary view of an organization's usage of a policy specified by the <paramref name="policyType"/>.
/// </summary>
/// <remarks>
/// This query is the entrypoint for consumers interested in understanding how a particular <see cref="PolicyType"/>
/// has been applied to an organization; the resultant <see cref="PolicyStatus"/> is not indicative of explicit
/// policy configuration.
/// </remarks>
Task<PolicyStatus> RunAsync(Guid organizationId, PolicyType policyType);
}

View File

@@ -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<PolicyStatus> RunAsync(Guid organizationId, PolicyType policyType)
{
var dbPolicy = await policyRepository.GetByOrganizationIdTypeAsync(organizationId, policyType);
return new PolicyStatus(organizationId, policyType, dbPolicy);
}
}

View File

@@ -18,6 +18,7 @@ public static class PolicyServiceCollectionExtensions
services.AddScoped<ISavePolicyCommand, SavePolicyCommand>(); services.AddScoped<ISavePolicyCommand, SavePolicyCommand>();
services.AddScoped<IVNextSavePolicyCommand, VNextSavePolicyCommand>(); services.AddScoped<IVNextSavePolicyCommand, VNextSavePolicyCommand>();
services.AddScoped<IPolicyRequirementQuery, PolicyRequirementQuery>(); services.AddScoped<IPolicyRequirementQuery, PolicyRequirementQuery>();
services.AddScoped<IPolicyQuery, PolicyQuery>();
services.AddScoped<IPolicyEventHandlerFactory, PolicyEventHandlerHandlerFactory>(); services.AddScoped<IPolicyEventHandlerFactory, PolicyEventHandlerHandlerFactory>();
services.AddPolicyValidators(); services.AddPolicyValidators();

View File

@@ -48,7 +48,7 @@ public class OrganizationService : IOrganizationService
private readonly IEventService _eventService; private readonly IEventService _eventService;
private readonly IApplicationCacheService _applicationCacheService; private readonly IApplicationCacheService _applicationCacheService;
private readonly IStripePaymentService _paymentService; private readonly IStripePaymentService _paymentService;
private readonly IPolicyRepository _policyRepository; private readonly IPolicyQuery _policyQuery;
private readonly IPolicyService _policyService; private readonly IPolicyService _policyService;
private readonly ISsoUserRepository _ssoUserRepository; private readonly ISsoUserRepository _ssoUserRepository;
private readonly IGlobalSettings _globalSettings; private readonly IGlobalSettings _globalSettings;
@@ -75,7 +75,7 @@ public class OrganizationService : IOrganizationService
IEventService eventService, IEventService eventService,
IApplicationCacheService applicationCacheService, IApplicationCacheService applicationCacheService,
IStripePaymentService paymentService, IStripePaymentService paymentService,
IPolicyRepository policyRepository, IPolicyQuery policyQuery,
IPolicyService policyService, IPolicyService policyService,
ISsoUserRepository ssoUserRepository, ISsoUserRepository ssoUserRepository,
IGlobalSettings globalSettings, IGlobalSettings globalSettings,
@@ -102,7 +102,7 @@ public class OrganizationService : IOrganizationService
_eventService = eventService; _eventService = eventService;
_applicationCacheService = applicationCacheService; _applicationCacheService = applicationCacheService;
_paymentService = paymentService; _paymentService = paymentService;
_policyRepository = policyRepository; _policyQuery = policyQuery;
_policyService = policyService; _policyService = policyService;
_ssoUserRepository = ssoUserRepository; _ssoUserRepository = ssoUserRepository;
_globalSettings = globalSettings; _globalSettings = globalSettings;
@@ -835,9 +835,8 @@ public class OrganizationService : IOrganizationService
} }
// Make sure the organization has the policy enabled // Make sure the organization has the policy enabled
var resetPasswordPolicy = var resetPasswordPolicy = await _policyQuery.RunAsync(organizationId, PolicyType.ResetPassword);
await _policyRepository.GetByOrganizationIdTypeAsync(organizationId, PolicyType.ResetPassword); if (!resetPasswordPolicy.Enabled)
if (resetPasswordPolicy == null || !resetPasswordPolicy.Enabled)
{ {
throw new BadRequestException("Organization does not have the password reset policy enabled."); throw new BadRequestException("Organization does not have the password reset policy enabled.");
} }

View File

@@ -5,9 +5,9 @@ using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data; using Bit.Core.AdminConsole.Models.Data;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; 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.Models;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.Entities; using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Enums; using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Repositories; using Bit.Core.Auth.Repositories;
@@ -21,7 +21,7 @@ namespace Bit.Core.Auth.Services;
public class SsoConfigService : ISsoConfigService public class SsoConfigService : ISsoConfigService
{ {
private readonly ISsoConfigRepository _ssoConfigRepository; private readonly ISsoConfigRepository _ssoConfigRepository;
private readonly IPolicyRepository _policyRepository; private readonly IPolicyQuery _policyQuery;
private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IEventService _eventService; private readonly IEventService _eventService;
@@ -29,14 +29,14 @@ public class SsoConfigService : ISsoConfigService
public SsoConfigService( public SsoConfigService(
ISsoConfigRepository ssoConfigRepository, ISsoConfigRepository ssoConfigRepository,
IPolicyRepository policyRepository, IPolicyQuery policyQuery,
IOrganizationRepository organizationRepository, IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository, IOrganizationUserRepository organizationUserRepository,
IEventService eventService, IEventService eventService,
IVNextSavePolicyCommand vNextSavePolicyCommand) IVNextSavePolicyCommand vNextSavePolicyCommand)
{ {
_ssoConfigRepository = ssoConfigRepository; _ssoConfigRepository = ssoConfigRepository;
_policyRepository = policyRepository; _policyQuery = policyQuery;
_organizationRepository = organizationRepository; _organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository; _organizationUserRepository = organizationUserRepository;
_eventService = eventService; _eventService = eventService;
@@ -114,14 +114,14 @@ public class SsoConfigService : ISsoConfigService
throw new BadRequestException("Organization cannot use Key Connector."); throw new BadRequestException("Organization cannot use Key Connector.");
} }
var singleOrgPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.SingleOrg); var singleOrgPolicy = await _policyQuery.RunAsync(config.OrganizationId, PolicyType.SingleOrg);
if (singleOrgPolicy is not { Enabled: true }) if (!singleOrgPolicy.Enabled)
{ {
throw new BadRequestException("Key Connector requires the Single Organization policy to be enabled."); throw new BadRequestException("Key Connector requires the Single Organization policy to be enabled.");
} }
var ssoPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.RequireSso); var ssoPolicy = await _policyQuery.RunAsync(config.OrganizationId, PolicyType.RequireSso);
if (ssoPolicy is not { Enabled: true }) if (!ssoPolicy.Enabled)
{ {
throw new BadRequestException("Key Connector requires the Single Sign-On Authentication policy to be enabled."); throw new BadRequestException("Key Connector requires the Single Sign-On Authentication policy to be enabled.");
} }

View File

@@ -1,6 +1,6 @@
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; 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.Enums;
using Bit.Core.Auth.Models; using Bit.Core.Auth.Models;
using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Models.Business.Tokenables;
@@ -27,7 +27,7 @@ public class RegisterUserCommand : IRegisterUserCommand
private readonly IGlobalSettings _globalSettings; private readonly IGlobalSettings _globalSettings;
private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationRepository _organizationRepository;
private readonly IPolicyRepository _policyRepository; private readonly IPolicyQuery _policyQuery;
private readonly IOrganizationDomainRepository _organizationDomainRepository; private readonly IOrganizationDomainRepository _organizationDomainRepository;
private readonly IFeatureService _featureService; private readonly IFeatureService _featureService;
@@ -50,7 +50,7 @@ public class RegisterUserCommand : IRegisterUserCommand
IGlobalSettings globalSettings, IGlobalSettings globalSettings,
IOrganizationUserRepository organizationUserRepository, IOrganizationUserRepository organizationUserRepository,
IOrganizationRepository organizationRepository, IOrganizationRepository organizationRepository,
IPolicyRepository policyRepository, IPolicyQuery policyQuery,
IOrganizationDomainRepository organizationDomainRepository, IOrganizationDomainRepository organizationDomainRepository,
IFeatureService featureService, IFeatureService featureService,
IDataProtectionProvider dataProtectionProvider, IDataProtectionProvider dataProtectionProvider,
@@ -65,7 +65,7 @@ public class RegisterUserCommand : IRegisterUserCommand
_globalSettings = globalSettings; _globalSettings = globalSettings;
_organizationUserRepository = organizationUserRepository; _organizationUserRepository = organizationUserRepository;
_organizationRepository = organizationRepository; _organizationRepository = organizationRepository;
_policyRepository = policyRepository; _policyQuery = policyQuery;
_organizationDomainRepository = organizationDomainRepository; _organizationDomainRepository = organizationDomainRepository;
_featureService = featureService; _featureService = featureService;
@@ -246,9 +246,9 @@ public class RegisterUserCommand : IRegisterUserCommand
var orgUser = await _organizationUserRepository.GetByIdAsync(orgUserId.Value); var orgUser = await _organizationUserRepository.GetByIdAsync(orgUserId.Value);
if (orgUser != null) if (orgUser != null)
{ {
var twoFactorPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgUser.OrganizationId, var twoFactorPolicy = await _policyQuery.RunAsync(orgUser.OrganizationId,
PolicyType.TwoFactorAuthentication); PolicyType.TwoFactorAuthentication);
if (twoFactorPolicy != null && twoFactorPolicy.Enabled) if (twoFactorPolicy.Enabled)
{ {
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider> user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{ {

View File

@@ -5,6 +5,7 @@ using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.OrganizationConnectionConfigs; using Bit.Core.AdminConsole.Models.OrganizationConnectionConfigs;
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations; using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.Enums; using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Repositories; using Bit.Core.Auth.Repositories;
@@ -30,6 +31,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
private readonly IGroupRepository _groupRepository; private readonly IGroupRepository _groupRepository;
private readonly IStripePaymentService _paymentService; private readonly IStripePaymentService _paymentService;
private readonly IPolicyRepository _policyRepository; private readonly IPolicyRepository _policyRepository;
private readonly IPolicyQuery _policyQuery;
private readonly ISsoConfigRepository _ssoConfigRepository; private readonly ISsoConfigRepository _ssoConfigRepository;
private readonly IOrganizationConnectionRepository _organizationConnectionRepository; private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
private readonly IServiceAccountRepository _serviceAccountRepository; private readonly IServiceAccountRepository _serviceAccountRepository;
@@ -45,6 +47,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
IGroupRepository groupRepository, IGroupRepository groupRepository,
IStripePaymentService paymentService, IStripePaymentService paymentService,
IPolicyRepository policyRepository, IPolicyRepository policyRepository,
IPolicyQuery policyQuery,
ISsoConfigRepository ssoConfigRepository, ISsoConfigRepository ssoConfigRepository,
IOrganizationConnectionRepository organizationConnectionRepository, IOrganizationConnectionRepository organizationConnectionRepository,
IServiceAccountRepository serviceAccountRepository, IServiceAccountRepository serviceAccountRepository,
@@ -59,6 +62,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
_groupRepository = groupRepository; _groupRepository = groupRepository;
_paymentService = paymentService; _paymentService = paymentService;
_policyRepository = policyRepository; _policyRepository = policyRepository;
_policyQuery = policyQuery;
_ssoConfigRepository = ssoConfigRepository; _ssoConfigRepository = ssoConfigRepository;
_organizationConnectionRepository = organizationConnectionRepository; _organizationConnectionRepository = organizationConnectionRepository;
_serviceAccountRepository = serviceAccountRepository; _serviceAccountRepository = serviceAccountRepository;
@@ -184,9 +188,8 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
if (!newPlan.HasResetPassword && organization.UseResetPassword) if (!newPlan.HasResetPassword && organization.UseResetPassword)
{ {
var resetPasswordPolicy = var resetPasswordPolicy = await _policyQuery.RunAsync(organization.Id, PolicyType.ResetPassword);
await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword); if (resetPasswordPolicy.Enabled)
if (resetPasswordPolicy != null && resetPasswordPolicy.Enabled)
{ {
throw new BadRequestException("Your new plan does not allow the Password Reset feature. " + throw new BadRequestException("Your new plan does not allow the Password Reset feature. " +
"Disable your Password Reset policy."); "Disable your Password Reset policy.");

View File

@@ -61,7 +61,7 @@ public class UserService : UserManager<User>, IUserService
private readonly IEventService _eventService; private readonly IEventService _eventService;
private readonly IApplicationCacheService _applicationCacheService; private readonly IApplicationCacheService _applicationCacheService;
private readonly IStripePaymentService _paymentService; private readonly IStripePaymentService _paymentService;
private readonly IPolicyRepository _policyRepository; private readonly IPolicyQuery _policyQuery;
private readonly IPolicyService _policyService; private readonly IPolicyService _policyService;
private readonly IFido2 _fido2; private readonly IFido2 _fido2;
private readonly ICurrentContext _currentContext; private readonly ICurrentContext _currentContext;
@@ -98,7 +98,7 @@ public class UserService : UserManager<User>, IUserService
IEventService eventService, IEventService eventService,
IApplicationCacheService applicationCacheService, IApplicationCacheService applicationCacheService,
IStripePaymentService paymentService, IStripePaymentService paymentService,
IPolicyRepository policyRepository, IPolicyQuery policyQuery,
IPolicyService policyService, IPolicyService policyService,
IFido2 fido2, IFido2 fido2,
ICurrentContext currentContext, ICurrentContext currentContext,
@@ -139,7 +139,7 @@ public class UserService : UserManager<User>, IUserService
_eventService = eventService; _eventService = eventService;
_applicationCacheService = applicationCacheService; _applicationCacheService = applicationCacheService;
_paymentService = paymentService; _paymentService = paymentService;
_policyRepository = policyRepository; _policyQuery = policyQuery;
_policyService = policyService; _policyService = policyService;
_fido2 = fido2; _fido2 = fido2;
_currentContext = currentContext; _currentContext = currentContext;
@@ -722,9 +722,8 @@ public class UserService : UserManager<User>, IUserService
} }
// Enterprise policy must be enabled // Enterprise policy must be enabled
var resetPasswordPolicy = var resetPasswordPolicy = await _policyQuery.RunAsync(orgId, PolicyType.ResetPassword);
await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); if (!resetPasswordPolicy.Enabled)
if (resetPasswordPolicy == null || !resetPasswordPolicy.Enabled)
{ {
throw new BadRequestException("Organization does not have the password reset policy enabled."); throw new BadRequestException("Organization does not have the password reset policy enabled.");
} }

View File

@@ -44,7 +44,7 @@ public record ResourcePassword(string Hash) : SendAuthenticationMethod;
/// <summary> /// <summary>
/// Create a send claim by requesting a one time password (OTP) confirmation code. /// Create a send claim by requesting a one time password (OTP) confirmation code.
/// </summary> /// </summary>
/// <param name="Emails"> /// <param name="EmailHashes">
/// The list of email address **hashes** permitted access to the send. /// The list of email address **hashes** permitted access to the send.
/// </param> /// </param>
public record EmailOtp(string[] Emails) : SendAuthenticationMethod; public record EmailOtp(string[] EmailHashes) : SendAuthenticationMethod;

View File

@@ -1,4 +1,6 @@
using System.Security.Claims; using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Bit.Core; using Bit.Core;
using Bit.Core.Auth.Identity; using Bit.Core.Auth.Identity;
using Bit.Core.Auth.Identity.TokenProviders; using Bit.Core.Auth.Identity.TokenProviders;
@@ -40,8 +42,10 @@ public class SendEmailOtpRequestValidator(
return BuildErrorResult(SendAccessConstants.EmailOtpValidatorResults.EmailRequired); return BuildErrorResult(SendAccessConstants.EmailOtpValidatorResults.EmailRequired);
} }
// email must be in the list of emails in the EmailOtp array // email hash must be in the list of email hashes in the EmailOtp array
if (!authMethod.Emails.Contains(email)) byte[] hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(email));
string hashEmailHex = Convert.ToHexString(hashBytes).ToUpperInvariant();
if (!authMethod.EmailHashes.Contains(hashEmailHex))
{ {
return BuildErrorResult(SendAccessConstants.EmailOtpValidatorResults.EmailInvalid); return BuildErrorResult(SendAccessConstants.EmailOtpValidatorResults.EmailInvalid);
} }

View File

@@ -14,7 +14,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Utilities.v2.Results; using Bit.Core.AdminConsole.Utilities.v2.Results;
using Bit.Core.Auth.Entities; using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Repositories; 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.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Test.AdminConsole.AutoFixture;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
@@ -137,23 +137,20 @@ public class OrganizationUsersControllerTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task Accept_WhenOrganizationUsePoliciesIsEnabledAndResetPolicyIsEnabled_ShouldHandleResetPassword(Guid orgId, Guid orgUserId, public async Task Accept_WhenOrganizationUsePoliciesIsEnabledAndResetPolicyIsEnabled_ShouldHandleResetPassword(Guid orgId, Guid orgUserId,
OrganizationUserAcceptRequestModel model, User user, SutProvider<OrganizationUsersController> sutProvider) OrganizationUserAcceptRequestModel model, User user,
[Policy(PolicyType.ResetPassword, true)] PolicyStatus policy,
SutProvider<OrganizationUsersController> sutProvider)
{ {
// Arrange // Arrange
var applicationCacheService = sutProvider.GetDependency<IApplicationCacheService>(); var applicationCacheService = sutProvider.GetDependency<IApplicationCacheService>();
applicationCacheService.GetOrganizationAbilityAsync(orgId).Returns(new OrganizationAbility { UsePolicies = true }); applicationCacheService.GetOrganizationAbilityAsync(orgId).Returns(new OrganizationAbility { UsePolicies = true });
var policy = new Policy policy.Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, });
{
Enabled = true,
Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, }),
};
var userService = sutProvider.GetDependency<IUserService>(); var userService = sutProvider.GetDependency<IUserService>();
userService.GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); userService.GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
var policyQuery = sutProvider.GetDependency<IPolicyQuery>();
var policyRepository = sutProvider.GetDependency<IPolicyRepository>(); policyQuery.RunAsync(orgId,
policyRepository.GetByOrganizationIdTypeAsync(orgId,
PolicyType.ResetPassword).Returns(policy); PolicyType.ResetPassword).Returns(policy);
// Act // Act
@@ -167,29 +164,27 @@ public class OrganizationUsersControllerTests
await userService.Received(1).GetUserByPrincipalAsync(default); await userService.Received(1).GetUserByPrincipalAsync(default);
await applicationCacheService.Received(1).GetOrganizationAbilityAsync(orgId); await applicationCacheService.Received(1).GetOrganizationAbilityAsync(orgId);
await policyRepository.Received(1).GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); await policyQuery.Received(1).RunAsync(orgId, PolicyType.ResetPassword);
} }
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task Accept_WhenOrganizationUsePoliciesIsDisabled_ShouldNotHandleResetPassword(Guid orgId, Guid orgUserId, public async Task Accept_WhenOrganizationUsePoliciesIsDisabled_ShouldNotHandleResetPassword(Guid orgId, Guid orgUserId,
OrganizationUserAcceptRequestModel model, User user, SutProvider<OrganizationUsersController> sutProvider) OrganizationUserAcceptRequestModel model, User user,
[Policy(PolicyType.ResetPassword, true)] PolicyStatus policy,
SutProvider<OrganizationUsersController> sutProvider)
{ {
// Arrange // Arrange
var applicationCacheService = sutProvider.GetDependency<IApplicationCacheService>(); var applicationCacheService = sutProvider.GetDependency<IApplicationCacheService>();
applicationCacheService.GetOrganizationAbilityAsync(orgId).Returns(new OrganizationAbility { UsePolicies = false }); applicationCacheService.GetOrganizationAbilityAsync(orgId).Returns(new OrganizationAbility { UsePolicies = false });
var policy = new Policy policy.Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, });
{
Enabled = true,
Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, }),
};
var userService = sutProvider.GetDependency<IUserService>(); var userService = sutProvider.GetDependency<IUserService>();
userService.GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); userService.GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
var policyRepository = sutProvider.GetDependency<IPolicyRepository>(); var policyQuery = sutProvider.GetDependency<IPolicyQuery>();
policyRepository.GetByOrganizationIdTypeAsync(orgId, policyQuery.RunAsync(orgId,
PolicyType.ResetPassword).Returns(policy); PolicyType.ResetPassword).Returns(policy);
// Act // Act
@@ -202,7 +197,7 @@ public class OrganizationUsersControllerTests
await sutProvider.GetDependency<IOrganizationService>().Received(0) await sutProvider.GetDependency<IOrganizationService>().Received(0)
.UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id); .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); await applicationCacheService.Received(1).GetOrganizationAbilityAsync(orgId);
} }
@@ -383,7 +378,7 @@ public class OrganizationUsersControllerTests
var policyRequirementQuery = sutProvider.GetDependency<IPolicyRequirementQuery>(); var policyRequirementQuery = sutProvider.GetDependency<IPolicyRequirementQuery>();
var policyRepository = sutProvider.GetDependency<IPolicyRepository>(); var policyQuery = sutProvider.GetDependency<IPolicyQuery>();
var policyRequirement = new ResetPasswordPolicyRequirement { AutoEnrollOrganizations = [orgId] }; var policyRequirement = new ResetPasswordPolicyRequirement { AutoEnrollOrganizations = [orgId] };
@@ -400,7 +395,7 @@ public class OrganizationUsersControllerTests
await userService.Received(1).GetUserByPrincipalAsync(default); await userService.Received(1).GetUserByPrincipalAsync(default);
await applicationCacheService.Received(0).GetOrganizationAbilityAsync(orgId); 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<ResetPasswordPolicyRequirement>(user.Id); await policyRequirementQuery.Received(1).GetAsync<ResetPasswordPolicyRequirement>(user.Id);
Assert.True(policyRequirement.AutoEnrollEnabled(orgId)); Assert.True(policyRequirement.AutoEnrollEnabled(orgId));
} }
@@ -425,7 +420,7 @@ public class OrganizationUsersControllerTests
var userService = sutProvider.GetDependency<IUserService>(); var userService = sutProvider.GetDependency<IUserService>();
userService.GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); userService.GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
var policyRepository = sutProvider.GetDependency<IPolicyRepository>(); var policyQuery = sutProvider.GetDependency<IPolicyQuery>();
var policyRequirementQuery = sutProvider.GetDependency<IPolicyRequirementQuery>(); var policyRequirementQuery = sutProvider.GetDependency<IPolicyRequirementQuery>();
@@ -445,7 +440,7 @@ public class OrganizationUsersControllerTests
await userService.Received(1).GetUserByPrincipalAsync(default); await userService.Received(1).GetUserByPrincipalAsync(default);
await applicationCacheService.Received(0).GetOrganizationAbilityAsync(orgId); 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<ResetPasswordPolicyRequirement>(user.Id); await policyRequirementQuery.Received(1).GetAsync<ResetPasswordPolicyRequirement>(user.Id);
Assert.Equal("Master Password reset is required, but not provided.", exception.Message); Assert.Equal("Master Password reset is required, but not provided.", exception.Message);

View File

@@ -7,6 +7,7 @@ using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Models.Business; 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.Organizations.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
@@ -25,6 +26,7 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Test.AdminConsole.AutoFixture;
using Bit.Core.Test.Billing.Mocks; using Bit.Core.Test.Billing.Mocks;
using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider; using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
@@ -200,28 +202,21 @@ public class OrganizationsControllerTests
SutProvider<OrganizationsController> sutProvider, SutProvider<OrganizationsController> sutProvider,
User user, User user,
Organization organization, 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<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdentifierAsync(organization.Id.ToString()).Returns(organization); sutProvider.GetDependency<IOrganizationRepository>().GetByIdentifierAsync(organization.Id.ToString()).Returns(organization);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.PolicyRequirements).Returns(false); sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.PolicyRequirements).Returns(false);
sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(organization.Id, user.Id).Returns(organizationUser); sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(organization.Id, user.Id).Returns(organizationUser);
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword).Returns(policy); sutProvider.GetDependency<IPolicyQuery>().RunAsync(organization.Id, PolicyType.ResetPassword).Returns(policy);
var result = await sutProvider.Sut.GetAutoEnrollStatus(organization.Id.ToString()); var result = await sutProvider.Sut.GetAutoEnrollStatus(organization.Id.ToString());
await sutProvider.GetDependency<IUserService>().Received(1).GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()); await sutProvider.GetDependency<IUserService>().Received(1).GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>());
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).GetByIdentifierAsync(organization.Id.ToString()); await sutProvider.GetDependency<IOrganizationRepository>().Received(1).GetByIdentifierAsync(organization.Id.ToString());
await sutProvider.GetDependency<IPolicyRequirementQuery>().Received(0).GetAsync<ResetPasswordPolicyRequirement>(user.Id); await sutProvider.GetDependency<IPolicyRequirementQuery>().Received(0).GetAsync<ResetPasswordPolicyRequirement>(user.Id);
await sutProvider.GetDependency<IPolicyRepository>().Received(1).GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword); await sutProvider.GetDependency<IPolicyQuery>().Received(1).RunAsync(organization.Id, PolicyType.ResetPassword);
Assert.True(result.ResetPasswordEnabled); Assert.True(result.ResetPasswordEnabled);
} }

View File

@@ -1,14 +1,13 @@
using AutoFixture; using Bit.Api.AdminConsole.Models.Response.Helpers;
using Bit.Api.AdminConsole.Models.Response.Helpers;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
using NSubstitute; using NSubstitute;
using Xunit; using Xunit;
namespace Bit.Api.Test.AdminConsole.Models.Response.Helpers; namespace Bit.Api.Test.AdminConsole.Models.Response.Helpers;
public class PolicyDetailResponsesTests public class PolicyStatusResponsesTests
{ {
[Theory] [Theory]
[InlineData(true, false)] [InlineData(true, false)]
@@ -17,19 +16,13 @@ public class PolicyDetailResponsesTests
bool policyEnabled, bool policyEnabled,
bool expectedCanToggle) bool expectedCanToggle)
{ {
var fixture = new Fixture(); var policy = new PolicyStatus(Guid.NewGuid(), PolicyType.SingleOrg) { Enabled = policyEnabled };
var policy = fixture.Build<Policy>()
.Without(p => p.Data)
.With(p => p.Type, PolicyType.SingleOrg)
.With(p => p.Enabled, policyEnabled)
.Create();
var querySub = Substitute.For<IOrganizationHasVerifiedDomainsQuery>(); var querySub = Substitute.For<IOrganizationHasVerifiedDomainsQuery>();
querySub.HasVerifiedDomainsAsync(policy.OrganizationId) querySub.HasVerifiedDomainsAsync(policy.OrganizationId)
.Returns(true); .Returns(true);
var result = await policy.GetSingleOrgPolicyDetailResponseAsync(querySub); var result = await policy.GetSingleOrgPolicyStatusResponseAsync(querySub);
Assert.Equal(expectedCanToggle, result.CanToggleState); Assert.Equal(expectedCanToggle, result.CanToggleState);
} }
@@ -37,18 +30,13 @@ public class PolicyDetailResponsesTests
[Fact] [Fact]
public async Task GetSingleOrgPolicyDetailResponseAsync_WhenIsNotSingleOrgType_ThenShouldThrowArgumentException() public async Task GetSingleOrgPolicyDetailResponseAsync_WhenIsNotSingleOrgType_ThenShouldThrowArgumentException()
{ {
var fixture = new Fixture(); var policy = new PolicyStatus(Guid.NewGuid(), PolicyType.TwoFactorAuthentication);
var policy = fixture.Build<Policy>()
.Without(p => p.Data)
.With(p => p.Type, PolicyType.TwoFactorAuthentication)
.Create();
var querySub = Substitute.For<IOrganizationHasVerifiedDomainsQuery>(); var querySub = Substitute.For<IOrganizationHasVerifiedDomainsQuery>();
querySub.HasVerifiedDomainsAsync(policy.OrganizationId) querySub.HasVerifiedDomainsAsync(policy.OrganizationId)
.Returns(true); .Returns(true);
var action = async () => await policy.GetSingleOrgPolicyDetailResponseAsync(querySub); var action = async () => await policy.GetSingleOrgPolicyStatusResponseAsync(querySub);
await Assert.ThrowsAsync<ArgumentException>("policy", action); await Assert.ThrowsAsync<ArgumentException>("policy", action);
} }
@@ -56,18 +44,13 @@ public class PolicyDetailResponsesTests
[Fact] [Fact]
public async Task GetSingleOrgPolicyDetailResponseAsync_WhenIsSingleOrgTypeAndDoesNotHaveVerifiedDomains_ThenShouldBeAbleToToggle() public async Task GetSingleOrgPolicyDetailResponseAsync_WhenIsSingleOrgTypeAndDoesNotHaveVerifiedDomains_ThenShouldBeAbleToToggle()
{ {
var fixture = new Fixture(); var policy = new PolicyStatus(Guid.NewGuid(), PolicyType.SingleOrg);
var policy = fixture.Build<Policy>()
.Without(p => p.Data)
.With(p => p.Type, PolicyType.SingleOrg)
.Create();
var querySub = Substitute.For<IOrganizationHasVerifiedDomainsQuery>(); var querySub = Substitute.For<IOrganizationHasVerifiedDomainsQuery>();
querySub.HasVerifiedDomainsAsync(policy.OrganizationId) querySub.HasVerifiedDomainsAsync(policy.OrganizationId)
.Returns(false); .Returns(false);
var result = await policy.GetSingleOrgPolicyDetailResponseAsync(querySub); var result = await policy.GetSingleOrgPolicyStatusResponseAsync(querySub);
Assert.True(result.CanToggleState); Assert.True(result.CanToggleState);
} }

View File

@@ -1,6 +1,9 @@
using Bit.Api.Billing.Controllers; using Bit.Api.Billing.Controllers;
using Bit.Api.Models.Request.Organizations; using Bit.Api.Models.Request.Organizations;
using Bit.Core.AdminConsole.Entities; 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.Billing.Enums;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
@@ -10,6 +13,7 @@ using Bit.Core.Models.Data;
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Test.AdminConsole.AutoFixture;
using Bit.Core.Test.Billing.Mocks; using Bit.Core.Test.Billing.Mocks;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
@@ -82,7 +86,9 @@ public class OrganizationSponsorshipsControllerTests
[BitAutoData] [BitAutoData]
public async Task RedeemSponsorship_NotSponsoredOrgOwner_Success(string sponsorshipToken, User user, public async Task RedeemSponsorship_NotSponsoredOrgOwner_Success(string sponsorshipToken, User user,
OrganizationSponsorship sponsorship, Organization sponsoringOrganization, OrganizationSponsorship sponsorship, Organization sponsoringOrganization,
OrganizationSponsorshipRedeemRequestModel model, SutProvider<OrganizationSponsorshipsController> sutProvider) OrganizationSponsorshipRedeemRequestModel model,
[Policy(PolicyType.FreeFamiliesSponsorshipPolicy, false)] PolicyStatus policy,
SutProvider<OrganizationSponsorshipsController> sutProvider)
{ {
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id); sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id);
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id) sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id)
@@ -91,6 +97,9 @@ public class OrganizationSponsorshipsControllerTests
user.Email).Returns((true, sponsorship)); user.Email).Returns((true, sponsorship));
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.SponsoredOrganizationId).Returns(true); sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.SponsoredOrganizationId).Returns(true);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(model.SponsoredOrganizationId).Returns(sponsoringOrganization); sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(model.SponsoredOrganizationId).Returns(sponsoringOrganization);
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), PolicyType.FreeFamiliesSponsorshipPolicy)
.Returns(policy);
await sutProvider.Sut.RedeemSponsorship(sponsorshipToken, model); await sutProvider.Sut.RedeemSponsorship(sponsorshipToken, model);
@@ -101,14 +110,18 @@ public class OrganizationSponsorshipsControllerTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task PreValidateSponsorshipToken_ValidatesToken_Success(string sponsorshipToken, User user, public async Task PreValidateSponsorshipToken_ValidatesToken_Success(string sponsorshipToken, User user,
OrganizationSponsorship sponsorship, SutProvider<OrganizationSponsorshipsController> sutProvider) OrganizationSponsorship sponsorship,
[Policy(PolicyType.FreeFamiliesSponsorshipPolicy, false)] PolicyStatus policy,
SutProvider<OrganizationSponsorshipsController> sutProvider)
{ {
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id); sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id);
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id) sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id)
.Returns(user); .Returns(user);
sutProvider.GetDependency<IValidateRedemptionTokenCommand>() sutProvider.GetDependency<IValidateRedemptionTokenCommand>()
.ValidateRedemptionTokenAsync(sponsorshipToken, user.Email).Returns((true, sponsorship)); .ValidateRedemptionTokenAsync(sponsorshipToken, user.Email).Returns((true, sponsorship));
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), PolicyType.FreeFamiliesSponsorshipPolicy)
.Returns(policy);
await sutProvider.Sut.PreValidateSponsorshipToken(sponsorshipToken); await sutProvider.Sut.PreValidateSponsorshipToken(sponsorshipToken);
await sutProvider.GetDependency<IValidateRedemptionTokenCommand>().Received(1) await sutProvider.GetDependency<IValidateRedemptionTokenCommand>().Received(1)

View File

@@ -49,7 +49,7 @@ public class PoliciesControllerTests
sutProvider.GetDependency<IUserService>() sutProvider.GetDependency<IUserService>()
.GetProperUserId(Arg.Any<ClaimsPrincipal>()) .GetProperUserId(Arg.Any<ClaimsPrincipal>())
.Returns((Guid?)userId); .Returns(userId);
sutProvider.GetDependency<IOrganizationUserRepository>() sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByOrganizationAsync(orgId, userId) .GetByOrganizationAsync(orgId, userId)
@@ -95,7 +95,7 @@ public class PoliciesControllerTests
// Arrange // Arrange
sutProvider.GetDependency<IUserService>() sutProvider.GetDependency<IUserService>()
.GetProperUserId(Arg.Any<ClaimsPrincipal>()) .GetProperUserId(Arg.Any<ClaimsPrincipal>())
.Returns((Guid?)userId); .Returns(userId);
sutProvider.GetDependency<IOrganizationUserRepository>() sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByOrganizationAsync(orgId, userId) .GetByOrganizationAsync(orgId, userId)
@@ -113,7 +113,7 @@ public class PoliciesControllerTests
// Arrange // Arrange
sutProvider.GetDependency<IUserService>() sutProvider.GetDependency<IUserService>()
.GetProperUserId(Arg.Any<ClaimsPrincipal>()) .GetProperUserId(Arg.Any<ClaimsPrincipal>())
.Returns((Guid?)userId); .Returns(userId);
sutProvider.GetDependency<IOrganizationUserRepository>() sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByOrganizationAsync(orgId, userId) .GetByOrganizationAsync(orgId, userId)
@@ -135,7 +135,7 @@ public class PoliciesControllerTests
// Arrange // Arrange
sutProvider.GetDependency<IUserService>() sutProvider.GetDependency<IUserService>()
.GetProperUserId(Arg.Any<ClaimsPrincipal>()) .GetProperUserId(Arg.Any<ClaimsPrincipal>())
.Returns((Guid?)userId); .Returns(userId);
sutProvider.GetDependency<IOrganizationUserRepository>() sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByOrganizationAsync(orgId, userId) .GetByOrganizationAsync(orgId, userId)
@@ -186,59 +186,35 @@ public class PoliciesControllerTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task Get_WhenUserCanManagePolicies_WithExistingType_ReturnsExistingPolicy( public async Task Get_WhenUserCanManagePolicies_WithExistingType_ReturnsExistingPolicy(
SutProvider<PoliciesController> sutProvider, Guid orgId, Policy policy, int type) SutProvider<PoliciesController> sutProvider, Guid orgId, PolicyStatus policy, PolicyType type)
{ {
// Arrange // Arrange
sutProvider.GetDependency<ICurrentContext>() sutProvider.GetDependency<ICurrentContext>()
.ManagePolicies(orgId) .ManagePolicies(orgId)
.Returns(true); .Returns(true);
policy.Type = (PolicyType)type; policy.Type = type;
policy.Enabled = true; policy.Enabled = true;
policy.Data = null; policy.Data = null;
sutProvider.GetDependency<IPolicyRepository>() sutProvider.GetDependency<IPolicyQuery>()
.GetByOrganizationIdTypeAsync(orgId, (PolicyType)type) .RunAsync(orgId, type)
.Returns(policy); .Returns(policy);
// Act // Act
var result = await sutProvider.Sut.Get(orgId, type); var result = await sutProvider.Sut.Get(orgId, type);
// Assert // Assert
Assert.IsType<PolicyDetailResponseModel>(result); Assert.IsType<PolicyStatusResponseModel>(result);
Assert.Equal(policy.Id, result.Id);
Assert.Equal(policy.Type, result.Type); Assert.Equal(policy.Type, result.Type);
Assert.Equal(policy.Enabled, result.Enabled); Assert.Equal(policy.Enabled, result.Enabled);
Assert.Equal(policy.OrganizationId, result.OrganizationId); Assert.Equal(policy.OrganizationId, result.OrganizationId);
} }
[Theory]
[BitAutoData]
public async Task Get_WhenUserCanManagePolicies_WithNonExistingType_ReturnsDefaultPolicy(
SutProvider<PoliciesController> sutProvider, Guid orgId, int type)
{
// Arrange
sutProvider.GetDependency<ICurrentContext>()
.ManagePolicies(orgId)
.Returns(true);
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(orgId, (PolicyType)type)
.Returns((Policy)null);
// Act
var result = await sutProvider.Sut.Get(orgId, type);
// Assert
Assert.IsType<PolicyDetailResponseModel>(result);
Assert.Equal(result.Type, (PolicyType)type);
Assert.False(result.Enabled);
}
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task Get_WhenUserCannotManagePolicies_ThrowsNotFoundException( public async Task Get_WhenUserCannotManagePolicies_ThrowsNotFoundException(
SutProvider<PoliciesController> sutProvider, Guid orgId, int type) SutProvider<PoliciesController> sutProvider, Guid orgId, PolicyType type)
{ {
// Arrange // Arrange
sutProvider.GetDependency<ICurrentContext>() sutProvider.GetDependency<ICurrentContext>()

View File

@@ -0,0 +1,17 @@
using System.Security.Cryptography;
using System.Text;
namespace Bit.Test.Common.Helpers;
public class CryptographyHelper
{
/// <summary>
/// Returns a hex-encoded, SHA256 hash for the given string
/// </summary>
public static string HashAndEncode(string text)
{
var hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(text));
var hashEncoded = Convert.ToHexString(hashBytes).ToUpperInvariant();
return hashEncoded;
}
}

View File

@@ -3,6 +3,7 @@ using AutoFixture;
using AutoFixture.Xunit2; using AutoFixture.Xunit2;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
namespace Bit.Core.Test.AdminConsole.AutoFixture; namespace Bit.Core.Test.AdminConsole.AutoFixture;
@@ -10,19 +11,30 @@ internal class PolicyCustomization : ICustomization
{ {
public PolicyType Type { get; set; } public PolicyType Type { get; set; }
public bool Enabled { 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; Type = type;
Enabled = enabled; Enabled = enabled;
Data = data;
} }
public void Customize(IFixture fixture) public void Customize(IFixture fixture)
{ {
var orgId = Guid.NewGuid();
fixture.Customize<Policy>(composer => composer fixture.Customize<Policy>(composer => composer
.With(o => o.OrganizationId, Guid.NewGuid()) .With(o => o.OrganizationId, orgId)
.With(o => o.Type, Type) .With(o => o.Type, Type)
.With(o => o.Enabled, Enabled)); .With(o => o.Enabled, Enabled)
.With(o => o.Data, Data));
fixture.Customize<PolicyStatus>(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 PolicyType _type;
private readonly bool _enabled; 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; _type = type;
_enabled = enabled; _enabled = enabled;
_data = data;
} }
public override ICustomization GetCustomization(ParameterInfo parameter) public override ICustomization GetCustomization(ParameterInfo parameter)
{ {
return new PolicyCustomization(_type, _enabled); return new PolicyCustomization(_type, _enabled, _data);
} }
} }

View File

@@ -1,14 +1,16 @@
using AutoFixture; using AutoFixture;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.AccountRecovery; using Bit.Core.AdminConsole.OrganizationFeatures.AccountRecovery;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Platform.Push; using Bit.Core.Platform.Push;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Test.AdminConsole.AutoFixture;
using Bit.Core.Test.AutoFixture.OrganizationUserFixtures; using Bit.Core.Test.AutoFixture.OrganizationUserFixtures;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
@@ -29,11 +31,12 @@ public class AdminRecoverAccountCommandTests
Organization organization, Organization organization,
OrganizationUser organizationUser, OrganizationUser organizationUser,
User user, User user,
[Policy(PolicyType.ResetPassword, true)] PolicyStatus policy,
SutProvider<AdminRecoverAccountCommand> sutProvider) SutProvider<AdminRecoverAccountCommand> sutProvider)
{ {
// Arrange // Arrange
SetupValidOrganization(sutProvider, organization); SetupValidOrganization(sutProvider, organization);
SetupValidPolicy(sutProvider, organization); SetupValidPolicy(sutProvider, organization, policy);
SetupValidOrganizationUser(organizationUser, organization.Id); SetupValidOrganizationUser(organizationUser, organization.Id);
SetupValidUser(sutProvider, user, organizationUser); SetupValidUser(sutProvider, user, organizationUser);
SetupSuccessfulPasswordUpdate(sutProvider, user, newMasterPassword); SetupSuccessfulPasswordUpdate(sutProvider, user, newMasterPassword);
@@ -87,25 +90,18 @@ public class AdminRecoverAccountCommandTests
Assert.Equal("Organization does not allow password reset.", exception.Message); Assert.Equal("Organization does not allow password reset.", exception.Message);
} }
public static IEnumerable<object[]> InvalidPolicies => new object[][]
{
[new Policy { Type = PolicyType.ResetPassword, Enabled = false }], [null]
};
[Theory] [Theory]
[BitMemberAutoData(nameof(InvalidPolicies))] [BitAutoData]
public async Task RecoverAccountAsync_InvalidPolicy_ThrowsBadRequest( public async Task RecoverAccountAsync_InvalidPolicy_ThrowsBadRequest(
Policy resetPasswordPolicy,
string newMasterPassword, string newMasterPassword,
string key, string key,
Organization organization, Organization organization,
[Policy(PolicyType.ResetPassword, false)] PolicyStatus policy,
SutProvider<AdminRecoverAccountCommand> sutProvider) SutProvider<AdminRecoverAccountCommand> sutProvider)
{ {
// Arrange // Arrange
SetupValidOrganization(sutProvider, organization); SetupValidOrganization(sutProvider, organization);
sutProvider.GetDependency<IPolicyRepository>() SetupValidPolicy(sutProvider, organization, policy);
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword)
.Returns(resetPasswordPolicy);
// Act & Assert // Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(() => var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
@@ -171,11 +167,12 @@ public class AdminRecoverAccountCommandTests
Organization organization, Organization organization,
string newMasterPassword, string newMasterPassword,
string key, string key,
[Policy(PolicyType.ResetPassword, true)] PolicyStatus policy,
SutProvider<AdminRecoverAccountCommand> sutProvider) SutProvider<AdminRecoverAccountCommand> sutProvider)
{ {
// Arrange // Arrange
SetupValidOrganization(sutProvider, organization); SetupValidOrganization(sutProvider, organization);
SetupValidPolicy(sutProvider, organization); SetupValidPolicy(sutProvider, organization, policy);
// Act & Assert // Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(() => var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
@@ -190,11 +187,12 @@ public class AdminRecoverAccountCommandTests
string key, string key,
Organization organization, Organization organization,
OrganizationUser organizationUser, OrganizationUser organizationUser,
[Policy(PolicyType.ResetPassword, true)] PolicyStatus policy,
SutProvider<AdminRecoverAccountCommand> sutProvider) SutProvider<AdminRecoverAccountCommand> sutProvider)
{ {
// Arrange // Arrange
SetupValidOrganization(sutProvider, organization); SetupValidOrganization(sutProvider, organization);
SetupValidPolicy(sutProvider, organization); SetupValidPolicy(sutProvider, organization, policy);
SetupValidOrganizationUser(organizationUser, organization.Id); SetupValidOrganizationUser(organizationUser, organization.Id);
sutProvider.GetDependency<IUserService>() sutProvider.GetDependency<IUserService>()
.GetUserByIdAsync(organizationUser.UserId!.Value) .GetUserByIdAsync(organizationUser.UserId!.Value)
@@ -213,11 +211,12 @@ public class AdminRecoverAccountCommandTests
Organization organization, Organization organization,
OrganizationUser organizationUser, OrganizationUser organizationUser,
User user, User user,
[Policy(PolicyType.ResetPassword, true)] PolicyStatus policy,
SutProvider<AdminRecoverAccountCommand> sutProvider) SutProvider<AdminRecoverAccountCommand> sutProvider)
{ {
// Arrange // Arrange
SetupValidOrganization(sutProvider, organization); SetupValidOrganization(sutProvider, organization);
SetupValidPolicy(sutProvider, organization); SetupValidPolicy(sutProvider, organization, policy);
SetupValidOrganizationUser(organizationUser, organization.Id); SetupValidOrganizationUser(organizationUser, organization.Id);
user.UsesKeyConnector = true; user.UsesKeyConnector = true;
sutProvider.GetDependency<IUserService>() sutProvider.GetDependency<IUserService>()
@@ -238,11 +237,10 @@ public class AdminRecoverAccountCommandTests
.Returns(organization); .Returns(organization);
} }
private static void SetupValidPolicy(SutProvider<AdminRecoverAccountCommand> sutProvider, Organization organization) private static void SetupValidPolicy(SutProvider<AdminRecoverAccountCommand> sutProvider, Organization organization, PolicyStatus policy)
{ {
var policy = new Policy { Type = PolicyType.ResetPassword, Enabled = true }; sutProvider.GetDependency<IPolicyQuery>()
sutProvider.GetDependency<IPolicyRepository>() .RunAsync(organization.Id, PolicyType.ResetPassword)
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword)
.Returns(policy); .Returns(policy);
} }

View File

@@ -7,7 +7,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.DeleteClaimed
using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Enforcement.AutoConfirm;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Entities; using Bit.Core.Entities;
@@ -120,7 +119,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
[Organization(useAutomaticUserConfirmation: true, planType: PlanType.EnterpriseAnnually)] Organization organization, [Organization(useAutomaticUserConfirmation: true, planType: PlanType.EnterpriseAnnually)] Organization organization,
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
User user, User user,
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy)
{ {
// Arrange // Arrange
organizationUser.UserId = user.Id; organizationUser.UserId = user.Id;
@@ -137,8 +136,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
Key = "test-key" Key = "test-key"
}; };
sutProvider.GetDependency<IPolicyRepository>() sutProvider.GetDependency<IPolicyQuery>()
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
.Returns(autoConfirmPolicy); .Returns(autoConfirmPolicy);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>() sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
@@ -280,7 +279,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
[Organization(useAutomaticUserConfirmation: true)] Organization organization, [Organization(useAutomaticUserConfirmation: true)] Organization organization,
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
Guid userId, Guid userId,
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy)
{ {
// Arrange // Arrange
organizationUser.UserId = userId; organizationUser.UserId = userId;
@@ -303,8 +302,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
PolicyType = PolicyType.TwoFactorAuthentication PolicyType = PolicyType.TwoFactorAuthentication
}; };
sutProvider.GetDependency<IPolicyRepository>() sutProvider.GetDependency<IPolicyQuery>()
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
.Returns(autoConfirmPolicy); .Returns(autoConfirmPolicy);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>() sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
@@ -334,7 +333,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
[Organization(useAutomaticUserConfirmation: true)] Organization organization, [Organization(useAutomaticUserConfirmation: true)] Organization organization,
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
User user, User user,
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy)
{ {
// Arrange // Arrange
organizationUser.UserId = user.Id; organizationUser.UserId = user.Id;
@@ -351,8 +350,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
Key = "test-key" Key = "test-key"
}; };
sutProvider.GetDependency<IPolicyRepository>() sutProvider.GetDependency<IPolicyQuery>()
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
.Returns(autoConfirmPolicy); .Returns(autoConfirmPolicy);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>() sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
@@ -389,7 +388,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
[Organization(useAutomaticUserConfirmation: true)] Organization organization, [Organization(useAutomaticUserConfirmation: true)] Organization organization,
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
User user, User user,
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy)
{ {
// Arrange // Arrange
organizationUser.UserId = user.Id; organizationUser.UserId = user.Id;
@@ -406,8 +405,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
Key = "test-key" Key = "test-key"
}; };
sutProvider.GetDependency<IPolicyRepository>() sutProvider.GetDependency<IPolicyQuery>()
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
.Returns(autoConfirmPolicy); .Returns(autoConfirmPolicy);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>() sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
@@ -448,7 +447,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
[Organization(useAutomaticUserConfirmation: true)] Organization organization, [Organization(useAutomaticUserConfirmation: true)] Organization organization,
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
User user, User user,
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy)
{ {
// Arrange // Arrange
organizationUser.UserId = user.Id; organizationUser.UserId = user.Id;
@@ -465,8 +464,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
Key = "test-key" Key = "test-key"
}; };
sutProvider.GetDependency<IPolicyRepository>() sutProvider.GetDependency<IPolicyQuery>()
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
.Returns(autoConfirmPolicy); .Returns(autoConfirmPolicy);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>() sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
@@ -501,7 +500,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
SutProvider<AutomaticallyConfirmOrganizationUsersValidator> sutProvider, SutProvider<AutomaticallyConfirmOrganizationUsersValidator> sutProvider,
Organization organization, Organization organization,
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
Guid userId) Guid userId,
[Policy(PolicyType.AutomaticUserConfirmation, false)] PolicyStatus policy)
{ {
// Arrange // Arrange
organizationUser.UserId = userId; organizationUser.UserId = userId;
@@ -518,9 +518,9 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
Key = "test-key" Key = "test-key"
}; };
sutProvider.GetDependency<IPolicyRepository>() sutProvider.GetDependency<IPolicyQuery>()
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
.Returns((Policy)null); .Returns(policy);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>() sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>()) .TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<Guid>>())
@@ -545,7 +545,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
[Organization(useAutomaticUserConfirmation: false)] Organization organization, [Organization(useAutomaticUserConfirmation: false)] Organization organization,
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
Guid userId, Guid userId,
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy)
{ {
// Arrange // Arrange
organizationUser.UserId = userId; organizationUser.UserId = userId;
@@ -562,8 +562,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
Key = "test-key" Key = "test-key"
}; };
sutProvider.GetDependency<IPolicyRepository>() sutProvider.GetDependency<IPolicyQuery>()
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
.Returns(autoConfirmPolicy); .Returns(autoConfirmPolicy);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>() sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
@@ -589,7 +589,7 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
[Organization(useAutomaticUserConfirmation: true)] Organization organization, [Organization(useAutomaticUserConfirmation: true)] Organization organization,
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser organizationUser,
User user, User user,
[Policy(PolicyType.AutomaticUserConfirmation)] Policy autoConfirmPolicy) [Policy(PolicyType.AutomaticUserConfirmation)] PolicyStatus autoConfirmPolicy)
{ {
// Arrange // Arrange
organizationUser.UserId = user.Id; organizationUser.UserId = user.Id;
@@ -606,8 +606,8 @@ public class AutomaticallyConfirmOrganizationUsersValidatorTests
Key = "test-key" Key = "test-key"
}; };
sutProvider.GetDependency<IPolicyRepository>() sutProvider.GetDependency<IPolicyQuery>()
.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.AutomaticUserConfirmation) .RunAsync(organization.Id, PolicyType.AutomaticUserConfirmation)
.Returns(autoConfirmPolicy); .Returns(autoConfirmPolicy);
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>() sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()

View File

@@ -1,7 +1,9 @@
using Bit.Core.AdminConsole.Entities; 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;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; 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.Entities;
using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Repositories; using Bit.Core.Auth.Repositories;
@@ -9,6 +11,7 @@ using Bit.Core.Billing.Enums;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Models.Mail; using Bit.Core.Models.Mail;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Test.AdminConsole.AutoFixture;
using Bit.Core.Test.AutoFixture.OrganizationFixtures; using Bit.Core.Test.AutoFixture.OrganizationFixtures;
using Bit.Core.Tokens; using Bit.Core.Tokens;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
@@ -31,6 +34,7 @@ public class SendOrganizationInvitesCommandTests
Organization organization, Organization organization,
SsoConfig ssoConfig, SsoConfig ssoConfig,
OrganizationUser invite, OrganizationUser invite,
[Policy(PolicyType.RequireSso, false)] PolicyStatus policy,
SutProvider<SendOrganizationInvitesCommand> sutProvider) SutProvider<SendOrganizationInvitesCommand> sutProvider)
{ {
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks // 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<ISsoConfigRepository>().GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig); sutProvider.GetDependency<ISsoConfigRepository>().GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig);
// Return null policy to mimic new org that's never turned on the require sso policy // Return null policy to mimic new org that's never turned on the require sso policy
sutProvider.GetDependency<IPolicyRepository>().GetManyByOrganizationIdAsync(organization.Id).ReturnsNull(); sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(organization.Id, PolicyType.RequireSso)
.Returns(policy);
// Mock tokenable factory to return a token that expires in 5 days // Mock tokenable factory to return a token that expires in 5 days
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>() sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()

View File

@@ -2,9 +2,9 @@
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data; using Bit.Core.AdminConsole.Models.Data;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; 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.Models;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.Entities; using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Enums; using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Data; using Bit.Core.Auth.Models.Data;
@@ -13,6 +13,7 @@ using Bit.Core.Auth.Services;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Test.AdminConsole.AutoFixture;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute; using NSubstitute;
@@ -163,7 +164,8 @@ public class SsoConfigServiceTests
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider, public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization) Organization organization,
[Policy(PolicyType.SingleOrg, false)] PolicyStatus policy)
{ {
var utcNow = DateTime.UtcNow; var utcNow = DateTime.UtcNow;
@@ -180,6 +182,9 @@ public class SsoConfigServiceTests
RevisionDate = utcNow.AddDays(-10), RevisionDate = utcNow.AddDays(-10),
}; };
sutProvider.GetDependency<IPolicyQuery>().RunAsync(
Arg.Any<Guid>(), PolicyType.SingleOrg).Returns(policy);
var exception = await Assert.ThrowsAsync<BadRequestException>( var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization)); () => sutProvider.Sut.SaveAsync(ssoConfig, organization));
@@ -191,7 +196,9 @@ public class SsoConfigServiceTests
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task SaveAsync_KeyConnector_SsoPolicyNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider, public async Task SaveAsync_KeyConnector_SsoPolicyNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization) Organization organization,
[Policy(PolicyType.SingleOrg, true)] PolicyStatus singleOrgPolicy,
[Policy(PolicyType.RequireSso, false)] PolicyStatus requireSsoPolicy)
{ {
var utcNow = DateTime.UtcNow; var utcNow = DateTime.UtcNow;
@@ -208,11 +215,10 @@ public class SsoConfigServiceTests
RevisionDate = utcNow.AddDays(-10), RevisionDate = utcNow.AddDays(-10),
}; };
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync( sutProvider.GetDependency<IPolicyQuery>().RunAsync(
Arg.Any<Guid>(), PolicyType.SingleOrg).Returns(new Policy Arg.Any<Guid>(), PolicyType.SingleOrg).Returns(singleOrgPolicy);
{ sutProvider.GetDependency<IPolicyQuery>().RunAsync(
Enabled = true Arg.Any<Guid>(), PolicyType.RequireSso).Returns(requireSsoPolicy);
});
var exception = await Assert.ThrowsAsync<BadRequestException>( var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization)); () => sutProvider.Sut.SaveAsync(ssoConfig, organization));
@@ -225,7 +231,8 @@ public class SsoConfigServiceTests
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task SaveAsync_KeyConnector_SsoConfigNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider, public async Task SaveAsync_KeyConnector_SsoConfigNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization) Organization organization,
[Policy(PolicyType.SingleOrg, true)] PolicyStatus policy)
{ {
var utcNow = DateTime.UtcNow; var utcNow = DateTime.UtcNow;
@@ -242,11 +249,8 @@ public class SsoConfigServiceTests
RevisionDate = utcNow.AddDays(-10), RevisionDate = utcNow.AddDays(-10),
}; };
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync( sutProvider.GetDependency<IPolicyQuery>().RunAsync(
Arg.Any<Guid>(), Arg.Any<PolicyType>()).Returns(new Policy Arg.Any<Guid>(), Arg.Any<PolicyType>()).Returns(policy);
{
Enabled = true
});
var exception = await Assert.ThrowsAsync<BadRequestException>( var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization)); () => sutProvider.Sut.SaveAsync(ssoConfig, organization));
@@ -259,7 +263,8 @@ public class SsoConfigServiceTests
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task SaveAsync_KeyConnector_KeyConnectorAbilityNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider, public async Task SaveAsync_KeyConnector_KeyConnectorAbilityNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization) Organization organization,
[Policy(PolicyType.SingleOrg, true)] PolicyStatus policy)
{ {
var utcNow = DateTime.UtcNow; var utcNow = DateTime.UtcNow;
@@ -277,11 +282,8 @@ public class SsoConfigServiceTests
RevisionDate = utcNow.AddDays(-10), RevisionDate = utcNow.AddDays(-10),
}; };
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync( sutProvider.GetDependency<IPolicyQuery>().RunAsync(
Arg.Any<Guid>(), Arg.Any<PolicyType>()).Returns(new Policy Arg.Any<Guid>(), Arg.Any<PolicyType>()).Returns(policy);
{
Enabled = true,
});
var exception = await Assert.ThrowsAsync<BadRequestException>( var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization)); () => sutProvider.Sut.SaveAsync(ssoConfig, organization));
@@ -294,7 +296,8 @@ public class SsoConfigServiceTests
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task SaveAsync_KeyConnector_Success(SutProvider<SsoConfigService> sutProvider, public async Task SaveAsync_KeyConnector_Success(SutProvider<SsoConfigService> sutProvider,
Organization organization) Organization organization,
[Policy(PolicyType.SingleOrg, true)] PolicyStatus policy)
{ {
var utcNow = DateTime.UtcNow; var utcNow = DateTime.UtcNow;
@@ -312,11 +315,8 @@ public class SsoConfigServiceTests
RevisionDate = utcNow.AddDays(-10), RevisionDate = utcNow.AddDays(-10),
}; };
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync( sutProvider.GetDependency<IPolicyQuery>().RunAsync(
Arg.Any<Guid>(), Arg.Any<PolicyType>()).Returns(new Policy Arg.Any<Guid>(), Arg.Any<PolicyType>()).Returns(policy);
{
Enabled = true,
});
await sutProvider.Sut.SaveAsync(ssoConfig, organization); await sutProvider.Sut.SaveAsync(ssoConfig, organization);

View File

@@ -1,7 +1,8 @@
using System.Text; using System.Text;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; 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.Enums;
using Bit.Core.Auth.Models; using Bit.Core.Auth.Models;
using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Models.Business.Tokenables;
@@ -13,6 +14,7 @@ using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterpri
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Settings; using Bit.Core.Settings;
using Bit.Core.Test.AdminConsole.AutoFixture;
using Bit.Core.Tokens; using Bit.Core.Tokens;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
@@ -241,7 +243,8 @@ public class RegisterUserCommandTests
[BitAutoData(true, "sampleInitiationPath")] [BitAutoData(true, "sampleInitiationPath")]
[BitAutoData(true, "Secrets Manager trial")] [BitAutoData(true, "Secrets Manager trial")]
public async Task RegisterUserViaOrganizationInviteToken_ComplexHappyPath_Succeeds(bool addUserReferenceData, string initiationPath, public async Task RegisterUserViaOrganizationInviteToken_ComplexHappyPath_Succeeds(bool addUserReferenceData, string initiationPath,
SutProvider<RegisterUserCommand> sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId, Policy twoFactorPolicy) SutProvider<RegisterUserCommand> sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId,
[Policy(PolicyType.TwoFactorAuthentication, true)] PolicyStatus policy)
{ {
// Arrange // Arrange
sutProvider.GetDependency<IGlobalSettings>() sutProvider.GetDependency<IGlobalSettings>()
@@ -267,10 +270,9 @@ public class RegisterUserCommandTests
.GetByIdAsync(orgUserId) .GetByIdAsync(orgUserId)
.Returns(orgUser); .Returns(orgUser);
twoFactorPolicy.Enabled = true; sutProvider.GetDependency<IPolicyQuery>()
sutProvider.GetDependency<IPolicyRepository>() .RunAsync(orgUser.OrganizationId, PolicyType.TwoFactorAuthentication)
.GetByOrganizationIdTypeAsync(orgUser.OrganizationId, PolicyType.TwoFactorAuthentication) .Returns(policy);
.Returns(twoFactorPolicy);
sutProvider.GetDependency<IUserService>() sutProvider.GetDependency<IUserService>()
.CreateUserAsync(user, masterPasswordHash) .CreateUserAsync(user, masterPasswordHash)
@@ -286,9 +288,9 @@ public class RegisterUserCommandTests
.Received(1) .Received(1)
.GetByIdAsync(orgUserId); .GetByIdAsync(orgUserId);
await sutProvider.GetDependency<IPolicyRepository>() await sutProvider.GetDependency<IPolicyQuery>()
.Received(1) .Received(1)
.GetByOrganizationIdTypeAsync(orgUser.OrganizationId, PolicyType.TwoFactorAuthentication); .RunAsync(orgUser.OrganizationId, PolicyType.TwoFactorAuthentication);
sutProvider.GetDependency<IUserService>() sutProvider.GetDependency<IUserService>()
.Received(1) .Received(1)
@@ -431,7 +433,8 @@ public class RegisterUserCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task RegisterUserViaOrganizationInviteToken_BlockedDomainFromDifferentOrg_ThrowsBadRequestException( public async Task RegisterUserViaOrganizationInviteToken_BlockedDomainFromDifferentOrg_ThrowsBadRequestException(
SutProvider<RegisterUserCommand> sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId) SutProvider<RegisterUserCommand> sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId,
[Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy)
{ {
// Arrange // Arrange
user.Email = "user@blocked-domain.com"; user.Email = "user@blocked-domain.com";
@@ -463,6 +466,10 @@ public class RegisterUserCommandTests
.HasVerifiedDomainWithBlockClaimedDomainPolicyAsync("blocked-domain.com", orgUser.OrganizationId) .HasVerifiedDomainWithBlockClaimedDomainPolicyAsync("blocked-domain.com", orgUser.OrganizationId)
.Returns(true); .Returns(true);
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), PolicyType.TwoFactorAuthentication)
.Returns(policy);
// Act & Assert // Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(() => var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId)); sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId));
@@ -472,7 +479,8 @@ public class RegisterUserCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task RegisterUserViaOrganizationInviteToken_BlockedDomainFromSameOrg_Succeeds( public async Task RegisterUserViaOrganizationInviteToken_BlockedDomainFromSameOrg_Succeeds(
SutProvider<RegisterUserCommand> sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId) SutProvider<RegisterUserCommand> sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId,
[Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy)
{ {
// Arrange // Arrange
user.Email = "user@company-domain.com"; user.Email = "user@company-domain.com";
@@ -509,6 +517,10 @@ public class RegisterUserCommandTests
.CreateUserAsync(user, masterPasswordHash) .CreateUserAsync(user, masterPasswordHash)
.Returns(IdentityResult.Success); .Returns(IdentityResult.Success);
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), PolicyType.TwoFactorAuthentication)
.Returns(policy);
// Act // Act
var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId); var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId);
@@ -1245,6 +1257,7 @@ public class RegisterUserCommandTests
OrganizationUser orgUser, OrganizationUser orgUser,
string orgInviteToken, string orgInviteToken,
string masterPasswordHash, string masterPasswordHash,
[Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy,
SutProvider<RegisterUserCommand> sutProvider) SutProvider<RegisterUserCommand> sutProvider)
{ {
// Arrange // Arrange
@@ -1259,9 +1272,9 @@ public class RegisterUserCommandTests
.GetByIdAsync(orgUser.Id) .GetByIdAsync(orgUser.Id)
.Returns(orgUser); .Returns(orgUser);
sutProvider.GetDependency<IPolicyRepository>() sutProvider.GetDependency<IPolicyQuery>()
.GetByOrganizationIdTypeAsync(Arg.Any<Guid>(), PolicyType.TwoFactorAuthentication) .RunAsync(Arg.Any<Guid>(), PolicyType.TwoFactorAuthentication)
.Returns((Policy)null); .Returns(policy);
sutProvider.GetDependency<IOrganizationRepository>() sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(orgUser.OrganizationId) .GetByIdAsync(orgUser.OrganizationId)
@@ -1331,6 +1344,7 @@ public class RegisterUserCommandTests
OrganizationUser orgUser, OrganizationUser orgUser,
string masterPasswordHash, string masterPasswordHash,
string orgInviteToken, string orgInviteToken,
[Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy,
SutProvider<RegisterUserCommand> sutProvider) SutProvider<RegisterUserCommand> sutProvider)
{ {
// Arrange // Arrange
@@ -1346,9 +1360,9 @@ public class RegisterUserCommandTests
.GetByIdAsync(orgUser.Id) .GetByIdAsync(orgUser.Id)
.Returns(orgUser); .Returns(orgUser);
sutProvider.GetDependency<IPolicyRepository>() sutProvider.GetDependency<IPolicyQuery>()
.GetByOrganizationIdTypeAsync(Arg.Any<Guid>(), PolicyType.TwoFactorAuthentication) .RunAsync(Arg.Any<Guid>(), PolicyType.TwoFactorAuthentication)
.Returns((Policy)null); .Returns(policy);
sutProvider.GetDependency<IOrganizationRepository>() sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(orgUser.OrganizationId) .GetByIdAsync(orgUser.OrganizationId)

View File

@@ -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.Pricing;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
@@ -9,6 +12,7 @@ using Bit.Core.OrganizationFeatures.OrganizationSubscriptions;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.SecretsManager.Repositories; using Bit.Core.SecretsManager.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Test.AdminConsole.AutoFixture;
using Bit.Core.Test.AutoFixture.OrganizationFixtures; using Bit.Core.Test.AutoFixture.OrganizationFixtures;
using Bit.Core.Test.Billing.Mocks; using Bit.Core.Test.Billing.Mocks;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
@@ -72,8 +76,12 @@ public class UpgradeOrganizationPlanCommandTests
[Theory] [Theory]
[FreeOrganizationUpgradeCustomize, BitAutoData] [FreeOrganizationUpgradeCustomize, BitAutoData]
public async Task UpgradePlan_Passes(Organization organization, OrganizationUpgrade upgrade, public async Task UpgradePlan_Passes(Organization organization, OrganizationUpgrade upgrade,
[Policy(PolicyType.ResetPassword, false)] PolicyStatus policy,
SutProvider<UpgradeOrganizationPlanCommand> sutProvider) SutProvider<UpgradeOrganizationPlanCommand> sutProvider)
{ {
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), Arg.Any<PolicyType>())
.Returns(policy);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization); sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType)); sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType));
upgrade.AdditionalSmSeats = 10; upgrade.AdditionalSmSeats = 10;
@@ -100,6 +108,7 @@ public class UpgradeOrganizationPlanCommandTests
PlanType planType, PlanType planType,
Organization organization, Organization organization,
OrganizationUpgrade organizationUpgrade, OrganizationUpgrade organizationUpgrade,
[Policy(PolicyType.ResetPassword, false)] PolicyStatus policy,
SutProvider<UpgradeOrganizationPlanCommand> sutProvider) SutProvider<UpgradeOrganizationPlanCommand> sutProvider)
{ {
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization); sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
@@ -116,6 +125,9 @@ public class UpgradeOrganizationPlanCommandTests
organizationUpgrade.Plan = planType; organizationUpgrade.Plan = planType;
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organizationUpgrade.Plan).Returns(MockPlans.Get(organizationUpgrade.Plan)); sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organizationUpgrade.Plan).Returns(MockPlans.Get(organizationUpgrade.Plan));
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), Arg.Any<PolicyType>())
.Returns(policy);
sutProvider.GetDependency<IOrganizationRepository>() sutProvider.GetDependency<IOrganizationRepository>()
.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts
{ {
@@ -141,15 +153,20 @@ public class UpgradeOrganizationPlanCommandTests
[BitAutoData(PlanType.TeamsAnnually)] [BitAutoData(PlanType.TeamsAnnually)]
[BitAutoData(PlanType.TeamsStarter)] [BitAutoData(PlanType.TeamsStarter)]
public async Task UpgradePlan_SM_Passes(PlanType planType, Organization organization, OrganizationUpgrade upgrade, public async Task UpgradePlan_SM_Passes(PlanType planType, Organization organization, OrganizationUpgrade upgrade,
[Policy(PolicyType.ResetPassword, false)] PolicyStatus policy,
SutProvider<UpgradeOrganizationPlanCommand> sutProvider) SutProvider<UpgradeOrganizationPlanCommand> sutProvider)
{ {
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType));
upgrade.Plan = planType; upgrade.Plan = planType;
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(upgrade.Plan).Returns(MockPlans.Get(upgrade.Plan)); sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(upgrade.Plan).Returns(MockPlans.Get(upgrade.Plan));
var plan = MockPlans.Get(upgrade.Plan); var plan = MockPlans.Get(upgrade.Plan);
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), Arg.Any<PolicyType>())
.Returns(policy);
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType));
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization); sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
upgrade.AdditionalSeats = 15; upgrade.AdditionalSeats = 15;
@@ -180,6 +197,7 @@ public class UpgradeOrganizationPlanCommandTests
[BitAutoData(PlanType.TeamsAnnually)] [BitAutoData(PlanType.TeamsAnnually)]
[BitAutoData(PlanType.TeamsStarter)] [BitAutoData(PlanType.TeamsStarter)]
public async Task UpgradePlan_SM_NotEnoughSmSeats_Throws(PlanType planType, Organization organization, OrganizationUpgrade upgrade, public async Task UpgradePlan_SM_NotEnoughSmSeats_Throws(PlanType planType, Organization organization, OrganizationUpgrade upgrade,
[Policy(PolicyType.ResetPassword, false)] PolicyStatus policy,
SutProvider<UpgradeOrganizationPlanCommand> sutProvider) SutProvider<UpgradeOrganizationPlanCommand> sutProvider)
{ {
upgrade.Plan = planType; upgrade.Plan = planType;
@@ -191,6 +209,10 @@ public class UpgradeOrganizationPlanCommandTests
organization.SmSeats = 2; organization.SmSeats = 2;
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType)); sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType));
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), Arg.Any<PolicyType>())
.Returns(policy);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization); sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<IOrganizationRepository>() sutProvider.GetDependency<IOrganizationRepository>()
.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts
@@ -214,7 +236,9 @@ public class UpgradeOrganizationPlanCommandTests
[BitAutoData(PlanType.TeamsAnnually, 51)] [BitAutoData(PlanType.TeamsAnnually, 51)]
[BitAutoData(PlanType.TeamsStarter, 51)] [BitAutoData(PlanType.TeamsStarter, 51)]
public async Task UpgradePlan_SM_NotEnoughServiceAccounts_Throws(PlanType planType, int currentServiceAccounts, public async Task UpgradePlan_SM_NotEnoughServiceAccounts_Throws(PlanType planType, int currentServiceAccounts,
Organization organization, OrganizationUpgrade upgrade, SutProvider<UpgradeOrganizationPlanCommand> sutProvider) Organization organization, OrganizationUpgrade upgrade,
[Policy(PolicyType.ResetPassword, false)] PolicyStatus policy,
SutProvider<UpgradeOrganizationPlanCommand> sutProvider)
{ {
upgrade.Plan = planType; upgrade.Plan = planType;
upgrade.AdditionalSeats = 15; upgrade.AdditionalSeats = 15;
@@ -226,6 +250,10 @@ public class UpgradeOrganizationPlanCommandTests
organization.SmServiceAccounts = currentServiceAccounts; organization.SmServiceAccounts = currentServiceAccounts;
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType)); sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType).Returns(MockPlans.Get(organization.PlanType));
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), Arg.Any<PolicyType>())
.Returns(policy);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization); sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<IOrganizationRepository>() sutProvider.GetDependency<IOrganizationRepository>()
.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(new OrganizationSeatCounts
@@ -251,6 +279,7 @@ public class UpgradeOrganizationPlanCommandTests
OrganizationUpgrade upgrade, OrganizationUpgrade upgrade,
string newPublicKey, string newPublicKey,
string newPrivateKey, string newPrivateKey,
[Policy(PolicyType.ResetPassword, false)] PolicyStatus policy,
SutProvider<UpgradeOrganizationPlanCommand> sutProvider) SutProvider<UpgradeOrganizationPlanCommand> sutProvider)
{ {
organization.PublicKey = null; organization.PublicKey = null;
@@ -262,6 +291,9 @@ public class UpgradeOrganizationPlanCommandTests
publicKey: newPublicKey); publicKey: newPublicKey);
upgrade.AdditionalSeats = 10; upgrade.AdditionalSeats = 10;
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), Arg.Any<PolicyType>())
.Returns(policy);
sutProvider.GetDependency<IOrganizationRepository>() sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization.Id) .GetByIdAsync(organization.Id)
.Returns(organization); .Returns(organization);
@@ -291,6 +323,7 @@ public class UpgradeOrganizationPlanCommandTests
public async Task UpgradePlan_WhenOrganizationAlreadyHasPublicAndPrivateKeys_DoesNotOverwriteWithNull( public async Task UpgradePlan_WhenOrganizationAlreadyHasPublicAndPrivateKeys_DoesNotOverwriteWithNull(
Organization organization, Organization organization,
OrganizationUpgrade upgrade, OrganizationUpgrade upgrade,
[Policy(PolicyType.ResetPassword, false)] PolicyStatus policy,
SutProvider<UpgradeOrganizationPlanCommand> sutProvider) SutProvider<UpgradeOrganizationPlanCommand> sutProvider)
{ {
// Arrange // Arrange
@@ -304,6 +337,9 @@ public class UpgradeOrganizationPlanCommandTests
upgrade.Keys = null; upgrade.Keys = null;
upgrade.AdditionalSeats = 10; upgrade.AdditionalSeats = 10;
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), Arg.Any<PolicyType>())
.Returns(policy);
sutProvider.GetDependency<IOrganizationRepository>() sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization.Id) .GetByIdAsync(organization.Id)
.Returns(organization); .Returns(organization);
@@ -333,6 +369,7 @@ public class UpgradeOrganizationPlanCommandTests
public async Task UpgradePlan_WhenOrganizationAlreadyHasPublicAndPrivateKeys_DoesNotBackfillWithNewKeys( public async Task UpgradePlan_WhenOrganizationAlreadyHasPublicAndPrivateKeys_DoesNotBackfillWithNewKeys(
Organization organization, Organization organization,
OrganizationUpgrade upgrade, OrganizationUpgrade upgrade,
[Policy(PolicyType.ResetPassword, false)] PolicyStatus policy,
SutProvider<UpgradeOrganizationPlanCommand> sutProvider) SutProvider<UpgradeOrganizationPlanCommand> sutProvider)
{ {
// Arrange // Arrange
@@ -343,6 +380,9 @@ public class UpgradeOrganizationPlanCommandTests
organization.PublicKey = existingPublicKey; organization.PublicKey = existingPublicKey;
organization.PrivateKey = existingPrivateKey; organization.PrivateKey = existingPrivateKey;
sutProvider.GetDependency<IPolicyQuery>()
.RunAsync(Arg.Any<Guid>(), Arg.Any<PolicyType>())
.Returns(policy);
upgrade.Plan = PlanType.TeamsAnnually; upgrade.Plan = PlanType.TeamsAnnually;
upgrade.Keys = new PublicKeyEncryptionKeyPairData( upgrade.Keys = new PublicKeyEncryptionKeyPairData(

View File

@@ -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<PolicyQuery> sutProvider,
Policy policy)
{
// Arrange
sutProvider.GetDependency<IPolicyRepository>()
.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<PolicyQuery> sutProvider,
Guid organizationId,
PolicyType policyType)
{
// Arrange
sutProvider.GetDependency<IPolicyRepository>()
.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);
}
}

View File

@@ -56,7 +56,7 @@ public class SendAuthenticationQueryTests
// Assert // Assert
var emailOtp = Assert.IsType<EmailOtp>(result); var emailOtp = Assert.IsType<EmailOtp>(result);
Assert.Equal(expectedEmailHashes, emailOtp.Emails); Assert.Equal(expectedEmailHashes, emailOtp.EmailHashes);
} }
[Fact] [Fact]

View File

@@ -3,6 +3,7 @@ using Bit.Core.Services;
using Bit.Core.Tools.Models.Data; using Bit.Core.Tools.Models.Data;
using Bit.Core.Tools.SendFeatures.Queries.Interfaces; using Bit.Core.Tools.SendFeatures.Queries.Interfaces;
using Bit.IntegrationTestCommon.Factories; using Bit.IntegrationTestCommon.Factories;
using Bit.Test.Common.Helpers;
using Duende.IdentityModel; using Duende.IdentityModel;
using NSubstitute; using NSubstitute;
using Xunit; using Xunit;
@@ -60,7 +61,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>(); var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>();
sendAuthQuery.GetAuthenticationMethod(sendId) sendAuthQuery.GetAuthenticationMethod(sendId)
.Returns(new EmailOtp([email])); .Returns(new EmailOtp([CryptographyHelper.HashAndEncode(email)]));
services.AddSingleton(sendAuthQuery); services.AddSingleton(sendAuthQuery);
// Mock OTP token provider // Mock OTP token provider
@@ -75,6 +76,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
}); });
}).CreateClient(); }).CreateClient();
var requestBody = SendAccessTestUtilities.CreateTokenRequestBody(sendId, email: email); // Email but no OTP var requestBody = SendAccessTestUtilities.CreateTokenRequestBody(sendId, email: email); // Email but no OTP
// Act // Act
@@ -104,7 +106,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>(); var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>();
sendAuthQuery.GetAuthenticationMethod(sendId) sendAuthQuery.GetAuthenticationMethod(sendId)
.Returns(new EmailOtp(new[] { email })); .Returns(new EmailOtp(new[] { CryptographyHelper.HashAndEncode(email) }));
services.AddSingleton(sendAuthQuery); services.AddSingleton(sendAuthQuery);
// Mock OTP token provider to validate successfully // Mock OTP token provider to validate successfully
@@ -148,7 +150,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>(); var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>();
sendAuthQuery.GetAuthenticationMethod(sendId) sendAuthQuery.GetAuthenticationMethod(sendId)
.Returns(new EmailOtp(new[] { email })); .Returns(new EmailOtp(new[] { CryptographyHelper.HashAndEncode(email) }));
services.AddSingleton(sendAuthQuery); services.AddSingleton(sendAuthQuery);
// Mock OTP token provider to validate as false // Mock OTP token provider to validate as false
@@ -190,7 +192,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>(); var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>();
sendAuthQuery.GetAuthenticationMethod(sendId) sendAuthQuery.GetAuthenticationMethod(sendId)
.Returns(new EmailOtp(new[] { email })); .Returns(new EmailOtp(new[] { CryptographyHelper.HashAndEncode(email) }));
services.AddSingleton(sendAuthQuery); services.AddSingleton(sendAuthQuery);
// Mock OTP token provider to fail generation // Mock OTP token provider to fail generation

View File

@@ -5,6 +5,7 @@ using Bit.Core.Tools.Models.Data;
using Bit.Identity.IdentityServer.RequestValidators.SendAccess; using Bit.Identity.IdentityServer.RequestValidators.SendAccess;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Helpers;
using Duende.IdentityModel; using Duende.IdentityModel;
using Duende.IdentityServer.Validation; using Duende.IdentityServer.Validation;
using NSubstitute; using NSubstitute;
@@ -105,7 +106,8 @@ public class SendEmailOtpRequestValidatorTests
expectedUniqueId) expectedUniqueId)
.Returns(generatedToken); .Returns(generatedToken);
emailOtp = emailOtp with { Emails = [email] }; var emailHash = CryptographyHelper.HashAndEncode(email);
emailOtp = emailOtp with { EmailHashes = [emailHash] };
// Act // Act
var result = await sutProvider.Sut.ValidateRequestAsync(context, emailOtp, sendId); var result = await sutProvider.Sut.ValidateRequestAsync(context, emailOtp, sendId);
@@ -144,7 +146,8 @@ public class SendEmailOtpRequestValidatorTests
Request = tokenRequest Request = tokenRequest
}; };
emailOtp = emailOtp with { Emails = [email] }; var emailHash = CryptographyHelper.HashAndEncode(email);
emailOtp = emailOtp with { EmailHashes = [emailHash] };
sutProvider.GetDependency<IOtpTokenProvider<DefaultOtpTokenProviderOptions>>() sutProvider.GetDependency<IOtpTokenProvider<DefaultOtpTokenProviderOptions>>()
.GenerateTokenAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>()) .GenerateTokenAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>())
@@ -179,7 +182,8 @@ public class SendEmailOtpRequestValidatorTests
Request = tokenRequest Request = tokenRequest
}; };
emailOtp = emailOtp with { Emails = [email] }; var emailHash = CryptographyHelper.HashAndEncode(email);
emailOtp = emailOtp with { EmailHashes = [emailHash] };
var expectedUniqueId = string.Format(SendAccessConstants.OtpToken.TokenUniqueIdentifier, sendId, email); var expectedUniqueId = string.Format(SendAccessConstants.OtpToken.TokenUniqueIdentifier, sendId, email);
@@ -231,7 +235,8 @@ public class SendEmailOtpRequestValidatorTests
Request = tokenRequest Request = tokenRequest
}; };
emailOtp = emailOtp with { Emails = [email] }; var emailHash = CryptographyHelper.HashAndEncode(email);
emailOtp = emailOtp with { EmailHashes = [emailHash] };
var expectedUniqueId = string.Format(SendAccessConstants.OtpToken.TokenUniqueIdentifier, sendId, email); var expectedUniqueId = string.Format(SendAccessConstants.OtpToken.TokenUniqueIdentifier, sendId, email);