1
0
mirror of https://github.com/bitwarden/server synced 2025-12-25 12:43:14 +00:00

Merge branch 'km/pm-10564' into km/pm-15084

# Conflicts:
#	src/Core/Enums/PushType.cs
This commit is contained in:
Maciej Zieniuk
2024-12-18 23:33:17 +00:00
365 changed files with 39467 additions and 3870 deletions

View File

@@ -7,6 +7,7 @@ using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Models.Business.Tokenables;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationApiKeys.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.Entities;
@@ -46,11 +47,11 @@ public class OrganizationsControllerTests : IDisposable
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
private readonly ICreateOrganizationApiKeyCommand _createOrganizationApiKeyCommand;
private readonly IFeatureService _featureService;
private readonly IPushNotificationService _pushNotificationService;
private readonly IProviderRepository _providerRepository;
private readonly IProviderBillingService _providerBillingService;
private readonly IDataProtectorTokenFactory<OrgDeleteTokenable> _orgDeleteTokenDataFactory;
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
private readonly ICloudOrganizationSignUpCommand _cloudOrganizationSignUpCommand;
private readonly OrganizationsController _sut;
public OrganizationsControllerTests()
@@ -69,11 +70,11 @@ public class OrganizationsControllerTests : IDisposable
_userService = Substitute.For<IUserService>();
_createOrganizationApiKeyCommand = Substitute.For<ICreateOrganizationApiKeyCommand>();
_featureService = Substitute.For<IFeatureService>();
_pushNotificationService = Substitute.For<IPushNotificationService>();
_providerRepository = Substitute.For<IProviderRepository>();
_providerBillingService = Substitute.For<IProviderBillingService>();
_orgDeleteTokenDataFactory = Substitute.For<IDataProtectorTokenFactory<OrgDeleteTokenable>>();
_removeOrganizationUserCommand = Substitute.For<IRemoveOrganizationUserCommand>();
_cloudOrganizationSignUpCommand = Substitute.For<ICloudOrganizationSignUpCommand>();
_sut = new OrganizationsController(
_organizationRepository,
@@ -90,11 +91,11 @@ public class OrganizationsControllerTests : IDisposable
_organizationApiKeyRepository,
_featureService,
_globalSettings,
_pushNotificationService,
_providerRepository,
_providerBillingService,
_orgDeleteTokenDataFactory,
_removeOrganizationUserCommand);
_removeOrganizationUserCommand,
_cloudOrganizationSignUpCommand);
}
public void Dispose()
@@ -129,7 +130,7 @@ public class OrganizationsControllerTests : IDisposable
Assert.Contains("Your organization's Single Sign-On settings prevent you from leaving.",
exception.Message);
await _removeOrganizationUserCommand.DidNotReceiveWithAnyArgs().RemoveUserAsync(default, default);
await _removeOrganizationUserCommand.DidNotReceiveWithAnyArgs().UserLeaveAsync(default, default);
}
[Theory, AutoData]
@@ -192,7 +193,7 @@ public class OrganizationsControllerTests : IDisposable
await _sut.Leave(orgId);
await _removeOrganizationUserCommand.Received(1).RemoveUserAsync(orgId, user.Id);
await _removeOrganizationUserCommand.Received(1).UserLeaveAsync(orgId, user.Id);
}
[Theory, AutoData]

View File

@@ -1,33 +0,0 @@
using Bit.Api.AdminConsole.Public.Controllers;
using Bit.Api.AdminConsole.Public.Models.Request;
using Bit.Api.AdminConsole.Public.Models.Response;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Context;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.Mvc;
using NSubstitute;
using Xunit;
namespace Bit.Api.Test.AdminConsole.Public.Controllers;
[ControllerCustomize(typeof(PoliciesController))]
[SutProviderCustomize]
public class PoliciesControllerTests
{
[Theory]
[BitAutoData]
[BitAutoData(PolicyType.SendOptions)]
public async Task Put_NewPolicy_AppliesCorrectType(PolicyType type, Organization organization, PolicyUpdateRequestModel model, SutProvider<PoliciesController> sutProvider)
{
sutProvider.GetDependency<ICurrentContext>().OrganizationId.Returns(organization.Id);
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(organization.Id, type).Returns((Policy)null);
var response = await sutProvider.Sut.Put(type, model) as JsonResult;
var responseValue = response.Value as PolicyResponseModel;
Assert.Equal(type, responseValue.Type);
}
}

View File

@@ -4,7 +4,7 @@ using Bit.Api.Auth.Controllers;
using Bit.Api.Auth.Models.Request;
using Bit.Api.Auth.Models.Request.Accounts;
using Bit.Api.Auth.Models.Request.WebAuthn;
using Bit.Api.Auth.Validators;
using Bit.Api.KeyManagement.Validators;
using Bit.Api.Tools.Models.Request;
using Bit.Api.Vault.Models.Request;
using Bit.Core;
@@ -14,12 +14,12 @@ using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
using Bit.Core.Auth.UserFeatures.UserKey;
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
using Bit.Core.Billing.Services;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.KeyManagement.UserKey;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;

View File

@@ -10,6 +10,7 @@ using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.Repositories;
using Bit.Core.Auth.Services;
using Bit.Core.Billing.Repositories;
using Bit.Core.Billing.Services;
using Bit.Core.Context;
using Bit.Core.Entities;
@@ -47,6 +48,7 @@ public class OrganizationsControllerTests : IDisposable
private readonly IReferenceEventService _referenceEventService;
private readonly ISubscriberService _subscriberService;
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
private readonly IOrganizationInstallationRepository _organizationInstallationRepository;
private readonly OrganizationsController _sut;
@@ -70,6 +72,7 @@ public class OrganizationsControllerTests : IDisposable
_referenceEventService = Substitute.For<IReferenceEventService>();
_subscriberService = Substitute.For<ISubscriberService>();
_removeOrganizationUserCommand = Substitute.For<IRemoveOrganizationUserCommand>();
_organizationInstallationRepository = Substitute.For<IOrganizationInstallationRepository>();
_sut = new OrganizationsController(
_organizationRepository,
@@ -85,7 +88,8 @@ public class OrganizationsControllerTests : IDisposable
_upgradeOrganizationPlanCommand,
_addSecretsManagerSubscriptionCommand,
_referenceEventService,
_subscriberService);
_subscriberService,
_organizationInstallationRepository);
}
public void Dispose()

View File

