mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 23:52:50 -05:00
Merge branch 'main' into ac/pm-19145-refactor-OrganizationService.ImportAsync
This commit is contained in:
@ -6,9 +6,6 @@ using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
||||
using Bit.Core.Tools.Enums;
|
||||
using Bit.Core.Tools.Models.Business;
|
||||
using Bit.Core.Tools.Services;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Bit.Test.Common.Helpers;
|
||||
@ -27,7 +24,6 @@ public class CreateGroupCommandTests
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received(1).CreateAsync(group);
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogGroupEventAsync(group, Enums.EventType.Group_Created);
|
||||
await sutProvider.GetDependency<IReferenceEventService>().Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(r => r.Type == ReferenceEventType.GroupCreated && r.Id == organization.Id && r.Source == ReferenceEventSource.Organization));
|
||||
AssertHelper.AssertRecent(group.CreationDate);
|
||||
AssertHelper.AssertRecent(group.RevisionDate);
|
||||
}
|
||||
@ -48,7 +44,6 @@ public class CreateGroupCommandTests
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received(1).CreateAsync(group, collections);
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogGroupEventAsync(group, Enums.EventType.Group_Created);
|
||||
await sutProvider.GetDependency<IReferenceEventService>().Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(r => r.Type == ReferenceEventType.GroupCreated && r.Id == organization.Id && r.Source == ReferenceEventSource.Organization));
|
||||
AssertHelper.AssertRecent(group.CreationDate);
|
||||
AssertHelper.AssertRecent(group.RevisionDate);
|
||||
}
|
||||
@ -60,7 +55,6 @@ public class CreateGroupCommandTests
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received(1).CreateAsync(group);
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogGroupEventAsync(group, Enums.EventType.Group_Created, eventSystemUser);
|
||||
await sutProvider.GetDependency<IReferenceEventService>().Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(r => r.Type == ReferenceEventType.GroupCreated && r.Id == organization.Id && r.Source == ReferenceEventSource.Organization));
|
||||
AssertHelper.AssertRecent(group.CreationDate);
|
||||
AssertHelper.AssertRecent(group.RevisionDate);
|
||||
}
|
||||
@ -74,7 +68,6 @@ public class CreateGroupCommandTests
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
|
||||
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogGroupEventAsync(default, default, default);
|
||||
await sutProvider.GetDependency<IReferenceEventService>().DidNotReceiveWithAnyArgs().RaiseEventAsync(default);
|
||||
}
|
||||
|
||||
[Theory, OrganizationCustomize(UseGroups = false), BitAutoData]
|
||||
@ -86,6 +79,5 @@ public class CreateGroupCommandTests
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
|
||||
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogGroupEventAsync(default, default, default);
|
||||
await sutProvider.GetDependency<IReferenceEventService>().DidNotReceiveWithAnyArgs().RaiseEventAsync(default);
|
||||
}
|
||||
}
|
||||
|
@ -166,7 +166,7 @@ public class VerifyOrganizationDomainCommandTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeEnabled(
|
||||
public async Task UserVerifyOrganizationDomainAsync_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeEnabled(
|
||||
OrganizationDomain domain, Guid userId, SutProvider<VerifyOrganizationDomainCommand> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
||||
@ -177,10 +177,6 @@ public class VerifyOrganizationDomainCommandTests
|
||||
.ResolveAsync(domain.DomainName, domain.Txt)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.UserId.Returns(userId);
|
||||
|
||||
@ -196,33 +192,7 @@ public class VerifyOrganizationDomainCommandTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningDisabled_WhenDomainIsVerified_ThenSingleOrgPolicyShouldBeNotBeEnabled(
|
||||
OrganizationDomain domain, SutProvider<VerifyOrganizationDomainCommand> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
||||
.GetClaimedDomainsByDomainNameAsync(domain.DomainName)
|
||||
.Returns([]);
|
||||
|
||||
sutProvider.GetDependency<IDnsResolverService>()
|
||||
.ResolveAsync(domain.DomainName, domain.Txt)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.UserId.Returns(Guid.NewGuid());
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(false);
|
||||
|
||||
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
||||
|
||||
await sutProvider.GetDependency<ISavePolicyCommand>()
|
||||
.DidNotReceive()
|
||||
.SaveAsync(Arg.Any<PolicyUpdate>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsNotVerified_ThenSingleOrgPolicyShouldNotBeEnabled(
|
||||
public async Task UserVerifyOrganizationDomainAsync_WhenDomainIsNotVerified_ThenSingleOrgPolicyShouldNotBeEnabled(
|
||||
OrganizationDomain domain, SutProvider<VerifyOrganizationDomainCommand> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
||||
@ -236,10 +206,6 @@ public class VerifyOrganizationDomainCommandTests
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.UserId.Returns(Guid.NewGuid());
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
|
||||
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
||||
|
||||
await sutProvider.GetDependency<ISavePolicyCommand>()
|
||||
@ -248,33 +214,7 @@ public class VerifyOrganizationDomainCommandTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningDisabled_WhenDomainIsNotVerified_ThenSingleOrgPolicyShouldBeNotBeEnabled(
|
||||
OrganizationDomain domain, SutProvider<VerifyOrganizationDomainCommand> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
||||
.GetClaimedDomainsByDomainNameAsync(domain.DomainName)
|
||||
.Returns([]);
|
||||
|
||||
sutProvider.GetDependency<IDnsResolverService>()
|
||||
.ResolveAsync(domain.DomainName, domain.Txt)
|
||||
.Returns(false);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.UserId.Returns(Guid.NewGuid());
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
|
||||
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
|
||||
|
||||
await sutProvider.GetDependency<ISavePolicyCommand>()
|
||||
.DidNotReceive()
|
||||
.SaveAsync(Arg.Any<PolicyUpdate>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UserVerifyOrganizationDomainAsync_GivenOrganizationDomainWithAccountDeprovisioningEnabled_WhenDomainIsVerified_ThenEmailShouldBeSentToUsersWhoBelongToTheDomain(
|
||||
public async Task UserVerifyOrganizationDomainAsync_WhenDomainIsVerified_ThenEmailShouldBeSentToUsersWhoBelongToTheDomain(
|
||||
ICollection<OrganizationUserUserDetails> organizationUsers,
|
||||
OrganizationDomain domain,
|
||||
Organization organization,
|
||||
@ -306,10 +246,6 @@ public class VerifyOrganizationDomainCommandTests
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.UserId.Returns(Guid.NewGuid());
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyDetailsByOrganizationAsync(domain.OrganizationId)
|
||||
.Returns(mockedUsers);
|
||||
|
@ -1,5 +1,8 @@
|
||||
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.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
@ -29,7 +32,6 @@ namespace Bit.Core.Test.OrganizationFeatures.OrganizationUsers;
|
||||
public class AcceptOrgUserCommandTests
|
||||
{
|
||||
private readonly IUserService _userService = Substitute.For<IUserService>();
|
||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery = Substitute.For<ITwoFactorIsEnabledQuery>();
|
||||
private readonly IOrgUserInviteTokenableFactory _orgUserInviteTokenableFactory = Substitute.For<IOrgUserInviteTokenableFactory>();
|
||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory = new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>();
|
||||
|
||||
@ -166,9 +168,6 @@ public class AcceptOrgUserCommandTests
|
||||
// Arrange
|
||||
SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails);
|
||||
|
||||
// User doesn't have 2FA enabled
|
||||
_twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user).Returns(false);
|
||||
|
||||
// Organization they are trying to join requires 2FA
|
||||
var twoFactorPolicy = new OrganizationUserPolicyDetails { OrganizationId = orgUser.OrganizationId };
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
@ -185,6 +184,107 @@ public class AcceptOrgUserCommandTests
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task AcceptOrgUserAsync_WithPolicyRequirementsEnabled_UserWithout2FAJoining2FARequiredOrg_ThrowsBadRequest(
|
||||
SutProvider<AcceptOrgUserCommand> sutProvider,
|
||||
User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails)
|
||||
{
|
||||
SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.PolicyRequirements)
|
||||
.Returns(true);
|
||||
|
||||
// Organization they are trying to join requires 2FA
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(new RequireTwoFactorPolicyRequirement(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = orgUser.OrganizationId,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Invited,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
}
|
||||
]));
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.AcceptOrgUserAsync(orgUser, user, _userService));
|
||||
|
||||
Assert.Equal("You cannot join this organization until you enable two-step login on your user account.",
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task AcceptOrgUserAsync_WithPolicyRequirementsEnabled_UserWith2FAJoining2FARequiredOrg_Succeeds(
|
||||
SutProvider<AcceptOrgUserCommand> sutProvider,
|
||||
User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails)
|
||||
{
|
||||
SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.PolicyRequirements)
|
||||
.Returns(true);
|
||||
|
||||
// User has 2FA enabled
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(user)
|
||||
.Returns(true);
|
||||
|
||||
// Organization they are trying to join requires 2FA
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(new RequireTwoFactorPolicyRequirement(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = orgUser.OrganizationId,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Invited,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
}
|
||||
]));
|
||||
|
||||
await sutProvider.Sut.AcceptOrgUserAsync(orgUser, user, _userService);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.ReplaceAsync(Arg.Is<OrganizationUser>(ou => ou.Status == OrganizationUserStatusType.Accepted));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task AcceptOrgUserAsync_WithPolicyRequirementsEnabled_UserJoiningOrgWithout2FARequirement_Succeeds(
|
||||
SutProvider<AcceptOrgUserCommand> sutProvider,
|
||||
User user, Organization org, OrganizationUser orgUser, OrganizationUserUserDetails adminUserDetails)
|
||||
{
|
||||
SetupCommonAcceptOrgUserMocks(sutProvider, user, org, orgUser, adminUserDetails);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.PolicyRequirements)
|
||||
.Returns(true);
|
||||
|
||||
// Organization they are trying to join doesn't require 2FA
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(new RequireTwoFactorPolicyRequirement(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = Guid.NewGuid(),
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Invited,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
}
|
||||
]));
|
||||
|
||||
await sutProvider.Sut.AcceptOrgUserAsync(orgUser, user, _userService);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.ReplaceAsync(Arg.Is<OrganizationUser>(ou => ou.Status == OrganizationUserStatusType.Accepted));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(OrganizationUserType.Admin)]
|
||||
[BitAutoData(OrganizationUserType.Owner)]
|
||||
@ -647,9 +747,6 @@ public class AcceptOrgUserCommandTests
|
||||
.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg)
|
||||
.Returns(false);
|
||||
|
||||
// User doesn't have 2FA enabled
|
||||
_twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user).Returns(false);
|
||||
|
||||
// Org does not require 2FA
|
||||
sutProvider.GetDependency<IPolicyService>().GetPoliciesApplicableToUserAsync(user.Id,
|
||||
PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Invited)
|
||||
|
@ -1,6 +1,9 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Billing.Enums;
|
||||
@ -321,4 +324,122 @@ public class ConfirmOrganizationUserCommandTests
|
||||
Assert.Contains("User does not have two-step login enabled.", result[1].Item2);
|
||||
Assert.Contains("Cannot confirm this member to the organization until they leave or remove all other organizations.", result[2].Item2);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorRequired_ThrowsBadRequestException(
|
||||
Organization org, OrganizationUser confirmingUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
||||
SutProvider<ConfirmOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
||||
var featureService = sutProvider.GetDependency<IFeatureService>();
|
||||
var policyRequirementQuery = sutProvider.GetDependency<IPolicyRequirementQuery>();
|
||||
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
|
||||
|
||||
org.PlanType = PlanType.EnterpriseAnnually;
|
||||
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
||||
orgUser.UserId = user.Id;
|
||||
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
||||
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
||||
userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user });
|
||||
featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements).Returns(true);
|
||||
policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(new RequireTwoFactorPolicyRequirement(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Accepted,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication
|
||||
}
|
||||
]));
|
||||
twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user.Id)))
|
||||
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (user.Id, false) });
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, "key", confirmingUser.Id));
|
||||
Assert.Contains("User does not have two-step login enabled.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorNotRequired_Succeeds(
|
||||
Organization org, OrganizationUser confirmingUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
||||
SutProvider<ConfirmOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
||||
var featureService = sutProvider.GetDependency<IFeatureService>();
|
||||
var policyRequirementQuery = sutProvider.GetDependency<IPolicyRequirementQuery>();
|
||||
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
|
||||
|
||||
org.PlanType = PlanType.EnterpriseAnnually;
|
||||
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
||||
orgUser.UserId = user.Id;
|
||||
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
||||
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
||||
userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user });
|
||||
featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements).Returns(true);
|
||||
policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(new RequireTwoFactorPolicyRequirement(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = Guid.NewGuid(),
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Invited,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
}
|
||||
]));
|
||||
twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user.Id)))
|
||||
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (user.Id, false) });
|
||||
|
||||
await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, "key", confirmingUser.Id);
|
||||
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Confirmed);
|
||||
await sutProvider.GetDependency<IMailService>().Received(1).SendOrganizationConfirmedEmailAsync(org.DisplayName(), user.Email, orgUser.AccessSecretsManager);
|
||||
await organizationUserRepository.Received(1).ReplaceManyAsync(Arg.Is<List<OrganizationUser>>(users => users.Contains(orgUser) && users.Count == 1));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ConfirmUserAsync_WithPolicyRequirementsEnabled_AndTwoFactorEnabled_Succeeds(
|
||||
Organization org, OrganizationUser confirmingUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
||||
SutProvider<ConfirmOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
||||
var featureService = sutProvider.GetDependency<IFeatureService>();
|
||||
var policyRequirementQuery = sutProvider.GetDependency<IPolicyRequirementQuery>();
|
||||
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
|
||||
|
||||
org.PlanType = PlanType.EnterpriseAnnually;
|
||||
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
||||
orgUser.UserId = user.Id;
|
||||
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
||||
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
||||
userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user });
|
||||
featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements).Returns(true);
|
||||
policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(new RequireTwoFactorPolicyRequirement(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Accepted,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication
|
||||
}
|
||||
]));
|
||||
twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user.Id)))
|
||||
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (user.Id, true) });
|
||||
|
||||
await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, "key", confirmingUser.Id);
|
||||
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Confirmed);
|
||||
await sutProvider.GetDependency<IMailService>().Received(1).SendOrganizationConfirmedEmailAsync(org.DisplayName(), user.Email, orgUser.AccessSecretsManager);
|
||||
await organizationUserRepository.Received(1).ReplaceManyAsync(Arg.Is<List<OrganizationUser>>(users => users.Contains(orgUser) && users.Count == 1));
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +25,13 @@ public class GetOrganizationUsersClaimedStatusQueryTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetUsersOrganizationManagementStatusAsync_WithUseSsoEnabled_Success(
|
||||
public async Task GetUsersOrganizationManagementStatusAsync_WithUseOrganizationDomainsEnabled_Success(
|
||||
Organization organization,
|
||||
ICollection<OrganizationUser> usersWithClaimedDomain,
|
||||
SutProvider<GetOrganizationUsersClaimedStatusQuery> sutProvider)
|
||||
{
|
||||
organization.Enabled = true;
|
||||
organization.UseSso = true;
|
||||
organization.UseOrganizationDomains = true;
|
||||
|
||||
var userIdWithoutClaimedDomain = Guid.NewGuid();
|
||||
var userIdsToCheck = usersWithClaimedDomain.Select(u => u.Id).Concat(new List<Guid> { userIdWithoutClaimedDomain }).ToList();
|
||||
@ -51,13 +51,13 @@ public class GetOrganizationUsersClaimedStatusQueryTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetUsersOrganizationManagementStatusAsync_WithUseSsoDisabled_ReturnsAllFalse(
|
||||
public async Task GetUsersOrganizationManagementStatusAsync_WithUseOrganizationDomainsDisabled_ReturnsAllFalse(
|
||||
Organization organization,
|
||||
ICollection<OrganizationUser> usersWithClaimedDomain,
|
||||
SutProvider<GetOrganizationUsersClaimedStatusQuery> sutProvider)
|
||||
{
|
||||
organization.Enabled = true;
|
||||
organization.UseSso = false;
|
||||
organization.UseOrganizationDomains = false;
|
||||
|
||||
var userIdWithoutClaimedDomain = Guid.NewGuid();
|
||||
var userIdsToCheck = usersWithClaimedDomain.Select(u => u.Id).Concat(new List<Guid> { userIdWithoutClaimedDomain }).ToList();
|
||||
|
@ -40,43 +40,6 @@ public class RemoveOrganizationUserCommandTests
|
||||
// Act
|
||||
await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.GetUsersOrganizationClaimedStatusAsync(default, default);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.DeleteAsync(organizationUser);
|
||||
await sutProvider.GetDependency<IEventService>()
|
||||
.Received(1)
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RemoveUser_WithDeletingUserId_WithAccountDeprovisioningEnabled_Success(
|
||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser organizationUser,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser deletingUser,
|
||||
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(organizationUser.Id)
|
||||
.Returns(organizationUser);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(deletingUser.Id)
|
||||
.Returns(deletingUser);
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.OrganizationOwner(deletingUser.OrganizationId)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||
.Received(1)
|
||||
@ -235,15 +198,12 @@ public class RemoveOrganizationUserCommandTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RemoveUserAsync_WithDeletingUserId_WithAccountDeprovisioningEnabled_WhenUserIsManaged_ThrowsException(
|
||||
public async Task RemoveUserAsync_WithDeletingUserId_WhenUserIsManaged_ThrowsException(
|
||||
[OrganizationUser(status: OrganizationUserStatusType.Confirmed)] OrganizationUser orgUser,
|
||||
Guid deletingUserId,
|
||||
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(orgUser.Id)
|
||||
.Returns(orgUser);
|
||||
@ -285,34 +245,6 @@ public class RemoveOrganizationUserCommandTests
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RemoveUser_WithEventSystemUser_WithAccountDeprovisioningEnabled_Success(
|
||||
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser organizationUser,
|
||||
EventSystemUser eventSystemUser, SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(organizationUser.Id)
|
||||
.Returns(organizationUser);
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.Id, eventSystemUser);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.GetUsersOrganizationClaimedStatusAsync(default, default);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.DeleteAsync(organizationUser);
|
||||
await sutProvider.GetDependency<IEventService>()
|
||||
.Received(1)
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RemoveUser_WithEventSystemUser_NotFound_ThrowsException(
|
||||
@ -474,7 +406,6 @@ public class RemoveOrganizationUserCommandTests
|
||||
var sutProvider = SutProviderFactory();
|
||||
var eventDate = sutProvider.GetDependency<FakeTimeProvider>().GetUtcNow().UtcDateTime;
|
||||
orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId;
|
||||
|
||||
var organizationUsers = new[] { orgUser1, orgUser2 };
|
||||
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
||||
|
||||
@ -499,60 +430,6 @@ public class RemoveOrganizationUserCommandTests
|
||||
// Act
|
||||
var result = await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, result.Count());
|
||||
Assert.All(result, r => Assert.Empty(r.ErrorMessage));
|
||||
await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.GetUsersOrganizationClaimedStatusAsync(default, default);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)));
|
||||
await sutProvider.GetDependency<IEventService>()
|
||||
.Received(1)
|
||||
.LogOrganizationUserEventsAsync(
|
||||
Arg.Is<IEnumerable<(OrganizationUser OrganizationUser, EventType EventType, DateTime? DateTime)>>(i =>
|
||||
i.First().OrganizationUser.Id == orgUser1.Id
|
||||
&& i.Last().OrganizationUser.Id == orgUser2.Id
|
||||
&& i.All(u => u.DateTime == eventDate)));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RemoveUsers_WithDeletingUserId_WithAccountDeprovisioningEnabled_Success(
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser deletingUser,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1, OrganizationUser orgUser2)
|
||||
{
|
||||
// Arrange
|
||||
var sutProvider = SutProviderFactory();
|
||||
var eventDate = sutProvider.GetDependency<FakeTimeProvider>().GetUtcNow().UtcDateTime;
|
||||
orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId;
|
||||
var organizationUsers = new[] { orgUser1, orgUser2 };
|
||||
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync(default)
|
||||
.ReturnsForAnyArgs(organizationUsers);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(deletingUser.Id)
|
||||
.Returns(deletingUser);
|
||||
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||
.HasConfirmedOwnersExceptAsync(deletingUser.OrganizationId, Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.OrganizationOwner(deletingUser.OrganizationId)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||
.GetUsersOrganizationClaimedStatusAsync(
|
||||
deletingUser.OrganizationId,
|
||||
Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)))
|
||||
.Returns(new Dictionary<Guid, bool> { { orgUser1.Id, false }, { orgUser2.Id, false } });
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.RemoveUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, result.Count());
|
||||
Assert.All(result, r => Assert.Empty(r.ErrorMessage));
|
||||
@ -638,7 +515,7 @@ public class RemoveOrganizationUserCommandTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RemoveUsers_WithDeletingUserId_RemovingClaimedUser_WithAccountDeprovisioningEnabled_ThrowsException(
|
||||
public async Task RemoveUsers_WithDeletingUserId_RemovingClaimedUser_ThrowsException(
|
||||
[OrganizationUser(status: OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser,
|
||||
OrganizationUser deletingUser,
|
||||
SutProvider<RemoveOrganizationUserCommand> sutProvider)
|
||||
@ -646,10 +523,6 @@ public class RemoveOrganizationUserCommandTests
|
||||
// Arrange
|
||||
orgUser.OrganizationId = deletingUser.OrganizationId;
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser.Id)))
|
||||
.Returns(new[] { orgUser });
|
||||
@ -739,51 +612,6 @@ public class RemoveOrganizationUserCommandTests
|
||||
&& u.DateTime == eventDate)));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RemoveUsers_WithEventSystemUser_WithAccountDeprovisioningEnabled_Success(
|
||||
EventSystemUser eventSystemUser,
|
||||
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1,
|
||||
OrganizationUser orgUser2)
|
||||
{
|
||||
// Arrange
|
||||
var sutProvider = SutProviderFactory();
|
||||
var eventDate = sutProvider.GetDependency<FakeTimeProvider>().GetUtcNow().UtcDateTime;
|
||||
orgUser1.OrganizationId = orgUser2.OrganizationId;
|
||||
var organizationUsers = new[] { orgUser1, orgUser2 };
|
||||
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyAsync(default)
|
||||
.ReturnsForAnyArgs(organizationUsers);
|
||||
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>()
|
||||
.HasConfirmedOwnersExceptAsync(orgUser1.OrganizationId, Arg.Any<IEnumerable<Guid>>())
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.RemoveUsersAsync(orgUser1.OrganizationId, organizationUserIds, eventSystemUser);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, result.Count());
|
||||
Assert.All(result, r => Assert.Empty(r.ErrorMessage));
|
||||
await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.GetUsersOrganizationClaimedStatusAsync(default, default);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)));
|
||||
await sutProvider.GetDependency<IEventService>()
|
||||
.Received(1)
|
||||
.LogOrganizationUserEventsAsync(
|
||||
Arg.Is<IEnumerable<(OrganizationUser OrganizationUser, EventType EventType, EventSystemUser EventSystemUser, DateTime? DateTime)>>(
|
||||
i => i.First().OrganizationUser.Id == orgUser1.Id
|
||||
&& i.Last().OrganizationUser.Id == orgUser2.Id
|
||||
&& i.All(u => u.EventSystemUser == eventSystemUser
|
||||
&& u.DateTime == eventDate)));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RemoveUsers_WithEventSystemUser_WithMismatchingOrganizationId_ThrowsException(
|
||||
EventSystemUser eventSystemUser,
|
||||
|
@ -1,6 +1,9 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.RestoreUser.v1;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Billing.Enums;
|
||||
@ -208,6 +211,57 @@ public class RestoreOrganizationUserCommandTests
|
||||
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUser_WithPolicyRequirementsEnabled_With2FAPolicyEnabled_WithoutUser2FAConfigured_Fails(
|
||||
Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
||||
SutProvider<RestoreOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
organizationUser.Email = null;
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.PolicyRequirements)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
|
||||
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, false) });
|
||||
|
||||
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(organizationUser.UserId.Value)
|
||||
.Returns(new RequireTwoFactorPolicyRequirement(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = organizationUser.OrganizationId,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Revoked,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication
|
||||
}
|
||||
]));
|
||||
|
||||
var user = new User();
|
||||
user.Email = "test@bitwarden.com";
|
||||
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(organizationUser.UserId.Value).Returns(user);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
|
||||
|
||||
Assert.Contains("test@bitwarden.com is not compliant with the two-step login policy", exception.Message.ToLowerInvariant());
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
|
||||
await sutProvider.GetDependency<IEventService>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
|
||||
await sutProvider.GetDependency<IPushNotificationService>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUser_With2FAPolicyEnabled_WithUser2FAConfigured_Success(
|
||||
Organization organization,
|
||||
@ -235,6 +289,46 @@ public class RestoreOrganizationUserCommandTests
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUser_WithPolicyRequirementsEnabled_With2FAPolicyEnabled_WithUser2FAConfigured_Success(
|
||||
Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
||||
SutProvider<RestoreOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.PolicyRequirements)
|
||||
.Returns(true);
|
||||
|
||||
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
|
||||
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, true) });
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(organizationUser.UserId.Value)
|
||||
.Returns(new RequireTwoFactorPolicyRequirement(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = organizationUser.OrganizationId,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Revoked,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication
|
||||
}
|
||||
]));
|
||||
|
||||
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Confirmed);
|
||||
await sutProvider.GetDependency<IEventService>()
|
||||
.Received(1)
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUser_WithSingleOrgPolicyEnabled_Fails(
|
||||
Organization organization,
|
||||
@ -277,45 +371,6 @@ public class RestoreOrganizationUserCommandTests
|
||||
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUser_vNext_WithOtherOrganizationSingleOrgPolicyEnabled_Fails(
|
||||
Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser secondOrganizationUser,
|
||||
SutProvider<RestoreOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
|
||||
secondOrganizationUser.UserId = organizationUser.UserId;
|
||||
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
|
||||
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUser.UserId.Value, true) });
|
||||
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.AnyPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>())
|
||||
.Returns(true);
|
||||
|
||||
var user = new User { Email = "test@bitwarden.com" };
|
||||
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(organizationUser.UserId.Value).Returns(user);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
|
||||
|
||||
Assert.Contains("test@bitwarden.com belongs to an organization that doesn't allow them to join multiple organizations", exception.Message.ToLowerInvariant());
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
|
||||
await sutProvider.GetDependency<IEventService>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
|
||||
await sutProvider.GetDependency<IPushNotificationService>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUser_WithSingleOrgPolicyEnabled_And_2FA_Policy_Fails(
|
||||
Organization organization,
|
||||
@ -364,20 +419,42 @@ public class RestoreOrganizationUserCommandTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUser_vNext_With2FAPolicyEnabled_WithoutUser2FAConfigured_Fails(
|
||||
public async Task RestoreUser_WithPolicyRequirementsEnabled_WithSingleOrgPolicyEnabled_And_2FA_Policy_Fails(
|
||||
Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser secondOrganizationUser,
|
||||
SutProvider<RestoreOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
organizationUser.Email = null;
|
||||
|
||||
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
|
||||
secondOrganizationUser.UserId = organizationUser.UserId;
|
||||
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.PolicyRequirements)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(organizationUser.UserId.Value)
|
||||
.Returns(new[] { organizationUser, secondOrganizationUser });
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
|
||||
.Returns([new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication }
|
||||
]);
|
||||
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>())
|
||||
.Returns(new[]
|
||||
{
|
||||
new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.SingleOrg, OrganizationUserStatus = OrganizationUserStatusType.Revoked }
|
||||
});
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(organizationUser.UserId.Value)
|
||||
.Returns(new RequireTwoFactorPolicyRequirement(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = organizationUser.OrganizationId,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Revoked,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication
|
||||
}
|
||||
]));
|
||||
|
||||
var user = new User { Email = "test@bitwarden.com" };
|
||||
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(organizationUser.UserId.Value).Returns(user);
|
||||
@ -385,7 +462,7 @@ public class RestoreOrganizationUserCommandTests
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
|
||||
|
||||
Assert.Contains("test@bitwarden.com is not compliant with the two-step login policy", exception.Message.ToLowerInvariant());
|
||||
Assert.Contains("test@bitwarden.com is not compliant with the single organization and two-step login policy", exception.Message.ToLowerInvariant());
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
@ -398,35 +475,6 @@ public class RestoreOrganizationUserCommandTests
|
||||
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUser_vNext_With2FAPolicyEnabled_WithUser2FAConfigured_Success(
|
||||
Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
||||
SutProvider<RestoreOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
|
||||
RestoreUser_Setup(organization, owner, organizationUser, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
|
||||
.Returns([new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication }
|
||||
]);
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
|
||||
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)> { (organizationUser.UserId.Value, true) });
|
||||
|
||||
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Confirmed);
|
||||
await sutProvider.GetDependency<IEventService>()
|
||||
.Received(1)
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUser_WhenUserOwningAnotherFreeOrganization_ThenRestoreUserFails(
|
||||
Organization organization,
|
||||
@ -672,6 +720,77 @@ public class RestoreOrganizationUserCommandTests
|
||||
.RestoreAsync(orgUser3.Id, OrganizationUserStatusType.Invited);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUsers_WithPolicyRequirementsEnabled_With2FAPolicy_BlocksNonCompliantUser(Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser1,
|
||||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser2,
|
||||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser orgUser3,
|
||||
SutProvider<RestoreOrganizationUserCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
RestoreUser_Setup(organization, owner, orgUser1, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.PolicyRequirements)
|
||||
.Returns(true);
|
||||
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
||||
var policyService = sutProvider.GetDependency<IPolicyService>();
|
||||
var userService = Substitute.For<IUserService>();
|
||||
|
||||
orgUser1.Email = orgUser2.Email = null;
|
||||
orgUser3.UserId = null;
|
||||
orgUser3.Key = null;
|
||||
orgUser1.OrganizationId = orgUser2.OrganizationId = orgUser3.OrganizationId = organization.Id;
|
||||
organizationUserRepository
|
||||
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser1.Id) && ids.Contains(orgUser2.Id) && ids.Contains(orgUser3.Id)))
|
||||
.Returns(new[] { orgUser1, orgUser2, orgUser3 });
|
||||
|
||||
userRepository.GetByIdAsync(orgUser2.UserId!.Value).Returns(new User { Email = "test@example.com" });
|
||||
|
||||
// Setup 2FA policy
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(Arg.Any<Guid>())
|
||||
.Returns(new RequireTwoFactorPolicyRequirement(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Revoked,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication
|
||||
}
|
||||
]));
|
||||
|
||||
// User1 has 2FA, User2 doesn't
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser1.UserId!.Value) && ids.Contains(orgUser2.UserId!.Value)))
|
||||
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>
|
||||
{
|
||||
(orgUser1.UserId!.Value, true),
|
||||
(orgUser2.UserId!.Value, false)
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.RestoreUsersAsync(organization.Id, [orgUser1.Id, orgUser2.Id, orgUser3.Id], owner.Id, userService);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Empty(result[0].Item2); // First user should succeed
|
||||
Assert.Contains("two-step login", result[1].Item2); // Second user should fail
|
||||
Assert.Empty(result[2].Item2); // Third user should succeed
|
||||
await organizationUserRepository
|
||||
.Received(1)
|
||||
.RestoreAsync(orgUser1.Id, OrganizationUserStatusType.Confirmed);
|
||||
await organizationUserRepository
|
||||
.DidNotReceive()
|
||||
.RestoreAsync(orgUser2.Id, Arg.Any<OrganizationUserStatusType>());
|
||||
await organizationUserRepository
|
||||
.Received(1)
|
||||
.RestoreAsync(orgUser3.Id, OrganizationUserStatusType.Invited);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUsers_UserOwnsAnotherFreeOrganization_BlocksOwnerUserFromBeingRestored(Organization organization,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
|
@ -10,9 +10,6 @@ using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Tools.Enums;
|
||||
using Bit.Core.Tools.Models.Business;
|
||||
using Bit.Core.Tools.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
@ -51,15 +48,6 @@ public class CloudICloudOrganizationSignUpCommandTests
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
|
||||
Arg.Is<OrganizationUser>(o => o.AccessSecretsManager == signup.UseSecretsManager));
|
||||
|
||||
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
|
||||
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
|
||||
referenceEvent.Type == ReferenceEventType.Signup &&
|
||||
referenceEvent.PlanName == plan.Name &&
|
||||
referenceEvent.PlanType == plan.Type &&
|
||||
referenceEvent.Seats == result.Organization.Seats &&
|
||||
referenceEvent.Storage == result.Organization.MaxStorageGb));
|
||||
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
|
||||
|
||||
Assert.NotNull(result.Organization);
|
||||
Assert.NotNull(result.OrganizationUser);
|
||||
|
||||
@ -145,15 +133,6 @@ public class CloudICloudOrganizationSignUpCommandTests
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
|
||||
Arg.Is<OrganizationUser>(o => o.AccessSecretsManager == signup.UseSecretsManager));
|
||||
|
||||
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
|
||||
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
|
||||
referenceEvent.Type == ReferenceEventType.Signup &&
|
||||
referenceEvent.PlanName == plan.Name &&
|
||||
referenceEvent.PlanType == plan.Type &&
|
||||
referenceEvent.Seats == result.Organization.Seats &&
|
||||
referenceEvent.Storage == result.Organization.MaxStorageGb));
|
||||
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
|
||||
|
||||
Assert.NotNull(result.Organization);
|
||||
Assert.NotNull(result.OrganizationUser);
|
||||
|
||||
|
@ -0,0 +1,155 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Billing.Pricing;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.StaticStore;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Organizations.OrganizationSignUp;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class ProviderClientOrganizationSignUpCommandTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData(PlanType.TeamsAnnually)]
|
||||
[BitAutoData(PlanType.TeamsMonthly)]
|
||||
[BitAutoData(PlanType.EnterpriseAnnually)]
|
||||
[BitAutoData(PlanType.EnterpriseMonthly)]
|
||||
public async Task SignupClientAsync_ValidParameters_CreatesOrganizationSuccessfully(
|
||||
PlanType planType,
|
||||
OrganizationSignup signup,
|
||||
string collectionName,
|
||||
SutProvider<ProviderClientOrganizationSignUpCommand> sutProvider)
|
||||
{
|
||||
signup.Plan = planType;
|
||||
signup.AdditionalSeats = 15;
|
||||
signup.CollectionName = collectionName;
|
||||
|
||||
var plan = StaticStore.GetPlan(signup.Plan);
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(signup.Plan)
|
||||
.Returns(plan);
|
||||
|
||||
var result = await sutProvider.Sut.SignUpClientOrganizationAsync(signup);
|
||||
|
||||
Assert.NotNull(result.Organization);
|
||||
Assert.NotNull(result.DefaultCollection);
|
||||
Assert.Equal(collectionName, result.DefaultCollection.Name);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.Received(1)
|
||||
.CreateAsync(
|
||||
Arg.Is<Organization>(o =>
|
||||
o.Name == signup.Name &&
|
||||
o.BillingEmail == signup.BillingEmail &&
|
||||
o.PlanType == plan.Type &&
|
||||
o.Seats == signup.AdditionalSeats &&
|
||||
o.MaxCollections == plan.PasswordManager.MaxCollections &&
|
||||
o.UsePasswordManager == true &&
|
||||
o.UseSecretsManager == false &&
|
||||
o.Status == OrganizationStatusType.Created
|
||||
)
|
||||
);
|
||||
|
||||
await sutProvider.GetDependency<ICollectionRepository>()
|
||||
.Received(1)
|
||||
.CreateAsync(
|
||||
Arg.Is<Collection>(c =>
|
||||
c.Name == collectionName &&
|
||||
c.OrganizationId == result.Organization.Id
|
||||
),
|
||||
Arg.Any<IEnumerable<CollectionAccessSelection>>(),
|
||||
Arg.Any<IEnumerable<CollectionAccessSelection>>()
|
||||
);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationApiKeyRepository>()
|
||||
.Received(1)
|
||||
.CreateAsync(
|
||||
Arg.Is<OrganizationApiKey>(k =>
|
||||
k.OrganizationId == result.Organization.Id &&
|
||||
k.Type == OrganizationApiKeyType.Default
|
||||
)
|
||||
);
|
||||
|
||||
await sutProvider.GetDependency<IApplicationCacheService>()
|
||||
.Received(1)
|
||||
.UpsertOrganizationAbilityAsync(Arg.Is<Organization>(o => o.Id == result.Organization.Id));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SignupClientAsync_NullPlan_ThrowsBadRequestException(
|
||||
OrganizationSignup signup,
|
||||
SutProvider<ProviderClientOrganizationSignUpCommand> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(signup.Plan)
|
||||
.Returns((Plan)null);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.SignUpClientOrganizationAsync(signup));
|
||||
|
||||
Assert.Contains(ProviderClientOrganizationSignUpCommand.PlanNullErrorMessage, exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SignupClientAsync_NegativeAdditionalSeats_ThrowsBadRequestException(
|
||||
OrganizationSignup signup,
|
||||
SutProvider<ProviderClientOrganizationSignUpCommand> sutProvider)
|
||||
{
|
||||
signup.Plan = PlanType.TeamsMonthly;
|
||||
signup.AdditionalSeats = -5;
|
||||
|
||||
var plan = StaticStore.GetPlan(signup.Plan);
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(signup.Plan)
|
||||
.Returns(plan);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.SignUpClientOrganizationAsync(signup));
|
||||
|
||||
Assert.Contains(ProviderClientOrganizationSignUpCommand.AdditionalSeatsNegativeErrorMessage, exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(PlanType.TeamsAnnually)]
|
||||
public async Task SignupClientAsync_WhenExceptionIsThrown_CleanupIsPerformed(
|
||||
PlanType planType,
|
||||
OrganizationSignup signup,
|
||||
SutProvider<ProviderClientOrganizationSignUpCommand> sutProvider)
|
||||
{
|
||||
signup.Plan = planType;
|
||||
|
||||
var plan = StaticStore.GetPlan(signup.Plan);
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(signup.Plan)
|
||||
.Returns(plan);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
|
||||
.When(x => x.CreateAsync(Arg.Any<OrganizationApiKey>()))
|
||||
.Do(_ => throw new Exception());
|
||||
|
||||
var thrownException = await Assert.ThrowsAsync<Exception>(
|
||||
() => sutProvider.Sut.SignUpClientOrganizationAsync(signup));
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.Received(1)
|
||||
.DeleteAsync(Arg.Is<Organization>(o => o.Name == signup.Name));
|
||||
|
||||
await sutProvider.GetDependency<IApplicationCacheService>()
|
||||
.Received(1)
|
||||
.DeleteOrganizationAbilityAsync(Arg.Any<Guid>());
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class RequireTwoFactorPolicyRequirementFactoryTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void IsTwoFactorRequiredForOrganization_WithNoPolicies_ReturnsFalse(
|
||||
Guid organizationId,
|
||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
||||
{
|
||||
var actual = sutProvider.Sut.Create([]);
|
||||
|
||||
Assert.False(actual.IsTwoFactorRequiredForOrganization(organizationId));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void IsTwoFactorRequiredForOrganization_WithOrganizationPolicy_ReturnsTrue(
|
||||
Guid organizationId,
|
||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
||||
{
|
||||
var actual = sutProvider.Sut.Create(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = organizationId,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
}
|
||||
]);
|
||||
|
||||
Assert.True(actual.IsTwoFactorRequiredForOrganization(organizationId));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void IsTwoFactorRequiredForOrganization_WithOtherOrganizationPolicy_ReturnsFalse(
|
||||
Guid organizationId,
|
||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
||||
{
|
||||
var actual = sutProvider.Sut.Create(
|
||||
[
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = Guid.NewGuid(),
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
},
|
||||
]);
|
||||
|
||||
Assert.False(actual.IsTwoFactorRequiredForOrganization(organizationId));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void OrganizationsRequiringTwoFactor_WithNoPolicies_ReturnsEmptyCollection(
|
||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
||||
{
|
||||
var actual = sutProvider.Sut.Create([]);
|
||||
|
||||
Assert.Empty(actual.OrganizationsRequiringTwoFactor);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void OrganizationsRequiringTwoFactor_WithMultiplePolicies_ReturnsActiveMemberships(
|
||||
Guid orgId1, Guid orgUserId1, Guid orgId2, Guid orgUserId2,
|
||||
Guid orgId3, Guid orgUserId3, Guid orgId4, Guid orgUserId4,
|
||||
SutProvider<RequireTwoFactorPolicyRequirementFactory> sutProvider)
|
||||
{
|
||||
var policies = new[]
|
||||
{
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = orgId1,
|
||||
OrganizationUserId = orgUserId1,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Accepted
|
||||
},
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = orgId2,
|
||||
OrganizationUserId = orgUserId2,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Confirmed
|
||||
},
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = orgId3,
|
||||
OrganizationUserId = orgUserId3,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Invited
|
||||
},
|
||||
new PolicyDetails
|
||||
{
|
||||
OrganizationId = orgId4,
|
||||
OrganizationUserId = orgUserId4,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Revoked
|
||||
}
|
||||
};
|
||||
|
||||
var actual = sutProvider.Sut.Create(policies);
|
||||
|
||||
var result = actual.OrganizationsRequiringTwoFactor.ToList();
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Contains(result, p => p.OrganizationId == orgId1 && p.OrganizationUserId == orgUserId1);
|
||||
Assert.Contains(result, p => p.OrganizationId == orgId2 && p.OrganizationUserId == orgUserId2);
|
||||
Assert.DoesNotContain(result, p => p.OrganizationId == orgId3 && p.OrganizationUserId == orgUserId3);
|
||||
Assert.DoesNotContain(result, p => p.OrganizationId == orgId4 && p.OrganizationUserId == orgUserId4);
|
||||
}
|
||||
}
|
@ -122,9 +122,6 @@ public class SingleOrgPolicyValidatorTests
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(savingUserId);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(policyUpdate.OrganizationId).Returns(organization);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
|
||||
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>())
|
||||
.Returns(new CommandResult());
|
||||
@ -148,161 +145,4 @@ public class SingleOrgPolicyValidatorTests
|
||||
.Received(1)
|
||||
.SendOrganizationUserRevokedForPolicySingleOrgEmailAsync(organization.DisplayName(), nonCompliantUser.Email);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task OnSaveSideEffectsAsync_RemovesNonCompliantUsers(
|
||||
[PolicyUpdate(PolicyType.SingleOrg)] PolicyUpdate policyUpdate,
|
||||
[Policy(PolicyType.SingleOrg, false)] Policy policy,
|
||||
Guid savingUserId,
|
||||
Guid nonCompliantUserId,
|
||||
Organization organization, SutProvider<SingleOrgPolicyValidator> sutProvider)
|
||||
{
|
||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
||||
|
||||
var compliantUser1 = new OrganizationUserUserDetails
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
OrganizationId = organization.Id,
|
||||
Type = OrganizationUserType.User,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
UserId = new Guid(),
|
||||
Email = "user1@example.com"
|
||||
};
|
||||
|
||||
var compliantUser2 = new OrganizationUserUserDetails
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
OrganizationId = organization.Id,
|
||||
Type = OrganizationUserType.User,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
UserId = new Guid(),
|
||||
Email = "user2@example.com"
|
||||
};
|
||||
|
||||
var nonCompliantUser = new OrganizationUserUserDetails
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
OrganizationId = organization.Id,
|
||||
Type = OrganizationUserType.User,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
UserId = nonCompliantUserId,
|
||||
Email = "user3@example.com"
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyDetailsByOrganizationAsync(policyUpdate.OrganizationId)
|
||||
.Returns([compliantUser1, compliantUser2, nonCompliantUser]);
|
||||
|
||||
var otherOrganizationUser = new OrganizationUser
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
OrganizationId = new Guid(),
|
||||
UserId = nonCompliantUserId,
|
||||
Status = OrganizationUserStatusType.Confirmed
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByManyUsersAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(nonCompliantUserId)))
|
||||
.Returns([otherOrganizationUser]);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(savingUserId);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(policyUpdate.OrganizationId).Returns(organization);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(false);
|
||||
|
||||
sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
|
||||
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>())
|
||||
.Returns(new CommandResult());
|
||||
|
||||
await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy);
|
||||
|
||||
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
|
||||
.DidNotReceive()
|
||||
.RemoveUserAsync(policyUpdate.OrganizationId, compliantUser1.Id, savingUserId);
|
||||
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
|
||||
.DidNotReceive()
|
||||
.RemoveUserAsync(policyUpdate.OrganizationId, compliantUser2.Id, savingUserId);
|
||||
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>()
|
||||
.Received(1)
|
||||
.RemoveUserAsync(policyUpdate.OrganizationId, nonCompliantUser.Id, savingUserId);
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.DidNotReceive()
|
||||
.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(organization.DisplayName(), compliantUser1.Email);
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.DidNotReceive()
|
||||
.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(organization.DisplayName(), compliantUser2.Email);
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(organization.DisplayName(), nonCompliantUser.Email);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task OnSaveSideEffectsAsync_WhenAccountDeprovisioningIsEnabled_ThenUsersAreRevoked(
|
||||
[PolicyUpdate(PolicyType.SingleOrg)] PolicyUpdate policyUpdate,
|
||||
[Policy(PolicyType.SingleOrg, false)] Policy policy,
|
||||
Guid savingUserId,
|
||||
Guid nonCompliantUserId,
|
||||
Organization organization, SutProvider<SingleOrgPolicyValidator> sutProvider)
|
||||
{
|
||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
||||
|
||||
var compliantUser1 = new OrganizationUserUserDetails
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Type = OrganizationUserType.User,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
UserId = new Guid(),
|
||||
Email = "user1@example.com"
|
||||
};
|
||||
|
||||
var compliantUser2 = new OrganizationUserUserDetails
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Type = OrganizationUserType.User,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
UserId = new Guid(),
|
||||
Email = "user2@example.com"
|
||||
};
|
||||
|
||||
var nonCompliantUser = new OrganizationUserUserDetails
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Type = OrganizationUserType.User,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
UserId = nonCompliantUserId,
|
||||
Email = "user3@example.com"
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyDetailsByOrganizationAsync(policyUpdate.OrganizationId)
|
||||
.Returns([compliantUser1, compliantUser2, nonCompliantUser]);
|
||||
|
||||
var otherOrganizationUser = new OrganizationUser
|
||||
{
|
||||
OrganizationId = new Guid(),
|
||||
UserId = nonCompliantUserId,
|
||||
Status = OrganizationUserStatusType.Confirmed
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByManyUsersAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(nonCompliantUserId)))
|
||||
.Returns([otherOrganizationUser]);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(savingUserId);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(policyUpdate.OrganizationId)
|
||||
.Returns(organization);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
|
||||
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>())
|
||||
.Returns(new CommandResult());
|
||||
|
||||
await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy);
|
||||
|
||||
await sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
|
||||
.Received()
|
||||
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>());
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
|
||||
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
@ -24,7 +23,7 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Policies.PolicyValidat
|
||||
public class TwoFactorAuthenticationPolicyValidatorTests
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task OnSaveSideEffectsAsync_RemovesNonCompliantUsers(
|
||||
public async Task OnSaveSideEffectsAsync_GivenNonCompliantUsersWithoutMasterPassword_Throws(
|
||||
Organization organization,
|
||||
[PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate,
|
||||
[Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy,
|
||||
@ -33,249 +32,6 @@ public class TwoFactorAuthenticationPolicyValidatorTests
|
||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
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(policyUpdate.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 savingUserId = Guid.NewGuid();
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(savingUserId);
|
||||
|
||||
await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy);
|
||||
|
||||
var removeOrganizationUserCommand = sutProvider.GetDependency<IRemoveOrganizationUserCommand>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task OnSaveSideEffectsAsync_UsersToBeRemovedDontHaveMasterPasswords_Throws(
|
||||
Organization organization,
|
||||
[PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate,
|
||||
[Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy,
|
||||
SutProvider<TwoFactorAuthenticationPolicyValidator> sutProvider)
|
||||
{
|
||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
||||
|
||||
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<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(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 badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy));
|
||||
|
||||
Assert.Equal(TwoFactorAuthenticationPolicyValidator.NonCompliantMembersWillLoseAccessMessage, badRequestException.Message);
|
||||
|
||||
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>().DidNotReceiveWithAnyArgs()
|
||||
.RemoveUserAsync(organizationId: default, organizationUserId: default, deletingUserId: default);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task OnSaveSideEffectsAsync_GivenUpdateTo2faPolicy_WhenAccountProvisioningIsDisabled_ThenRevokeUserCommandShouldNotBeCalled(
|
||||
Organization organization,
|
||||
[PolicyUpdate(PolicyType.TwoFactorAuthentication)]
|
||||
PolicyUpdate policyUpdate,
|
||||
[Policy(PolicyType.TwoFactorAuthentication, false)]
|
||||
Policy policy,
|
||||
SutProvider<TwoFactorAuthenticationPolicyValidator> sutProvider)
|
||||
{
|
||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(false);
|
||||
|
||||
var orgUserDetailUserAcceptedWithout2Fa = new OrganizationUserUserDetails
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Status = OrganizationUserStatusType.Accepted,
|
||||
Type = OrganizationUserType.User,
|
||||
Email = "user3@test.com",
|
||||
Name = "TEST",
|
||||
UserId = Guid.NewGuid(),
|
||||
HasMasterPassword = true
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyDetailsByOrganizationAsync(policyUpdate.OrganizationId)
|
||||
.Returns(new List<OrganizationUserUserDetails>
|
||||
{
|
||||
orgUserDetailUserAcceptedWithout2Fa
|
||||
});
|
||||
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(Arg.Any<IEnumerable<OrganizationUserUserDetails>>())
|
||||
.Returns(new List<(OrganizationUserUserDetails user, bool hasTwoFactor)>()
|
||||
{
|
||||
(orgUserDetailUserAcceptedWithout2Fa, false),
|
||||
});
|
||||
|
||||
await sutProvider.Sut.OnSaveSideEffectsAsync(policyUpdate, policy);
|
||||
|
||||
await sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>()
|
||||
.DidNotReceive()
|
||||
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task OnSaveSideEffectsAsync_GivenUpdateTo2faPolicy_WhenAccountProvisioningIsEnabledAndUserDoesNotHaveMasterPassword_ThenNonCompliantMembersErrorMessageWillReturn(
|
||||
Organization organization,
|
||||
[PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate,
|
||||
[Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy,
|
||||
SutProvider<TwoFactorAuthenticationPolicyValidator> sutProvider)
|
||||
{
|
||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
|
||||
var orgUserDetailUserWithout2Fa = new OrganizationUserUserDetails
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
@ -304,7 +60,7 @@ public class TwoFactorAuthenticationPolicyValidatorTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task OnSaveSideEffectsAsync_WhenAccountProvisioningIsEnabledAndUserHasMasterPassword_ThenUserWillBeRevoked(
|
||||
public async Task OnSaveSideEffectsAsync_RevokesNonCompliantUsers(
|
||||
Organization organization,
|
||||
[PolicyUpdate(PolicyType.TwoFactorAuthentication)] PolicyUpdate policyUpdate,
|
||||
[Policy(PolicyType.TwoFactorAuthentication, false)] Policy policy,
|
||||
@ -313,10 +69,6 @@ public class TwoFactorAuthenticationPolicyValidatorTests
|
||||
policy.OrganizationId = organization.Id = policyUpdate.OrganizationId;
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||
.Returns(true);
|
||||
|
||||
var orgUserDetailUserWithout2Fa = new OrganizationUserUserDetails
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
|
Reference in New Issue
Block a user