Files
server/test/Core.Test/OrganizationFeatures/Policies/PolicyQueryTests.cs
John Harrington 7e02832672 [PM-31885] Consolidate all Send policies to a single policy (#7113)
* initial send controls

* update vNext methods and add test coverage for policy validators

* add comments to tests

* Apply suggestion from @mkincaid-bw

• `IX_Policy_OrganizationId_Type` is a unique index

Co-authored-by: mkincaid-bw <mkincaid@bitwarden.com>

* renamne migrations for correct sorting

* respond to csharp related review comments

* fix failing lints

* fix tests

* revise policy sync logic

* revise policy event logic and tests

* add integration tests

- fix SQL syntax error
- escape Sqlite format specifier
- update migration IDs to match sorted filename
- fix SQL syntax error

* OR legacy policy data with SendControls policy data

* remove migrations and associated integration test

* whitespacing and comment correction

* aggregate kegacy Send policies in PolicyQuery and adjust PoliciesController logic

* add comments to simplify post-migration cleanup

* consolidate legacy Send policy synthesis from PoliciesController into PolicyQuery.GetAllAsync

* respond to review comments and other minor fixes

---------

Co-authored-by: mkincaid-bw <mkincaid@bitwarden.com>
2026-04-01 08:03:46 -07:00

282 lines
11 KiB
C#

using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using NSubstitute.ReturnsExtensions;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.Policies;
[SutProviderCustomize]
public class PolicyQueryTests
{
[Theory, BitAutoData]
public async Task RunAsync_WithExistingPolicy_ReturnsPolicy(SutProvider<PolicyQuery> sutProvider,
Policy policy)
{
// Arrange
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, policy.Type)
.Returns(policy);
// Act
var policyData = await sutProvider.Sut.RunAsync(policy.OrganizationId, policy.Type);
// Assert
Assert.Equal(policy.Data, policyData.Data);
Assert.Equal(policy.Type, policyData.Type);
Assert.Equal(policy.Enabled, policyData.Enabled);
Assert.Equal(policy.OrganizationId, policyData.OrganizationId);
}
[Theory, BitAutoData]
public async Task RunAsync_WithNonExistentPolicy_ReturnsDefaultDisabledPolicy(
SutProvider<PolicyQuery> sutProvider,
Guid organizationId,
PolicyType policyType)
{
// Arrange
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(organizationId, policyType)
.ReturnsNull();
// Act
var policyData = await sutProvider.Sut.RunAsync(organizationId, policyType);
// Assert
Assert.Equal(organizationId, policyData.OrganizationId);
Assert.Equal(policyType, policyData.Type);
Assert.False(policyData.Enabled);
Assert.Null(policyData.Data);
}
[Theory, BitAutoData]
public async Task GetAllAsync_WithSendControlsInDb_ReturnsAsIs(
SutProvider<PolicyQuery> sutProvider,
Guid organizationId)
{
// Arrange
var sendControlsPolicy = new Policy
{
OrganizationId = organizationId,
Type = PolicyType.SendControls,
Enabled = true,
Data = CoreHelpers.ClassToJsonData(new SendControlsPolicyData
{
DisableSend = true,
DisableHideEmail = false,
}),
};
var otherPolicy = new Policy
{
OrganizationId = organizationId,
Type = PolicyType.SingleOrg,
Enabled = true,
};
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByOrganizationIdAsync(organizationId)
.Returns(new List<Policy> { sendControlsPolicy, otherPolicy });
// Act
var results = (await sutProvider.Sut.GetAllAsync(organizationId)).ToList();
// Assert
Assert.Equal(2, results.Count);
Assert.Contains(results, p => p.Type == PolicyType.SendControls);
Assert.Contains(results, p => p.Type == PolicyType.SingleOrg);
// Should not attempt to synthesize
// Aggregation is not necessary if DB is already reporting SendControls policy state
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceive()
.GetByOrganizationIdTypeAsync(organizationId, PolicyType.DisableSend);
}
[Theory, BitAutoData]
public async Task GetAllAsync_WithoutSendControls_LegacyEnabled_SynthesizesSendControls(
SutProvider<PolicyQuery> sutProvider,
Guid organizationId)
{
// Arrange — legacy DisableSend is enabled, no SendControls row
var disableSendPolicy = new Policy
{
OrganizationId = organizationId,
Type = PolicyType.DisableSend,
Enabled = true,
};
var singleOrgPolicy = new Policy
{
OrganizationId = organizationId,
Type = PolicyType.SingleOrg,
Enabled = true,
};
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByOrganizationIdAsync(organizationId)
.Returns(new List<Policy> { disableSendPolicy, singleOrgPolicy });
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(organizationId, PolicyType.DisableSend)
.Returns(disableSendPolicy);
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(organizationId, PolicyType.SendOptions)
.ReturnsNull();
// Act
var results = (await sutProvider.Sut.GetAllAsync(organizationId)).ToList();
// Assert — original 2 + synthesized SendControls
Assert.Equal(3, results.Count);
var synthesized = results.Single(p => p.Type == PolicyType.SendControls);
Assert.True(synthesized.Enabled);
var data = synthesized.GetDataModel<SendControlsPolicyData>();
Assert.True(data.DisableSend);
Assert.False(data.DisableHideEmail);
}
[Theory, BitAutoData]
public async Task GetAllAsync_WithoutSendControls_NoLegacyEnabled_SynthesizesDisabledEntry(
SutProvider<PolicyQuery> sutProvider,
Guid organizationId)
{
// Arrange — no SendControls, legacy policies disabled
var singleOrgPolicy = new Policy
{
OrganizationId = organizationId,
Type = PolicyType.SingleOrg,
Enabled = true,
};
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByOrganizationIdAsync(organizationId)
.Returns(new List<Policy> { singleOrgPolicy });
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(organizationId, PolicyType.DisableSend)
.ReturnsNull();
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(organizationId, PolicyType.SendOptions)
.ReturnsNull();
// Act
var results = (await sutProvider.Sut.GetAllAsync(organizationId)).ToList();
// Assert — original policy + disabled synthesized SendControls entry
Assert.Equal(2, results.Count);
Assert.Contains(results, p => p.Type == PolicyType.SingleOrg);
var synthesized = results.Single(p => p.Type == PolicyType.SendControls);
Assert.False(synthesized.Enabled);
}
[Theory, BitAutoData]
public async Task GetAllAsync_WithoutSendControls_DisabledSendOptionsWithConfig_PreservesDisableHideEmail(
SutProvider<PolicyQuery> sutProvider,
Guid organizationId)
{
// Arrange — SendOptions is disabled but has DisableHideEmail = true configured
var sendOptionsPolicy = new Policy
{
OrganizationId = organizationId,
Type = PolicyType.SendOptions,
Enabled = false,
};
sendOptionsPolicy.SetDataModel(new SendOptionsPolicyData { DisableHideEmail = true });
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByOrganizationIdAsync(organizationId)
.Returns(new List<Policy> { sendOptionsPolicy });
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(organizationId, PolicyType.DisableSend)
.ReturnsNull();
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(organizationId, PolicyType.SendOptions)
.Returns(sendOptionsPolicy);
// Act
var results = (await sutProvider.Sut.GetAllAsync(organizationId)).ToList();
// Assert — synthesized SendControls preserves DisableHideEmail but is not enabled
var synthesized = results.Single(p => p.Type == PolicyType.SendControls);
Assert.False(synthesized.Enabled);
var data = synthesized.GetDataModel<SendControlsPolicyData>();
Assert.True(data.DisableHideEmail);
}
[Theory, BitAutoData]
public async Task GetAllAsync_WithoutSendControls_EnabledSendOptionsWithDisableHideEmail_SynthesizesEnabledEntry(
SutProvider<PolicyQuery> sutProvider,
Guid organizationId)
{
// Arrange — SendOptions is enabled AND DisableHideEmail is true
var sendOptionsPolicy = new Policy
{
OrganizationId = organizationId,
Type = PolicyType.SendOptions,
Enabled = true,
};
sendOptionsPolicy.SetDataModel(new SendOptionsPolicyData { DisableHideEmail = true });
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByOrganizationIdAsync(organizationId)
.Returns(new List<Policy> { sendOptionsPolicy });
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(organizationId, PolicyType.DisableSend)
.ReturnsNull();
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(organizationId, PolicyType.SendOptions)
.Returns(sendOptionsPolicy);
// Act
var results = (await sutProvider.Sut.GetAllAsync(organizationId)).ToList();
// Assert — Enabled derives from SendOptions.Enabled, DisableHideEmail from SendOptions data
var synthesized = results.Single(p => p.Type == PolicyType.SendControls);
Assert.True(synthesized.Enabled);
var data = synthesized.GetDataModel<SendControlsPolicyData>();
Assert.True(data.DisableHideEmail);
}
[Theory, BitAutoData]
public async Task GetAllAsync_WithoutSendControls_EnabledSendOptionsWithoutDisableHideEmail_StillEnabled(
SutProvider<PolicyQuery> sutProvider,
Guid organizationId)
{
// Arrange — SendOptions is enabled but DisableHideEmail is false
var sendOptionsPolicy = new Policy
{
OrganizationId = organizationId,
Type = PolicyType.SendOptions,
Enabled = true,
};
sendOptionsPolicy.SetDataModel(new SendOptionsPolicyData { DisableHideEmail = false });
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByOrganizationIdAsync(organizationId)
.Returns(new List<Policy> { sendOptionsPolicy });
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(organizationId, PolicyType.DisableSend)
.ReturnsNull();
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(organizationId, PolicyType.SendOptions)
.Returns(sendOptionsPolicy);
// Act
var results = (await sutProvider.Sut.GetAllAsync(organizationId)).ToList();
// Assert — Enabled is true because SendOptions is enabled, regardless of DisableHideEmail
var synthesized = results.Single(p => p.Type == PolicyType.SendControls);
Assert.True(synthesized.Enabled);
var data = synthesized.GetDataModel<SendControlsPolicyData>();
Assert.False(data.DisableHideEmail);
}
}