@@ -6,11 +6,13 @@ 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;
@@ -28,10 +30,19 @@ public class PoliciesControllerTests
[Theory]
[BitAutoData]
public async Task GetMasterPasswordPolicy_WhenCalled_ReturnsMasterPasswordPolicy(
SutProvider<PoliciesController> sutProvider, Guid orgId, Guid userId, OrganizationUser orgUser,
Policy policy, MasterPasswordPolicyData mpPolicyData)
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);
@@ -135,6 +146,39 @@ public class PoliciesControllerTests
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(
@@ -142,16 +186,16 @@ public class PoliciesControllerTests
{
// Arrange
sutProvider.GetDependency<ICurrentContext>()
.ManagePolicies(orgId)
.Returns(true);
.ManagePolicies(orgId)
.Returns(true);
policy.Type = (PolicyType)type;
policy.Enabled = true;
policy.Data = null;
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(orgId, (PolicyType)type)
.Returns(policy);
.GetByOrganizationIdTypeAsync(orgId, (PolicyType)type)
.Returns(policy);
// Act
var result = await sutProvider.Sut.Get(orgId, type);
@@ -171,12 +215,12 @@ public class PoliciesControllerTests
{
// Arrange
sutProvider.GetDependency<ICurrentContext>()
.ManagePolicies(orgId)
.Returns(true);
.ManagePolicies(orgId)
.Returns(true);
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(orgId, (PolicyType)type)
.Returns((Policy)null);
.GetByOrganizationIdTypeAsync(orgId, (PolicyType)type)
.Returns((Policy)null);
// Act
var result = await sutProvider.Sut.Get(orgId, type);
@@ -194,11 +238,221 @@ public class PoliciesControllerTests
{
// Arrange
sutProvider.GetDependency<ICurrentContext>()
.ManagePolicies(orgId)
.Returns(false);
.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);
}
}

View File

@@ -0,0 +1,96 @@
#nullable enable
using System.Security.Claims;
using Bit.Api.KeyManagement.Controllers;
using Bit.Api.KeyManagement.Models.Requests;
using Bit.Core;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.KeyManagement.Commands.Interfaces;
using Bit.Core.KeyManagement.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using NSubstitute.ReturnsExtensions;
using Xunit;
namespace Bit.Api.Test.KeyManagement.Controllers;
[ControllerCustomize(typeof(AccountsKeyManagementController))]
[SutProviderCustomize]
[JsonDocumentCustomize]
public class AccountsKeyManagementControllerTests
{
[Theory]
[BitAutoData]
public async Task RegenerateKeysAsync_FeatureFlagOff_Throws(
SutProvider<AccountsKeyManagementController> sutProvider,
KeyRegenerationRequestModel data)
{
sutProvider.GetDependency<IFeatureService>().IsEnabled(Arg.Is(FeatureFlagKeys.PrivateKeyRegeneration))
.Returns(false);
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).ReturnsNull();
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.RegenerateKeysAsync(data));
await sutProvider.GetDependency<IOrganizationUserRepository>().ReceivedWithAnyArgs(0)
.GetManyByUserAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IEmergencyAccessRepository>().ReceivedWithAnyArgs(0)
.GetManyDetailsByGranteeIdAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IRegenerateUserAsymmetricKeysCommand>().ReceivedWithAnyArgs(0)
.RegenerateKeysAsync(Arg.Any<UserAsymmetricKeys>(),
Arg.Any<ICollection<OrganizationUser>>(),
Arg.Any<ICollection<EmergencyAccessDetails>>());
}
[Theory]
[BitAutoData]
public async Task RegenerateKeysAsync_UserNull_Throws(SutProvider<AccountsKeyManagementController> sutProvider,
KeyRegenerationRequestModel data)
{
sutProvider.GetDependency<IFeatureService>().IsEnabled(Arg.Is(FeatureFlagKeys.PrivateKeyRegeneration))
.Returns(true);
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).ReturnsNull();
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.RegenerateKeysAsync(data));
await sutProvider.GetDependency<IOrganizationUserRepository>().ReceivedWithAnyArgs(0)
.GetManyByUserAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IEmergencyAccessRepository>().ReceivedWithAnyArgs(0)
.GetManyDetailsByGranteeIdAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IRegenerateUserAsymmetricKeysCommand>().ReceivedWithAnyArgs(0)
.RegenerateKeysAsync(Arg.Any<UserAsymmetricKeys>(),
Arg.Any<ICollection<OrganizationUser>>(),
Arg.Any<ICollection<EmergencyAccessDetails>>());
}
[Theory]
[BitAutoData]
public async Task RegenerateKeysAsync_Success(SutProvider<AccountsKeyManagementController> sutProvider,
KeyRegenerationRequestModel data, User user, ICollection<OrganizationUser> orgUsers,
ICollection<EmergencyAccessDetails> accessDetails)
{
sutProvider.GetDependency<IFeatureService>().IsEnabled(Arg.Is(FeatureFlagKeys.PrivateKeyRegeneration))
.Returns(true);
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(Arg.Is(user.Id)).Returns(orgUsers);
sutProvider.GetDependency<IEmergencyAccessRepository>().GetManyDetailsByGranteeIdAsync(Arg.Is(user.Id))
.Returns(accessDetails);
await sutProvider.Sut.RegenerateKeysAsync(data);
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
.GetManyByUserAsync(Arg.Is(user.Id));
await sutProvider.GetDependency<IEmergencyAccessRepository>().Received(1)
.GetManyDetailsByGranteeIdAsync(Arg.Is(user.Id));
await sutProvider.GetDependency<IRegenerateUserAsymmetricKeysCommand>().Received(1)
.RegenerateKeysAsync(
Arg.Is<UserAsymmetricKeys>(u =>
u.UserId == user.Id && u.PublicKey == data.UserPublicKey &&
u.UserKeyEncryptedPrivateKey == data.UserKeyEncryptedUserPrivateKey),
Arg.Is(orgUsers),
Arg.Is(accessDetails));
}
}

View File

@@ -1,5 +1,5 @@
using Bit.Api.Vault.Models.Request;
using Bit.Api.Vault.Validators;
using Bit.Api.KeyManagement.Validators;
using Bit.Api.Vault.Models.Request;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Vault.Models.Data;
@@ -9,7 +9,7 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Api.Test.Vault.Validators;
namespace Bit.Api.Test.KeyManagement.Validators;
[SutProviderCustomize]
public class CipherRotationValidatorTests

View File

@@ -1,5 +1,5 @@
using Bit.Api.Auth.Models.Request;
using Bit.Api.Auth.Validators;
using Bit.Api.KeyManagement.Validators;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
@@ -10,7 +10,7 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Api.Test.Auth.Validators;
namespace Bit.Api.Test.KeyManagement.Validators;
[SutProviderCustomize]
public class EmergencyAccessRotationValidatorTests

