mirror of
https://github.com/bitwarden/server.git
synced 2025-04-05 05:00:19 -05:00
Code Review changes.
This commit is contained in:
parent
1384d9c58e
commit
46d36b1ef8
@ -18,6 +18,5 @@ public interface IInviteOrganizationUsersCommand
|
||||
/// Contains the details for inviting a single organization user via email.
|
||||
/// </param>
|
||||
/// <returns>Response from InviteScimOrganiation<see cref="ScimInviteOrganizationUsersResponse"/></returns>
|
||||
Task<CommandResult<ScimInviteOrganizationUsersResponse>> InviteScimOrganizationUserAsync(
|
||||
InviteOrganizationUsersRequest request);
|
||||
Task<CommandResult<ScimInviteOrganizationUsersResponse>> InviteScimOrganizationUserAsync(InviteOrganizationUsersRequest request);
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
|
||||
|
||||
await AdjustPasswordManagerSeatsAsync(validatedRequest, organization);
|
||||
|
||||
await AdjustSecretsManagerSeatsAsync(validatedRequest, organization);
|
||||
await AdjustSecretsManagerSeatsAsync(validatedRequest);
|
||||
|
||||
await SendAdditionalEmailsAsync(validatedRequest, organization);
|
||||
|
||||
@ -126,7 +126,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
|
||||
|
||||
await organizationUserRepository.DeleteManyAsync(organizationUserToInviteEntities.Select(x => x.OrganizationUser.Id));
|
||||
|
||||
await RevertSecretsManagerChangesAsync(validatedRequest, organization);
|
||||
await RevertSecretsManagerChangesAsync(validatedRequest, organization, validatedRequest.Value.InviteOrganization.SmSeats);
|
||||
|
||||
await RevertPasswordManagerChangesAsync(validatedRequest, organization);
|
||||
|
||||
@ -164,16 +164,19 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RevertSecretsManagerChangesAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization)
|
||||
private async Task RevertSecretsManagerChangesAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization, int? initialSmSeats)
|
||||
{
|
||||
if (validatedResult.Value.SecretsManagerSubscriptionUpdate.SeatsRequiredToAdd < 0)
|
||||
if (validatedResult.Value.InviteOrganization.UseSecretsManager && validatedResult.Value.SecretsManagerSubscriptionUpdate.SmSeatsChanged)
|
||||
{
|
||||
var updateRevert = new SecretsManagerSubscriptionUpdate(organization, validatedResult.Value.InviteOrganization.Plan, false)
|
||||
var smSubscriptionUpdateRevert = new SecretsManagerSubscriptionUpdate(
|
||||
organization: organization,
|
||||
plan: validatedResult.Value.InviteOrganization.Plan,
|
||||
autoscaling: false)
|
||||
{
|
||||
SmSeats = validatedResult.Value.SecretsManagerSubscriptionUpdate.Seats
|
||||
SmSeats = initialSmSeats
|
||||
};
|
||||
|
||||
await updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(updateRevert);
|
||||
await updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(smSubscriptionUpdateRevert);
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,17 +237,14 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
|
||||
.Select(u => u.Email).Distinct();
|
||||
}
|
||||
|
||||
private async Task AdjustSecretsManagerSeatsAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization)
|
||||
private async Task AdjustSecretsManagerSeatsAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult)
|
||||
{
|
||||
if (validatedResult.Value.SecretsManagerSubscriptionUpdate.SeatsRequiredToAdd <= 0)
|
||||
if (validatedResult.Value.SecretsManagerSubscriptionUpdate?.SmSeatsChanged is not true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var subscriptionUpdate = new SecretsManagerSubscriptionUpdate(organization, validatedResult.Value.InviteOrganization.Plan, true)
|
||||
.AdjustSeats(validatedResult.Value.SecretsManagerSubscriptionUpdate.SeatsRequiredToAdd);
|
||||
|
||||
await updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(subscriptionUpdate);
|
||||
await updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(validatedResult.Value.SecretsManagerSubscriptionUpdate);
|
||||
}
|
||||
|
||||
private async Task AdjustPasswordManagerSeatsAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization)
|
||||
|
@ -1,6 +1,6 @@
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.SecretsManager;
|
||||
using Bit.Core.Models.Business;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
using Bit.Core.AdminConsole.Errors;
|
||||
using Bit.Core.Settings;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.GlobalSettings;
|
||||
|
||||
public record CannotAutoScaleOnSelfHostError(IGlobalSettings InvalidSettings) : Error<IGlobalSettings>(Code, InvalidSettings)
|
||||
public record CannotAutoScaleOnSelfHostError(EnvironmentRequest Invalid) : Error<EnvironmentRequest>(Code, Invalid)
|
||||
{
|
||||
public const string Code = "Cannot auto scale self-host.";
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
#nullable enable
|
||||
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||
using Bit.Core.Settings;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.GlobalSettings;
|
||||
|
||||
public class EnvironmentRequest
|
||||
{
|
||||
public bool IsSelfHosted { get; init; }
|
||||
public PasswordManagerSubscriptionUpdate PasswordManagerSubscriptionUpdate { get; init; }
|
||||
|
||||
public EnvironmentRequest(IGlobalSettings globalSettings, PasswordManagerSubscriptionUpdate passwordManagerSubscriptionUpdate)
|
||||
{
|
||||
IsSelfHosted = globalSettings.SelfHosted;
|
||||
PasswordManagerSubscriptionUpdate = passwordManagerSubscriptionUpdate;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using Bit.Core.AdminConsole.Shared.Validation;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.GlobalSettings;
|
||||
|
||||
public interface IEnvironmentValidator : IValidator<EnvironmentRequest>;
|
||||
|
||||
public class EnvironmentValidator : IEnvironmentValidator
|
||||
{
|
||||
public async Task<ValidationResult<EnvironmentRequest>> ValidateAsync(EnvironmentRequest value) =>
|
||||
value.IsSelfHosted && value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd > 0 ?
|
||||
new Invalid<EnvironmentRequest>(new CannotAutoScaleOnSelfHostError(value)) :
|
||||
new Valid<EnvironmentRequest>(value);
|
||||
}
|
@ -1,89 +1,64 @@
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.Errors;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.GlobalSettings;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Provider;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.SecretsManager;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.AdminConsole.Shared.Validation;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using SecretsManagerSubscriptionUpdate = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.SecretsManager.SecretsManagerSubscriptionUpdate;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
||||
|
||||
public interface IInviteUsersValidator : IValidator<InviteUserOrganizationValidationRequest>;
|
||||
|
||||
public class InviteUsersValidator(
|
||||
IGlobalSettings globalSettings,
|
||||
IProviderRepository providerRepository,
|
||||
IPaymentService paymentService,
|
||||
IOrganizationRepository organizationRepository) : IInviteUsersValidator
|
||||
IOrganizationRepository organizationRepository,
|
||||
IPasswordManagerInviteUserValidator passwordManagerInviteUserValidator,
|
||||
IUpdateSecretsManagerSubscriptionCommand secretsManagerSubscriptionCommand) : IInviteUsersValidator
|
||||
{
|
||||
public async Task<ValidationResult<InviteUserOrganizationValidationRequest>> ValidateAsync(InviteUserOrganizationValidationRequest request)
|
||||
{
|
||||
var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(request);
|
||||
var passwordManagerValidationResult = PasswordManagerInviteUserValidator.Validate(subscriptionUpdate);
|
||||
|
||||
var passwordManagerValidationResult = await passwordManagerInviteUserValidator.ValidateAsync(subscriptionUpdate);
|
||||
|
||||
if (passwordManagerValidationResult is Invalid<PasswordManagerSubscriptionUpdate> invalidSubscriptionUpdate)
|
||||
{
|
||||
return invalidSubscriptionUpdate.Map(request);
|
||||
}
|
||||
|
||||
if (ValidateEnvironment(globalSettings, subscriptionUpdate) is Invalid<IGlobalSettings> invalidEnvironment)
|
||||
if (request.InviteOrganization.UseSecretsManager && request.Invites.Any(x => x.AccessSecretsManager))
|
||||
{
|
||||
return invalidEnvironment.Map(request);
|
||||
return await ValidateSecretsManagerSubscriptionUpdateAsync(request, subscriptionUpdate);
|
||||
}
|
||||
|
||||
var organizationValidationResult = InviteUserOrganizationValidator.Validate(request.InviteOrganization, subscriptionUpdate);
|
||||
|
||||
if (organizationValidationResult is Invalid<InviteOrganization> organizationValidation)
|
||||
{
|
||||
return organizationValidation.Map(request);
|
||||
return new Valid<InviteUserOrganizationValidationRequest>(new InviteUserOrganizationValidationRequest(
|
||||
request,
|
||||
subscriptionUpdate,
|
||||
null));
|
||||
}
|
||||
|
||||
var smSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(request, subscriptionUpdate);
|
||||
|
||||
var secretsManagerValidationResult = SecretsManagerInviteUserValidation.Validate(smSubscriptionUpdate);
|
||||
|
||||
if (secretsManagerValidationResult is Invalid<SecretsManagerSubscriptionUpdate> invalidSmSubscriptionUpdate)
|
||||
private async Task<ValidationResult<InviteUserOrganizationValidationRequest>> ValidateSecretsManagerSubscriptionUpdateAsync(
|
||||
InviteUserOrganizationValidationRequest request,
|
||||
PasswordManagerSubscriptionUpdate subscriptionUpdate)
|
||||
{
|
||||
return invalidSmSubscriptionUpdate.Map(request);
|
||||
}
|
||||
|
||||
var provider = await providerRepository.GetByOrganizationIdAsync(request.InviteOrganization.OrganizationId);
|
||||
if (provider is not null)
|
||||
try
|
||||
{
|
||||
var providerValidationResult = InvitingUserOrganizationProviderValidator.Validate(new InviteOrganizationProvider(provider));
|
||||
var smSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(
|
||||
organization: await organizationRepository.GetByIdAsync(request.InviteOrganization.OrganizationId),
|
||||
plan: request.InviteOrganization.Plan,
|
||||
autoscaling: true)
|
||||
.AdjustSeats(request.Invites.Count(x => x.AccessSecretsManager));
|
||||
|
||||
if (providerValidationResult is Invalid<InviteOrganizationProvider> invalidProviderValidation)
|
||||
{
|
||||
return invalidProviderValidation.Map(request);
|
||||
}
|
||||
}
|
||||
|
||||
var paymentSubscription = await paymentService.GetSubscriptionAsync(
|
||||
await organizationRepository.GetByIdAsync(request.InviteOrganization.OrganizationId));
|
||||
|
||||
var paymentValidationResult = InviteUserPaymentValidation.Validate(
|
||||
new PaymentsSubscription(paymentSubscription, request.InviteOrganization));
|
||||
|
||||
if (paymentValidationResult is Invalid<PaymentsSubscription> invalidPaymentValidation)
|
||||
{
|
||||
return invalidPaymentValidation.Map(request);
|
||||
}
|
||||
await secretsManagerSubscriptionCommand.ValidateUpdateAsync(smSubscriptionUpdate);
|
||||
|
||||
return new Valid<InviteUserOrganizationValidationRequest>(new InviteUserOrganizationValidationRequest(
|
||||
request,
|
||||
subscriptionUpdate,
|
||||
smSubscriptionUpdate));
|
||||
}
|
||||
|
||||
public static ValidationResult<IGlobalSettings> ValidateEnvironment(IGlobalSettings globalSettings, PasswordManagerSubscriptionUpdate subscriptionUpdate) =>
|
||||
globalSettings.SelfHosted && subscriptionUpdate.SeatsRequiredToAdd > 0
|
||||
? new Invalid<IGlobalSettings>(new CannotAutoScaleOnSelfHostError(globalSettings))
|
||||
: new Valid<IGlobalSettings>(globalSettings);
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new Invalid<InviteUserOrganizationValidationRequest>(new Error<InviteUserOrganizationValidationRequest>(ex.Message, request));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||
using Bit.Core.AdminConsole.Shared.Validation;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
||||
|
||||
public static class InviteUserOrganizationValidator
|
||||
public interface IInviteUserOrganizationValidator : IValidator<InviteOrganization>;
|
||||
|
||||
public class InviteUserOrganizationValidator : IInviteUserOrganizationValidator
|
||||
{
|
||||
public static ValidationResult<InviteOrganization> Validate(InviteOrganization inviteOrganization,
|
||||
PasswordManagerSubscriptionUpdate subscriptionUpdate)
|
||||
public async Task<ValidationResult<InviteOrganization>> ValidateAsync(InviteOrganization inviteOrganization)
|
||||
{
|
||||
if (inviteOrganization.Seats is null || subscriptionUpdate.SeatsRequiredToAdd is 0)
|
||||
if (inviteOrganization.Seats is null)
|
||||
{
|
||||
return new Valid<InviteOrganization>(inviteOrganization);
|
||||
}
|
||||
|
@ -1,15 +1,33 @@
|
||||
using Bit.Core.AdminConsole.Shared.Validation;
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.GlobalSettings;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Provider;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.AdminConsole.Shared.Validation;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||
|
||||
public static class PasswordManagerInviteUserValidator
|
||||
public interface IPasswordManagerInviteUserValidator : IValidator<PasswordManagerSubscriptionUpdate>;
|
||||
|
||||
public class PasswordManagerInviteUserValidator(
|
||||
IGlobalSettings globalSettings,
|
||||
IEnvironmentValidator environmentValidator,
|
||||
IInviteUserOrganizationValidator inviteUserOrganizationValidator,
|
||||
IProviderRepository providerRepository,
|
||||
IPaymentService paymentService,
|
||||
IOrganizationRepository organizationRepository
|
||||
) : IPasswordManagerInviteUserValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// This is for validating if the organization can add additional users.
|
||||
/// </summary>
|
||||
/// <param name="subscriptionUpdate"></param>
|
||||
/// <returns></returns>
|
||||
public static ValidationResult<PasswordManagerSubscriptionUpdate> Validate(PasswordManagerSubscriptionUpdate subscriptionUpdate)
|
||||
public static ValidationResult<PasswordManagerSubscriptionUpdate> ValidatePasswordManager(PasswordManagerSubscriptionUpdate subscriptionUpdate)
|
||||
{
|
||||
if (subscriptionUpdate.Seats is null)
|
||||
{
|
||||
@ -43,4 +61,52 @@ public static class PasswordManagerInviteUserValidator
|
||||
|
||||
return new Valid<PasswordManagerSubscriptionUpdate>(subscriptionUpdate);
|
||||
}
|
||||
|
||||
public async Task<ValidationResult<PasswordManagerSubscriptionUpdate>> ValidateAsync(PasswordManagerSubscriptionUpdate request)
|
||||
{
|
||||
switch (ValidatePasswordManager(request))
|
||||
{
|
||||
case Valid<PasswordManagerSubscriptionUpdate> valid
|
||||
when valid.Value.SeatsRequiredToAdd is 0:
|
||||
return new Valid<PasswordManagerSubscriptionUpdate>(request);
|
||||
case Invalid<PasswordManagerSubscriptionUpdate> invalid:
|
||||
return invalid;
|
||||
}
|
||||
|
||||
if (await environmentValidator.ValidateAsync(new EnvironmentRequest(globalSettings, request)) is Invalid<EnvironmentRequest> invalidEnvironment)
|
||||
{
|
||||
return invalidEnvironment.Map(request);
|
||||
}
|
||||
|
||||
var organizationValidationResult = await inviteUserOrganizationValidator.ValidateAsync(request.InviteOrganization);
|
||||
|
||||
if (organizationValidationResult is Invalid<InviteOrganization> organizationValidation)
|
||||
{
|
||||
return organizationValidation.Map(request);
|
||||
}
|
||||
|
||||
var provider = await providerRepository.GetByOrganizationIdAsync(request.InviteOrganization.OrganizationId);
|
||||
if (provider is not null)
|
||||
{
|
||||
var providerValidationResult = InvitingUserOrganizationProviderValidator.Validate(new InviteOrganizationProvider(provider));
|
||||
|
||||
if (providerValidationResult is Invalid<InviteOrganizationProvider> invalidProviderValidation)
|
||||
{
|
||||
return invalidProviderValidation.Map(request);
|
||||
}
|
||||
}
|
||||
|
||||
var paymentSubscription = await paymentService.GetSubscriptionAsync(
|
||||
await organizationRepository.GetByIdAsync(request.InviteOrganization.OrganizationId));
|
||||
|
||||
var paymentValidationResult = InviteUserPaymentValidation.Validate(
|
||||
new PaymentsSubscription(paymentSubscription, request.InviteOrganization));
|
||||
|
||||
if (paymentValidationResult is Invalid<PaymentsSubscription> invalidPaymentValidation)
|
||||
{
|
||||
return invalidPaymentValidation.Map(request);
|
||||
}
|
||||
|
||||
return new Valid<PasswordManagerSubscriptionUpdate>(request);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ public class PasswordManagerSubscriptionUpdate
|
||||
public int? AvailableSeats => Seats - OccupiedSeats;
|
||||
|
||||
/// <summary>
|
||||
/// Number of seats to scale the organization to.
|
||||
/// Number of seats to scale the organization by.
|
||||
///
|
||||
/// If Organization has no seat limit (Seats is null), then there are no new seats to add.
|
||||
/// </summary>
|
||||
@ -50,17 +50,21 @@ public class PasswordManagerSubscriptionUpdate
|
||||
|
||||
public Plan.PasswordManagerPlanFeatures PasswordManagerPlan { get; }
|
||||
|
||||
public InviteOrganization InviteOrganization { get; }
|
||||
|
||||
private PasswordManagerSubscriptionUpdate(int? organizationSeats,
|
||||
int? organizationAutoScaleSeatLimit,
|
||||
int currentSeats,
|
||||
int newUsersToAdd,
|
||||
Plan.PasswordManagerPlanFeatures plan)
|
||||
Plan.PasswordManagerPlanFeatures plan,
|
||||
InviteOrganization inviteOrganization)
|
||||
{
|
||||
Seats = organizationSeats;
|
||||
MaxAutoScaleSeats = organizationAutoScaleSeatLimit;
|
||||
OccupiedSeats = currentSeats;
|
||||
NewUsersToAdd = newUsersToAdd;
|
||||
PasswordManagerPlan = plan;
|
||||
InviteOrganization = inviteOrganization;
|
||||
}
|
||||
|
||||
public PasswordManagerSubscriptionUpdate(InviteOrganization inviteOrganization, int occupiedSeats, int newUsersToAdd) :
|
||||
@ -69,7 +73,8 @@ public class PasswordManagerSubscriptionUpdate
|
||||
organizationAutoScaleSeatLimit: inviteOrganization.MaxAutoScaleSeats,
|
||||
currentSeats: occupiedSeats,
|
||||
newUsersToAdd: newUsersToAdd,
|
||||
plan: inviteOrganization.Plan.PasswordManager)
|
||||
plan: inviteOrganization.Plan.PasswordManager,
|
||||
inviteOrganization: inviteOrganization)
|
||||
{ }
|
||||
|
||||
public PasswordManagerSubscriptionUpdate(InviteUserOrganizationValidationRequest validationRequest) :
|
||||
@ -78,6 +83,7 @@ public class PasswordManagerSubscriptionUpdate
|
||||
organizationAutoScaleSeatLimit: validationRequest.InviteOrganization.MaxAutoScaleSeats,
|
||||
currentSeats: validationRequest.OccupiedPmSeats,
|
||||
newUsersToAdd: validationRequest.Invites.Length,
|
||||
plan: validationRequest.InviteOrganization.Plan.PasswordManager)
|
||||
plan: validationRequest.InviteOrganization.Plan.PasswordManager,
|
||||
inviteOrganization: validationRequest.InviteOrganization)
|
||||
{ }
|
||||
}
|
||||
|
@ -15,6 +15,9 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.GlobalSettings;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||
using Bit.Core.Models.Business.Tokenables;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationCollections;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationCollections.Interfaces;
|
||||
@ -175,8 +178,12 @@ public static class OrganizationServiceCollectionExtensions
|
||||
services.AddScoped<IHasConfirmedOwnersExceptQuery, HasConfirmedOwnersExceptQuery>();
|
||||
|
||||
services.AddScoped<IInviteOrganizationUsersCommand, InviteOrganizationUsersCommand>();
|
||||
services.AddScoped<IInviteUsersValidator, InviteUsersValidator>();
|
||||
services.AddScoped<ISendOrganizationInvitesCommand, SendOrganizationInvitesCommand>();
|
||||
|
||||
services.AddScoped<IInviteUsersValidator, InviteUsersValidator>();
|
||||
services.AddScoped<IInviteUserOrganizationValidator, InviteUserOrganizationValidator>();
|
||||
services.AddScoped<IPasswordManagerInviteUserValidator, PasswordManagerInviteUserValidator>();
|
||||
services.AddScoped<IEnvironmentValidator, EnvironmentValidator>();
|
||||
}
|
||||
|
||||
// TODO: move to OrganizationSubscriptionServiceCollectionExtensions when OrganizationUser methods are moved out of
|
||||
|
@ -5,4 +5,5 @@ namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
||||
public interface IUpdateSecretsManagerSubscriptionCommand
|
||||
{
|
||||
Task UpdateSubscriptionAsync(SecretsManagerSubscriptionUpdate update);
|
||||
Task ValidateUpdateAsync(SecretsManagerSubscriptionUpdate update);
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubs
|
||||
|
||||
}
|
||||
|
||||
private async Task ValidateUpdateAsync(SecretsManagerSubscriptionUpdate update)
|
||||
public async Task ValidateUpdateAsync(SecretsManagerSubscriptionUpdate update)
|
||||
{
|
||||
if (_globalSettings.SelfHosted)
|
||||
{
|
||||
|
@ -1,14 +1,15 @@
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.SecretsManager;
|
||||
using Bit.Core.Models.Business;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Helpers;
|
||||
|
||||
public static class InviteUserOrganizationValidationRequestHelpers
|
||||
{
|
||||
public static InviteUserOrganizationValidationRequest GetInviteValidationRequestMock(InviteOrganizationUsersRequest request,
|
||||
InviteOrganization inviteOrganization) =>
|
||||
InviteOrganization inviteOrganization, Organization organization) =>
|
||||
new()
|
||||
{
|
||||
Invites = request.Invites,
|
||||
@ -18,7 +19,8 @@ public static class InviteUserOrganizationValidationRequestHelpers
|
||||
OccupiedPmSeats = 0,
|
||||
OccupiedSmSeats = 0,
|
||||
PasswordManagerSubscriptionUpdate = new PasswordManagerSubscriptionUpdate(inviteOrganization, 0, 0),
|
||||
SecretsManagerSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(inviteOrganization, 0, 0, 0)
|
||||
SecretsManagerSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(organization, inviteOrganization.Plan, true)
|
||||
.AdjustSeats(request.Invites.Count(x => x.AccessSecretsManager))
|
||||
};
|
||||
|
||||
public static InviteUserOrganizationValidationRequest WithPasswordManagerUpdate(this InviteUserOrganizationValidationRequest request, PasswordManagerSubscriptionUpdate passwordManagerSubscriptionUpdate) =>
|
||||
|
@ -6,11 +6,11 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.SecretsManager;
|
||||
using Bit.Core.AdminConsole.Shared.Validation;
|
||||
using Bit.Core.Billing.Models.StaticStore.Plans;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Models.Commands;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
@ -24,6 +24,7 @@ using Microsoft.Extensions.Time.Testing;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using static Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Helpers.InviteUserOrganizationValidationRequestHelpers;
|
||||
using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
|
||||
@ -66,7 +67,7 @@ public class InviteOrganizationUserCommandTests
|
||||
|
||||
sutProvider.GetDependency<IInviteUsersValidator>()
|
||||
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization)));
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
|
||||
@ -128,7 +129,7 @@ public class InviteOrganizationUserCommandTests
|
||||
|
||||
sutProvider.GetDependency<IInviteUsersValidator>()
|
||||
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization)));
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)));
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
|
||||
@ -259,7 +260,7 @@ public class InviteOrganizationUserCommandTests
|
||||
|
||||
sutProvider.GetDependency<IInviteUsersValidator>()
|
||||
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization)
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)
|
||||
.WithPasswordManagerUpdate(new PasswordManagerSubscriptionUpdate(inviteOrganization, organization.Seats.Value, 1))));
|
||||
|
||||
// Act
|
||||
@ -329,7 +330,7 @@ public class InviteOrganizationUserCommandTests
|
||||
|
||||
sutProvider.GetDependency<IInviteUsersValidator>()
|
||||
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization)
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)
|
||||
.WithPasswordManagerUpdate(passwordManagerUpdate)));
|
||||
|
||||
// Act
|
||||
@ -364,6 +365,7 @@ public class InviteOrganizationUserCommandTests
|
||||
organization.Seats = 1;
|
||||
organization.SmSeats = 1;
|
||||
organization.MaxAutoscaleSeats = 2;
|
||||
organization.MaxAutoscaleSmSeats = 2;
|
||||
ownerDetails.Type = OrganizationUserType.Owner;
|
||||
|
||||
var inviteOrganization = new InviteOrganization(organization, new FreePlan());
|
||||
@ -383,11 +385,8 @@ public class InviteOrganizationUserCommandTests
|
||||
performedBy: Guid.Empty,
|
||||
timeProvider.GetUtcNow());
|
||||
|
||||
var secretsManagerSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(
|
||||
inviteOrganization,
|
||||
organization.SmSeats.Value,
|
||||
1,
|
||||
organization.Seats.Value);
|
||||
var secretsManagerSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(organization, inviteOrganization.Plan, true)
|
||||
.AdjustSeats(request.Invites.Count(x => x.AccessSecretsManager));
|
||||
|
||||
var orgUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
|
||||
@ -397,6 +396,8 @@ public class InviteOrganizationUserCommandTests
|
||||
orgUserRepository
|
||||
.GetManyByMinimumRoleAsync(inviteOrganization.OrganizationId, OrganizationUserType.Owner)
|
||||
.Returns([ownerDetails]);
|
||||
orgUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id).Returns(1);
|
||||
orgUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id).Returns(1);
|
||||
|
||||
var orgRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
|
||||
@ -405,7 +406,7 @@ public class InviteOrganizationUserCommandTests
|
||||
|
||||
sutProvider.GetDependency<IInviteUsersValidator>()
|
||||
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization)
|
||||
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)
|
||||
.WithSecretsManagerUpdate(secretsManagerSubscriptionUpdate)));
|
||||
|
||||
// Act
|
||||
@ -416,7 +417,6 @@ public class InviteOrganizationUserCommandTests
|
||||
|
||||
await sutProvider.GetDependency<IUpdateSecretsManagerSubscriptionCommand>()
|
||||
.Received(1)
|
||||
.UpdateSubscriptionAsync(Arg.Is<Core.Models.Business.SecretsManagerSubscriptionUpdate>(update =>
|
||||
update.SmSeats == secretsManagerSubscriptionUpdate.UpdatedSeatTotal));
|
||||
.UpdateSubscriptionAsync(secretsManagerSubscriptionUpdate);
|
||||
}
|
||||
}
|
||||
|
@ -1,40 +1,39 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||
using Bit.Core.AdminConsole.Shared.Validation;
|
||||
using Bit.Core.Billing.Models.StaticStore.Plans;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class InviteUserOrganizationValidationTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void Validate_WhenOrganizationIsFreeTier_ShouldReturnValidResponse(Organization organization)
|
||||
public async Task Validate_WhenOrganizationIsFreeTier_ShouldReturnValidResponse(Organization organization, SutProvider<InviteUserOrganizationValidator> sutProvider)
|
||||
{
|
||||
var inviteOrganization = new InviteOrganization(organization, new FreePlan());
|
||||
var validSubscriptionUpdate = new PasswordManagerSubscriptionUpdate(inviteOrganization, 0, 0);
|
||||
|
||||
var result = InviteUserOrganizationValidator.Validate(inviteOrganization, validSubscriptionUpdate);
|
||||
var result = await sutProvider.Sut.ValidateAsync(inviteOrganization);
|
||||
|
||||
Assert.IsType<Valid<InviteOrganization>>(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void Validate_WhenOrganizationDoesNotHavePaymentMethod_ShouldReturnInvalidResponseWithPaymentMethodMessage(
|
||||
Organization organization)
|
||||
public async Task Validate_WhenOrganizationDoesNotHavePaymentMethod_ShouldReturnInvalidResponseWithPaymentMethodMessage(
|
||||
Organization organization, SutProvider<InviteUserOrganizationValidator> sutProvider)
|
||||
{
|
||||
organization.GatewayCustomerId = string.Empty;
|
||||
organization.Seats = 3;
|
||||
|
||||
var inviteOrganization = new InviteOrganization(organization, new FreePlan());
|
||||
var validSubscriptionUpdate = new PasswordManagerSubscriptionUpdate(inviteOrganization, 3, 1);
|
||||
|
||||
var result = InviteUserOrganizationValidator.Validate(inviteOrganization, validSubscriptionUpdate);
|
||||
var result = await sutProvider.Sut.ValidateAsync(inviteOrganization);
|
||||
|
||||
Assert.IsType<Invalid<InviteOrganization>>(result);
|
||||
Assert.Equal(OrganizationNoPaymentMethodFoundError.Code, (result as Invalid<InviteOrganization>)!.ErrorMessageString);
|
||||
@ -42,17 +41,16 @@ public class InviteUserOrganizationValidationTests
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void Validate_WhenOrganizationDoesNotHaveSubscription_ShouldReturnInvalidResponseWithSubscriptionMessage(
|
||||
Organization organization)
|
||||
public async Task Validate_WhenOrganizationDoesNotHaveSubscription_ShouldReturnInvalidResponseWithSubscriptionMessage(
|
||||
Organization organization, SutProvider<InviteUserOrganizationValidator> sutProvider)
|
||||
{
|
||||
organization.GatewaySubscriptionId = string.Empty;
|
||||
organization.Seats = 3;
|
||||
organization.MaxAutoscaleSeats = 4;
|
||||
|
||||
var inviteOrganization = new InviteOrganization(organization, new FreePlan());
|
||||
var validSubscriptionUpdate = new PasswordManagerSubscriptionUpdate(inviteOrganization, 3, 1);
|
||||
|
||||
var result = InviteUserOrganizationValidator.Validate(inviteOrganization, validSubscriptionUpdate);
|
||||
var result = await sutProvider.Sut.ValidateAsync(inviteOrganization);
|
||||
|
||||
Assert.IsType<Invalid<InviteOrganization>>(result);
|
||||
Assert.Equal(OrganizationNoSubscriptionFoundError.Code, (result as Invalid<InviteOrganization>)!.ErrorMessageString);
|
||||
|
@ -4,17 +4,20 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.V
|
||||
using Bit.Core.AdminConsole.Shared.Validation;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Billing.Models.StaticStore.Plans;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
||||
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class PasswordManagerInviteUserValidatorTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void Validate_OrganizationDoesNotHaveSeatsLimit_ShouldReturnValidResult(Organization organization)
|
||||
public async Task Validate_OrganizationDoesNotHaveSeatsLimit_ShouldReturnValidResult(Organization organization,
|
||||
SutProvider<PasswordManagerInviteUserValidator> sutProvider)
|
||||
{
|
||||
organization.Seats = null;
|
||||
|
||||
@ -22,14 +25,15 @@ public class PasswordManagerInviteUserValidatorTests
|
||||
|
||||
var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(organizationDto, 0, 0);
|
||||
|
||||
var result = PasswordManagerInviteUserValidator.Validate(subscriptionUpdate);
|
||||
var result = await sutProvider.Sut.ValidateAsync(subscriptionUpdate);
|
||||
|
||||
Assert.IsType<Valid<PasswordManagerSubscriptionUpdate>>(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void Validate_NumberOfSeatsToAddMatchesSeatsAvailable_ShouldReturnValidResult(Organization organization)
|
||||
public async Task Validate_NumberOfSeatsToAddMatchesSeatsAvailable_ShouldReturnValidResult(Organization organization,
|
||||
SutProvider<PasswordManagerInviteUserValidator> sutProvider)
|
||||
{
|
||||
organization.Seats = 8;
|
||||
organization.PlanType = PlanType.EnterpriseAnnually;
|
||||
@ -40,14 +44,15 @@ public class PasswordManagerInviteUserValidatorTests
|
||||
|
||||
var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(organizationDto, seatsOccupiedByUsers, additionalSeats);
|
||||
|
||||
var result = PasswordManagerInviteUserValidator.Validate(subscriptionUpdate);
|
||||
var result = await sutProvider.Sut.ValidateAsync(subscriptionUpdate);
|
||||
|
||||
Assert.IsType<Valid<PasswordManagerSubscriptionUpdate>>(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void Validate_NumberOfSeatsToAddIsGreaterThanMaxSeatsAllowed_ShouldBeInvalidWithSeatLimitMessage(Organization organization)
|
||||
public async Task Validate_NumberOfSeatsToAddIsGreaterThanMaxSeatsAllowed_ShouldBeInvalidWithSeatLimitMessage(Organization organization,
|
||||
SutProvider<PasswordManagerInviteUserValidator> sutProvider)
|
||||
{
|
||||
organization.Seats = 4;
|
||||
organization.MaxAutoscaleSeats = 4;
|
||||
@ -59,7 +64,7 @@ public class PasswordManagerInviteUserValidatorTests
|
||||
|
||||
var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(organizationDto, seatsOccupiedByUsers, additionalSeats);
|
||||
|
||||
var result = PasswordManagerInviteUserValidator.Validate(subscriptionUpdate);
|
||||
var result = await sutProvider.Sut.ValidateAsync(subscriptionUpdate);
|
||||
|
||||
Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result);
|
||||
Assert.Equal(PasswordManagerSeatLimitHasBeenReachedError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.ErrorMessageString);
|
||||
@ -67,7 +72,8 @@ public class PasswordManagerInviteUserValidatorTests
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public void Validate_GivenThePlanDoesNotAllowAdditionalSeats_ShouldBeInvalidMessageOfPlanNotAllowingSeats(Organization organization)
|
||||
public async Task Validate_GivenThePlanDoesNotAllowAdditionalSeats_ShouldBeInvalidMessageOfPlanNotAllowingSeats(Organization organization,
|
||||
SutProvider<PasswordManagerInviteUserValidator> sutProvider)
|
||||
{
|
||||
organization.Seats = 8;
|
||||
organization.MaxAutoscaleSeats = 9;
|
||||
@ -79,7 +85,7 @@ public class PasswordManagerInviteUserValidatorTests
|
||||
|
||||
var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(organizationDto, seatsOccupiedByUsers, additionalSeats);
|
||||
|
||||
var result = PasswordManagerInviteUserValidator.Validate(subscriptionUpdate);
|
||||
var result = await sutProvider.Sut.ValidateAsync(subscriptionUpdate);
|
||||
|
||||
Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result);
|
||||
Assert.Equal(PasswordManagerPlanDoesNotAllowAdditionalSeatsError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.ErrorMessageString);
|
||||
|
Loading…
x
Reference in New Issue
Block a user