using System.Text.Json; using Bit.Api.AdminConsole.Models.Request; using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models; using Bit.Core.Context; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; using Xunit; namespace Bit.Api.Test.AdminConsole.Models.Request; [SutProviderCustomize] public class SavePolicyRequestTests { [Theory, BitAutoData] public async Task ToSavePolicyModelAsync_WithValidData_ReturnsCorrectSavePolicyModel( Guid organizationId, Guid userId) { // Arrange var currentContext = Substitute.For(); currentContext.UserId.Returns(userId); currentContext.OrganizationOwner(organizationId).Returns(true); var testData = new Dictionary { { "test", "value" } }; var model = new SavePolicyRequest { Policy = new PolicyRequestModel { Type = PolicyType.TwoFactorAuthentication, Enabled = true, Data = testData }, Metadata = new Dictionary() }; // Act var result = await model.ToSavePolicyModelAsync(organizationId, currentContext); // Assert Assert.Equal(PolicyType.TwoFactorAuthentication, result.PolicyUpdate.Type); Assert.Equal(organizationId, result.PolicyUpdate.OrganizationId); Assert.True(result.PolicyUpdate.Enabled); Assert.NotNull(result.PolicyUpdate.Data); var deserializedData = JsonSerializer.Deserialize>(result.PolicyUpdate.Data); Assert.Equal("value", deserializedData["test"].ToString()); Assert.Equal(userId, result!.PerformedBy.UserId); Assert.True(result!.PerformedBy.IsOrganizationOwnerOrProvider); Assert.IsType(result.Metadata); } [Theory, BitAutoData] public async Task ToSavePolicyModelAsync_WithNullData_HandlesCorrectly( Guid organizationId, Guid userId) { // Arrange var currentContext = Substitute.For(); currentContext.UserId.Returns(userId); currentContext.OrganizationOwner(organizationId).Returns(false); var model = new SavePolicyRequest { Policy = new PolicyRequestModel { Type = PolicyType.SingleOrg, Enabled = false, Data = null }, Metadata = null }; // Act var result = await model.ToSavePolicyModelAsync(organizationId, currentContext); // Assert Assert.Null(result.PolicyUpdate.Data); Assert.False(result.PolicyUpdate.Enabled); Assert.Equal(userId, result!.PerformedBy.UserId); Assert.False(result!.PerformedBy.IsOrganizationOwnerOrProvider); } [Theory, BitAutoData] public async Task ToSavePolicyModelAsync_WithNonOrganizationOwner_HandlesCorrectly( Guid organizationId, Guid userId) { // Arrange var currentContext = Substitute.For(); currentContext.UserId.Returns(userId); currentContext.OrganizationOwner(organizationId).Returns(true); var model = new SavePolicyRequest { Policy = new PolicyRequestModel { Type = PolicyType.SingleOrg, Enabled = false, Data = null }, Metadata = null }; // Act var result = await model.ToSavePolicyModelAsync(organizationId, currentContext); // Assert Assert.Null(result.PolicyUpdate.Data); Assert.False(result.PolicyUpdate.Enabled); Assert.Equal(userId, result!.PerformedBy.UserId); Assert.True(result!.PerformedBy.IsOrganizationOwnerOrProvider); } [Theory, BitAutoData] public async Task ToSavePolicyModelAsync_OrganizationDataOwnership_WithValidMetadata_ReturnsCorrectMetadata( Guid organizationId, Guid userId, string defaultCollectionName) { // Arrange var currentContext = Substitute.For(); currentContext.UserId.Returns(userId); currentContext.OrganizationOwner(organizationId).Returns(true); var model = new SavePolicyRequest { Policy = new PolicyRequestModel { Type = PolicyType.OrganizationDataOwnership, Enabled = true, Data = null }, Metadata = new Dictionary { { "defaultUserCollectionName", defaultCollectionName } } }; // Act var result = await model.ToSavePolicyModelAsync(organizationId, currentContext); // Assert Assert.IsType(result.Metadata); var metadata = (OrganizationModelOwnershipPolicyModel)result.Metadata; Assert.Equal(defaultCollectionName, metadata.DefaultUserCollectionName); } [Theory, BitAutoData] public async Task ToSavePolicyModelAsync_OrganizationDataOwnership_WithNullMetadata_ReturnsEmptyMetadata( Guid organizationId, Guid userId) { // Arrange var currentContext = Substitute.For(); currentContext.UserId.Returns(userId); currentContext.OrganizationOwner(organizationId).Returns(true); var model = new SavePolicyRequest { Policy = new PolicyRequestModel { Type = PolicyType.OrganizationDataOwnership, Enabled = true, Data = null }, Metadata = null }; // Act var result = await model.ToSavePolicyModelAsync(organizationId, currentContext); // Assert Assert.NotNull(result); Assert.IsType(result.Metadata); } private static readonly Dictionary _complexData = new Dictionary { { "stringValue", "test" }, { "numberValue", 42 }, { "boolValue", true }, { "arrayValue", new[] { "item1", "item2" } }, { "nestedObject", new Dictionary { { "nested", "value" } } } }; [Theory, BitAutoData] public async Task ToSavePolicyModelAsync_ComplexData_SerializesCorrectly( Guid organizationId, Guid userId) { // Arrange var currentContext = Substitute.For(); currentContext.UserId.Returns(userId); currentContext.OrganizationOwner(organizationId).Returns(true); var model = new SavePolicyRequest { Policy = new PolicyRequestModel { Type = PolicyType.ResetPassword, Enabled = true, Data = _complexData }, Metadata = new Dictionary() }; // Act var result = await model.ToSavePolicyModelAsync(organizationId, currentContext); // Assert var deserializedData = JsonSerializer.Deserialize>(result.PolicyUpdate.Data); Assert.Equal("test", deserializedData["stringValue"].GetString()); Assert.Equal(42, deserializedData["numberValue"].GetInt32()); Assert.True(deserializedData["boolValue"].GetBoolean()); Assert.Equal(2, deserializedData["arrayValue"].GetArrayLength()); var array = deserializedData["arrayValue"].EnumerateArray() .Select(e => e.GetString()) .ToArray(); Assert.Contains("item1", array); Assert.Contains("item2", array); Assert.True(deserializedData["nestedObject"].TryGetProperty("nested", out var nestedValue)); Assert.Equal("value", nestedValue.GetString()); } [Theory, BitAutoData] public async Task MapToPolicyMetadata_UnknownPolicyType_ReturnsEmptyMetadata( Guid organizationId, Guid userId) { // Arrange var currentContext = Substitute.For(); currentContext.UserId.Returns(userId); currentContext.OrganizationOwner(organizationId).Returns(true); var model = new SavePolicyRequest { Policy = new PolicyRequestModel { Type = PolicyType.MaximumVaultTimeout, Enabled = true, Data = null }, Metadata = new Dictionary { { "someProperty", "someValue" } } }; // Act var result = await model.ToSavePolicyModelAsync(organizationId, currentContext); // Assert Assert.NotNull(result); Assert.IsType(result.Metadata); } [Theory, BitAutoData] public async Task MapToPolicyMetadata_JsonSerializationException_ReturnsEmptyMetadata( Guid organizationId, Guid userId) { // Arrange var currentContext = Substitute.For(); currentContext.UserId.Returns(userId); currentContext.OrganizationOwner(organizationId).Returns(true); var errorDictionary = BuildErrorDictionary(); var model = new SavePolicyRequest { Policy = new PolicyRequestModel { Type = PolicyType.OrganizationDataOwnership, Enabled = true, Data = null }, Metadata = errorDictionary }; // Act var result = await model.ToSavePolicyModelAsync(organizationId, currentContext); // Assert Assert.NotNull(result); Assert.IsType(result.Metadata); } private static Dictionary BuildErrorDictionary() { var circularDict = new Dictionary(); circularDict["self"] = circularDict; return circularDict; } }