View File

@@ -1,5 +1,5 @@
using Bit.Api.Vault.Models.Request;
using Bit.Api.Vault.Validators;
using Bit.Api.KeyManagement.Validators;
using Bit.Api.Vault.Models.Request;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Vault.Entities;
@@ -9,7 +9,7 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Api.Test.Vault.Validators;
namespace Bit.Api.Test.KeyManagement.Validators;
[SutProviderCustomize]
public class FolderRotationValidatorTests

View File

@@ -1,5 +1,5 @@
using Bit.Api.AdminConsole.Models.Request.Organizations;
using Bit.Api.AdminConsole.Validators;
using Bit.Api.KeyManagement.Validators;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
@@ -8,7 +8,7 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Api.Test.AdminConsole.Validators;
namespace Bit.Api.Test.KeyManagement.Validators;
[SutProviderCustomize]
public class OrganizationUserRotationValidatorTests

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using Bit.Api.KeyManagement.Validators;
using Bit.Api.Tools.Models;
using Bit.Api.Tools.Models.Request;
using Bit.Api.Tools.Validators;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Tools.Entities;
@@ -14,7 +14,7 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Api.Test.Tools.Validators;
namespace Bit.Api.Test.KeyManagement.Validators;
[SutProviderCustomize]
public class SendRotationValidatorTests

View File

@@ -1,5 +1,5 @@
using Bit.Api.Auth.Models.Request.WebAuthn;
using Bit.Api.Auth.Validators;
using Bit.Api.KeyManagement.Validators;
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Repositories;
using Bit.Core.Entities;
@@ -9,7 +9,7 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Api.Test.Auth.Validators;
namespace Bit.Api.Test.KeyManagement.Validators;
[SutProviderCustomize]
public class WebAuthnLoginKeyRotationValidatorTests

View File

