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.
458 lines
16 KiB
458 lines
16 KiB
using System.Security.Claims; |
|
using System.Text.Json; |
|
using Bit.Api.AdminConsole.Controllers; |
|
using Bit.Api.AdminConsole.Models.Response.Organizations; |
|
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.Auth.Models.Business.Tokenables; |
|
using Bit.Core.Context; |
|
using Bit.Core.Entities; |
|
using Bit.Core.Exceptions; |
|
using Bit.Core.Repositories; |
|
using Bit.Core.Services; |
|
using Bit.Core.Tokens; |
|
using Bit.Test.Common.AutoFixture; |
|
using Bit.Test.Common.AutoFixture.Attributes; |
|
using NSubstitute; |
|
using Xunit; |
|
|
|
namespace Bit.Api.Test.Controllers; |
|
|
|
|
|
// Note: test names follow MethodName_StateUnderTest_ExpectedBehavior pattern. |
|
[ControllerCustomize(typeof(PoliciesController))] |
|
[SutProviderCustomize] |
|
public class PoliciesControllerTests |
|
{ |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetMasterPasswordPolicy_WhenCalled_ReturnsMasterPasswordPolicy( |
|
SutProvider<PoliciesController> sutProvider, |
|
Guid orgId, Guid userId, |
|
OrganizationUser orgUser, |
|
Policy policy, |
|
MasterPasswordPolicyData mpPolicyData, |
|
Organization organization) |
|
{ |
|
// Arrange |
|
organization.UsePolicies = true; |
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>(); |
|
organizationRepository.GetByIdAsync(orgId).Returns(organization); |
|
|
|
sutProvider.GetDependency<IUserService>() |
|
.GetProperUserId(Arg.Any<ClaimsPrincipal>()) |
|
.Returns((Guid?)userId); |
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>() |
|
.GetByOrganizationAsync(orgId, userId) |
|
.Returns(orgUser); |
|
|
|
|
|
policy.Type = PolicyType.MasterPassword; |
|
policy.Enabled = true; |
|
// data should be a JSON serialized version of the mpPolicyData object |
|
policy.Data = JsonSerializer.Serialize(mpPolicyData); |
|
|
|
sutProvider.GetDependency<IPolicyRepository>() |
|
.GetByOrganizationIdTypeAsync(orgId, PolicyType.MasterPassword) |
|
.Returns(policy); |
|
|
|
// Act |
|
var result = await sutProvider.Sut.GetMasterPasswordPolicy(orgId); |
|
|
|
// Assert |
|
|
|
Assert.NotNull(result); |
|
Assert.Equal(policy.Id, result.Id); |
|
Assert.Equal(policy.Type, result.Type); |
|
Assert.Equal(policy.Enabled, result.Enabled); |
|
|
|
// Assert that the data is deserialized correctly into a Dictionary<string, object> |
|
// for all MasterPasswordPolicyData properties |
|
Assert.Equal(mpPolicyData.MinComplexity, ((JsonElement)result.Data["MinComplexity"]).GetInt32()); |
|
Assert.Equal(mpPolicyData.MinLength, ((JsonElement)result.Data["MinLength"]).GetInt32()); |
|
Assert.Equal(mpPolicyData.RequireLower, ((JsonElement)result.Data["RequireLower"]).GetBoolean()); |
|
Assert.Equal(mpPolicyData.RequireUpper, ((JsonElement)result.Data["RequireUpper"]).GetBoolean()); |
|
Assert.Equal(mpPolicyData.RequireNumbers, ((JsonElement)result.Data["RequireNumbers"]).GetBoolean()); |
|
Assert.Equal(mpPolicyData.RequireSpecial, ((JsonElement)result.Data["RequireSpecial"]).GetBoolean()); |
|
Assert.Equal(mpPolicyData.EnforceOnLogin, ((JsonElement)result.Data["EnforceOnLogin"]).GetBoolean()); |
|
} |
|
|
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetMasterPasswordPolicy_OrgUserIsNull_ThrowsNotFoundException( |
|
SutProvider<PoliciesController> sutProvider, Guid orgId, Guid userId) |
|
{ |
|
// Arrange |
|
sutProvider.GetDependency<IUserService>() |
|
.GetProperUserId(Arg.Any<ClaimsPrincipal>()) |
|
.Returns((Guid?)userId); |
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>() |
|
.GetByOrganizationAsync(orgId, userId) |
|
.Returns((OrganizationUser)null); |
|
|
|
// Act & Assert |
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetMasterPasswordPolicy(orgId)); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetMasterPasswordPolicy_PolicyIsNull_ThrowsNotFoundException( |
|
SutProvider<PoliciesController> sutProvider, Guid orgId, Guid userId, OrganizationUser orgUser) |
|
{ |
|
// Arrange |
|
sutProvider.GetDependency<IUserService>() |
|
.GetProperUserId(Arg.Any<ClaimsPrincipal>()) |
|
.Returns((Guid?)userId); |
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>() |
|
.GetByOrganizationAsync(orgId, userId) |
|
.Returns(orgUser); |
|
|
|
sutProvider.GetDependency<IPolicyRepository>() |
|
.GetByOrganizationIdTypeAsync(orgId, PolicyType.MasterPassword) |
|
.Returns((Policy)null); |
|
|
|
// Act & Assert |
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetMasterPasswordPolicy(orgId)); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetMasterPasswordPolicy_PolicyNotEnabled_ThrowsNotFoundException( |
|
SutProvider<PoliciesController> sutProvider, Guid orgId, Guid userId, OrganizationUser orgUser, Policy policy) |
|
{ |
|
// Arrange |
|
sutProvider.GetDependency<IUserService>() |
|
.GetProperUserId(Arg.Any<ClaimsPrincipal>()) |
|
.Returns((Guid?)userId); |
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>() |
|
.GetByOrganizationAsync(orgId, userId) |
|
.Returns(orgUser); |
|
|
|
policy.Enabled = false; // Ensuring the policy is not enabled |
|
sutProvider.GetDependency<IPolicyRepository>() |
|
.GetByOrganizationIdTypeAsync(orgId, PolicyType.MasterPassword) |
|
.Returns(policy); |
|
|
|
// Act & Assert |
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetMasterPasswordPolicy(orgId)); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetMasterPasswordPolicy_WhenUsePoliciesIsFalse_ThrowsNotFoundException( |
|
SutProvider<PoliciesController> sutProvider, |
|
Guid orgId) |
|
{ |
|
// Arrange |
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>(); |
|
organizationRepository.GetByIdAsync(orgId).Returns((Organization)null); |
|
|
|
|
|
// Act & Assert |
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetMasterPasswordPolicy(orgId)); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetMasterPasswordPolicy_WhenOrgIsNull_ThrowsNotFoundException( |
|
SutProvider<PoliciesController> sutProvider, |
|
Guid orgId, |
|
Organization organization) |
|
{ |
|
// Arrange |
|
organization.UsePolicies = false; |
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>(); |
|
organizationRepository.GetByIdAsync(orgId).Returns(organization); |
|
|
|
|
|
// Act & Assert |
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetMasterPasswordPolicy(orgId)); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task Get_WhenUserCanManagePolicies_WithExistingType_ReturnsExistingPolicy( |
|
SutProvider<PoliciesController> sutProvider, Guid orgId, Policy policy, int type) |
|
{ |
|
// Arrange |
|
sutProvider.GetDependency<ICurrentContext>() |
|
.ManagePolicies(orgId) |
|
.Returns(true); |
|
|
|
policy.Type = (PolicyType)type; |
|
policy.Enabled = true; |
|
policy.Data = null; |
|
|
|
sutProvider.GetDependency<IPolicyRepository>() |
|
.GetByOrganizationIdTypeAsync(orgId, (PolicyType)type) |
|
.Returns(policy); |
|
|
|
// Act |
|
var result = await sutProvider.Sut.Get(orgId, type); |
|
|
|
// Assert |
|
Assert.IsType<PolicyDetailResponseModel>(result); |
|
Assert.Equal(policy.Id, result.Id); |
|
Assert.Equal(policy.Type, result.Type); |
|
Assert.Equal(policy.Enabled, result.Enabled); |
|
Assert.Equal(policy.OrganizationId, result.OrganizationId); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task Get_WhenUserCanManagePolicies_WithNonExistingType_ReturnsDefaultPolicy( |
|
SutProvider<PoliciesController> sutProvider, Guid orgId, int type) |
|
{ |
|
// Arrange |
|
sutProvider.GetDependency<ICurrentContext>() |
|
.ManagePolicies(orgId) |
|
.Returns(true); |
|
|
|
sutProvider.GetDependency<IPolicyRepository>() |
|
.GetByOrganizationIdTypeAsync(orgId, (PolicyType)type) |
|
.Returns((Policy)null); |
|
|
|
// Act |
|
var result = await sutProvider.Sut.Get(orgId, type); |
|
|
|
// Assert |
|
Assert.IsType<PolicyDetailResponseModel>(result); |
|
Assert.Equal(result.Type, (PolicyType)type); |
|
Assert.False(result.Enabled); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task Get_WhenUserCannotManagePolicies_ThrowsNotFoundException( |
|
SutProvider<PoliciesController> sutProvider, Guid orgId, int type) |
|
{ |
|
// Arrange |
|
sutProvider.GetDependency<ICurrentContext>() |
|
.ManagePolicies(orgId) |
|
.Returns(false); |
|
|
|
// Act & Assert |
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.Get(orgId, type)); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetByToken_WhenOrganizationUseUsePoliciesIsFalse_ThrowsNotFoundException( |
|
SutProvider<PoliciesController> sutProvider, Guid orgId, Guid organizationUserId, string token, string email, |
|
Organization organization) |
|
{ |
|
// Arrange |
|
organization.UsePolicies = false; |
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>(); |
|
organizationRepository.GetByIdAsync(orgId).Returns(organization); |
|
|
|
|
|
// Act & Assert |
|
await Assert.ThrowsAsync<NotFoundException>(() => |
|
sutProvider.Sut.GetByToken(orgId, email, token, organizationUserId)); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetByToken_WhenOrganizationIsNull_ThrowsNotFoundException( |
|
SutProvider<PoliciesController> sutProvider, Guid orgId, Guid organizationUserId, string token, string email) |
|
{ |
|
// Arrange |
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>(); |
|
organizationRepository.GetByIdAsync(orgId).Returns((Organization)null); |
|
|
|
// Act & Assert |
|
await Assert.ThrowsAsync<NotFoundException>(() => |
|
sutProvider.Sut.GetByToken(orgId, email, token, organizationUserId)); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetByToken_WhenTokenIsInvalid_ThrowsNotFoundException( |
|
SutProvider<PoliciesController> sutProvider, |
|
Guid orgId, |
|
Guid organizationUserId, |
|
string token, |
|
string email, |
|
Organization organization |
|
) |
|
{ |
|
// Arrange |
|
organization.UsePolicies = true; |
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>(); |
|
organizationRepository.GetByIdAsync(orgId).Returns(organization); |
|
|
|
var decryptedToken = Substitute.For<OrgUserInviteTokenable>(); |
|
decryptedToken.Valid.Returns(false); |
|
|
|
var orgUserInviteTokenDataFactory = sutProvider.GetDependency<IDataProtectorTokenFactory<OrgUserInviteTokenable>>(); |
|
|
|
orgUserInviteTokenDataFactory.TryUnprotect(token, out Arg.Any<OrgUserInviteTokenable>()) |
|
.Returns(x => |
|
{ |
|
x[1] = decryptedToken; |
|
return true; |
|
}); |
|
|
|
// Act & Assert |
|
await Assert.ThrowsAsync<NotFoundException>(() => |
|
sutProvider.Sut.GetByToken(orgId, email, token, organizationUserId)); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetByToken_WhenUserIsNull_ThrowsNotFoundException( |
|
SutProvider<PoliciesController> sutProvider, |
|
Guid orgId, |
|
Guid organizationUserId, |
|
string token, |
|
string email, |
|
Organization organization |
|
) |
|
{ |
|
// Arrange |
|
organization.UsePolicies = true; |
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>(); |
|
organizationRepository.GetByIdAsync(orgId).Returns(organization); |
|
|
|
var decryptedToken = Substitute.For<OrgUserInviteTokenable>(); |
|
decryptedToken.Valid.Returns(true); |
|
decryptedToken.OrgUserId = organizationUserId; |
|
decryptedToken.OrgUserEmail = email; |
|
|
|
var orgUserInviteTokenDataFactory = sutProvider.GetDependency<IDataProtectorTokenFactory<OrgUserInviteTokenable>>(); |
|
|
|
orgUserInviteTokenDataFactory.TryUnprotect(token, out Arg.Any<OrgUserInviteTokenable>()) |
|
.Returns(x => |
|
{ |
|
x[1] = decryptedToken; |
|
return true; |
|
}); |
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>() |
|
.GetByIdAsync(organizationUserId) |
|
.Returns((OrganizationUser)null); |
|
|
|
// Act & Assert |
|
await Assert.ThrowsAsync<NotFoundException>(() => |
|
sutProvider.Sut.GetByToken(orgId, email, token, organizationUserId)); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetByToken_WhenUserOrgIdDoesNotMatchOrgId_ThrowsNotFoundException( |
|
SutProvider<PoliciesController> sutProvider, |
|
Guid orgId, |
|
Guid organizationUserId, |
|
string token, |
|
string email, |
|
OrganizationUser orgUser, |
|
Organization organization |
|
) |
|
{ |
|
// Arrange |
|
organization.UsePolicies = true; |
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>(); |
|
organizationRepository.GetByIdAsync(orgId).Returns(organization); |
|
|
|
var decryptedToken = Substitute.For<OrgUserInviteTokenable>(); |
|
decryptedToken.Valid.Returns(true); |
|
decryptedToken.OrgUserId = organizationUserId; |
|
decryptedToken.OrgUserEmail = email; |
|
|
|
var orgUserInviteTokenDataFactory = sutProvider.GetDependency<IDataProtectorTokenFactory<OrgUserInviteTokenable>>(); |
|
|
|
orgUserInviteTokenDataFactory.TryUnprotect(token, out Arg.Any<OrgUserInviteTokenable>()) |
|
.Returns(x => |
|
{ |
|
x[1] = decryptedToken; |
|
return true; |
|
}); |
|
|
|
orgUser.OrganizationId = Guid.Empty; |
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>() |
|
.GetByIdAsync(organizationUserId) |
|
.Returns(orgUser); |
|
|
|
// Act & Assert |
|
await Assert.ThrowsAsync<NotFoundException>(() => |
|
sutProvider.Sut.GetByToken(orgId, email, token, organizationUserId)); |
|
} |
|
|
|
[Theory] |
|
[BitAutoData] |
|
public async Task GetByToken_ShouldReturnEnabledPolicies( |
|
SutProvider<PoliciesController> sutProvider, |
|
Guid orgId, |
|
Guid organizationUserId, |
|
string token, |
|
string email, |
|
OrganizationUser orgUser, |
|
Organization organization |
|
) |
|
{ |
|
// Arrange |
|
organization.UsePolicies = true; |
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>(); |
|
organizationRepository.GetByIdAsync(orgId).Returns(organization); |
|
|
|
var decryptedToken = Substitute.For<OrgUserInviteTokenable>(); |
|
decryptedToken.Valid.Returns(true); |
|
decryptedToken.OrgUserId = organizationUserId; |
|
decryptedToken.OrgUserEmail = email; |
|
|
|
var orgUserInviteTokenDataFactory = sutProvider.GetDependency<IDataProtectorTokenFactory<OrgUserInviteTokenable>>(); |
|
|
|
orgUserInviteTokenDataFactory.TryUnprotect(token, out Arg.Any<OrgUserInviteTokenable>()) |
|
.Returns(x => |
|
{ |
|
x[1] = decryptedToken; |
|
return true; |
|
}); |
|
|
|
orgUser.OrganizationId = orgId; |
|
sutProvider.GetDependency<IOrganizationUserRepository>() |
|
.GetByIdAsync(organizationUserId) |
|
.Returns(orgUser); |
|
|
|
var enabledPolicy = Substitute.For<Policy>(); |
|
enabledPolicy.Enabled = true; |
|
var disabledPolicy = Substitute.For<Policy>(); |
|
disabledPolicy.Enabled = false; |
|
|
|
var policies = new[] { enabledPolicy, disabledPolicy }; |
|
|
|
|
|
sutProvider.GetDependency<IPolicyRepository>() |
|
.GetManyByOrganizationIdAsync(orgId) |
|
.Returns(policies); |
|
|
|
// Act |
|
var result = await sutProvider.Sut.GetByToken(orgId, email, token, organizationUserId); |
|
|
|
// Assert |
|
var expectedPolicy = result.Data.Single(); |
|
|
|
Assert.NotNull(result); |
|
|
|
Assert.Equal(enabledPolicy.Id, expectedPolicy.Id); |
|
Assert.Equal(enabledPolicy.Type, expectedPolicy.Type); |
|
Assert.Equal(enabledPolicy.Enabled, expectedPolicy.Enabled); |
|
} |
|
}
|
|
|