mirror of
https://github.com/bitwarden/server.git
synced 2024-12-04 14:13:28 +01:00
Remove PolicyService.SaveAsync and use command instead
This commit is contained in:
parent
1b75e35c31
commit
4de9a00612
@ -6,8 +6,8 @@ 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.OrganizationFeatures.OrganizationDomains.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
|
||||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -28,7 +28,6 @@ namespace Bit.Api.AdminConsole.Controllers;
|
|||||||
public class PoliciesController : Controller
|
public class PoliciesController : Controller
|
||||||
{
|
{
|
||||||
private readonly IPolicyRepository _policyRepository;
|
private readonly IPolicyRepository _policyRepository;
|
||||||
private readonly IPolicyService _policyService;
|
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
@ -37,10 +36,10 @@ public class PoliciesController : Controller
|
|||||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
private readonly IOrganizationHasVerifiedDomainsQuery _organizationHasVerifiedDomainsQuery;
|
private readonly IOrganizationHasVerifiedDomainsQuery _organizationHasVerifiedDomainsQuery;
|
||||||
|
private readonly ISavePolicyCommand _savePolicyCommand;
|
||||||
|
|
||||||
public PoliciesController(
|
public PoliciesController(
|
||||||
IPolicyRepository policyRepository,
|
IPolicyRepository policyRepository,
|
||||||
IPolicyService policyService,
|
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
@ -48,10 +47,10 @@ public class PoliciesController : Controller
|
|||||||
IDataProtectionProvider dataProtectionProvider,
|
IDataProtectionProvider dataProtectionProvider,
|
||||||
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
IOrganizationHasVerifiedDomainsQuery organizationHasVerifiedDomainsQuery)
|
IOrganizationHasVerifiedDomainsQuery organizationHasVerifiedDomainsQuery,
|
||||||
|
ISavePolicyCommand savePolicyCommand)
|
||||||
{
|
{
|
||||||
_policyRepository = policyRepository;
|
_policyRepository = policyRepository;
|
||||||
_policyService = policyService;
|
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
@ -62,6 +61,7 @@ public class PoliciesController : Controller
|
|||||||
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
_organizationHasVerifiedDomainsQuery = organizationHasVerifiedDomainsQuery;
|
_organizationHasVerifiedDomainsQuery = organizationHasVerifiedDomainsQuery;
|
||||||
|
_savePolicyCommand = savePolicyCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{type}")]
|
[HttpGet("{type}")]
|
||||||
@ -178,25 +178,20 @@ public class PoliciesController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("{type}")]
|
[HttpPut("{type}")]
|
||||||
public async Task<PolicyResponseModel> Put(string orgId, int type, [FromBody] PolicyRequestModel model)
|
public async Task<PolicyResponseModel> Put(Guid orgId, PolicyType type, [FromBody] PolicyRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(orgId);
|
if (!await _currentContext.ManagePolicies(orgId))
|
||||||
if (!await _currentContext.ManagePolicies(orgIdGuid))
|
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(new Guid(orgId), (PolicyType)type);
|
|
||||||
if (policy == null)
|
if (type != model.Type)
|
||||||
{
|
{
|
||||||
policy = model.ToPolicy(orgIdGuid);
|
throw new BadRequestException("Mismatched policy type");
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
policy = model.ToPolicy(policy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var userId = _userService.GetProperUserId(User);
|
var policyUpdate = await model.ToPolicyUpdateAsync(orgId, _currentContext);
|
||||||
await _policyService.SaveAsync(policy, userId);
|
var policy = await _savePolicyCommand.SaveAsync(policyUpdate);
|
||||||
return new PolicyResponseModel(policy);
|
return new PolicyResponseModel(policy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
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.OrganizationFeatures.Policies.Models;
|
||||||
|
using Bit.Core.Context;
|
||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Models.Request;
|
namespace Bit.Api.AdminConsole.Models.Request;
|
||||||
|
|
||||||
@ -13,19 +15,12 @@ public class PolicyRequestModel
|
|||||||
public bool? Enabled { get; set; }
|
public bool? Enabled { get; set; }
|
||||||
public Dictionary<string, object> Data { get; set; }
|
public Dictionary<string, object> Data { get; set; }
|
||||||
|
|
||||||
public Policy ToPolicy(Guid orgId)
|
public async Task<PolicyUpdate> ToPolicyUpdateAsync(Guid organizationId, ICurrentContext currentContext) => new()
|
||||||
{
|
{
|
||||||
return ToPolicy(new Policy
|
Type = Type!.Value,
|
||||||
{
|
OrganizationId = organizationId,
|
||||||
Type = Type.Value,
|
Data = Data != null ? JsonSerializer.Serialize(Data) : null,
|
||||||
OrganizationId = orgId
|
Enabled = Enabled.GetValueOrDefault(),
|
||||||
});
|
PerformedBy = new StandardUser(currentContext.UserId!.Value, await currentContext.OrganizationOwner(organizationId))
|
||||||
}
|
};
|
||||||
|
|
||||||
public Policy ToPolicy(Policy existingPolicy)
|
|
||||||
{
|
|
||||||
existingPolicy.Enabled = Enabled.GetValueOrDefault();
|
|
||||||
existingPolicy.Data = Data != null ? JsonSerializer.Serialize(Data) : null;
|
|
||||||
return existingPolicy;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ using Bit.Api.AdminConsole.Public.Models.Request;
|
|||||||
using Bit.Api.AdminConsole.Public.Models.Response;
|
using Bit.Api.AdminConsole.Public.Models.Response;
|
||||||
using Bit.Api.Models.Public.Response;
|
using Bit.Api.Models.Public.Response;
|
||||||
using Bit.Core.AdminConsole.Enums;
|
using Bit.Core.AdminConsole.Enums;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
@ -18,15 +19,18 @@ public class PoliciesController : Controller
|
|||||||
private readonly IPolicyRepository _policyRepository;
|
private readonly IPolicyRepository _policyRepository;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
|
private readonly ISavePolicyCommand _savePolicyCommand;
|
||||||
|
|
||||||
public PoliciesController(
|
public PoliciesController(
|
||||||
IPolicyRepository policyRepository,
|
IPolicyRepository policyRepository,
|
||||||
IPolicyService policyService,
|
IPolicyService policyService,
|
||||||
ICurrentContext currentContext)
|
ICurrentContext currentContext,
|
||||||
|
ISavePolicyCommand savePolicyCommand)
|
||||||
{
|
{
|
||||||
_policyRepository = policyRepository;
|
_policyRepository = policyRepository;
|
||||||
_policyService = policyService;
|
_policyService = policyService;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
|
_savePolicyCommand = savePolicyCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -80,17 +84,9 @@ public class PoliciesController : Controller
|
|||||||
[ProducesResponseType((int)HttpStatusCode.NotFound)]
|
[ProducesResponseType((int)HttpStatusCode.NotFound)]
|
||||||
public async Task<IActionResult> Put(PolicyType type, [FromBody] PolicyUpdateRequestModel model)
|
public async Task<IActionResult> Put(PolicyType type, [FromBody] PolicyUpdateRequestModel model)
|
||||||
{
|
{
|
||||||
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(
|
var policyUpdate = model.ToPolicyUpdate(_currentContext.OrganizationId!.Value, type);
|
||||||
_currentContext.OrganizationId.Value, type);
|
var policy = await _savePolicyCommand.SaveAsync(policyUpdate);
|
||||||
if (policy == null)
|
|
||||||
{
|
|
||||||
policy = model.ToPolicy(_currentContext.OrganizationId.Value, type);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
policy = model.ToPolicy(policy);
|
|
||||||
}
|
|
||||||
await _policyService.SaveAsync(policy, null);
|
|
||||||
var response = new PolicyResponseModel(policy);
|
var response = new PolicyResponseModel(policy);
|
||||||
return new JsonResult(response);
|
return new JsonResult(response);
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,19 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
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.OrganizationFeatures.Policies.Models;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Public.Models.Request;
|
namespace Bit.Api.AdminConsole.Public.Models.Request;
|
||||||
|
|
||||||
public class PolicyUpdateRequestModel : PolicyBaseModel
|
public class PolicyUpdateRequestModel : PolicyBaseModel
|
||||||
{
|
{
|
||||||
public Policy ToPolicy(Guid orgId, PolicyType type)
|
public PolicyUpdate ToPolicyUpdate(Guid organizationId, PolicyType type) => new()
|
||||||
{
|
{
|
||||||
return ToPolicy(new Policy
|
Type = type,
|
||||||
{
|
OrganizationId = organizationId,
|
||||||
OrganizationId = orgId,
|
|
||||||
Enabled = Enabled.GetValueOrDefault(),
|
|
||||||
Data = Data != null ? JsonSerializer.Serialize(Data) : null,
|
Data = Data != null ? JsonSerializer.Serialize(Data) : null,
|
||||||
Type = type
|
Enabled = Enabled.GetValueOrDefault(),
|
||||||
});
|
PerformedBy = new SystemUser(EventSystemUser.PublicApi)
|
||||||
}
|
};
|
||||||
|
|
||||||
public virtual Policy ToPolicy(Policy existingPolicy)
|
|
||||||
{
|
|
||||||
existingPolicy.Enabled = Enabled.GetValueOrDefault();
|
|
||||||
existingPolicy.Data = Data != null ? JsonSerializer.Serialize(Data) : null;
|
|
||||||
return existingPolicy;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
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.OrganizationFeatures.OrganizationDomains.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -19,9 +19,9 @@ public class VerifyOrganizationDomainCommand(
|
|||||||
IDnsResolverService dnsResolverService,
|
IDnsResolverService dnsResolverService,
|
||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
IGlobalSettings globalSettings,
|
IGlobalSettings globalSettings,
|
||||||
IPolicyService policyService,
|
|
||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
|
ISavePolicyCommand savePolicyCommand,
|
||||||
ILogger<VerifyOrganizationDomainCommand> logger)
|
ILogger<VerifyOrganizationDomainCommand> logger)
|
||||||
: IVerifyOrganizationDomainCommand
|
: IVerifyOrganizationDomainCommand
|
||||||
{
|
{
|
||||||
@ -125,10 +125,15 @@ public class VerifyOrganizationDomainCommand(
|
|||||||
{
|
{
|
||||||
if (featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
if (featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
||||||
{
|
{
|
||||||
await policyService.SaveAsync(
|
var policyUpdate = new PolicyUpdate
|
||||||
new Policy { OrganizationId = organizationId, Type = PolicyType.SingleOrg, Enabled = true },
|
{
|
||||||
savingUserId: actingUser is StandardUser standardUser ? standardUser.UserId : null,
|
OrganizationId = organizationId,
|
||||||
eventSystemUser: actingUser is SystemUser systemUser ? systemUser.SystemUserType : null);
|
Type = PolicyType.SingleOrg,
|
||||||
|
Enabled = true,
|
||||||
|
PerformedBy = actingUser
|
||||||
|
};
|
||||||
|
|
||||||
|
await savePolicyCommand.SaveAsync(policyUpdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||||
|
|
||||||
public interface ISavePolicyCommand
|
public interface ISavePolicyCommand
|
||||||
{
|
{
|
||||||
Task SaveAsync(PolicyUpdate policy);
|
Task<Policy> SaveAsync(PolicyUpdate policy);
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ public class SavePolicyCommand : ISavePolicyCommand
|
|||||||
_policyValidators = policyValidatorsDict;
|
_policyValidators = policyValidatorsDict;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveAsync(PolicyUpdate policyUpdate)
|
public async Task<Policy> SaveAsync(PolicyUpdate policyUpdate)
|
||||||
{
|
{
|
||||||
var org = await _applicationCacheService.GetOrganizationAbilityAsync(policyUpdate.OrganizationId);
|
var org = await _applicationCacheService.GetOrganizationAbilityAsync(policyUpdate.OrganizationId);
|
||||||
if (org == null)
|
if (org == null)
|
||||||
@ -74,6 +74,8 @@ public class SavePolicyCommand : ISavePolicyCommand
|
|||||||
|
|
||||||
await _policyRepository.UpsertAsync(policy);
|
await _policyRepository.UpsertAsync(policy);
|
||||||
await _eventService.LogPolicyEventAsync(policy, EventType.Policy_Updated);
|
await _eventService.LogPolicyEventAsync(policy, EventType.Policy_Updated);
|
||||||
|
|
||||||
|
return policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunValidatorAsync(IPolicyValidator validator, PolicyUpdate policyUpdate)
|
private async Task RunValidatorAsync(IPolicyValidator validator, PolicyUpdate policyUpdate)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
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.Models.Data.Organizations.Policies;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -9,8 +8,6 @@ namespace Bit.Core.AdminConsole.Services;
|
|||||||
|
|
||||||
public interface IPolicyService
|
public interface IPolicyService
|
||||||
{
|
{
|
||||||
Task SaveAsync(Policy policy, Guid? savingUserId, EventSystemUser? eventSystemUser = null);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the combined master password policy options for the specified user.
|
/// Get the combined master password policy options for the specified user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,19 +1,8 @@
|
|||||||
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.Organizations.Policies;
|
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Auth.Enums;
|
|
||||||
using Bit.Core.Auth.Repositories;
|
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
|
||||||
using Bit.Core.Context;
|
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
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.Services;
|
using Bit.Core.Services;
|
||||||
@ -24,107 +13,20 @@ namespace Bit.Core.AdminConsole.Services.Implementations;
|
|||||||
public class PolicyService : IPolicyService
|
public class PolicyService : IPolicyService
|
||||||
{
|
{
|
||||||
private readonly IApplicationCacheService _applicationCacheService;
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
private readonly IEventService _eventService;
|
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IPolicyRepository _policyRepository;
|
private readonly IPolicyRepository _policyRepository;
|
||||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
|
||||||
private readonly IMailService _mailService;
|
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
|
||||||
private readonly IFeatureService _featureService;
|
|
||||||
private readonly ISavePolicyCommand _savePolicyCommand;
|
|
||||||
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
|
|
||||||
private readonly IOrganizationHasVerifiedDomainsQuery _organizationHasVerifiedDomainsQuery;
|
|
||||||
private readonly ICurrentContext _currentContext;
|
|
||||||
|
|
||||||
public PolicyService(
|
public PolicyService(
|
||||||
IApplicationCacheService applicationCacheService,
|
IApplicationCacheService applicationCacheService,
|
||||||
IEventService eventService,
|
|
||||||
IOrganizationRepository organizationRepository,
|
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IPolicyRepository policyRepository,
|
IPolicyRepository policyRepository,
|
||||||
ISsoConfigRepository ssoConfigRepository,
|
GlobalSettings globalSettings)
|
||||||
IMailService mailService,
|
|
||||||
GlobalSettings globalSettings,
|
|
||||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
|
||||||
IFeatureService featureService,
|
|
||||||
ISavePolicyCommand savePolicyCommand,
|
|
||||||
IRemoveOrganizationUserCommand removeOrganizationUserCommand,
|
|
||||||
IOrganizationHasVerifiedDomainsQuery organizationHasVerifiedDomainsQuery,
|
|
||||||
ICurrentContext currentContext)
|
|
||||||
{
|
{
|
||||||
_applicationCacheService = applicationCacheService;
|
_applicationCacheService = applicationCacheService;
|
||||||
_eventService = eventService;
|
|
||||||
_organizationRepository = organizationRepository;
|
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_policyRepository = policyRepository;
|
_policyRepository = policyRepository;
|
||||||
_ssoConfigRepository = ssoConfigRepository;
|
|
||||||
_mailService = mailService;
|
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
|
||||||
_featureService = featureService;
|
|
||||||
_savePolicyCommand = savePolicyCommand;
|
|
||||||
_removeOrganizationUserCommand = removeOrganizationUserCommand;
|
|
||||||
_organizationHasVerifiedDomainsQuery = organizationHasVerifiedDomainsQuery;
|
|
||||||
_currentContext = currentContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SaveAsync(Policy policy, Guid? savingUserId, EventSystemUser? eventSystemUser = null)
|
|
||||||
{
|
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.Pm13322AddPolicyDefinitions))
|
|
||||||
{
|
|
||||||
// Transitional mapping - this will be moved to callers once the feature flag is removed
|
|
||||||
// TODO make sure to populate with SystemUser if not an actual user
|
|
||||||
var policyUpdate = new PolicyUpdate
|
|
||||||
{
|
|
||||||
OrganizationId = policy.OrganizationId,
|
|
||||||
Type = policy.Type,
|
|
||||||
Enabled = policy.Enabled,
|
|
||||||
Data = policy.Data,
|
|
||||||
PerformedBy = savingUserId.HasValue
|
|
||||||
? new StandardUser(savingUserId.Value, await _currentContext.OrganizationOwner(policy.OrganizationId))
|
|
||||||
: new SystemUser(eventSystemUser ?? EventSystemUser.Unknown)
|
|
||||||
};
|
|
||||||
|
|
||||||
await _savePolicyCommand.SaveAsync(policyUpdate);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var org = await _organizationRepository.GetByIdAsync(policy.OrganizationId);
|
|
||||||
if (org == null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Organization not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!org.UsePolicies)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("This organization cannot use policies.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: This method will throw a bunch of errors based on if the
|
|
||||||
// policy that is being applied requires some other policy that is
|
|
||||||
// not enabled. It may be advisable to refactor this into a domain
|
|
||||||
// object and get this kind of stuff out of the service.
|
|
||||||
await HandleDependentPoliciesAsync(policy, org);
|
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
|
||||||
if (policy.Id == default(Guid))
|
|
||||||
{
|
|
||||||
policy.CreationDate = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
policy.RevisionDate = now;
|
|
||||||
|
|
||||||
// We can exit early for disable operations, because they are
|
|
||||||
// simpler.
|
|
||||||
if (!policy.Enabled)
|
|
||||||
{
|
|
||||||
await SetPolicyConfiguration(policy);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await EnablePolicyAsync(policy, org, savingUserId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MasterPasswordPolicyData> GetMasterPasswordPolicyForUserAsync(User user)
|
public async Task<MasterPasswordPolicyData> GetMasterPasswordPolicyForUserAsync(User user)
|
||||||
@ -190,178 +92,4 @@ public class PolicyService : IPolicyService
|
|||||||
|
|
||||||
return new[] { OrganizationUserType.Owner, OrganizationUserType.Admin };
|
return new[] { OrganizationUserType.Owner, OrganizationUserType.Admin };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DependsOnSingleOrgAsync(Organization org)
|
|
||||||
{
|
|
||||||
var singleOrg = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.SingleOrg);
|
|
||||||
if (singleOrg?.Enabled != true)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Single Organization policy not enabled.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RequiredBySsoAsync(Organization org)
|
|
||||||
{
|
|
||||||
var requireSso = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.RequireSso);
|
|
||||||
if (requireSso?.Enabled == true)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Single Sign-On Authentication policy is enabled.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RequiredByKeyConnectorAsync(Organization org)
|
|
||||||
{
|
|
||||||
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(org.Id);
|
|
||||||
if (ssoConfig?.GetData()?.MemberDecryptionType == MemberDecryptionType.KeyConnector)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Key Connector is enabled.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RequiredByAccountRecoveryAsync(Organization org)
|
|
||||||
{
|
|
||||||
var requireSso = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.ResetPassword);
|
|
||||||
if (requireSso?.Enabled == true)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Account recovery policy is enabled.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RequiredByVaultTimeoutAsync(Organization org)
|
|
||||||
{
|
|
||||||
var vaultTimeout = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.MaximumVaultTimeout);
|
|
||||||
if (vaultTimeout?.Enabled == true)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Maximum Vault Timeout policy is enabled.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RequiredBySsoTrustedDeviceEncryptionAsync(Organization org)
|
|
||||||
{
|
|
||||||
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(org.Id);
|
|
||||||
if (ssoConfig?.GetData()?.MemberDecryptionType == MemberDecryptionType.TrustedDeviceEncryption)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Trusted device encryption is on and requires this policy.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleDependentPoliciesAsync(Policy policy, Organization org)
|
|
||||||
{
|
|
||||||
switch (policy.Type)
|
|
||||||
{
|
|
||||||
case PolicyType.SingleOrg:
|
|
||||||
if (!policy.Enabled)
|
|
||||||
{
|
|
||||||
await HasVerifiedDomainsAsync(org);
|
|
||||||
await RequiredBySsoAsync(org);
|
|
||||||
await RequiredByVaultTimeoutAsync(org);
|
|
||||||
await RequiredByKeyConnectorAsync(org);
|
|
||||||
await RequiredByAccountRecoveryAsync(org);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PolicyType.RequireSso:
|
|
||||||
if (policy.Enabled)
|
|
||||||
{
|
|
||||||
await DependsOnSingleOrgAsync(org);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await RequiredByKeyConnectorAsync(org);
|
|
||||||
await RequiredBySsoTrustedDeviceEncryptionAsync(org);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PolicyType.ResetPassword:
|
|
||||||
if (!policy.Enabled || policy.GetDataModel<ResetPasswordDataModel>()?.AutoEnrollEnabled == false)
|
|
||||||
{
|
|
||||||
await RequiredBySsoTrustedDeviceEncryptionAsync(org);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (policy.Enabled)
|
|
||||||
{
|
|
||||||
await DependsOnSingleOrgAsync(org);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PolicyType.MaximumVaultTimeout:
|
|
||||||
if (policy.Enabled)
|
|
||||||
{
|
|
||||||
await DependsOnSingleOrgAsync(org);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HasVerifiedDomainsAsync(Organization org)
|
|
||||||
{
|
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
&& await _organizationHasVerifiedDomainsQuery.HasVerifiedDomainsAsync(org.Id))
|
|
||||||
{
|
|
||||||
throw new BadRequestException("The Single organization policy is required for organizations that have enabled domain verification.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SetPolicyConfiguration(Policy policy)
|
|
||||||
{
|
|
||||||
await _policyRepository.UpsertAsync(policy);
|
|
||||||
await _eventService.LogPolicyEventAsync(policy, EventType.Policy_Updated);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task EnablePolicyAsync(Policy policy, Organization org, Guid? savingUserId)
|
|
||||||
{
|
|
||||||
var currentPolicy = await _policyRepository.GetByIdAsync(policy.Id);
|
|
||||||
if (!currentPolicy?.Enabled ?? true)
|
|
||||||
{
|
|
||||||
var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(policy.OrganizationId);
|
|
||||||
var organizationUsersTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(orgUsers);
|
|
||||||
var removableOrgUsers = orgUsers.Where(ou =>
|
|
||||||
ou.Status != OrganizationUserStatusType.Invited && ou.Status != OrganizationUserStatusType.Revoked &&
|
|
||||||
ou.Type != OrganizationUserType.Owner && ou.Type != OrganizationUserType.Admin &&
|
|
||||||
ou.UserId != savingUserId);
|
|
||||||
switch (policy.Type)
|
|
||||||
{
|
|
||||||
case PolicyType.TwoFactorAuthentication:
|
|
||||||
// Reorder by HasMasterPassword to prioritize checking users without a master if they have 2FA enabled
|
|
||||||
foreach (var orgUser in removableOrgUsers.OrderBy(ou => ou.HasMasterPassword))
|
|
||||||
{
|
|
||||||
var userTwoFactorEnabled = organizationUsersTwoFactorEnabled.FirstOrDefault(u => u.user.Id == orgUser.Id).twoFactorIsEnabled;
|
|
||||||
if (!userTwoFactorEnabled)
|
|
||||||
{
|
|
||||||
if (!orgUser.HasMasterPassword)
|
|
||||||
{
|
|
||||||
throw new BadRequestException(
|
|
||||||
"Policy could not be enabled. Non-compliant members will lose access to their accounts. Identify members without two-step login from the policies column in the members page.");
|
|
||||||
}
|
|
||||||
|
|
||||||
await _removeOrganizationUserCommand.RemoveUserAsync(policy.OrganizationId, orgUser.Id,
|
|
||||||
savingUserId);
|
|
||||||
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
|
|
||||||
org.DisplayName(), orgUser.Email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PolicyType.SingleOrg:
|
|
||||||
var userOrgs = await _organizationUserRepository.GetManyByManyUsersAsync(
|
|
||||||
removableOrgUsers.Select(ou => ou.UserId.Value));
|
|
||||||
foreach (var orgUser in removableOrgUsers)
|
|
||||||
{
|
|
||||||
if (userOrgs.Any(ou => ou.UserId == orgUser.UserId
|
|
||||||
&& ou.OrganizationId != org.Id
|
|
||||||
&& ou.Status != OrganizationUserStatusType.Invited))
|
|
||||||
{
|
|
||||||
await _removeOrganizationUserCommand.RemoveUserAsync(policy.OrganizationId, orgUser.Id,
|
|
||||||
savingUserId);
|
|
||||||
await _mailService.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(
|
|
||||||
org.DisplayName(), orgUser.Email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await SetPolicyConfiguration(policy);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
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.Models.Data.Organizations.Policies;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
|
||||||
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;
|
||||||
@ -17,25 +18,25 @@ public class SsoConfigService : ISsoConfigService
|
|||||||
{
|
{
|
||||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||||
private readonly IPolicyRepository _policyRepository;
|
private readonly IPolicyRepository _policyRepository;
|
||||||
private readonly IPolicyService _policyService;
|
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
|
private readonly ISavePolicyCommand _savePolicyCommand;
|
||||||
|
|
||||||
public SsoConfigService(
|
public SsoConfigService(
|
||||||
ISsoConfigRepository ssoConfigRepository,
|
ISsoConfigRepository ssoConfigRepository,
|
||||||
IPolicyRepository policyRepository,
|
IPolicyRepository policyRepository,
|
||||||
IPolicyService policyService,
|
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IEventService eventService)
|
IEventService eventService,
|
||||||
|
ISavePolicyCommand savePolicyCommand)
|
||||||
{
|
{
|
||||||
_ssoConfigRepository = ssoConfigRepository;
|
_ssoConfigRepository = ssoConfigRepository;
|
||||||
_policyRepository = policyRepository;
|
_policyRepository = policyRepository;
|
||||||
_policyService = policyService;
|
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
|
_savePolicyCommand = savePolicyCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveAsync(SsoConfig config, Organization organization)
|
public async Task SaveAsync(SsoConfig config, Organization organization)
|
||||||
@ -63,25 +64,29 @@ public class SsoConfigService : ISsoConfigService
|
|||||||
// Automatically enable account recovery, SSO required, and single org policies if trusted device encryption is selected
|
// Automatically enable account recovery, SSO required, and single org policies if trusted device encryption is selected
|
||||||
if (config.GetData().MemberDecryptionType == MemberDecryptionType.TrustedDeviceEncryption)
|
if (config.GetData().MemberDecryptionType == MemberDecryptionType.TrustedDeviceEncryption)
|
||||||
{
|
{
|
||||||
var singleOrgPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.SingleOrg) ??
|
|
||||||
new Policy { OrganizationId = config.OrganizationId, Type = PolicyType.SingleOrg };
|
|
||||||
|
|
||||||
singleOrgPolicy.Enabled = true;
|
await _savePolicyCommand.SaveAsync(new()
|
||||||
|
{
|
||||||
|
OrganizationId = config.OrganizationId,
|
||||||
|
Type = PolicyType.SingleOrg,
|
||||||
|
Enabled = true
|
||||||
|
});
|
||||||
|
|
||||||
await _policyService.SaveAsync(singleOrgPolicy, null);
|
var resetPasswordPolicy = new PolicyUpdate
|
||||||
|
{
|
||||||
|
OrganizationId = config.OrganizationId,
|
||||||
|
Type = PolicyType.ResetPassword,
|
||||||
|
Enabled = true,
|
||||||
|
};
|
||||||
|
resetPasswordPolicy.SetDataModel(new ResetPasswordDataModel { AutoEnrollEnabled = true });
|
||||||
|
await _savePolicyCommand.SaveAsync(resetPasswordPolicy);
|
||||||
|
|
||||||
var resetPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.ResetPassword) ??
|
await _savePolicyCommand.SaveAsync(new()
|
||||||
new Policy { OrganizationId = config.OrganizationId, Type = PolicyType.ResetPassword, };
|
{
|
||||||
|
OrganizationId = config.OrganizationId,
|
||||||
resetPolicy.Enabled = true;
|
Type = PolicyType.RequireSso,
|
||||||
resetPolicy.SetDataModel(new ResetPasswordDataModel { AutoEnrollEnabled = true });
|
Enabled = true
|
||||||
await _policyService.SaveAsync(resetPolicy, null);
|
});
|
||||||
|
|
||||||
var ssoRequiredPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.RequireSso) ??
|
|
||||||
new Policy { OrganizationId = config.OrganizationId, Type = PolicyType.RequireSso, };
|
|
||||||
|
|
||||||
ssoRequiredPolicy.Enabled = true;
|
|
||||||
await _policyService.SaveAsync(ssoRequiredPolicy, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await LogEventsAsync(config, oldConfig);
|
await LogEventsAsync(config, oldConfig);
|
||||||
|
@ -8,5 +8,5 @@ public class CommandResult(IEnumerable<string> errors)
|
|||||||
public bool HasErrors => ErrorMessages.Count > 0;
|
public bool HasErrors => ErrorMessages.Count > 0;
|
||||||
public List<string> ErrorMessages { get; } = errors.ToList();
|
public List<string> ErrorMessages { get; } = errors.ToList();
|
||||||
|
|
||||||
public CommandResult() : this([]) { }
|
public CommandResult() : this((IEnumerable<string>)[]) { }
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
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.OrganizationFeatures.OrganizationDomains;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -182,9 +183,13 @@ public class VerifyOrganizationDomainCommandTests
|
|||||||
|
|
||||||
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyService>()
|
await sutProvider.GetDependency<ISavePolicyCommand>()
|
||||||
.Received(1)
|
.Received(1)
|
||||||
.SaveAsync(Arg.Is<Policy>(x => x.Type == PolicyType.SingleOrg && x.OrganizationId == domain.OrganizationId && x.Enabled), userId);
|
.SaveAsync(Arg.Is<PolicyUpdate>(x => x.Type == PolicyType.SingleOrg &&
|
||||||
|
x.OrganizationId == domain.OrganizationId &&
|
||||||
|
x.Enabled &&
|
||||||
|
x.PerformedBy is StandardUser &&
|
||||||
|
x.PerformedBy.UserId == userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@ -208,9 +213,9 @@ public class VerifyOrganizationDomainCommandTests
|
|||||||
|
|
||||||
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyService>()
|
await sutProvider.GetDependency<ISavePolicyCommand>()
|
||||||
.DidNotReceive()
|
.DidNotReceive()
|
||||||
.SaveAsync(Arg.Any<Policy>(), null);
|
.SaveAsync(Arg.Any<PolicyUpdate>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@ -234,10 +239,9 @@ public class VerifyOrganizationDomainCommandTests
|
|||||||
|
|
||||||
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyService>()
|
await sutProvider.GetDependency<ISavePolicyCommand>()
|
||||||
.DidNotReceive()
|
.DidNotReceive()
|
||||||
.SaveAsync(Arg.Any<Policy>(), null);
|
.SaveAsync(Arg.Any<PolicyUpdate>());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@ -261,8 +265,8 @@ public class VerifyOrganizationDomainCommandTests
|
|||||||
|
|
||||||
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyService>()
|
await sutProvider.GetDependency<ISavePolicyCommand>()
|
||||||
.DidNotReceive()
|
.DidNotReceive()
|
||||||
.SaveAsync(Arg.Any<Policy>(), null);
|
.SaveAsync(Arg.Any<PolicyUpdate>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,13 @@
|
|||||||
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.OrganizationDomains.Interfaces;
|
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
|
||||||
using Bit.Core.AdminConsole.Services.Implementations;
|
using Bit.Core.AdminConsole.Services.Implementations;
|
||||||
using Bit.Core.Auth.Entities;
|
|
||||||
using Bit.Core.Auth.Enums;
|
|
||||||
using Bit.Core.Auth.Models.Data;
|
|
||||||
using Bit.Core.Auth.Repositories;
|
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
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.Services;
|
|
||||||
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;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using AdminConsoleFixtures = Bit.Core.Test.AdminConsole.AutoFixture;
|
|
||||||
using GlobalSettings = Bit.Core.Settings.GlobalSettings;
|
using GlobalSettings = Bit.Core.Settings.GlobalSettings;
|
||||||
|
|
||||||
namespace Bit.Core.Test.AdminConsole.Services;
|
namespace Bit.Core.Test.AdminConsole.Services;
|
||||||
@ -27,667 +15,6 @@ namespace Bit.Core.Test.AdminConsole.Services;
|
|||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
public class PolicyServiceTests
|
public class PolicyServiceTests
|
||||||
{
|
{
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_OrganizationDoesNotExist_ThrowsBadRequest(
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, null);
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy,
|
|
||||||
Guid.NewGuid()));
|
|
||||||
|
|
||||||
Assert.Contains("Organization not found", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.LogPolicyEventAsync(default, default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_OrganizationCannotUsePolicies_ThrowsBadRequest(
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
var orgId = Guid.NewGuid();
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
|
||||||
{
|
|
||||||
UsePolicies = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy,
|
|
||||||
Guid.NewGuid()));
|
|
||||||
|
|
||||||
Assert.Contains("cannot use policies", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.LogPolicyEventAsync(default, default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_SingleOrg_RequireSsoEnabled_ThrowsBadRequest(
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
policy.Enabled = false;
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
|
||||||
{
|
|
||||||
Id = policy.OrganizationId,
|
|
||||||
UsePolicies = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.RequireSso)
|
|
||||||
.Returns(Task.FromResult(new Policy { Enabled = true }));
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy,
|
|
||||||
Guid.NewGuid()));
|
|
||||||
|
|
||||||
Assert.Contains("Single Sign-On Authentication policy is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.LogPolicyEventAsync(default, default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_SingleOrg_VaultTimeoutEnabled_ThrowsBadRequest([AdminConsoleFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
policy.Enabled = false;
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
|
||||||
{
|
|
||||||
Id = policy.OrganizationId,
|
|
||||||
UsePolicies = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.MaximumVaultTimeout)
|
|
||||||
.Returns(new Policy { Enabled = true });
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy,
|
|
||||||
Guid.NewGuid()));
|
|
||||||
|
|
||||||
Assert.Contains("Maximum Vault Timeout policy is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(PolicyType.SingleOrg)]
|
|
||||||
[BitAutoData(PolicyType.RequireSso)]
|
|
||||||
public async Task SaveAsync_PolicyRequiredByKeyConnector_DisablePolicy_ThrowsBadRequest(
|
|
||||||
PolicyType policyType,
|
|
||||||
Policy policy,
|
|
||||||
SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
policy.Enabled = false;
|
|
||||||
policy.Type = policyType;
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
|
||||||
{
|
|
||||||
Id = policy.OrganizationId,
|
|
||||||
UsePolicies = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
var ssoConfig = new SsoConfig { Enabled = true };
|
|
||||||
var data = new SsoConfigurationData { MemberDecryptionType = MemberDecryptionType.KeyConnector };
|
|
||||||
ssoConfig.SetData(data);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ISsoConfigRepository>()
|
|
||||||
.GetByOrganizationIdAsync(policy.OrganizationId)
|
|
||||||
.Returns(ssoConfig);
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy,
|
|
||||||
Guid.NewGuid()));
|
|
||||||
|
|
||||||
Assert.Contains("Key Connector is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_RequireSsoPolicy_NotEnabled_ThrowsBadRequestAsync(
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.RequireSso)] Policy policy, SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
policy.Enabled = true;
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
|
||||||
{
|
|
||||||
Id = policy.OrganizationId,
|
|
||||||
UsePolicies = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.SingleOrg)
|
|
||||||
.Returns(Task.FromResult(new Policy { Enabled = false }));
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy,
|
|
||||||
Guid.NewGuid()));
|
|
||||||
|
|
||||||
Assert.Contains("Single Organization policy not enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.LogPolicyEventAsync(default, default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_NewPolicy_Created(
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.ResetPassword)] Policy policy, SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
policy.Id = default;
|
|
||||||
policy.Data = null;
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
|
||||||
{
|
|
||||||
Id = policy.OrganizationId,
|
|
||||||
UsePolicies = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.SingleOrg)
|
|
||||||
.Returns(Task.FromResult(new Policy { Enabled = true }));
|
|
||||||
|
|
||||||
var utcNow = DateTime.UtcNow;
|
|
||||||
|
|
||||||
await sutProvider.Sut.SaveAsync(policy, Guid.NewGuid());
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>().Received()
|
|
||||||
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>().Received()
|
|
||||||
.UpsertAsync(policy);
|
|
||||||
|
|
||||||
Assert.True(policy.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_VaultTimeoutPolicy_NotEnabled_ThrowsBadRequestAsync(
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.MaximumVaultTimeout)] Policy policy, SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
policy.Enabled = true;
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
|
||||||
{
|
|
||||||
Id = policy.OrganizationId,
|
|
||||||
UsePolicies = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.SingleOrg)
|
|
||||||
.Returns(Task.FromResult(new Policy { Enabled = false }));
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy,
|
|
||||||
Guid.NewGuid()));
|
|
||||||
|
|
||||||
Assert.Contains("Single Organization policy not enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.LogPolicyEventAsync(default, default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_ExistingPolicy_UpdateTwoFactor(
|
|
||||||
Organization organization,
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy,
|
|
||||||
SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
// If the policy that this is updating isn't enabled then do some work now that the current one is enabled
|
|
||||||
|
|
||||||
organization.UsePolicies = true;
|
|
||||||
policy.OrganizationId = organization.Id;
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, organization.Id, organization);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.GetByIdAsync(policy.Id)
|
|
||||||
.Returns(new Policy
|
|
||||||
{
|
|
||||||
Id = policy.Id,
|
|
||||||
Type = PolicyType.TwoFactorAuthentication,
|
|
||||||
Enabled = false
|
|
||||||
});
|
|
||||||
|
|
||||||
var orgUserDetailUserInvited = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Invited,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user1@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = false
|
|
||||||
};
|
|
||||||
var orgUserDetailUserAcceptedWith2FA = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Accepted,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user2@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = true
|
|
||||||
};
|
|
||||||
var orgUserDetailUserAcceptedWithout2FA = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Accepted,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user3@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = true
|
|
||||||
};
|
|
||||||
var orgUserDetailAdmin = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
Type = OrganizationUserType.Admin,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "admin@test.com",
|
|
||||||
Name = "ADMIN",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = false
|
|
||||||
};
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyDetailsByOrganizationAsync(policy.OrganizationId)
|
|
||||||
.Returns(new List<OrganizationUserUserDetails>
|
|
||||||
{
|
|
||||||
orgUserDetailUserInvited,
|
|
||||||
orgUserDetailUserAcceptedWith2FA,
|
|
||||||
orgUserDetailUserAcceptedWithout2FA,
|
|
||||||
orgUserDetailAdmin
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
|
||||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<OrganizationUserUserDetails>>())
|
|
||||||
.Returns(new List<(OrganizationUserUserDetails user, bool hasTwoFactor)>()
|
|
||||||
{
|
|
||||||
(orgUserDetailUserInvited, false),
|
|
||||||
(orgUserDetailUserAcceptedWith2FA, true),
|
|
||||||
(orgUserDetailUserAcceptedWithout2FA, false),
|
|
||||||
(orgUserDetailAdmin, false),
|
|
||||||
});
|
|
||||||
|
|
||||||
var removeOrganizationUserCommand = sutProvider.GetDependency<IRemoveOrganizationUserCommand>();
|
|
||||||
|
|
||||||
var utcNow = DateTime.UtcNow;
|
|
||||||
|
|
||||||
var savingUserId = Guid.NewGuid();
|
|
||||||
|
|
||||||
await sutProvider.Sut.SaveAsync(policy, savingUserId);
|
|
||||||
|
|
||||||
await removeOrganizationUserCommand.Received()
|
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWithout2FA.Id, savingUserId);
|
|
||||||
await sutProvider.GetDependency<IMailService>().Received()
|
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWithout2FA.Email);
|
|
||||||
|
|
||||||
await removeOrganizationUserCommand.DidNotReceive()
|
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserInvited.Id, savingUserId);
|
|
||||||
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserInvited.Email);
|
|
||||||
await removeOrganizationUserCommand.DidNotReceive()
|
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWith2FA.Id, savingUserId);
|
|
||||||
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWith2FA.Email);
|
|
||||||
await removeOrganizationUserCommand.DidNotReceive()
|
|
||||||
.RemoveUserAsync(policy.OrganizationId, orgUserDetailAdmin.Id, savingUserId);
|
|
||||||
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailAdmin.Email);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>().Received()
|
|
||||||
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>().Received()
|
|
||||||
.UpsertAsync(policy);
|
|
||||||
|
|
||||||
Assert.True(policy.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_EnableTwoFactor_WithoutMasterPasswordOr2FA_ThrowsBadRequest(
|
|
||||||
Organization organization,
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy,
|
|
||||||
SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
organization.UsePolicies = true;
|
|
||||||
policy.OrganizationId = organization.Id;
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, organization.Id, organization);
|
|
||||||
|
|
||||||
var orgUserDetailUserWith2FAAndMP = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user1@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = true
|
|
||||||
};
|
|
||||||
var orgUserDetailUserWith2FANoMP = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user2@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = false
|
|
||||||
};
|
|
||||||
var orgUserDetailUserWithout2FA = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "user3@test.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = false
|
|
||||||
};
|
|
||||||
var orgUserDetailAdmin = new OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Confirmed,
|
|
||||||
Type = OrganizationUserType.Admin,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "admin@test.com",
|
|
||||||
Name = "ADMIN",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = false
|
|
||||||
};
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyDetailsByOrganizationAsync(policy.OrganizationId)
|
|
||||||
.Returns(new List<OrganizationUserUserDetails>
|
|
||||||
{
|
|
||||||
orgUserDetailUserWith2FAAndMP,
|
|
||||||
orgUserDetailUserWith2FANoMP,
|
|
||||||
orgUserDetailUserWithout2FA,
|
|
||||||
orgUserDetailAdmin
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
|
||||||
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids =>
|
|
||||||
ids.Contains(orgUserDetailUserWith2FANoMP.UserId.Value)
|
|
||||||
&& ids.Contains(orgUserDetailUserWithout2FA.UserId.Value)
|
|
||||||
&& ids.Contains(orgUserDetailAdmin.UserId.Value)))
|
|
||||||
.Returns(new List<(Guid userId, bool hasTwoFactor)>()
|
|
||||||
{
|
|
||||||
(orgUserDetailUserWith2FANoMP.UserId.Value, true),
|
|
||||||
(orgUserDetailUserWithout2FA.UserId.Value, false),
|
|
||||||
(orgUserDetailAdmin.UserId.Value, false),
|
|
||||||
});
|
|
||||||
|
|
||||||
var removeOrganizationUserCommand = sutProvider.GetDependency<IRemoveOrganizationUserCommand>();
|
|
||||||
|
|
||||||
var savingUserId = Guid.NewGuid();
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy, savingUserId));
|
|
||||||
|
|
||||||
Assert.Contains("Policy could not be enabled. Non-compliant members will lose access to their accounts. Identify members without two-step login from the policies column in the members page.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await removeOrganizationUserCommand.DidNotReceiveWithAnyArgs()
|
|
||||||
.RemoveUserAsync(organizationId: default, organizationUserId: default, deletingUserId: default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs()
|
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(default, default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs()
|
|
||||||
.LogPolicyEventAsync(default, default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>().DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_ExistingPolicy_UpdateSingleOrg(
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
// If the policy that this is updating isn't enabled then do some work now that the current one is enabled
|
|
||||||
|
|
||||||
var org = new Organization
|
|
||||||
{
|
|
||||||
Id = policy.OrganizationId,
|
|
||||||
UsePolicies = true,
|
|
||||||
Name = "TEST",
|
|
||||||
};
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, org);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.GetByIdAsync(policy.Id)
|
|
||||||
.Returns(new Policy
|
|
||||||
{
|
|
||||||
Id = policy.Id,
|
|
||||||
Type = PolicyType.SingleOrg,
|
|
||||||
Enabled = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
var orgUserDetail = new Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Status = OrganizationUserStatusType.Accepted,
|
|
||||||
Type = OrganizationUserType.User,
|
|
||||||
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
|
|
||||||
Email = "test@bitwarden.com",
|
|
||||||
Name = "TEST",
|
|
||||||
UserId = Guid.NewGuid(),
|
|
||||||
HasMasterPassword = true
|
|
||||||
};
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetManyDetailsByOrganizationAsync(policy.OrganizationId)
|
|
||||||
.Returns(new List<Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails>
|
|
||||||
{
|
|
||||||
orgUserDetail,
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
|
||||||
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUserDetail.UserId.Value)))
|
|
||||||
.Returns(new List<(Guid userId, bool hasTwoFactor)>()
|
|
||||||
{
|
|
||||||
(orgUserDetail.UserId.Value, false),
|
|
||||||
});
|
|
||||||
|
|
||||||
var utcNow = DateTime.UtcNow;
|
|
||||||
|
|
||||||
var savingUserId = Guid.NewGuid();
|
|
||||||
|
|
||||||
await sutProvider.Sut.SaveAsync(policy, savingUserId);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>().Received()
|
|
||||||
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>().Received()
|
|
||||||
.UpsertAsync(policy);
|
|
||||||
|
|
||||||
Assert.True(policy.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(true, false)]
|
|
||||||
[BitAutoData(false, true)]
|
|
||||||
[BitAutoData(false, false)]
|
|
||||||
public async Task SaveAsync_ResetPasswordPolicyRequiredByTrustedDeviceEncryption_DisablePolicyOrDisableAutomaticEnrollment_ThrowsBadRequest(
|
|
||||||
bool policyEnabled,
|
|
||||||
bool autoEnrollEnabled,
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.ResetPassword)] Policy policy,
|
|
||||||
SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
policy.Enabled = policyEnabled;
|
|
||||||
policy.SetDataModel(new ResetPasswordDataModel
|
|
||||||
{
|
|
||||||
AutoEnrollEnabled = autoEnrollEnabled
|
|
||||||
});
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
|
||||||
{
|
|
||||||
Id = policy.OrganizationId,
|
|
||||||
UsePolicies = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
var ssoConfig = new SsoConfig { Enabled = true };
|
|
||||||
ssoConfig.SetData(new SsoConfigurationData { MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption });
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ISsoConfigRepository>()
|
|
||||||
.GetByOrganizationIdAsync(policy.OrganizationId)
|
|
||||||
.Returns(ssoConfig);
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy,
|
|
||||||
Guid.NewGuid()));
|
|
||||||
|
|
||||||
Assert.Contains("Trusted device encryption is on and requires this policy.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.LogPolicyEventAsync(default, default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_RequireSsoPolicyRequiredByTrustedDeviceEncryption_DisablePolicy_ThrowsBadRequest(
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.RequireSso)] Policy policy,
|
|
||||||
SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
policy.Enabled = false;
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
|
||||||
{
|
|
||||||
Id = policy.OrganizationId,
|
|
||||||
UsePolicies = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
var ssoConfig = new SsoConfig { Enabled = true };
|
|
||||||
ssoConfig.SetData(new SsoConfigurationData { MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption });
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ISsoConfigRepository>()
|
|
||||||
.GetByOrganizationIdAsync(policy.OrganizationId)
|
|
||||||
.Returns(ssoConfig);
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy,
|
|
||||||
Guid.NewGuid()));
|
|
||||||
|
|
||||||
Assert.Contains("Trusted device encryption is on and requires this policy.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.LogPolicyEventAsync(default, default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_PolicyRequiredForAccountRecovery_NotEnabled_ThrowsBadRequestAsync(
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.ResetPassword)] Policy policy, SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
policy.Enabled = true;
|
|
||||||
policy.SetDataModel(new ResetPasswordDataModel());
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
|
||||||
{
|
|
||||||
Id = policy.OrganizationId,
|
|
||||||
UsePolicies = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.SingleOrg)
|
|
||||||
.Returns(Task.FromResult(new Policy { Enabled = false }));
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy,
|
|
||||||
Guid.NewGuid()));
|
|
||||||
|
|
||||||
Assert.Contains("Single Organization policy not enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.LogPolicyEventAsync(default, default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_SingleOrg_AccountRecoveryEnabled_ThrowsBadRequest(
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
policy.Enabled = false;
|
|
||||||
|
|
||||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
|
||||||
{
|
|
||||||
Id = policy.OrganizationId,
|
|
||||||
UsePolicies = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.ResetPassword)
|
|
||||||
.Returns(new Policy { Enabled = true });
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy,
|
|
||||||
Guid.NewGuid()));
|
|
||||||
|
|
||||||
Assert.Contains("Account recovery policy is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.UpsertAsync(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task GetPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsNoPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
|
public async Task GetPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsNoPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
|
||||||
{
|
{
|
||||||
@ -816,32 +143,4 @@ public class PolicyServiceTests
|
|||||||
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = true }
|
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = true }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_GivenOrganizationUsingPoliciesAndHasVerifiedDomains_WhenSingleOrgPolicyIsDisabled_ThenAnErrorShouldBeThrownOrganizationHasVerifiedDomains(
|
|
||||||
[AdminConsoleFixtures.Policy(PolicyType.SingleOrg)] Policy policy, Organization org, SutProvider<PolicyService> sutProvider)
|
|
||||||
{
|
|
||||||
org.Id = policy.OrganizationId;
|
|
||||||
org.UsePolicies = true;
|
|
||||||
|
|
||||||
policy.Enabled = false;
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
|
||||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
|
||||||
.GetByIdAsync(policy.OrganizationId)
|
|
||||||
.Returns(org);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationHasVerifiedDomainsQuery>()
|
|
||||||
.HasVerifiedDomainsAsync(org.Id)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
() => sutProvider.Sut.SaveAsync(policy, null));
|
|
||||||
|
|
||||||
Assert.Equal("The Single organization policy is required for organizations that have enabled domain verification.", badRequestException.Message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
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.Models.Data.Organizations.Policies;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
|
||||||
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;
|
||||||
@ -338,16 +339,26 @@ public class SsoConfigServiceTests
|
|||||||
|
|
||||||
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
|
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyService>().Received(1)
|
await sutProvider.GetDependency<ISavePolicyCommand>().Received(1)
|
||||||
.SaveAsync(
|
.SaveAsync(
|
||||||
Arg.Is<Policy>(t => t.Type == PolicyType.SingleOrg),
|
Arg.Is<PolicyUpdate>(t => t.Type == PolicyType.SingleOrg &&
|
||||||
null
|
t.OrganizationId == organization.Id &&
|
||||||
|
t.Enabled)
|
||||||
);
|
);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPolicyService>().Received(1)
|
await sutProvider.GetDependency<ISavePolicyCommand>().Received(1)
|
||||||
.SaveAsync(
|
.SaveAsync(
|
||||||
Arg.Is<Policy>(t => t.Type == PolicyType.ResetPassword && t.GetDataModel<ResetPasswordDataModel>().AutoEnrollEnabled),
|
Arg.Is<PolicyUpdate>(t => t.Type == PolicyType.ResetPassword &&
|
||||||
null
|
t.GetDataModel<ResetPasswordDataModel>().AutoEnrollEnabled &&
|
||||||
|
t.OrganizationId == organization.Id &&
|
||||||
|
t.Enabled)
|
||||||
|
);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<ISavePolicyCommand>().Received(1)
|
||||||
|
.SaveAsync(
|
||||||
|
Arg.Is<PolicyUpdate>(t => t.Type == PolicyType.RequireSso &&
|
||||||
|
t.OrganizationId == organization.Id &&
|
||||||
|
t.Enabled)
|
||||||
);
|
);
|
||||||
|
|
||||||
await sutProvider.GetDependency<ISsoConfigRepository>().ReceivedWithAnyArgs()
|
await sutProvider.GetDependency<ISsoConfigRepository>().ReceivedWithAnyArgs()
|
||||||
|
Loading…
Reference in New Issue
Block a user