mirror of
https://github.com/bitwarden/server.git
synced 2024-11-24 12:35:25 +01:00
366eef7e23
* [PM-6934] Prevent enabling two step login policy if any Org member has no master password and no 2FA set up * [PM-6934] PR feedback * [PM-6934] Updated policy check to only check users that will be deleted * [PM-6934] Removed unnecessary code * [PM-6934] Fixed unit tests and policy update logic * [PM-6934] Updated error message
827 lines
35 KiB
C#
827 lines
35 KiB
C#
using Bit.Core.AdminConsole.Entities;
|
|
using Bit.Core.AdminConsole.Enums;
|
|
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
|
using Bit.Core.AdminConsole.Repositories;
|
|
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.Enums;
|
|
using Bit.Core.Exceptions;
|
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
|
using Bit.Core.Repositories;
|
|
using Bit.Core.Services;
|
|
using Bit.Test.Common.AutoFixture;
|
|
using Bit.Test.Common.AutoFixture.Attributes;
|
|
using NSubstitute;
|
|
using Xunit;
|
|
using AdminConsoleFixtures = Bit.Core.Test.AdminConsole.AutoFixture;
|
|
using GlobalSettings = Bit.Core.Settings.GlobalSettings;
|
|
|
|
namespace Bit.Core.Test.AdminConsole.Services;
|
|
|
|
[SutProviderCustomize]
|
|
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,
|
|
Substitute.For<IUserService>(),
|
|
Substitute.For<IOrganizationService>(),
|
|
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,
|
|
Substitute.For<IUserService>(),
|
|
Substitute.For<IOrganizationService>(),
|
|
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,
|
|
Substitute.For<IUserService>(),
|
|
Substitute.For<IOrganizationService>(),
|
|
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,
|
|
Substitute.For<IUserService>(),
|
|
Substitute.For<IOrganizationService>(),
|
|
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,
|
|
Substitute.For<IUserService>(),
|
|
Substitute.For<IOrganizationService>(),
|
|
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,
|
|
Substitute.For<IUserService>(),
|
|
Substitute.For<IOrganizationService>(),
|
|
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, Substitute.For<IUserService>(), Substitute.For<IOrganizationService>(), 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,
|
|
Substitute.For<IUserService>(),
|
|
Substitute.For<IOrganizationService>(),
|
|
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
|
|
});
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
var organizationService = Substitute.For<IOrganizationService>();
|
|
|
|
userService.TwoFactorIsEnabledAsync(orgUserDetailUserInvited).Returns(false);
|
|
userService.TwoFactorIsEnabledAsync(orgUserDetailUserAcceptedWith2FA).Returns(true);
|
|
userService.TwoFactorIsEnabledAsync(orgUserDetailUserAcceptedWithout2FA).Returns(false);
|
|
userService.TwoFactorIsEnabledAsync(orgUserDetailAdmin).Returns(false);
|
|
|
|
var utcNow = DateTime.UtcNow;
|
|
|
|
var savingUserId = Guid.NewGuid();
|
|
|
|
await sutProvider.Sut.SaveAsync(policy, userService, organizationService, savingUserId);
|
|
|
|
await organizationService.Received()
|
|
.DeleteUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWithout2FA.Id, savingUserId);
|
|
await sutProvider.GetDependency<IMailService>().Received()
|
|
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWithout2FA.Email);
|
|
|
|
await organizationService.DidNotReceive()
|
|
.DeleteUserAsync(policy.OrganizationId, orgUserDetailUserInvited.Id, savingUserId);
|
|
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
|
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserInvited.Email);
|
|
await organizationService.DidNotReceive()
|
|
.DeleteUserAsync(policy.OrganizationId, orgUserDetailUserAcceptedWith2FA.Id, savingUserId);
|
|
await sutProvider.GetDependency<IMailService>().DidNotReceive()
|
|
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(organization.DisplayName(), orgUserDetailUserAcceptedWith2FA.Email);
|
|
await organizationService.DidNotReceive()
|
|
.DeleteUserAsync(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
|
|
});
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
var organizationService = Substitute.For<IOrganizationService>();
|
|
|
|
userService.TwoFactorIsEnabledAsync(orgUserDetailUserWith2FANoMP).Returns(true);
|
|
userService.TwoFactorIsEnabledAsync(orgUserDetailUserWithout2FA).Returns(false);
|
|
userService.TwoFactorIsEnabledAsync(orgUserDetailAdmin).Returns(false);
|
|
|
|
var savingUserId = Guid.NewGuid();
|
|
|
|
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
|
() => sutProvider.Sut.SaveAsync(policy, userService, organizationService, 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 organizationService.DidNotReceiveWithAnyArgs()
|
|
.DeleteUserAsync(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,
|
|
});
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
var organizationService = Substitute.For<IOrganizationService>();
|
|
|
|
userService.TwoFactorIsEnabledAsync(orgUserDetail)
|
|
.Returns(false);
|
|
|
|
var utcNow = DateTime.UtcNow;
|
|
|
|
var savingUserId = Guid.NewGuid();
|
|
|
|
await sutProvider.Sut.SaveAsync(policy, userService, organizationService, 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,
|
|
Substitute.For<IUserService>(),
|
|
Substitute.For<IOrganizationService>(),
|
|
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,
|
|
Substitute.For<IUserService>(),
|
|
Substitute.For<IOrganizationService>(),
|
|
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,
|
|
Substitute.For<IUserService>(),
|
|
Substitute.For<IOrganizationService>(),
|
|
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,
|
|
Substitute.For<IUserService>(),
|
|
Substitute.For<IOrganizationService>(),
|
|
Guid.NewGuid()));
|
|
|
|
Assert.Contains("Account recovery policy is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
|
|
|
await sutProvider.GetDependency<IPolicyRepository>()
|
|
.DidNotReceiveWithAnyArgs()
|
|
.UpsertAsync(default);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task GetPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsNoPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
|
|
{
|
|
SetupUserPolicies(userId, sutProvider);
|
|
|
|
var result = await sutProvider.Sut
|
|
.GetPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
|
|
|
|
Assert.Empty(result);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task GetPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsOnePolicy(Guid userId, SutProvider<PolicyService> sutProvider)
|
|
{
|
|
SetupUserPolicies(userId, sutProvider);
|
|
|
|
sutProvider.GetDependency<GlobalSettings>().Sso.EnforceSsoPolicyForAllUsers.Returns(true);
|
|
|
|
var result = await sutProvider.Sut
|
|
.GetPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
|
|
|
|
Assert.Single(result);
|
|
Assert.True(result.All(details => details.PolicyEnabled));
|
|
Assert.True(result.All(details => details.PolicyType == PolicyType.RequireSso));
|
|
Assert.True(result.All(details => details.OrganizationUserType == OrganizationUserType.Owner));
|
|
Assert.True(result.All(details => details.OrganizationUserStatus == OrganizationUserStatusType.Confirmed));
|
|
Assert.True(result.All(details => !details.IsProvider));
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task GetPoliciesApplicableToUserAsync_WithDisableTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsNoPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
|
|
{
|
|
SetupUserPolicies(userId, sutProvider);
|
|
|
|
var result = await sutProvider.Sut
|
|
.GetPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend);
|
|
|
|
Assert.Empty(result);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task GetPoliciesApplicableToUserAsync_WithDisableSendTypeFilter_WithInvitedUserStatusFilter_ReturnsOnePolicy(Guid userId, SutProvider<PolicyService> sutProvider)
|
|
{
|
|
SetupUserPolicies(userId, sutProvider);
|
|
|
|
var result = await sutProvider.Sut
|
|
.GetPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend, OrganizationUserStatusType.Invited);
|
|
|
|
Assert.Single(result);
|
|
Assert.True(result.All(details => details.PolicyEnabled));
|
|
Assert.True(result.All(details => details.PolicyType == PolicyType.DisableSend));
|
|
Assert.True(result.All(details => details.OrganizationUserType == OrganizationUserType.User));
|
|
Assert.True(result.All(details => details.OrganizationUserStatus == OrganizationUserStatusType.Invited));
|
|
Assert.True(result.All(details => !details.IsProvider));
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task AnyPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsFalse(Guid userId, SutProvider<PolicyService> sutProvider)
|
|
{
|
|
SetupUserPolicies(userId, sutProvider);
|
|
|
|
var result = await sutProvider.Sut
|
|
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
|
|
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task AnyPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsTrue(Guid userId, SutProvider<PolicyService> sutProvider)
|
|
{
|
|
SetupUserPolicies(userId, sutProvider);
|
|
|
|
sutProvider.GetDependency<GlobalSettings>().Sso.EnforceSsoPolicyForAllUsers.Returns(true);
|
|
|
|
var result = await sutProvider.Sut
|
|
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
|
|
|
|
Assert.True(result);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task AnyPoliciesApplicableToUserAsync_WithDisableTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsFalse(Guid userId, SutProvider<PolicyService> sutProvider)
|
|
{
|
|
SetupUserPolicies(userId, sutProvider);
|
|
|
|
var result = await sutProvider.Sut
|
|
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend);
|
|
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task AnyPoliciesApplicableToUserAsync_WithDisableSendTypeFilter_WithInvitedUserStatusFilter_ReturnsTrue(Guid userId, SutProvider<PolicyService> sutProvider)
|
|
{
|
|
SetupUserPolicies(userId, sutProvider);
|
|
|
|
var result = await sutProvider.Sut
|
|
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend, OrganizationUserStatusType.Invited);
|
|
|
|
Assert.True(result);
|
|
}
|
|
|
|
private static void SetupOrg(SutProvider<PolicyService> sutProvider, Guid organizationId, Organization organization)
|
|
{
|
|
sutProvider.GetDependency<IOrganizationRepository>()
|
|
.GetByIdAsync(organizationId)
|
|
.Returns(Task.FromResult(organization));
|
|
}
|
|
|
|
private static void SetupUserPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
|
|
{
|
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
.GetByUserIdWithPolicyDetailsAsync(userId, PolicyType.RequireSso)
|
|
.Returns(new List<OrganizationUserPolicyDetails>
|
|
{
|
|
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = false, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = false},
|
|
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = false },
|
|
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = true }
|
|
});
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
.GetByUserIdWithPolicyDetailsAsync(userId, PolicyType.DisableSend)
|
|
.Returns(new List<OrganizationUserPolicyDetails>
|
|
{
|
|
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = false },
|
|
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = true }
|
|
});
|
|
}
|
|
}
|