@@ -0,0 +1,202 @@
#nullable enable
using Bit.Api.NotificationCenter.Controllers;
using Bit.Api.NotificationCenter.Models.Request;
using Bit.Core.Models.Data;
using Bit.Core.NotificationCenter.Commands.Interfaces;
using Bit.Core.NotificationCenter.Models.Data;
using Bit.Core.NotificationCenter.Models.Filter;
using Bit.Core.NotificationCenter.Queries.Interfaces;
using Bit.Core.Test.NotificationCenter.AutoFixture;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Api.Test.NotificationCenter.Controllers;
[ControllerCustomize(typeof(NotificationsController))]
[SutProviderCustomize]
public class NotificationsControllerTests
{
[Theory]
[BitAutoData([null, null])]
[BitAutoData([null, false])]
[BitAutoData([null, true])]
[BitAutoData(false, null)]
[BitAutoData(true, null)]
[BitAutoData(false, false)]
[BitAutoData(false, true)]
[BitAutoData(true, false)]
[BitAutoData(true, true)]
[NotificationStatusDetailsListCustomize(5)]
public async Task ListAsync_StatusFilter_ReturnedMatchingNotifications(bool? readStatusFilter, bool? deletedStatusFilter,
SutProvider<NotificationsController> sutProvider,
IEnumerable<NotificationStatusDetails> notificationStatusDetailsEnumerable)
{
var notificationStatusDetailsList = notificationStatusDetailsEnumerable
.OrderByDescending(n => n.Priority)
.ThenByDescending(n => n.CreationDate)
.ToList();
sutProvider.GetDependency<IGetNotificationStatusDetailsForUserQuery>()
.GetByUserIdStatusFilterAsync(Arg.Any<NotificationStatusFilter>(), Arg.Any<PageOptions>())
.Returns(new PagedResult<NotificationStatusDetails> { Data = notificationStatusDetailsList });
var expectedNotificationStatusDetailsMap = notificationStatusDetailsList
.Take(10)
.ToDictionary(n => n.Id);
var listResponse = await sutProvider.Sut.ListAsync(new NotificationFilterRequestModel
{
ReadStatusFilter = readStatusFilter,
DeletedStatusFilter = deletedStatusFilter
});
Assert.Equal("list", listResponse.Object);
Assert.Equal(5, listResponse.Data.Count());
Assert.All(listResponse.Data, notificationResponseModel =>
{
Assert.Equal("notification", notificationResponseModel.Object);
Assert.True(expectedNotificationStatusDetailsMap.ContainsKey(notificationResponseModel.Id));
var expectedNotificationStatusDetails = expectedNotificationStatusDetailsMap[notificationResponseModel.Id];
Assert.NotNull(expectedNotificationStatusDetails);
Assert.Equal(expectedNotificationStatusDetails.Id, notificationResponseModel.Id);
Assert.Equal(expectedNotificationStatusDetails.Priority, notificationResponseModel.Priority);
Assert.Equal(expectedNotificationStatusDetails.Title, notificationResponseModel.Title);
Assert.Equal(expectedNotificationStatusDetails.Body, notificationResponseModel.Body);
Assert.Equal(expectedNotificationStatusDetails.RevisionDate, notificationResponseModel.Date);
Assert.Equal(expectedNotificationStatusDetails.ReadDate, notificationResponseModel.ReadDate);
Assert.Equal(expectedNotificationStatusDetails.DeletedDate, notificationResponseModel.DeletedDate);
});
Assert.Null(listResponse.ContinuationToken);
await sutProvider.GetDependency<IGetNotificationStatusDetailsForUserQuery>()
.Received(1)
.GetByUserIdStatusFilterAsync(Arg.Is<NotificationStatusFilter>(filter =>
filter.Read == readStatusFilter && filter.Deleted == deletedStatusFilter),
Arg.Is<PageOptions>(pageOptions =>
pageOptions.ContinuationToken == null && pageOptions.PageSize == 10));
}
[Theory]
[BitAutoData]
[NotificationStatusDetailsListCustomize(19)]
public async Task ListAsync_PagingRequestNoContinuationToken_ReturnedFirst10MatchingNotifications(
SutProvider<NotificationsController> sutProvider,
IEnumerable<NotificationStatusDetails> notificationStatusDetailsEnumerable)
{
var notificationStatusDetailsList = notificationStatusDetailsEnumerable
.OrderByDescending(n => n.Priority)
.ThenByDescending(n => n.CreationDate)
.ToList();
sutProvider.GetDependency<IGetNotificationStatusDetailsForUserQuery>()
.GetByUserIdStatusFilterAsync(Arg.Any<NotificationStatusFilter>(), Arg.Any<PageOptions>())
.Returns(new PagedResult<NotificationStatusDetails>
{ Data = notificationStatusDetailsList.Take(10).ToList(), ContinuationToken = "2" });
var expectedNotificationStatusDetailsMap = notificationStatusDetailsList
.Take(10)
.ToDictionary(n => n.Id);
var listResponse = await sutProvider.Sut.ListAsync(new NotificationFilterRequestModel());
Assert.Equal("list", listResponse.Object);
Assert.Equal(10, listResponse.Data.Count());
Assert.All(listResponse.Data, notificationResponseModel =>
{
Assert.Equal("notification", notificationResponseModel.Object);
Assert.True(expectedNotificationStatusDetailsMap.ContainsKey(notificationResponseModel.Id));
var expectedNotificationStatusDetails = expectedNotificationStatusDetailsMap[notificationResponseModel.Id];
Assert.NotNull(expectedNotificationStatusDetails);
Assert.Equal(expectedNotificationStatusDetails.Id, notificationResponseModel.Id);
Assert.Equal(expectedNotificationStatusDetails.Priority, notificationResponseModel.Priority);
Assert.Equal(expectedNotificationStatusDetails.Title, notificationResponseModel.Title);
Assert.Equal(expectedNotificationStatusDetails.Body, notificationResponseModel.Body);
Assert.Equal(expectedNotificationStatusDetails.RevisionDate, notificationResponseModel.Date);
Assert.Equal(expectedNotificationStatusDetails.ReadDate, notificationResponseModel.ReadDate);
Assert.Equal(expectedNotificationStatusDetails.DeletedDate, notificationResponseModel.DeletedDate);
});
Assert.Equal("2", listResponse.ContinuationToken);
await sutProvider.GetDependency<IGetNotificationStatusDetailsForUserQuery>()
.Received(1)
.GetByUserIdStatusFilterAsync(Arg.Any<NotificationStatusFilter>(),
Arg.Is<PageOptions>(pageOptions =>
pageOptions.ContinuationToken == null && pageOptions.PageSize == 10));
}
[Theory]
[BitAutoData]
[NotificationStatusDetailsListCustomize(19)]
public async Task ListAsync_PagingRequestUsingContinuationToken_ReturnedLast9MatchingNotifications(
SutProvider<NotificationsController> sutProvider,
IEnumerable<NotificationStatusDetails> notificationStatusDetailsEnumerable)
{
var notificationStatusDetailsList = notificationStatusDetailsEnumerable
.OrderByDescending(n => n.Priority)
.ThenByDescending(n => n.CreationDate)
.ToList();
sutProvider.GetDependency<IGetNotificationStatusDetailsForUserQuery>()
.GetByUserIdStatusFilterAsync(Arg.Any<NotificationStatusFilter>(), Arg.Any<PageOptions>())
.Returns(new PagedResult<NotificationStatusDetails>
{ Data = notificationStatusDetailsList.Skip(10).ToList() });
var expectedNotificationStatusDetailsMap = notificationStatusDetailsList
.Skip(10)
.ToDictionary(n => n.Id);
var listResponse = await sutProvider.Sut.ListAsync(new NotificationFilterRequestModel { ContinuationToken = "2" });
Assert.Equal("list", listResponse.Object);
Assert.Equal(9, listResponse.Data.Count());
Assert.All(listResponse.Data, notificationResponseModel =>
{
Assert.Equal("notification", notificationResponseModel.Object);
Assert.True(expectedNotificationStatusDetailsMap.ContainsKey(notificationResponseModel.Id));
var expectedNotificationStatusDetails = expectedNotificationStatusDetailsMap[notificationResponseModel.Id];
Assert.NotNull(expectedNotificationStatusDetails);
Assert.Equal(expectedNotificationStatusDetails.Id, notificationResponseModel.Id);
Assert.Equal(expectedNotificationStatusDetails.Priority, notificationResponseModel.Priority);
Assert.Equal(expectedNotificationStatusDetails.Title, notificationResponseModel.Title);
Assert.Equal(expectedNotificationStatusDetails.Body, notificationResponseModel.Body);
Assert.Equal(expectedNotificationStatusDetails.RevisionDate, notificationResponseModel.Date);
Assert.Equal(expectedNotificationStatusDetails.ReadDate, notificationResponseModel.ReadDate);
Assert.Equal(expectedNotificationStatusDetails.DeletedDate, notificationResponseModel.DeletedDate);
});
Assert.Null(listResponse.ContinuationToken);
await sutProvider.GetDependency<IGetNotificationStatusDetailsForUserQuery>()
.Received(1)
.GetByUserIdStatusFilterAsync(Arg.Any<NotificationStatusFilter>(),
Arg.Is<PageOptions>(pageOptions =>
pageOptions.ContinuationToken == "2" && pageOptions.PageSize == 10));
}
[Theory]
[BitAutoData]
public async Task MarkAsDeletedAsync_NotificationId_MarkedAsDeleted(
SutProvider<NotificationsController> sutProvider,
Guid notificationId)
{
await sutProvider.Sut.MarkAsDeletedAsync(notificationId);
await sutProvider.GetDependency<IMarkNotificationDeletedCommand>()
.Received(1)
.MarkDeletedAsync(notificationId);
}
[Theory]
[BitAutoData]
public async Task MarkAsReadAsync_NotificationId_MarkedAsRead(
SutProvider<NotificationsController> sutProvider,
Guid notificationId)
{
await sutProvider.Sut.MarkAsReadAsync(notificationId);
await sutProvider.GetDependency<IMarkNotificationReadCommand>()
.Received(1)
.MarkReadAsync(notificationId);
}
}

View File

