mirror of
https://github.com/bitwarden/server.git
synced 2026-01-31 14:13:18 +08:00
Implement InitPendingOrganizationValidator for improved organization initialization validation
- Introduced IInitPendingOrganizationValidator interface and its implementation to encapsulate validation logic for organization initialization. - Refactored InitPendingOrganizationCommand to utilize the new validator for token validation, user email matching, organization state checks, and policy enforcement. - Enhanced dependency injection in OrganizationServiceCollectionExtensions to include the new validator. - Added comprehensive unit tests for the validator to ensure robust validation logic and error handling.
This commit is contained in:
@@ -7,7 +7,6 @@ using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.AdminConsole.Utilities.v2.Results;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
@@ -21,7 +20,6 @@ using Bit.Core.Tokens;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using OneOf.Types;
|
||||
using Error = Bit.Core.AdminConsole.Utilities.v2.Error;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||
|
||||
@@ -44,6 +42,7 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
|
||||
private readonly IPushNotificationService _pushNotificationService;
|
||||
private readonly IPushRegistrationService _pushRegistrationService;
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IInitPendingOrganizationValidator _validator;
|
||||
|
||||
public InitPendingOrganizationCommand(
|
||||
IOrganizationService organizationService,
|
||||
@@ -62,7 +61,8 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
|
||||
IUserRepository userRepository,
|
||||
IPushNotificationService pushNotificationService,
|
||||
IPushRegistrationService pushRegistrationService,
|
||||
IDeviceRepository deviceRepository
|
||||
IDeviceRepository deviceRepository,
|
||||
IInitPendingOrganizationValidator validator
|
||||
)
|
||||
{
|
||||
_organizationService = organizationService;
|
||||
@@ -82,6 +82,7 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
|
||||
_pushNotificationService = pushNotificationService;
|
||||
_pushRegistrationService = pushRegistrationService;
|
||||
_deviceRepository = deviceRepository;
|
||||
_validator = validator;
|
||||
}
|
||||
|
||||
public async Task InitPendingOrganizationAsync(User user, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName, string emailToken)
|
||||
@@ -94,7 +95,7 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
|
||||
throw new BadRequestException("User invalid.");
|
||||
}
|
||||
|
||||
var tokenValid = ValidateInviteToken(orgUser, user, emailToken);
|
||||
var tokenValid = _validator.ValidateInviteToken(orgUser, user, emailToken);
|
||||
|
||||
if (!tokenValid)
|
||||
{
|
||||
@@ -170,14 +171,6 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateInviteToken(OrganizationUser orgUser, User user, string emailToken)
|
||||
{
|
||||
var tokenValid = OrgUserInviteTokenable.ValidateOrgUserInviteStringToken(
|
||||
_orgUserInviteTokenDataFactory, emailToken, orgUser);
|
||||
|
||||
return tokenValid;
|
||||
}
|
||||
|
||||
public async Task<CommandResult> InitPendingOrganizationVNextAsync(InitPendingOrganizationRequest request)
|
||||
{
|
||||
var orgUser = await _organizationUserRepository.GetByIdAsync(request.OrganizationUserId);
|
||||
@@ -186,12 +179,12 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
|
||||
return new OrganizationUserNotFoundError();
|
||||
}
|
||||
|
||||
if (!ValidateInviteToken(orgUser, request.User, request.EmailToken))
|
||||
if (!_validator.ValidateInviteToken(orgUser, request.User, request.EmailToken))
|
||||
{
|
||||
return new InvalidTokenError();
|
||||
}
|
||||
|
||||
var validationError = ValidateUserEmail(orgUser, request.User);
|
||||
var validationError = _validator.ValidateUserEmail(orgUser, request.User);
|
||||
if (validationError != null)
|
||||
{
|
||||
return validationError;
|
||||
@@ -203,18 +196,25 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
|
||||
return new OrganizationNotFoundError();
|
||||
}
|
||||
|
||||
if (orgUser.OrganizationId != request.OrganizationId)
|
||||
{
|
||||
return new OrganizationMismatchError();
|
||||
}
|
||||
|
||||
validationError = ValidateOrganizationState(org);
|
||||
validationError = _validator.ValidateOrganizationMatch(orgUser, request.OrganizationId);
|
||||
if (validationError != null)
|
||||
{
|
||||
return validationError;
|
||||
}
|
||||
|
||||
validationError = await ValidatePoliciesAsync(request.User, request.OrganizationId, org, orgUser);
|
||||
validationError = _validator.ValidateOrganizationState(org);
|
||||
if (validationError != null)
|
||||
{
|
||||
return validationError;
|
||||
}
|
||||
|
||||
validationError = await _validator.ValidatePoliciesAsync(request.User, request.OrganizationId);
|
||||
if (validationError != null)
|
||||
{
|
||||
return validationError;
|
||||
}
|
||||
|
||||
validationError = await _validator.ValidateBusinessRulesAsync(request.User, org, orgUser);
|
||||
if (validationError != null)
|
||||
{
|
||||
return validationError;
|
||||
@@ -224,7 +224,6 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
|
||||
PrepareOrganizationUserForConfirmation(orgUser, request);
|
||||
|
||||
var updateActions = BuildDatabaseUpdateActions(org, orgUser, request);
|
||||
|
||||
await _organizationRepository.ExecuteOrganizationInitializationUpdatesAsync(updateActions);
|
||||
|
||||
await SendNotificationsAsync(org, orgUser, request.User, request.OrganizationId);
|
||||
@@ -232,76 +231,6 @@ public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
|
||||
return new None();
|
||||
}
|
||||
|
||||
private static Error? ValidateUserEmail(OrganizationUser orgUser, User user)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(orgUser.Email) ||
|
||||
!orgUser.Email.Equals(user.Email, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return new EmailMismatchError();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Error? ValidateOrganizationState(Organization org)
|
||||
{
|
||||
if (org.Enabled)
|
||||
{
|
||||
return new OrganizationAlreadyEnabledError();
|
||||
}
|
||||
|
||||
if (org.Status != OrganizationStatusType.Pending)
|
||||
{
|
||||
return new OrganizationNotPendingError();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(org.PublicKey) || !string.IsNullOrEmpty(org.PrivateKey))
|
||||
{
|
||||
return new OrganizationHasKeysError();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<Error?> ValidatePoliciesAsync(User user, Guid organizationId, Organization org, OrganizationUser orgUser)
|
||||
{
|
||||
// Enforce Automatic User Confirmation Policy (when feature flag is enabled)
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers))
|
||||
{
|
||||
var autoConfirmReq = await _policyRequirementQuery.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id);
|
||||
if (autoConfirmReq.CannotCreateNewOrganization())
|
||||
{
|
||||
return new SingleOrgPolicyViolationError();
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce Single Organization Policy
|
||||
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg);
|
||||
if (anySingleOrgPolicies)
|
||||
{
|
||||
return new SingleOrgPolicyViolationError();
|
||||
}
|
||||
|
||||
var twoFactorReq = await _policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id);
|
||||
if (twoFactorReq.IsTwoFactorRequiredForOrganization(organizationId) &&
|
||||
!await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user))
|
||||
{
|
||||
return new TwoFactorRequiredError();
|
||||
}
|
||||
|
||||
if (org.PlanType == PlanType.Free &&
|
||||
(orgUser.Type == OrganizationUserType.Owner || orgUser.Type == OrganizationUserType.Admin))
|
||||
{
|
||||
var adminCount = await _organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(user.Id);
|
||||
if (adminCount > 0)
|
||||
{
|
||||
return new FreeOrgAdminLimitError();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void PrepareOrganizationForInitialization(Organization org, InitPendingOrganizationRequest request)
|
||||
{
|
||||
org.Enabled = true;
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tokens;
|
||||
using Error = Bit.Core.AdminConsole.Utilities.v2.Error;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||
|
||||
public interface IInitPendingOrganizationValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates the invite token for an organization user.
|
||||
/// </summary>
|
||||
bool ValidateInviteToken(OrganizationUser orgUser, User user, string emailToken);
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the user's email matches the organization user's email.
|
||||
/// </summary>
|
||||
Error? ValidateUserEmail(OrganizationUser orgUser, User user);
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the organization is in the correct state for initialization.
|
||||
/// </summary>
|
||||
Error? ValidateOrganizationState(Organization org);
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the organization user's organization ID matches the expected organization ID.
|
||||
/// </summary>
|
||||
Error? ValidateOrganizationMatch(OrganizationUser orgUser, Guid organizationId);
|
||||
|
||||
/// <summary>
|
||||
/// Validates policy requirements for the user joining the organization.
|
||||
/// </summary>
|
||||
Task<Error?> ValidatePoliciesAsync(User user, Guid organizationId);
|
||||
|
||||
/// <summary>
|
||||
/// Validates business rules for the user joining the organization (e.g., free org admin limits).
|
||||
/// </summary>
|
||||
Task<Error?> ValidateBusinessRulesAsync(User user, Organization org, OrganizationUser orgUser);
|
||||
}
|
||||
|
||||
public class InitPendingOrganizationValidator : IInitPendingOrganizationValidator
|
||||
{
|
||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly IPolicyRequirementQuery _policyRequirementQuery;
|
||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
|
||||
public InitPendingOrganizationValidator(
|
||||
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
||||
IFeatureService featureService,
|
||||
IPolicyService policyService,
|
||||
IPolicyRequirementQuery policyRequirementQuery,
|
||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||
IOrganizationUserRepository organizationUserRepository)
|
||||
{
|
||||
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
||||
_featureService = featureService;
|
||||
_policyService = policyService;
|
||||
_policyRequirementQuery = policyRequirementQuery;
|
||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
}
|
||||
|
||||
public bool ValidateInviteToken(OrganizationUser orgUser, User user, string emailToken)
|
||||
{
|
||||
return OrgUserInviteTokenable.ValidateOrgUserInviteStringToken(
|
||||
_orgUserInviteTokenDataFactory, emailToken, orgUser);
|
||||
}
|
||||
|
||||
public Error? ValidateUserEmail(OrganizationUser orgUser, User user)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(orgUser.Email) ||
|
||||
!orgUser.Email.Equals(user.Email, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return new EmailMismatchError();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Error? ValidateOrganizationState(Organization org)
|
||||
{
|
||||
if (org.Enabled)
|
||||
{
|
||||
return new OrganizationAlreadyEnabledError();
|
||||
}
|
||||
|
||||
if (org.Status != OrganizationStatusType.Pending)
|
||||
{
|
||||
return new OrganizationNotPendingError();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(org.PublicKey) || !string.IsNullOrEmpty(org.PrivateKey))
|
||||
{
|
||||
return new OrganizationHasKeysError();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Error? ValidateOrganizationMatch(OrganizationUser orgUser, Guid organizationId)
|
||||
{
|
||||
if (orgUser.OrganizationId != organizationId)
|
||||
{
|
||||
return new OrganizationMismatchError();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<Error?> ValidatePoliciesAsync(User user, Guid organizationId)
|
||||
{
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers))
|
||||
{
|
||||
var autoConfirmReq = await _policyRequirementQuery.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id);
|
||||
if (autoConfirmReq.CannotCreateNewOrganization())
|
||||
{
|
||||
return new SingleOrgPolicyViolationError();
|
||||
}
|
||||
}
|
||||
|
||||
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg);
|
||||
if (anySingleOrgPolicies)
|
||||
{
|
||||
return new SingleOrgPolicyViolationError();
|
||||
}
|
||||
|
||||
var twoFactorReq = await _policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id);
|
||||
if (twoFactorReq.IsTwoFactorRequiredForOrganization(organizationId) &&
|
||||
!await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user))
|
||||
{
|
||||
return new TwoFactorRequiredError();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<Error?> ValidateBusinessRulesAsync(User user, Organization org, OrganizationUser orgUser)
|
||||
{
|
||||
if (org.PlanType == PlanType.Free &&
|
||||
(orgUser.Type == OrganizationUserType.Owner || orgUser.Type == OrganizationUserType.Admin))
|
||||
{
|
||||
var adminCount = await _organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(user.Id);
|
||||
if (adminCount > 0)
|
||||
{
|
||||
return new FreeOrgAdminLimitError();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -214,6 +214,7 @@ public static class OrganizationServiceCollectionExtensions
|
||||
services.AddScoped<IInviteUsersOrganizationValidator, InviteUsersOrganizationValidator>();
|
||||
services.AddScoped<IInviteUsersPasswordManagerValidator, InviteUsersPasswordManagerValidator>();
|
||||
services.AddScoped<IInviteUsersEnvironmentValidator, InviteUsersEnvironmentValidator>();
|
||||
services.AddScoped<IInitPendingOrganizationValidator, InitPendingOrganizationValidator>();
|
||||
services.AddScoped<IInitPendingOrganizationCommand, InitPendingOrganizationCommand>();
|
||||
services.AddScoped<IImportOrganizationUsersAndGroupsCommand, ImportOrganizationUsersAndGroupsCommand>();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
@@ -161,6 +155,11 @@ public class InitPendingOrganizationCommandTests
|
||||
var protectedToken = _orgUserInviteTokenDataFactory.Protect(orgUserInviteTokenable);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(orgUserId).Returns(orgUser);
|
||||
|
||||
// Setup validator to accept the token for old InitPendingOrganizationAsync method
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateInviteToken(orgUser, Arg.Any<User>(), protectedToken)
|
||||
.Returns(true);
|
||||
|
||||
return protectedToken;
|
||||
}
|
||||
|
||||
@@ -248,13 +247,14 @@ public class InitPendingOrganizationCommandTests
|
||||
orgUser.Email = user.Email;
|
||||
var requestWithInvalidToken = request with { User = user, EmailToken = "invalid-token" };
|
||||
|
||||
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
||||
sutProvider.Create();
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(requestWithInvalidToken.OrganizationUserId)
|
||||
.Returns(orgUser);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateInviteToken(orgUser, user, "invalid-token")
|
||||
.Returns(false);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InitPendingOrganizationVNextAsync(requestWithInvalidToken);
|
||||
|
||||
@@ -274,13 +274,20 @@ public class InitPendingOrganizationCommandTests
|
||||
orgUser.Email = "different@email.com";
|
||||
user.Email = "user@email.com";
|
||||
|
||||
var token = CreateToken(orgUser, request.OrganizationUserId, sutProvider);
|
||||
var requestWithUser = request with { User = user, EmailToken = token };
|
||||
var requestWithUser = request with { User = user, EmailToken = "valid-token" };
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(requestWithUser.OrganizationUserId)
|
||||
.Returns(orgUser);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateInviteToken(orgUser, user, Arg.Any<string>())
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateUserEmail(orgUser, user)
|
||||
.Returns(new EmailMismatchError());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InitPendingOrganizationVNextAsync(requestWithUser);
|
||||
|
||||
@@ -299,13 +306,20 @@ public class InitPendingOrganizationCommandTests
|
||||
// Arrange
|
||||
orgUser.Email = user.Email;
|
||||
|
||||
var token = CreateToken(orgUser, request.OrganizationUserId, sutProvider);
|
||||
var requestWithUser = request with { User = user, EmailToken = token };
|
||||
var requestWithUser = request with { User = user, EmailToken = "valid-token" };
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(requestWithUser.OrganizationUserId)
|
||||
.Returns(orgUser);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateInviteToken(orgUser, user, Arg.Any<string>())
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateUserEmail(orgUser, user)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(requestWithUser.OrganizationId)
|
||||
.Returns((Organization)null);
|
||||
@@ -330,17 +344,28 @@ public class InitPendingOrganizationCommandTests
|
||||
orgUser.Email = user.Email;
|
||||
orgUser.OrganizationId = Guid.NewGuid(); // Different from request
|
||||
|
||||
var token = CreateToken(orgUser, request.OrganizationUserId, sutProvider);
|
||||
var requestWithUser = request with { User = user, EmailToken = token };
|
||||
var requestWithUser = request with { User = user, EmailToken = "valid-token" };
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(requestWithUser.OrganizationUserId)
|
||||
.Returns(orgUser);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateInviteToken(orgUser, user, Arg.Any<string>())
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateUserEmail(orgUser, user)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(requestWithUser.OrganizationId)
|
||||
.Returns(org);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateOrganizationMatch(orgUser, requestWithUser.OrganizationId)
|
||||
.Returns(new OrganizationMismatchError());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InitPendingOrganizationVNextAsync(requestWithUser);
|
||||
|
||||
@@ -370,6 +395,14 @@ public class InitPendingOrganizationCommandTests
|
||||
.GetByIdAsync(updatedRequest.OrganizationId)
|
||||
.Returns(org);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateOrganizationMatch(orgUser, updatedRequest.OrganizationId)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateOrganizationState(org)
|
||||
.Returns(new OrganizationAlreadyEnabledError());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InitPendingOrganizationVNextAsync(updatedRequest);
|
||||
|
||||
@@ -399,6 +432,14 @@ public class InitPendingOrganizationCommandTests
|
||||
.GetByIdAsync(updatedRequest.OrganizationId)
|
||||
.Returns(org);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateOrganizationMatch(orgUser, updatedRequest.OrganizationId)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateOrganizationState(org)
|
||||
.Returns(new OrganizationNotPendingError());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InitPendingOrganizationVNextAsync(updatedRequest);
|
||||
|
||||
@@ -428,6 +469,14 @@ public class InitPendingOrganizationCommandTests
|
||||
.GetByIdAsync(updatedRequest.OrganizationId)
|
||||
.Returns(org);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateOrganizationMatch(orgUser, updatedRequest.OrganizationId)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateOrganizationState(org)
|
||||
.Returns(new OrganizationHasKeysError());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InitPendingOrganizationVNextAsync(updatedRequest);
|
||||
|
||||
@@ -457,6 +506,14 @@ public class InitPendingOrganizationCommandTests
|
||||
.GetByIdAsync(updatedRequest.OrganizationId)
|
||||
.Returns(org);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateOrganizationMatch(orgUser, updatedRequest.OrganizationId)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateOrganizationState(org)
|
||||
.Returns(new OrganizationHasKeysError());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InitPendingOrganizationVNextAsync(updatedRequest);
|
||||
|
||||
@@ -476,9 +533,9 @@ public class InitPendingOrganizationCommandTests
|
||||
// Arrange
|
||||
var updatedRequest = SetupValidOrgAndOrgUser(user, org, orgUser, request, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidatePoliciesAsync(user, updatedRequest.OrganizationId)
|
||||
.Returns(new SingleOrgPolicyViolationError());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InitPendingOrganizationVNextAsync(updatedRequest);
|
||||
@@ -499,33 +556,9 @@ public class InitPendingOrganizationCommandTests
|
||||
// Arrange
|
||||
var updatedRequest = SetupValidOrgAndOrgUser(user, org, orgUser, request, sutProvider);
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(Arg.Any<string>())
|
||||
.Returns(false);
|
||||
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg)
|
||||
.Returns(false);
|
||||
|
||||
// Create a PolicyDetails that requires 2FA for this organization
|
||||
var policyDetails = new PolicyDetails
|
||||
{
|
||||
OrganizationId = updatedRequest.OrganizationId,
|
||||
OrganizationUserId = updatedRequest.OrganizationUserId,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
OrganizationUserType = OrganizationUserType.Owner,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Invited
|
||||
};
|
||||
|
||||
var twoFactorReq = new RequireTwoFactorPolicyRequirement(new[] { policyDetails });
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(twoFactorReq);
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(user)
|
||||
.Returns(false);
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidatePoliciesAsync(user, updatedRequest.OrganizationId)
|
||||
.Returns(new TwoFactorRequiredError());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InitPendingOrganizationVNextAsync(updatedRequest);
|
||||
@@ -549,24 +582,13 @@ public class InitPendingOrganizationCommandTests
|
||||
org.PlanType = PlanType.Free;
|
||||
orgUser.Type = OrganizationUserType.Owner;
|
||||
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(Arg.Any<string>())
|
||||
.Returns(false);
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidatePoliciesAsync(user, updatedRequest.OrganizationId)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg)
|
||||
.Returns(false);
|
||||
|
||||
// Create a RequireTwoFactorPolicyRequirement with no policies (2FA not required)
|
||||
var twoFactorReq = new RequireTwoFactorPolicyRequirement(Enumerable.Empty<PolicyDetails>());
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(twoFactorReq);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetCountByFreeOrganizationAdminUserAsync(user.Id)
|
||||
.Returns(1);
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateBusinessRulesAsync(user, org, orgUser)
|
||||
.Returns(new FreeOrgAdminLimitError());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InitPendingOrganizationVNextAsync(updatedRequest);
|
||||
@@ -585,25 +607,14 @@ public class InitPendingOrganizationCommandTests
|
||||
{
|
||||
var updatedRequest = SetupValidOrgAndOrgUser(user, org, orgUser, request, sutProvider);
|
||||
|
||||
// Setup policy checks to pass
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(Arg.Any<string>())
|
||||
.Returns(false);
|
||||
// Setup validator to pass all policy and business rule checks
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidatePoliciesAsync(user, updatedRequest.OrganizationId)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg)
|
||||
.Returns(false);
|
||||
|
||||
// Create a RequireTwoFactorPolicyRequirement with no policies (2FA not required)
|
||||
var twoFactorReq = new RequireTwoFactorPolicyRequirement(Enumerable.Empty<PolicyDetails>());
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(twoFactorReq);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetCountByFreeOrganizationAdminUserAsync(user.Id)
|
||||
.Returns(0);
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateBusinessRulesAsync(user, org, orgUser)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
// Setup repositories to return update delegates
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
@@ -649,6 +660,15 @@ public class InitPendingOrganizationCommandTests
|
||||
.GetByIdAsync(updatedRequest.OrganizationId)
|
||||
.Returns(org);
|
||||
|
||||
// Setup validator for organization match and state
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateOrganizationMatch(orgUser, updatedRequest.OrganizationId)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateOrganizationState(org)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
return updatedRequest;
|
||||
}
|
||||
|
||||
@@ -660,13 +680,20 @@ public class InitPendingOrganizationCommandTests
|
||||
{
|
||||
orgUser.Email = user.Email;
|
||||
|
||||
var token = CreateToken(orgUser, request.OrganizationUserId, sutProvider);
|
||||
var updatedRequest = request with { User = user, EmailToken = token };
|
||||
var updatedRequest = request with { User = user, EmailToken = "valid-token" };
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(updatedRequest.OrganizationUserId)
|
||||
.Returns(orgUser);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateInviteToken(orgUser, user, Arg.Any<string>())
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IInitPendingOrganizationValidator>()
|
||||
.ValidateUserEmail(orgUser, user)
|
||||
.Returns((Bit.Core.AdminConsole.Utilities.v2.Error)null);
|
||||
|
||||
return updatedRequest;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tokens;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Bit.Test.Common.Fakes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Organizations;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class InitPendingOrganizationValidatorTests
|
||||
{
|
||||
private readonly IOrgUserInviteTokenableFactory _orgUserInviteTokenableFactory = Substitute.For<IOrgUserInviteTokenableFactory>();
|
||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory = new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>();
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateInviteToken_ValidToken_ReturnsTrue(
|
||||
User user,
|
||||
OrganizationUser orgUser,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
orgUser.Email = user.Email;
|
||||
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
||||
sutProvider.Create();
|
||||
|
||||
_orgUserInviteTokenableFactory.CreateToken(orgUser).Returns(new OrgUserInviteTokenable(orgUser)
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
||||
});
|
||||
|
||||
var orgUserInviteTokenable = _orgUserInviteTokenableFactory.CreateToken(orgUser);
|
||||
var protectedToken = _orgUserInviteTokenDataFactory.Protect(orgUserInviteTokenable);
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.ValidateInviteToken(orgUser, user, protectedToken);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateInviteToken_InvalidToken_ReturnsFalse(
|
||||
User user,
|
||||
OrganizationUser orgUser,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
||||
sutProvider.Create();
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.ValidateInviteToken(orgUser, user, "invalid-token");
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateUserEmail_MatchingEmail_ReturnsNull(
|
||||
User user,
|
||||
OrganizationUser orgUser,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
orgUser.Email = user.Email;
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.ValidateUserEmail(orgUser, user);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateUserEmail_MismatchedEmail_ReturnsError(
|
||||
User user,
|
||||
OrganizationUser orgUser,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
orgUser.Email = "different@example.com";
|
||||
user.Email = "user@example.com";
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.ValidateUserEmail(orgUser, user);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<EmailMismatchError>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateUserEmail_NullOrgUserEmail_ReturnsError(
|
||||
User user,
|
||||
OrganizationUser orgUser,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
orgUser.Email = null;
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.ValidateUserEmail(orgUser, user);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<EmailMismatchError>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateOrganizationState_ValidState_ReturnsNull(
|
||||
Organization org,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.Enabled = false;
|
||||
org.Status = OrganizationStatusType.Pending;
|
||||
org.PublicKey = null;
|
||||
org.PrivateKey = null;
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.ValidateOrganizationState(org);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateOrganizationState_OrgEnabled_ReturnsError(
|
||||
Organization org,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.Enabled = true;
|
||||
org.Status = OrganizationStatusType.Pending;
|
||||
org.PublicKey = null;
|
||||
org.PrivateKey = null;
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.ValidateOrganizationState(org);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<OrganizationAlreadyEnabledError>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateOrganizationState_OrgNotPending_ReturnsError(
|
||||
Organization org,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.Enabled = false;
|
||||
org.Status = OrganizationStatusType.Created;
|
||||
org.PublicKey = null;
|
||||
org.PrivateKey = null;
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.ValidateOrganizationState(org);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<OrganizationNotPendingError>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateOrganizationState_OrgHasKeys_ReturnsError(
|
||||
Organization org,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.Enabled = false;
|
||||
org.Status = OrganizationStatusType.Pending;
|
||||
org.PublicKey = "existing-public-key";
|
||||
org.PrivateKey = "existing-private-key";
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.ValidateOrganizationState(org);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<OrganizationHasKeysError>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateOrganizationMatch_Matching_ReturnsNull(
|
||||
OrganizationUser orgUser,
|
||||
Guid organizationId,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
orgUser.OrganizationId = organizationId;
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.ValidateOrganizationMatch(orgUser, organizationId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateOrganizationMatch_NotMatching_ReturnsError(
|
||||
OrganizationUser orgUser,
|
||||
Guid organizationId,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
orgUser.OrganizationId = Guid.NewGuid();
|
||||
|
||||
// Act
|
||||
var result = sutProvider.Sut.ValidateOrganizationMatch(orgUser, organizationId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<OrganizationMismatchError>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ValidatePoliciesAsync_AllPoliciesPass_ReturnsNull(
|
||||
User user,
|
||||
Guid organizationId,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(Arg.Any<string>())
|
||||
.Returns(false);
|
||||
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg)
|
||||
.Returns(false);
|
||||
|
||||
var twoFactorReq = new RequireTwoFactorPolicyRequirement(Enumerable.Empty<PolicyDetails>());
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(twoFactorReq);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidatePoliciesAsync(user, organizationId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ValidatePoliciesAsync_SingleOrgPolicyViolation_ReturnsError(
|
||||
User user,
|
||||
Guid organizationId,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(Arg.Any<string>())
|
||||
.Returns(false);
|
||||
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidatePoliciesAsync(user, organizationId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<SingleOrgPolicyViolationError>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ValidatePoliciesAsync_TwoFactorRequired_UserDoesNotHave2FA_ReturnsError(
|
||||
User user,
|
||||
Guid organizationId,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(Arg.Any<string>())
|
||||
.Returns(false);
|
||||
|
||||
sutProvider.GetDependency<IPolicyService>()
|
||||
.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg)
|
||||
.Returns(false);
|
||||
|
||||
var policyDetails = new PolicyDetails
|
||||
{
|
||||
OrganizationId = organizationId,
|
||||
PolicyType = PolicyType.TwoFactorAuthentication,
|
||||
OrganizationUserType = OrganizationUserType.Owner,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Invited
|
||||
};
|
||||
|
||||
var twoFactorReq = new RequireTwoFactorPolicyRequirement(new[] { policyDetails });
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id)
|
||||
.Returns(twoFactorReq);
|
||||
|
||||
sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
|
||||
.TwoFactorIsEnabledAsync(user)
|
||||
.Returns(false);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidatePoliciesAsync(user, organizationId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<TwoFactorRequiredError>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ValidateBusinessRulesAsync_PaidOrg_ReturnsNull(
|
||||
User user,
|
||||
Organization org,
|
||||
OrganizationUser orgUser,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.PlanType = PlanType.EnterpriseAnnually;
|
||||
orgUser.Type = OrganizationUserType.Owner;
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateBusinessRulesAsync(user, org, orgUser);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ValidateBusinessRulesAsync_FreeOrgNonAdmin_ReturnsNull(
|
||||
User user,
|
||||
Organization org,
|
||||
OrganizationUser orgUser,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.PlanType = PlanType.Free;
|
||||
orgUser.Type = OrganizationUserType.User;
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateBusinessRulesAsync(user, org, orgUser);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ValidateBusinessRulesAsync_FreeOrgAdminNoExisting_ReturnsNull(
|
||||
User user,
|
||||
Organization org,
|
||||
OrganizationUser orgUser,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.PlanType = PlanType.Free;
|
||||
orgUser.Type = OrganizationUserType.Owner;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetCountByFreeOrganizationAdminUserAsync(user.Id)
|
||||
.Returns(0);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateBusinessRulesAsync(user, org, orgUser);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ValidateBusinessRulesAsync_FreeOrgAdminLimitExceeded_ReturnsError(
|
||||
User user,
|
||||
Organization org,
|
||||
OrganizationUser orgUser,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
org.PlanType = PlanType.Free;
|
||||
orgUser.Type = OrganizationUserType.Owner;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetCountByFreeOrganizationAdminUserAsync(user.Id)
|
||||
.Returns(1);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidateBusinessRulesAsync(user, org, orgUser);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<FreeOrgAdminLimitError>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ValidatePoliciesAsync_AutoConfirmPolicyViolation_ReturnsError(
|
||||
User user,
|
||||
Guid organizationId,
|
||||
SutProvider<InitPendingOrganizationValidator> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IFeatureService>()
|
||||
.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)
|
||||
.Returns(true);
|
||||
|
||||
var policyDetails = new PolicyDetails
|
||||
{
|
||||
OrganizationId = organizationId,
|
||||
PolicyType = PolicyType.AutomaticUserConfirmation,
|
||||
OrganizationUserType = OrganizationUserType.Owner,
|
||||
OrganizationUserStatus = OrganizationUserStatusType.Invited
|
||||
};
|
||||
|
||||
var autoConfirmReq = new AutomaticUserConfirmationPolicyRequirement(new[] { policyDetails });
|
||||
|
||||
sutProvider.GetDependency<IPolicyRequirementQuery>()
|
||||
.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id)
|
||||
.Returns(autoConfirmReq);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.ValidatePoliciesAsync(user, organizationId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<SingleOrgPolicyViolationError>(result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user