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

Use delegates to simplify mocking

This commit is contained in:
Thomas Rittson 2024-10-07 09:04:33 +10:00
parent b3006ef64e
commit 4983e413e4
No known key found for this signature in database
GPG Key ID: CDDDA03861C35E27
6 changed files with 39 additions and 46 deletions

View File

@ -2,7 +2,6 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.Entities;
namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies;
@ -26,7 +25,7 @@ public interface IPolicyDefinition
/// <param name="currentPolicy">The current policy, if any</param>
/// <param name="modifiedPolicy">The modified policy to be saved</param>
/// <returns>A sequence of validation errors if validation was unsuccessful</returns>
public Task<string?> ValidateAsync(Policy? currentPolicy, Policy modifiedPolicy);
public Func<Policy?, Policy, Task<string?>> Validate { get; }
/// <summary>
/// Optionally performs side effects after a policy is validated but before it is saved.
@ -34,5 +33,5 @@ public interface IPolicyDefinition
/// </summary>
/// <param name="currentPolicy">The current policy, if any</param>
/// <param name="modifiedPolicy">The modified policy to be saved</param>
public Task OnSaveSideEffectsAsync(Policy? currentPolicy, Policy modifiedPolicy);
public Func<Policy?, Policy, Task> OnSaveSideEffects { get; }
}

View File

@ -1,13 +1,10 @@
#nullable enable
using System.ComponentModel;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Repositories;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
@ -40,13 +37,27 @@ public class SingleOrgPolicyDefinition : IPolicyDefinition
_currentContext = currentContext;
}
public async Task OnSaveSideEffectsAsync(Policy? currentPolicy, Policy modifiedPolicy)
public Func<Policy?, Policy, Task<string?>> Validate => async (currentPolicy, modifiedPolicy) =>
{
var organizationId = modifiedPolicy.OrganizationId;
// Do not allow this policy to be disabled if Key Connector is being used
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organizationId);
if (ssoConfig?.GetData()?.MemberDecryptionType == MemberDecryptionType.KeyConnector)
{
return "Key Connector is enabled.";
}
return null;
};
public Func<Policy?, Policy, Task> OnSaveSideEffects => async (currentPolicy, modifiedPolicy) =>
{
if (currentPolicy is null or { Enabled: false } && modifiedPolicy is { Enabled: true })
{
await RemoveNonCompliantUsersAsync(modifiedPolicy.OrganizationId);
}
}
};
private async Task RemoveNonCompliantUsersAsync(Guid organizationId)
{
@ -85,17 +96,4 @@ public class SingleOrgPolicyDefinition : IPolicyDefinition
}
}
public async Task<string?> ValidateAsync(Policy? currentPolicy, Policy modifiedPolicy)
{
var organizationId = modifiedPolicy.OrganizationId;
// Do not allow this policy to be disabled if Key Connector is being used
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organizationId);
if (ssoConfig?.GetData()?.MemberDecryptionType == MemberDecryptionType.KeyConnector)
{
return "Key Connector is enabled.";
}
return null;
}
}

View File

@ -82,21 +82,21 @@ public class PolicyServicevNext : IPolicyServicevNext
.Where(otherPolicy => otherPolicy is { Enabled: true })
.ToList();
if (dependentPolicies is { Count: > 0})
if (dependentPolicies is { Count: > 0 })
{
throw new BadRequestException("This policy is required by " + dependentPolicies.First() + ". Try disabling that policy first." );
throw new BadRequestException("This policy is required by " + dependentPolicies.First() + ". Try disabling that policy first.");
}
}
// Run other validation
var validationError = await policyDefinition.ValidateAsync(currentPolicy, policy);
var validationError = await policyDefinition.Validate(currentPolicy, policy);
if (validationError != null)
{
throw new BadRequestException(validationError);
}
// Run side effects
await policyDefinition.OnSaveSideEffectsAsync(currentPolicy, policy);
await policyDefinition.OnSaveSideEffects(currentPolicy, policy);
var now = DateTime.UtcNow;
if (policy.Id == default)

View File

@ -1,11 +1,9 @@
#nullable enable
using System.Reflection;
using AutoFixture;
using AutoFixture.Kernel;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.Services.Implementations;
using Bit.Test.Common.AutoFixture.Attributes;
namespace Bit.Core.Test.AdminConsole.AutoFixture;

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.OrganizationFeatures.Policies;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
namespace Bit.Core.Test.AdminConsole.Services;
@ -10,18 +11,15 @@ public class FakeSingleOrgPolicyDefinition : IPolicyDefinition
{
public PolicyType Type => PolicyType.SingleOrg;
public IEnumerable<PolicyType> RequiredPolicies => Array.Empty<PolicyType>();
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 Task<string>? ValidateAsync(Policy? currentPolicy, Policy modifiedPolicy)
{
return ValidateAsyncMock(currentPolicy, modifiedPolicy);
}
public Task OnSaveSideEffectsAsync(Policy? currentPolicy, Policy modifiedPolicy)
{
OnSaveSideEffectsAsyncMock(currentPolicy, modifiedPolicy);
return Task.FromResult(0);
}
public Func<Policy?, Policy, Task<string?>> Validate => Substitute.For<Func<Policy?, Policy, Task<string?>>>();
public Func<Policy?, Policy, Task> OnSaveSideEffects => Substitute.For<Func<Policy?, Policy, Task>>();
}
public class FakeRequireSsoPolicyDefinition : IPolicyDefinition
{
public PolicyType Type => PolicyType.RequireSso;
public IEnumerable<PolicyType> RequiredPolicies => [PolicyType.SingleOrg];
public Func<Policy?, Policy, Task<string?>> Validate => Substitute.For<Func<Policy?, Policy, Task<string?>>>();
public Func<Policy?, Policy, Task> OnSaveSideEffects => Substitute.For<Func<Policy?, Policy, Task>>();
}

View File

@ -10,9 +10,9 @@ using Bit.Core.Services;
using Bit.Core.Test.AdminConsole.AutoFixture;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using AdminConsoleFixtures = Bit.Core.Test.AdminConsole.AutoFixture;
using NSubstitute;
using Xunit;
using AdminConsoleFixtures = Bit.Core.Test.AdminConsole.AutoFixture;
namespace Bit.Core.Test.AdminConsole.Services;
@ -72,7 +72,7 @@ public class PolicyServicevNextTests
}
[Theory, BitAutoData]
public async Task SaveAsync_PolicyDefinitionNotFound_Throws([Policy(PolicyType.SingleOrg)]Policy policy)
public async Task SaveAsync_PolicyDefinitionNotFound_Throws([Policy(PolicyType.SingleOrg)] Policy policy)
{
var sutProvider = SutProviderFactory();
ArrangeOrganization(sutProvider, policy);
@ -91,7 +91,7 @@ public class PolicyServicevNextTests
public async Task SaveAsync_ThrowsOnValidationError([AdminConsoleFixtures.Policy(PolicyType.SingleOrg)] Policy policy)
{
var fakePolicyDefinition = new FakeSingleOrgPolicyDefinition();
fakePolicyDefinition.ValidateAsyncMock(null, policy).Returns("Validation error!");
fakePolicyDefinition.Validate(null, policy).Returns("Validation error!");
var sutProvider = SutProviderFactory([fakePolicyDefinition]);
ArrangeOrganization(sutProvider, policy);