@@ -0,0 +1,93 @@
#nullable enable
using System.ComponentModel.DataAnnotations;
using Bit.Api.NotificationCenter.Models.Request;
using Xunit;
namespace Bit.Api.Test.NotificationCenter.Models.Request;
public class NotificationFilterRequestModelTests
{
[Theory]
[InlineData("invalid")]
[InlineData("-1")]
[InlineData("0")]
public void Validate_ContinuationTokenInvalidNumber_Invalid(string continuationToken)
{
var model = new NotificationFilterRequestModel
{
ContinuationToken = continuationToken,
};
var result = Validate(model);
Assert.Single(result);
Assert.Contains("Continuation token must be a positive, non zero integer.", result[0].ErrorMessage);
Assert.Contains("ContinuationToken", result[0].MemberNames);
}
[Fact]
public void Validate_ContinuationTokenMaxLengthExceeded_Invalid()
{
var model = new NotificationFilterRequestModel
{
ContinuationToken = "1234567890"
};
var result = Validate(model);
Assert.Single(result);
Assert.Contains("The field ContinuationToken must be a string with a maximum length of 9.",
result[0].ErrorMessage);
Assert.Contains("ContinuationToken", result[0].MemberNames);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
[InlineData("1")]
[InlineData("123456789")]
public void Validate_ContinuationTokenCorrect_Valid(string? continuationToken)
{
var model = new NotificationFilterRequestModel
{
ContinuationToken = continuationToken
};
var result = Validate(model);
Assert.Empty(result);
}
[Theory]
[InlineData(9)]
[InlineData(1001)]
public void Validate_PageSizeInvalidRange_Invalid(int pageSize)
{
var model = new NotificationFilterRequestModel
{
PageSize = pageSize
};
var result = Validate(model);
Assert.Single(result);
Assert.Contains("The field PageSize must be between 10 and 1000.", result[0].ErrorMessage);
Assert.Contains("PageSize", result[0].MemberNames);
}
[Theory]
[InlineData(null)]
[InlineData(10)]
[InlineData(1000)]
public void Validate_PageSizeCorrect_Valid(int? pageSize)
{
var model = pageSize == null
? new NotificationFilterRequestModel()
: new NotificationFilterRequestModel
{
PageSize = pageSize.Value
};
var result = Validate(model);
Assert.Empty(result);
}
private static List<ValidationResult> Validate(NotificationFilterRequestModel model)
{
var results = new List<ValidationResult>();
Validator.TryValidateObject(model, new ValidationContext(model), results, true);
return results;
}
}

View File

@@ -0,0 +1,43 @@
#nullable enable
using Bit.Api.NotificationCenter.Models.Response;
using Bit.Core.Enums;
using Bit.Core.NotificationCenter.Enums;
using Bit.Core.NotificationCenter.Models.Data;
using Xunit;
namespace Bit.Api.Test.NotificationCenter.Models.Response;
public class NotificationResponseModelTests
{
[Fact]
public void Constructor_NotificationStatusDetailsNull_CorrectFields()
{
Assert.Throws<ArgumentNullException>(() => new NotificationResponseModel(null!));
}
[Fact]
public void Constructor_NotificationStatusDetails_CorrectFields()
{
var notificationStatusDetails = new NotificationStatusDetails
{
Id = Guid.NewGuid(),
Global = true,
Priority = Priority.High,
ClientType = ClientType.All,
Title = "Test Title",
Body = "Test Body",
RevisionDate = DateTime.UtcNow - TimeSpan.FromMinutes(3),
ReadDate = DateTime.UtcNow - TimeSpan.FromMinutes(1),
DeletedDate = DateTime.UtcNow,
};
var model = new NotificationResponseModel(notificationStatusDetails);
Assert.Equal(model.Id, notificationStatusDetails.Id);
Assert.Equal(model.Priority, notificationStatusDetails.Priority);
Assert.Equal(model.Title, notificationStatusDetails.Title);
Assert.Equal(model.Body, notificationStatusDetails.Body);
Assert.Equal(model.Date, notificationStatusDetails.RevisionDate);
Assert.Equal(model.ReadDate, notificationStatusDetails.ReadDate);
Assert.Equal(model.DeletedDate, notificationStatusDetails.DeletedDate);
}
}

View File

@@ -0,0 +1,95 @@
using System.Security.Claims;
using Bit.Api.Tools.Authorization;
using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Test.AdminConsole.Helpers;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.Authorization;
using NSubstitute;
using Xunit;
namespace Bit.Api.Test.Tools.Authorization;
[SutProviderCustomize]
public class VaultExportAuthorizationHandlerTests
{
public static IEnumerable<object[]> CanExportWholeVault => new List<CurrentContextOrganization>
{
new () { Type = OrganizationUserType.Owner },
new () { Type = OrganizationUserType.Admin },
new ()
{
Type = OrganizationUserType.Custom, Permissions = new Permissions { AccessImportExport = true }
}
}.Select(org => new[] { org });
[Theory]
[BitMemberAutoData(nameof(CanExportWholeVault))]
public async Task ExportAll_PermittedRoles_Success(CurrentContextOrganization org, OrganizationScope orgScope, ClaimsPrincipal user,
SutProvider<VaultExportAuthorizationHandler> sutProvider)
{
org.Id = orgScope;
sutProvider.GetDependency<ICurrentContext>().GetOrganization(orgScope).Returns(org);
var authContext = new AuthorizationHandlerContext(new[] { VaultExportOperations.ExportWholeVault }, user, orgScope);
await sutProvider.Sut.HandleAsync(authContext);
Assert.True(authContext.HasSucceeded);
}
public static IEnumerable<object[]> CannotExportWholeVault => new List<CurrentContextOrganization>
{
new () { Type = OrganizationUserType.User },
new ()
{
Type = OrganizationUserType.Custom, Permissions = new Permissions { AccessImportExport = true }.Invert()
}
}.Select(org => new[] { org });
[Theory]
[BitMemberAutoData(nameof(CannotExportWholeVault))]
public async Task ExportAll_NotPermitted_Failure(CurrentContextOrganization org, OrganizationScope orgScope, ClaimsPrincipal user,
SutProvider<VaultExportAuthorizationHandler> sutProvider)
{
org.Id = orgScope;
sutProvider.GetDependency<ICurrentContext>().GetOrganization(orgScope).Returns(org);
var authContext = new AuthorizationHandlerContext(new[] { VaultExportOperations.ExportWholeVault }, user, orgScope);
await sutProvider.Sut.HandleAsync(authContext);
Assert.False(authContext.HasSucceeded);
}
public static IEnumerable<object[]> CanExportManagedCollections =>
AuthorizationHelpers.AllRoles().Select(o => new[] { o });
[Theory]
[BitMemberAutoData(nameof(CanExportManagedCollections))]
public async Task ExportManagedCollections_PermittedRoles_Success(CurrentContextOrganization org, OrganizationScope orgScope, ClaimsPrincipal user,
SutProvider<VaultExportAuthorizationHandler> sutProvider)
{
org.Id = orgScope;
sutProvider.GetDependency<ICurrentContext>().GetOrganization(orgScope).Returns(org);
var authContext = new AuthorizationHandlerContext(new[] { VaultExportOperations.ExportManagedCollections }, user, orgScope);
await sutProvider.Sut.HandleAsync(authContext);
Assert.True(authContext.HasSucceeded);
}
[Theory]
[BitAutoData([null])]
public async Task ExportManagedCollections_NotPermitted_Failure(CurrentContextOrganization org, OrganizationScope orgScope, ClaimsPrincipal user,
SutProvider<VaultExportAuthorizationHandler> sutProvider)
{
sutProvider.GetDependency<ICurrentContext>().GetOrganization(orgScope).Returns(org);
var authContext = new AuthorizationHandlerContext(new[] { VaultExportOperations.ExportManagedCollections }, user, orgScope);
await sutProvider.Sut.HandleAsync(authContext);
Assert.False(authContext.HasSucceeded);
}
}

View File

@@ -1,7 +1,9 @@
using Bit.Api.Tools.Controllers;
using AutoFixture;
using Bit.Api.Tools.Controllers;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Tools.ReportFeatures.Interfaces;
using Bit.Core.Tools.ReportFeatures.Requests;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
@@ -45,5 +47,98 @@ public class ReportsControllerTests
.Received(0);
}
[Theory, BitAutoData]
public async Task AddPasswordHealthReportApplicationAsync_withAccess_success(SutProvider<ReportsController> sutProvider)
{
// Arrange
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(true);
// Act
var request = new Api.Tools.Models.PasswordHealthReportApplicationModel
{
OrganizationId = Guid.NewGuid(),
Url = "https://example.com",
};
await sutProvider.Sut.AddPasswordHealthReportApplication(request);
// Assert
_ = sutProvider.GetDependency<IAddPasswordHealthReportApplicationCommand>()
.Received(1)
.AddPasswordHealthReportApplicationAsync(Arg.Is<AddPasswordHealthReportApplicationRequest>(_ =>
_.OrganizationId == request.OrganizationId && _.Url == request.Url));
}
[Theory, BitAutoData]
public async Task AddPasswordHealthReportApplicationAsync_multiple_withAccess_success(
SutProvider<ReportsController> sutProvider)
{
// Arrange
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(true);
// Act
var fixture = new Fixture();
var request = fixture.CreateMany<Api.Tools.Models.PasswordHealthReportApplicationModel>(2);
await sutProvider.Sut.AddPasswordHealthReportApplications(request);
// Assert
_ = sutProvider.GetDependency<IAddPasswordHealthReportApplicationCommand>()
.Received(1)
.AddPasswordHealthReportApplicationAsync(Arg.Any<IEnumerable<AddPasswordHealthReportApplicationRequest>>());
}
[Theory, BitAutoData]
public async Task AddPasswordHealthReportApplicationAsync_withoutAccess(SutProvider<ReportsController> sutProvider)
{
// Arrange
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(false);
// Act
var request = new Api.Tools.Models.PasswordHealthReportApplicationModel
{
OrganizationId = Guid.NewGuid(),
Url = "https://example.com",
};
await Assert.ThrowsAsync<NotFoundException>(async () =>
await sutProvider.Sut.AddPasswordHealthReportApplication(request));
// Assert
_ = sutProvider.GetDependency<IAddPasswordHealthReportApplicationCommand>()
.Received(0);
}
[Theory, BitAutoData]
public async Task DropPasswordHealthReportApplicationAsync_withoutAccess(SutProvider<ReportsController> sutProvider)
{
// Arrange
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(false);
// Act
var fixture = new Fixture();
var request = fixture.Create<Api.Tools.Models.PasswordHealthReportApplicationModel>();
await Assert.ThrowsAsync<NotFoundException>(async () =>
await sutProvider.Sut.AddPasswordHealthReportApplication(request));
// Assert
_ = sutProvider.GetDependency<IDropPasswordHealthReportApplicationCommand>()
.Received(0);
}
[Theory, BitAutoData]
public async Task DropPasswordHealthReportApplicationAsync_withAccess_success(SutProvider<ReportsController> sutProvider)
{
// Arrange
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(true);
// Act
var fixture = new Fixture();
var request = fixture.Create<DropPasswordHealthReportApplicationRequest>();
await sutProvider.Sut.DropPasswordHealthReportApplication(request);
// Assert
_ = sutProvider.GetDependency<IDropPasswordHealthReportApplicationCommand>()
.Received(1)
.DropPasswordHealthReportApplicationAsync(Arg.Is<DropPasswordHealthReportApplicationRequest>(_ =>
_.OrganizationId == request.OrganizationId &&
_.PasswordHealthReportApplicationIds == request.PasswordHealthReportApplicationIds));
}
}

