The core infrastructure backend (API, database, Docker, etc).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

306 lines
13 KiB

using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services.Implementations;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Data.Organizations;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
using GlobalSettings = Bit.Core.Settings.GlobalSettings;
namespace Bit.Core.Test.AdminConsole.Services;
[SutProviderCustomize]
public class PolicyServiceTests
{
[Theory, BitAutoData]
public async Task GetPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsNoPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
{
SetupUserPolicies(userId, sutProvider);
var result = await sutProvider.Sut
.GetPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
Assert.Empty(result);
}
[Theory, BitAutoData]
public async Task GetPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsOnePolicy(Guid userId, SutProvider<PolicyService> sutProvider)
{
SetupUserPolicies(userId, sutProvider);
sutProvider.GetDependency<GlobalSettings>().Sso.EnforceSsoPolicyForAllUsers.Returns(true);
var result = await sutProvider.Sut
.GetPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
Assert.Single(result);
Assert.True(result.All(details => details.PolicyEnabled));
Assert.True(result.All(details => details.PolicyType == PolicyType.RequireSso));
Assert.True(result.All(details => details.OrganizationUserType == OrganizationUserType.Owner));
Assert.True(result.All(details => details.OrganizationUserStatus == OrganizationUserStatusType.Confirmed));
Assert.True(result.All(details => !details.IsProvider));
}
[Theory, BitAutoData]
public async Task GetPoliciesApplicableToUserAsync_WithDisableTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsNoPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
{
SetupUserPolicies(userId, sutProvider);
var result = await sutProvider.Sut
.GetPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend);
Assert.Empty(result);
}
[Theory, BitAutoData]
public async Task GetPoliciesApplicableToUserAsync_WithDisableSendTypeFilter_WithInvitedUserStatusFilter_ReturnsOnePolicy(Guid userId, SutProvider<PolicyService> sutProvider)
{
SetupUserPolicies(userId, sutProvider);
var result = await sutProvider.Sut
.GetPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend, OrganizationUserStatusType.Invited);
Assert.Single(result);
Assert.True(result.All(details => details.PolicyEnabled));
Assert.True(result.All(details => details.PolicyType == PolicyType.DisableSend));
Assert.True(result.All(details => details.OrganizationUserType == OrganizationUserType.User));
Assert.True(result.All(details => details.OrganizationUserStatus == OrganizationUserStatusType.Invited));
Assert.True(result.All(details => !details.IsProvider));
}
[Theory, BitAutoData]
public async Task AnyPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsFalse(Guid userId, SutProvider<PolicyService> sutProvider)
{
SetupUserPolicies(userId, sutProvider);
var result = await sutProvider.Sut
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
Assert.False(result);
}
[Theory, BitAutoData]
public async Task AnyPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsTrue(Guid userId, SutProvider<PolicyService> sutProvider)
{
SetupUserPolicies(userId, sutProvider);
sutProvider.GetDependency<GlobalSettings>().Sso.EnforceSsoPolicyForAllUsers.Returns(true);
var result = await sutProvider.Sut
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
Assert.True(result);
}
[Theory, BitAutoData]
public async Task AnyPoliciesApplicableToUserAsync_WithDisableTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsFalse(Guid userId, SutProvider<PolicyService> sutProvider)
{
SetupUserPolicies(userId, sutProvider);
var result = await sutProvider.Sut
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend);
Assert.False(result);
}
[Theory, BitAutoData]
public async Task AnyPoliciesApplicableToUserAsync_WithDisableSendTypeFilter_WithInvitedUserStatusFilter_ReturnsTrue(Guid userId, SutProvider<PolicyService> sutProvider)
{
SetupUserPolicies(userId, sutProvider);
var result = await sutProvider.Sut
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend, OrganizationUserStatusType.Invited);
Assert.True(result);
}
[Theory, BitAutoData]
public async Task GetMasterPasswordPolicyForUserAsync_ReturnsEnforcedOptions(User user, SutProvider<PolicyService> sutProvider)
{
// Arrange: Create three policies with different requirements to test combining behavior
var policy1 = new Policy
{
Id = Guid.NewGuid(),
OrganizationId = Guid.NewGuid(),
Type = PolicyType.MasterPassword,
Enabled = true
};
policy1.SetDataModel(new MasterPasswordPolicyData
{
MinComplexity = 3,
MinLength = 12,
RequireLower = true,
RequireUpper = false,
RequireNumbers = true,
RequireSpecial = false,
EnforceOnLogin = true
});
var policy2 = new Policy
{
Id = Guid.NewGuid(),
OrganizationId = Guid.NewGuid(),
Type = PolicyType.MasterPassword,
Enabled = true
};
policy2.SetDataModel(new MasterPasswordPolicyData
{
MinComplexity = 4,
MinLength = 10,
RequireLower = false,
RequireUpper = true,
RequireNumbers = false,
RequireSpecial = true,
EnforceOnLogin = false
});
var policy3 = new Policy
{
Id = Guid.NewGuid(),
OrganizationId = Guid.NewGuid(),
Type = PolicyType.MasterPassword,
Enabled = true
};
policy3.SetDataModel(new MasterPasswordPolicyData
{
MinComplexity = 2,
MinLength = 15,
RequireLower = false,
RequireUpper = false,
RequireNumbers = false,
RequireSpecial = false,
EnforceOnLogin = false
});
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns([policy1, policy2, policy3]);
// Act
var result = await sutProvider.Sut.GetMasterPasswordPolicyForUserAsync(user);
// Assert: Verify that policies were combined correctly
Assert.NotNull(result);
// MinComplexity and MinLength should take the highest values
Assert.Equal(4, result.MinComplexity); // highest from policy2
Assert.Equal(15, result.MinLength); // highest from policy3
// Boolean flags should use OR logic (true if any policy has true)
Assert.True(result.RequireLower); // true from policy1
Assert.True(result.RequireUpper); // true from policy2
Assert.True(result.RequireNumbers); // true from policy1
Assert.True(result.RequireSpecial); // true from policy2
Assert.True(result.EnforceOnLogin); // true from policy1
}
[Theory, BitAutoData]
public async Task GetMasterPasswordPolicyForUserAsync_WithNoPolicies_ReturnsNull(User user, SutProvider<PolicyService> sutProvider)
{
// Arrange: No enabled policies
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(new List<Policy>());
// Act
var result = await sutProvider.Sut.GetMasterPasswordPolicyForUserAsync(user);
// Assert
Assert.Null(result);
}
[Theory, BitAutoData]
public async Task GetMasterPasswordPolicyForUserAsync_WithDisabledPolicies_ReturnsNull(User user, SutProvider<PolicyService> sutProvider)
{
// Arrange: Policies exist but are disabled
var disabledPolicy = new Policy
{
Id = Guid.NewGuid(),
OrganizationId = Guid.NewGuid(),
Type = PolicyType.MasterPassword,
Enabled = false
};
disabledPolicy.SetDataModel(new MasterPasswordPolicyData
{
MinComplexity = 3,
MinLength = 12
});
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(new List<Policy> { disabledPolicy });
// Act
var result = await sutProvider.Sut.GetMasterPasswordPolicyForUserAsync(user);
// Assert
Assert.Null(result);
}
[Theory, BitAutoData]
public async Task GetPoliciesApplicableToUserAsync_OnlyFetchesAbilitiesForFilteredOrgs(
Guid userId, SutProvider<PolicyService> sutProvider)
{
var includedOrgId = Guid.NewGuid();
var excludedOrgId = Guid.NewGuid(); // filtered out because IsProvider = true
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByUserIdWithPolicyDetailsAsync(userId, PolicyType.DisableSend)
.Returns(new List<OrganizationUserPolicyDetails>
{
new() { OrganizationId = includedOrgId, PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = false },
new() { OrganizationId = excludedOrgId, PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = true }
});
sutProvider.GetDependency<IApplicationCacheService>()
.GetOrganizationAbilitiesAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, OrganizationAbility>
{
{ includedOrgId, new OrganizationAbility { Id = includedOrgId, UsePolicies = true } }
});
await sutProvider.Sut.GetPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend, OrganizationUserStatusType.Invited);
// Assert - only the non-provider org ID should be requested
await sutProvider.GetDependency<IApplicationCacheService>()
.Received(1)
.GetOrganizationAbilitiesAsync(Arg.Is<IEnumerable<Guid>>(ids =>
ids.Contains(includedOrgId) &&
!ids.Contains(excludedOrgId)));
}
private static void SetupOrg(SutProvider<PolicyService> sutProvider, Guid organizationId, Organization organization)
{
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organizationId)
.Returns(Task.FromResult(organization));
}
private static void SetupUserPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
{
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByUserIdWithPolicyDetailsAsync(userId, PolicyType.RequireSso)
.Returns(new List<OrganizationUserPolicyDetails>
{
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = false, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = false},
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = false },
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = true }
});
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByUserIdWithPolicyDetailsAsync(userId, PolicyType.DisableSend)
.Returns(new List<OrganizationUserPolicyDetails>
{
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = false },
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = true }
});
}
}