1
0
mirror of https://github.com/bitwarden/server.git synced 2024-12-01 13:43:23 +01:00

Tests for required policies

This commit is contained in:
Thomas Rittson 2024-10-07 09:52:14 +10:00
parent 7b66fff98c
commit 9459c31663
No known key found for this signature in database
GPG Key ID: CDDDA03861C35E27
4 changed files with 105 additions and 11 deletions

View File

@ -90,7 +90,7 @@ public class PolicyServicevNext : IPolicyServicevNext
// Run other validation // Run other validation
var validationError = await policyDefinition.ValidateAsync(currentPolicy, policy); var validationError = await policyDefinition.ValidateAsync(currentPolicy, policy);
if (validationError != null) if (!string.IsNullOrEmpty(validationError))
{ {
throw new BadRequestException(validationError); throw new BadRequestException(validationError);
} }

View File

@ -9,10 +9,12 @@ namespace Bit.Core.Test.AdminConsole.AutoFixture;
internal class PolicyCustomization : ICustomization internal class PolicyCustomization : ICustomization
{ {
public PolicyType Type { get; set; } public PolicyType Type { get; set; }
public bool Enabled { get; set; }
public PolicyCustomization(PolicyType type) public PolicyCustomization(PolicyType type, bool enabled)
{ {
Type = type; Type = type;
Enabled = enabled;
} }
public void Customize(IFixture fixture) public void Customize(IFixture fixture)
@ -20,21 +22,23 @@ internal class PolicyCustomization : ICustomization
fixture.Customize<Policy>(composer => composer fixture.Customize<Policy>(composer => composer
.With(o => o.OrganizationId, Guid.NewGuid()) .With(o => o.OrganizationId, Guid.NewGuid())
.With(o => o.Type, Type) .With(o => o.Type, Type)
.With(o => o.Enabled, true)); .With(o => o.Enabled, Enabled));
} }
} }
public class PolicyAttribute : CustomizeAttribute public class PolicyAttribute : CustomizeAttribute
{ {
private readonly PolicyType _type; private readonly PolicyType _type;
private readonly bool _enabled;
public PolicyAttribute(PolicyType type) public PolicyAttribute(PolicyType type, bool enabled = true)
{ {
_type = type; _type = type;
_enabled = enabled;
} }
public override ICustomization GetCustomization(ParameterInfo parameter) public override ICustomization GetCustomization(ParameterInfo parameter)
{ {
return new PolicyCustomization(_type); return new PolicyCustomization(_type, _enabled);
} }
} }

View File

@ -1,8 +1,9 @@
using Bit.Core.AdminConsole.Entities; #nullable enable
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using NSubstitute; using NSubstitute;
using NSubstitute.ExceptionExtensions;
namespace Bit.Core.Test.AdminConsole.Services; namespace Bit.Core.Test.AdminConsole.Services;
@ -11,10 +12,10 @@ public class FakeSingleOrgPolicyDefinition : IPolicyDefinition
public PolicyType Type => PolicyType.SingleOrg; public PolicyType Type => PolicyType.SingleOrg;
public IEnumerable<PolicyType> RequiredPolicies => Array.Empty<PolicyType>(); public IEnumerable<PolicyType> RequiredPolicies => Array.Empty<PolicyType>();
public readonly Func<Policy?, Policy, Task<string?>> ValidateAsyncMock = Substitute.For<Func<Policy, Policy, Task<string>>>(); public readonly Func<Policy?, Policy, Task<string?>> ValidateAsyncMock = Substitute.For<Func<Policy?, Policy, Task<string?>>>();
public readonly Action<Policy?, Policy> OnSaveSideEffectsAsyncMock = Substitute.For<Action<Policy, Policy>>(); public readonly Action<Policy?, Policy> OnSaveSideEffectsAsyncMock = Substitute.For<Action<Policy?, Policy>>();
public Task<string>? ValidateAsync(Policy? currentPolicy, Policy modifiedPolicy) public Task<string?> ValidateAsync(Policy? currentPolicy, Policy modifiedPolicy)
{ {
return ValidateAsyncMock(currentPolicy, modifiedPolicy); return ValidateAsyncMock(currentPolicy, modifiedPolicy);
} }
@ -25,3 +26,18 @@ public class FakeSingleOrgPolicyDefinition : IPolicyDefinition
return Task.FromResult(0); return Task.FromResult(0);
} }
} }
public class FakeRequireSsoPolicyDefinition : IPolicyDefinition
{
public PolicyType Type => PolicyType.RequireSso;
public IEnumerable<PolicyType> RequiredPolicies => [PolicyType.SingleOrg];
public Task<string?> ValidateAsync(Policy? currentPolicy, Policy modifiedPolicy)
{
return Task.FromResult((string)null);
}
public Task OnSaveSideEffectsAsync(Policy? currentPolicy, Policy modifiedPolicy)
{
return Task.FromResult(0);
}
}

View File

@ -1,4 +1,6 @@
using AutoFixture; #nullable enable
using AutoFixture;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
@ -107,6 +109,78 @@ public class PolicyServicevNextTests
await AssertPolicyNotSavedAsync(sutProvider); await AssertPolicyNotSavedAsync(sutProvider);
} }
[Theory, BitAutoData]
public async Task SaveAsync_RequiredPolicyIsNull_Throws(
[Policy(PolicyType.RequireSso)] Policy policy)
{
var sutProvider = SutProviderFactory([
new FakeRequireSsoPolicyDefinition(),
new FakeSingleOrgPolicyDefinition()
]);
ArrangeOrganization(sutProvider, policy);
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByOrganizationIdAsync(policy.OrganizationId)
.Returns([]);
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Policy requires PolicyType SingleOrg to be enabled", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await AssertPolicyNotSavedAsync(sutProvider);
}
[Theory, BitAutoData]
public async Task SaveAsync_RequiredPolicyNotEnabled_Throws(
[Policy(PolicyType.SingleOrg, false)] Policy singleOrgPolicy,
[Policy(PolicyType.RequireSso)] Policy policy)
{
var sutProvider = SutProviderFactory([
new FakeRequireSsoPolicyDefinition(),
new FakeSingleOrgPolicyDefinition()
]);
ArrangeOrganization(sutProvider, policy);
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByOrganizationIdAsync(policy.OrganizationId)
.Returns([singleOrgPolicy]);
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Policy requires PolicyType SingleOrg to be enabled", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await AssertPolicyNotSavedAsync(sutProvider);
}
[Theory, BitAutoData]
public async Task SaveAsync_RequiredPolicyEnabled_Success(
[Policy(PolicyType.SingleOrg, true)] Policy singleOrgPolicy,
[Policy(PolicyType.RequireSso)] Policy policy)
{
var sutProvider = SutProviderFactory([
new FakeRequireSsoPolicyDefinition(),
new FakeSingleOrgPolicyDefinition()
]);
ArrangeOrganization(sutProvider, policy);
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByOrganizationIdAsync(policy.OrganizationId)
.Returns([singleOrgPolicy]);
await sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid());
await sutProvider.GetDependency<IPolicyRepository>().Received(1).UpsertAsync(policy);
}
/// <summary> /// <summary>
/// Returns a new SutProvider with the PolicyDefinitions registered in the Sut. /// Returns a new SutProvider with the PolicyDefinitions registered in the Sut.
/// </summary> /// </summary>