View File

@@ -1,6 +1,5 @@
using System.Security.Claims;
using Bit.Api.Vault.AuthorizationHandlers.Collections;
using Bit.Core;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
@@ -33,10 +32,7 @@ public class BulkCollectionAuthorizationHandlerTests
organization.Type = userType;
organization.Permissions = new Permissions();
// `LimitCollectonCreationDeletionSplit` feature flag state isn't
// relevant for this test. The flag is never checked for in this
// test. This is asserted below.
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, true, true);
ArrangeOrganizationAbility(sutProvider, organization, true, true);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Create },
@@ -48,12 +44,11 @@ public class BulkCollectionAuthorizationHandlerTests
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().DidNotReceiveWithAnyArgs().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanCreateAsync_WhenUser_WithLimitCollectionCreationFalse_WithLimitCollectionCreationDeletionSplitFeatureDisabled_Success(
public async Task CanCreateAsync_WhenUser_WithLimitCollectionCreationFalse_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
@@ -62,7 +57,7 @@ public class BulkCollectionAuthorizationHandlerTests
organization.Type = OrganizationUserType.User;
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureDisabled(sutProvider, organization, false, false);
ArrangeOrganizationAbility(sutProvider, organization, false, false);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Create },
@@ -71,49 +66,16 @@ public class BulkCollectionAuthorizationHandlerTests
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit)
.Returns(false);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanCreateAsync_WhenUser_WithLimitCollectionCreationFalse_WithLimitCollectionCreationDeletionSplitFeatureEnabled_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, false, false);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit)
.Returns(true);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanCreateAsync_WhenMissingPermissions_WithLimitCollectionCreationDeletionSplitFeatureDisabled_NoSuccess(
public async Task CanCreateAsync_WhenMissingPermissions_NoSuccess(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
@@ -130,7 +92,7 @@ public class BulkCollectionAuthorizationHandlerTests
ManageUsers = false
};
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureDisabled(sutProvider, organization, true, true);
ArrangeOrganizationAbility(sutProvider, organization, true, true);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Create },
@@ -140,61 +102,21 @@ public class BulkCollectionAuthorizationHandlerTests
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(false);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanCreateAsync_WhenMissingPermissions_WithLimitCollectionCreationDeletionSplitFeatureEnabled_NoSuccess(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
EditAnyCollection = false,
DeleteAnyCollection = false,
ManageGroups = false,
ManageUsers = false
};
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, true, true);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(true);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanCreateAsync_WhenMissingOrgAccess_WithLimitCollectionCreationDeletionSplitDisabled_NoSuccess(
public async Task CanCreateAsync_WhenMissingOrgAccess_NoSuccess(
Guid userId,
CurrentContextOrganization organization,
List<Collection> collections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
collections.ForEach(c => c.OrganizationId = organization.Id);
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureDisabled(sutProvider, organization, true, true);
ArrangeOrganizationAbility(sutProvider, organization, true, true);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Create },
@@ -205,38 +127,9 @@ public class BulkCollectionAuthorizationHandlerTests
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(false);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanCreateAsync_WhenMissingOrgAccess_WithLimitCollectionCreationDeletionSplitEnabled_NoSuccess(
Guid userId,
CurrentContextOrganization organization,
List<Collection> collections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
collections.ForEach(c => c.OrganizationId = organization.Id);
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, true, true);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Create },
new ClaimsPrincipal(),
collections
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(true);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
@@ -1015,7 +908,7 @@ public class BulkCollectionAuthorizationHandlerTests
// `LimitCollectonCreationDeletionSplit` feature flag state isn't
// relevant for this test. The flag is never checked for in this
// test. This is asserted below.
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, true, true);
ArrangeOrganizationAbility(sutProvider, organization, true, true);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
@@ -1027,7 +920,6 @@ public class BulkCollectionAuthorizationHandlerTests
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().DidNotReceiveWithAnyArgs().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.True(context.HasSucceeded);
}
@@ -1046,7 +938,7 @@ public class BulkCollectionAuthorizationHandlerTests
// `LimitCollectonCreationDeletionSplit` feature flag state isn't
// relevant for this test. The flag is never checked for in this
// test. This is asserted below.
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, true, true);
ArrangeOrganizationAbility(sutProvider, organization, true, true);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
@@ -1058,12 +950,11 @@ public class BulkCollectionAuthorizationHandlerTests
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().DidNotReceiveWithAnyArgs().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WhenUser_LimitCollectionDeletionFalse_WithCanManagePermission_WithLimitCollectionCreationDeletionSplitFeatureDisabled_Success(
public async Task CanDeleteAsync_WhenUser_LimitCollectionDeletionFalse_WithCanManagePermission_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
@@ -1073,12 +964,11 @@ public class BulkCollectionAuthorizationHandlerTests
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureDisabled(sutProvider, organization, false, false);
ArrangeOrganizationAbility(sutProvider, organization, false, false);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(false);
foreach (var c in collections)
{
@@ -1092,41 +982,6 @@ public class BulkCollectionAuthorizationHandlerTests
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WhenUser_LimitCollectionDeletionFalse_WithCanManagePermission_WithLimitCollectionCreationDeletionSplitFeatureEnabled_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, false, false);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(true);
foreach (var c in collections)
{
c.Manage = true;
}
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.True(context.HasSucceeded);
}
@@ -1134,7 +989,7 @@ public class BulkCollectionAuthorizationHandlerTests
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.User)]
public async Task CanDeleteAsync_LimitCollectionDeletionFalse_AllowAdminAccessToAllCollectionItemsFalse_WithCanManagePermission_WithLimitCollectionCreationDeletionSplitFeatureDisabled_Success(
public async Task CanDeleteAsync_LimitCollectionDeletionFalse_AllowAdminAccessToAllCollectionItemsFalse_WithCanManagePermission_Success(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
@@ -1145,12 +1000,11 @@ public class BulkCollectionAuthorizationHandlerTests
organization.Type = userType;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureDisabled(sutProvider, organization, false, false, false);
ArrangeOrganizationAbility(sutProvider, organization, false, false, false);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(false);
foreach (var c in collections)
{
@@ -1164,15 +1018,13 @@ public class BulkCollectionAuthorizationHandlerTests
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.User)]
public async Task CanDeleteAsync_LimitCollectionDeletionFalse_AllowAdminAccessToAllCollectionItemsFalse_WithCanManagePermission_WithLimitCollectionCreationDeletionSplitFeatureEnabled_Success(
public async Task CanDeleteAsync_WhenAdminOrOwner_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsFalse_WithCanManagePermission_Success(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
@@ -1183,12 +1035,11 @@ public class BulkCollectionAuthorizationHandlerTests
organization.Type = userType;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, false, false, false);
ArrangeOrganizationAbility(sutProvider, organization, true, true, false);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(true);
foreach (var c in collections)
{
@@ -1202,14 +1053,13 @@ public class BulkCollectionAuthorizationHandlerTests
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanDeleteAsync_WhenAdminOrOwner_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsFalse_WithCanManagePermission_WithLimitCollectionCreationDeletionSplitFeatureDisabled_Success(
public async Task CanDeleteAsync_WhenAdminOrOwner_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsFalse_WithoutCanManagePermission_Failure(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
@@ -1220,87 +1070,12 @@ public class BulkCollectionAuthorizationHandlerTests
organization.Type = userType;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureDisabled(sutProvider, organization, true, true, false);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(false);
foreach (var c in collections)
{
c.Manage = true;
}
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanDeleteAsync_WhenAdminOrOwner_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsFalse_WithCanManagePermission_WithLimitCollectionCreationDeletionSplitFeatureEnabled_Success(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, true, true, false);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(true);
foreach (var c in collections)
{
c.Manage = true;
}
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanDeleteAsync_WhenAdminOrOwner_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsFalse_WithoutCanManagePermission_WithLimitCollectionCreationDeletionSplitFeatureDisabled_Failure(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureDisabled(sutProvider, organization, true, true, false);
ArrangeOrganizationAbility(sutProvider, organization, true, true, false);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(false);
foreach (var c in collections)
{
@@ -1314,50 +1089,11 @@ public class BulkCollectionAuthorizationHandlerTests
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanDeleteAsync_WhenAdminOrOwner_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsFalse_WithoutCanManagePermission_WithLimitCollectionCreationDeletionSplitFeatureEnabled_Failure(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, true, true, false);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(true);
foreach (var c in collections)
{
c.Manage = false;
}
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WhenUser_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsTrue_WithLimitCollectionCreationDeletionSplitFeatureDisabled_Failure(
public async Task CanDeleteAsync_WhenUser_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsTrue_Failure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
@@ -1367,13 +1103,12 @@ public class BulkCollectionAuthorizationHandlerTests
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureDisabled(sutProvider, organization, true, true);
ArrangeOrganizationAbility(sutProvider, organization, true, true);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(false);
foreach (var c in collections)
{
@@ -1387,12 +1122,11 @@ public class BulkCollectionAuthorizationHandlerTests
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WhenUser_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsTrue_WithLimitCollectionCreationDeletionSplitFeatureEnabled_Failure(
public async Task CanDeleteAsync_WhenUser_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsFalse_Failure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
@@ -1402,13 +1136,12 @@ public class BulkCollectionAuthorizationHandlerTests
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, true, true);
ArrangeOrganizationAbility(sutProvider, organization, true, true, false);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(true);
foreach (var c in collections)
{
@@ -1422,88 +1155,13 @@ public class BulkCollectionAuthorizationHandlerTests
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WhenUser_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsFalse_WithLimitCollectionCreationDeletionSplitFeatureDisabled_Failure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureDisabled(sutProvider, organization, true, true, false);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit)
.Returns(false);
foreach (var c in collections)
{
c.Manage = true;
}
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WhenUser_LimitCollectionDeletionTrue_AllowAdminAccessToAllCollectionItemsFalse_WithLimitCollectionCreationDeletionSplitFeatureEnabled_Failure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, true, true, false);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit)
.Returns(true);
foreach (var c in collections)
{
c.Manage = true;
}
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanDeleteAsync_WhenMissingPermissions_WithLimitCollectionCreationDeletionSplitFeatureDisabled_NoSuccess(
public async Task CanDeleteAsync_WhenMissingPermissions_NoSuccess(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
@@ -1520,7 +1178,7 @@ public class BulkCollectionAuthorizationHandlerTests
ManageUsers = false
};
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureDisabled(sutProvider, organization, true, true);
ArrangeOrganizationAbility(sutProvider, organization, true, true);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
@@ -1530,54 +1188,14 @@ public class BulkCollectionAuthorizationHandlerTests
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(false);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanDeleteAsync_WhenMissingPermissions_WithLimitCollectionCreationDeletionSplitFeatureEnabled_NoSuccess(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
EditAnyCollection = false,
DeleteAnyCollection = false,
ManageGroups = false,
ManageUsers = false
};
ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(sutProvider, organization, true, true);
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(true);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WhenMissingOrgAccess_WithLimitCollectionCreationDeletionSplitFeatureDisabled_NoSuccess(
public async Task CanDeleteAsync_WhenMissingOrgAccess_NoSuccess(
Guid userId,
ICollection<Collection> collections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
@@ -1591,34 +1209,9 @@ public class BulkCollectionAuthorizationHandlerTests
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(false);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WhenMissingOrgAccess_WithLimitCollectionCreationDeletionSplitFeatureEnabled_NoSuccess(
Guid userId,
ICollection<Collection> collections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
var context = new AuthorizationHandlerContext(
new[] { BulkCollectionOperations.Delete },
new ClaimsPrincipal(),
collections
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(false);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(true);
await sutProvider.Sut.HandleAsync(context);
sutProvider.GetDependency<IFeatureService>().Received(1).IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
Assert.False(context.HasSucceeded);
}
@@ -1639,7 +1232,6 @@ public class BulkCollectionAuthorizationHandlerTests
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasFailed);
sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs();
sutProvider.GetDependency<IFeatureService>().DidNotReceiveWithAnyArgs().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
}
[Theory, BitAutoData, CollectionCustomization]
@@ -1663,66 +1255,10 @@ public class BulkCollectionAuthorizationHandlerTests
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.HandleAsync(context));
Assert.Equal("Requested collections must belong to the same organization.", exception.Message);
sutProvider.GetDependency<ICurrentContext>().DidNotReceiveWithAnyArgs().GetOrganization(default);
sutProvider.GetDependency<IFeatureService>().DidNotReceiveWithAnyArgs().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_Provider_WithLimitCollectionCreationDeletionSplitFeatureDisabled_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections)
{
var actingUserId = Guid.NewGuid();
var orgId = collections.First().OrganizationId;
var organizationAbilities = new Dictionary<Guid, OrganizationAbility>
{
{ collections.First().OrganizationId,
new OrganizationAbility
{
LimitCollectionCreationDeletion = true,
AllowAdminAccessToAllCollectionItems = true
}
}
};
var operationsToTest = new[]
{
BulkCollectionOperations.Create,
BulkCollectionOperations.Read,
BulkCollectionOperations.ReadAccess,
BulkCollectionOperations.Update,
BulkCollectionOperations.ModifyUserAccess,
BulkCollectionOperations.ModifyGroupAccess,
BulkCollectionOperations.Delete,
};
foreach (var op in operationsToTest)
{
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(orgId).Returns((CurrentContextOrganization)null);
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync()
.Returns(organizationAbilities);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(true);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(false);
var context = new AuthorizationHandlerContext(
new[] { op },
new ClaimsPrincipal(),
collections
);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
await sutProvider.GetDependency<ICurrentContext>().Received().ProviderUserForOrgAsync(orgId);
// Recreate the SUT to reset the mocks/dependencies between tests
sutProvider.Recreate();
}
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_Provider_WithLimitCollectionCreationDeletionSplitFeatureEnabled_Success(
public async Task HandleRequirementAsync_Provider_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections)
{
@@ -1759,7 +1295,6 @@ public class BulkCollectionAuthorizationHandlerTests
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync()
.Returns(organizationAbilities);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(true);
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.LimitCollectionCreationDeletionSplit).Returns(true);
var context = new AuthorizationHandlerContext(
new[] { op },
@@ -1810,30 +1345,12 @@ public class BulkCollectionAuthorizationHandlerTests
await sutProvider.GetDependency<ICollectionRepository>().Received(1).GetManyByUserIdAsync(Arg.Any<Guid>());
}
private static void ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureDisabled(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
CurrentContextOrganization organization,
bool limitCollectionCreation,
bool limitCollectionDeletion,
bool allowAdminAccessToAllCollectionItems = true)
{
var organizationAbility = new OrganizationAbility();
organizationAbility.Id = organization.Id;
organizationAbility.LimitCollectionCreationDeletion = limitCollectionCreation || limitCollectionDeletion;
organizationAbility.AllowAdminAccessToAllCollectionItems = allowAdminAccessToAllCollectionItems;
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organizationAbility.Id)
.Returns(organizationAbility);
}
private static void ArrangeOrganizationAbility_WithLimitCollectionCreationDeletionSplitFeatureEnabled(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
CurrentContextOrganization organization,
bool limitCollectionCreation,
bool limitCollectionDeletion,
bool allowAdminAccessToAllCollectionItems = true)
private static void ArrangeOrganizationAbility(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
CurrentContextOrganization organization,
bool limitCollectionCreation,
bool limitCollectionDeletion,
bool allowAdminAccessToAllCollectionItems = true)
{
var organizationAbility = new OrganizationAbility();
organizationAbility.Id = organization.Id;