mirror of
https://github.com/bitwarden/server.git
synced 2024-11-21 12:05:42 +01:00
[AC-1435] Single Organization policy prerequisite for Account Recovery policy (#3082)
* [AC-1435] Automatically enable Single Org policy when selecting TDE * [AC-1435] Add test for automatic policy enablement * [AC-1435] Prevent disabling single org when account recovery is enabled * [AC-1435] Require Single Org policy when enabling Account recovery * [AC-1435] Add unit test to check for account recovery policy when attempting to disable single org * [AC-1435] Add test to verify single org policy is enabled for account recovery policy * [AC-1435] Fix failing test
This commit is contained in:
parent
fe570cb6c8
commit
a095e02e86
@ -63,9 +63,16 @@ public class SsoConfigService : ISsoConfigService
|
||||
throw new BadRequestException("Key Connector cannot be disabled at this moment.");
|
||||
}
|
||||
|
||||
// Automatically enable reset password policy if trusted device encryption is selected
|
||||
// Automatically enable account recovery and single org policies if trusted device encryption is selected
|
||||
if (config.GetData().MemberDecryptionType == MemberDecryptionType.TrustedDeviceEncryption)
|
||||
{
|
||||
var singleOrgPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.SingleOrg) ??
|
||||
new Policy { OrganizationId = config.OrganizationId, Type = PolicyType.SingleOrg };
|
||||
|
||||
singleOrgPolicy.Enabled = true;
|
||||
|
||||
await _policyService.SaveAsync(singleOrgPolicy, _userService, _organizationService, null);
|
||||
|
||||
var resetPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.ResetPassword) ??
|
||||
new Policy { OrganizationId = config.OrganizationId, Type = PolicyType.ResetPassword, };
|
||||
|
||||
|
@ -61,6 +61,7 @@ public class PolicyService : IPolicyService
|
||||
await RequiredBySsoAsync(org);
|
||||
await RequiredByVaultTimeoutAsync(org);
|
||||
await RequiredByKeyConnectorAsync(org);
|
||||
await RequiredByAccountRecoveryAsync(org);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -80,6 +81,11 @@ public class PolicyService : IPolicyService
|
||||
{
|
||||
await RequiredBySsoTrustedDeviceEncryptionAsync(org);
|
||||
}
|
||||
|
||||
if (policy.Enabled)
|
||||
{
|
||||
await DependsOnSingleOrgAsync(org);
|
||||
}
|
||||
break;
|
||||
|
||||
case PolicyType.MaximumVaultTimeout:
|
||||
@ -244,6 +250,15 @@ public class PolicyService : IPolicyService
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RequiredByAccountRecoveryAsync(Organization org)
|
||||
{
|
||||
var requireSso = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.ResetPassword);
|
||||
if (requireSso?.Enabled == true)
|
||||
{
|
||||
throw new BadRequestException("Account recovery policy is enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RequiredByVaultTimeoutAsync(Organization org)
|
||||
{
|
||||
var vaultTimeout = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.MaximumVaultTimeout);
|
||||
|
@ -6,7 +6,9 @@ using Bit.Core.Auth.Services;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
using Bit.Core.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
@ -317,4 +319,40 @@ public class SsoConfigServiceTests
|
||||
await sutProvider.GetDependency<ISsoConfigRepository>().ReceivedWithAnyArgs()
|
||||
.UpsertAsync(default);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveAsync_Tde_Enable_Required_Policies(SutProvider<SsoConfigService> sutProvider, Organization organization)
|
||||
{
|
||||
var ssoConfig = new SsoConfig
|
||||
{
|
||||
Id = default,
|
||||
Data = new SsoConfigurationData
|
||||
{
|
||||
MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption,
|
||||
}.Serialize(),
|
||||
Enabled = true,
|
||||
OrganizationId = organization.Id,
|
||||
};
|
||||
|
||||
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
|
||||
|
||||
await sutProvider.GetDependency<IPolicyService>().Received(1)
|
||||
.SaveAsync(
|
||||
Arg.Is<Policy>(t => t.Type == Enums.PolicyType.SingleOrg),
|
||||
Arg.Any<IUserService>(),
|
||||
Arg.Any<IOrganizationService>(),
|
||||
null
|
||||
);
|
||||
|
||||
await sutProvider.GetDependency<IPolicyService>().Received(1)
|
||||
.SaveAsync(
|
||||
Arg.Is<Policy>(t => t.Type == Enums.PolicyType.ResetPassword && t.GetDataModel<ResetPasswordDataModel>().AutoEnrollEnabled),
|
||||
Arg.Any<IUserService>(),
|
||||
Arg.Any<IOrganizationService>(),
|
||||
null
|
||||
);
|
||||
|
||||
await sutProvider.GetDependency<ISsoConfigRepository>().ReceivedWithAnyArgs()
|
||||
.UpsertAsync(default);
|
||||
}
|
||||
}
|
||||
|
@ -217,6 +217,10 @@ public class PolicyServiceTests
|
||||
UsePolicies = true,
|
||||
});
|
||||
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetByOrganizationIdTypeAsync(policy.OrganizationId, Enums.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());
|
||||
@ -444,6 +448,70 @@ public class PolicyServiceTests
|
||||
.LogPolicyEventAsync(default, default, default);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveAsync_PolicyRequiredForAccountRecovery_NotEnabled_ThrowsBadRequestAsync(
|
||||
[PolicyFixtures.Policy(Enums.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(
|
||||
[PolicyFixtures.Policy(Enums.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, Enums.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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user