1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-05 05:00:19 -05:00

Corrected model name. Corrected SM seat calculation. Added test for it.

This commit is contained in:
jrmccannon 2025-04-02 20:44:17 -05:00
parent 739bc65e87
commit e80bbe1caa
No known key found for this signature in database
GPG Key ID: CF03F3DB01CE96A6
9 changed files with 129 additions and 63 deletions

View File

@ -87,7 +87,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
new InviteOrganizationUsersResponse(request.InviteOrganization.OrganizationId))); new InviteOrganizationUsersResponse(request.InviteOrganization.OrganizationId)));
} }
var validationResult = await inviteUsersValidator.ValidateAsync(new InviteUserOrganizationValidationRequest var validationResult = await inviteUsersValidator.ValidateAsync(new InviteOrganizationUsersValidationRequest
{ {
Invites = invitesToSend.ToArray(), Invites = invitesToSend.ToArray(),
InviteOrganization = request.InviteOrganization, InviteOrganization = request.InviteOrganization,
@ -97,12 +97,12 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
OccupiedSmSeats = await organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(request.InviteOrganization.OrganizationId) OccupiedSmSeats = await organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(request.InviteOrganization.OrganizationId)
}); });
if (validationResult is Invalid<InviteUserOrganizationValidationRequest> invalid) if (validationResult is Invalid<InviteOrganizationUsersValidationRequest> invalid)
{ {
return invalid.MapToFailure(r => new InviteOrganizationUsersResponse(r)); return invalid.MapToFailure(r => new InviteOrganizationUsersResponse(r));
} }
var validatedRequest = validationResult as Valid<InviteUserOrganizationValidationRequest>; var validatedRequest = validationResult as Valid<InviteOrganizationUsersValidationRequest>;
var organizationUserToInviteEntities = invitesToSend var organizationUserToInviteEntities = invitesToSend
.Select(x => x.MapToDataModel(request.PerformedAt, validatedRequest!.Value.InviteOrganization)) .Select(x => x.MapToDataModel(request.PerformedAt, validatedRequest!.Value.InviteOrganization))
@ -135,7 +135,8 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
await RevertPasswordManagerChangesAsync(validatedRequest, organization); await RevertPasswordManagerChangesAsync(validatedRequest, organization);
return new Failure<InviteOrganizationUsersResponse>(new FailedToInviteUsersError( return new Failure<InviteOrganizationUsersResponse>(
new FailedToInviteUsersError(
new InviteOrganizationUsersResponse(validatedRequest.Value))); new InviteOrganizationUsersResponse(validatedRequest.Value)));
} }
@ -156,7 +157,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
.ToArray(); .ToArray();
} }
private async Task RevertPasswordManagerChangesAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization) private async Task RevertPasswordManagerChangesAsync(Valid<InviteOrganizationUsersValidationRequest> validatedResult, Organization organization)
{ {
if (validatedResult.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd > 0) if (validatedResult.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd > 0)
{ {
@ -173,7 +174,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
} }
} }
private async Task RevertSecretsManagerChangesAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization, int? initialSmSeats) private async Task RevertSecretsManagerChangesAsync(Valid<InviteOrganizationUsersValidationRequest> validatedResult, Organization organization, int? initialSmSeats)
{ {
if (validatedResult.Value.SecretsManagerSubscriptionUpdate?.SmSeatsChanged is true) if (validatedResult.Value.SecretsManagerSubscriptionUpdate?.SmSeatsChanged is true)
{ {
@ -189,7 +190,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
} }
} }
private async Task PublishReferenceEventAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, private async Task PublishReferenceEventAsync(Valid<InviteOrganizationUsersValidationRequest> validatedResult,
Organization organization) => Organization organization) =>
await referenceEventService.RaiseEventAsync( await referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.InvitedUsers, organization, currentContext) new ReferenceEvent(ReferenceEventType.InvitedUsers, organization, currentContext)
@ -203,12 +204,12 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
users.Select(x => x.OrganizationUser), users.Select(x => x.OrganizationUser),
organization)); organization));
private async Task SendAdditionalEmailsAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization) private async Task SendAdditionalEmailsAsync(Valid<InviteOrganizationUsersValidationRequest> validatedResult, Organization organization)
{ {
await SendPasswordManagerMaxSeatLimitEmailsAsync(validatedResult, organization); await SendPasswordManagerMaxSeatLimitEmailsAsync(validatedResult, organization);
} }
private async Task SendPasswordManagerMaxSeatLimitEmailsAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization) private async Task SendPasswordManagerMaxSeatLimitEmailsAsync(Valid<InviteOrganizationUsersValidationRequest> validatedResult, Organization organization)
{ {
if (!validatedResult.Value.PasswordManagerSubscriptionUpdate.MaxSeatsReached) if (!validatedResult.Value.PasswordManagerSubscriptionUpdate.MaxSeatsReached)
{ {
@ -246,7 +247,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
.Select(u => u.Email).Distinct(); .Select(u => u.Email).Distinct();
} }
private async Task AdjustSecretsManagerSeatsAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult) private async Task AdjustSecretsManagerSeatsAsync(Valid<InviteOrganizationUsersValidationRequest> validatedResult)
{ {
if (validatedResult.Value.SecretsManagerSubscriptionUpdate?.SmSeatsChanged is true) if (validatedResult.Value.SecretsManagerSubscriptionUpdate?.SmSeatsChanged is true)
{ {
@ -255,7 +256,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
} }
private async Task AdjustPasswordManagerSeatsAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization) private async Task AdjustPasswordManagerSeatsAsync(Valid<InviteOrganizationUsersValidationRequest> validatedResult, Organization organization)
{ {
if (validatedResult.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd <= 0) if (validatedResult.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd <= 0)
{ {

View File

@ -7,10 +7,10 @@ public class InviteOrganizationUsersResponse(Guid organizationId)
public IEnumerable<OrganizationUser> InvitedUsers { get; } = []; public IEnumerable<OrganizationUser> InvitedUsers { get; } = [];
public Guid OrganizationId { get; } = organizationId; public Guid OrganizationId { get; } = organizationId;
public InviteOrganizationUsersResponse(InviteUserOrganizationValidationRequest validationRequest) public InviteOrganizationUsersResponse(InviteOrganizationUsersValidationRequest usersValidationRequest)
: this(validationRequest.InviteOrganization.OrganizationId) : this(usersValidationRequest.InviteOrganization.OrganizationId)
{ {
InvitedUsers = validationRequest.Invites.Select(x => new OrganizationUser { Email = x.Email }); InvitedUsers = usersValidationRequest.Invites.Select(x => new OrganizationUser { Email = x.Email });
} }
public InviteOrganizationUsersResponse(IEnumerable<OrganizationUser> invitedOrganizationUsers, Guid organizationId) public InviteOrganizationUsersResponse(IEnumerable<OrganizationUser> invitedOrganizationUsers, Guid organizationId)

View File

@ -4,13 +4,13 @@ using Bit.Core.Models.Business;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
public class InviteUserOrganizationValidationRequest public class InviteOrganizationUsersValidationRequest
{ {
public InviteUserOrganizationValidationRequest() public InviteOrganizationUsersValidationRequest()
{ {
} }
public InviteUserOrganizationValidationRequest(InviteUserOrganizationValidationRequest request) public InviteOrganizationUsersValidationRequest(InviteOrganizationUsersValidationRequest request)
{ {
Invites = request.Invites; Invites = request.Invites;
InviteOrganization = request.InviteOrganization; InviteOrganization = request.InviteOrganization;
@ -20,7 +20,7 @@ public class InviteUserOrganizationValidationRequest
OccupiedSmSeats = request.OccupiedSmSeats; OccupiedSmSeats = request.OccupiedSmSeats;
} }
public InviteUserOrganizationValidationRequest(InviteUserOrganizationValidationRequest request, public InviteOrganizationUsersValidationRequest(InviteOrganizationUsersValidationRequest request,
PasswordManagerSubscriptionUpdate subscriptionUpdate, PasswordManagerSubscriptionUpdate subscriptionUpdate,
SecretsManagerSubscriptionUpdate smSubscriptionUpdate) SecretsManagerSubscriptionUpdate smSubscriptionUpdate)
: this(request) : this(request)

View File

@ -1,5 +1,4 @@
using Bit.Core.AdminConsole.Errors; using Bit.Core.AdminConsole.Errors;
using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; 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.PasswordManager;
using Bit.Core.AdminConsole.Shared.Validation; using Bit.Core.AdminConsole.Shared.Validation;
@ -11,15 +10,15 @@ using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.Organi
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
public interface IInviteUsersValidator : IValidator<InviteUserOrganizationValidationRequest>; public interface IInviteUsersValidator : IValidator<InviteOrganizationUsersValidationRequest>;
public class InviteUsersValidator( public class InviteOrganizationUsersValidator(
IOrganizationRepository organizationRepository, IOrganizationRepository organizationRepository,
IInviteUsersPasswordManagerValidator inviteUsersPasswordManagerValidator, IInviteUsersPasswordManagerValidator inviteUsersPasswordManagerValidator,
IUpdateSecretsManagerSubscriptionCommand secretsManagerSubscriptionCommand, IUpdateSecretsManagerSubscriptionCommand secretsManagerSubscriptionCommand,
IPaymentService paymentService) : IInviteUsersValidator IPaymentService paymentService) : IInviteUsersValidator
{ {
public async Task<ValidationResult<InviteUserOrganizationValidationRequest>> ValidateAsync(InviteUserOrganizationValidationRequest request) public async Task<ValidationResult<InviteOrganizationUsersValidationRequest>> ValidateAsync(InviteOrganizationUsersValidationRequest request)
{ {
var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(request); var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(request);
@ -34,7 +33,7 @@ public class InviteUsersValidator(
// This is an expensive call, so we're doing it now to delay the check as long as possible. // This is an expensive call, so we're doing it now to delay the check as long as possible.
if (await paymentService.HasSecretsManagerStandalone(request.InviteOrganization)) if (await paymentService.HasSecretsManagerStandalone(request.InviteOrganization))
{ {
request = new InviteUserOrganizationValidationRequest(request) request = new InviteOrganizationUsersValidationRequest(request)
{ {
Invites = request.Invites Invites = request.Invites
.Select(x => new OrganizationUserInvite(x, accessSecretsManager: true)) .Select(x => new OrganizationUserInvite(x, accessSecretsManager: true))
@ -47,14 +46,14 @@ public class InviteUsersValidator(
return await ValidateSecretsManagerSubscriptionUpdateAsync(request, subscriptionUpdate); return await ValidateSecretsManagerSubscriptionUpdateAsync(request, subscriptionUpdate);
} }
return new Valid<InviteUserOrganizationValidationRequest>(new InviteUserOrganizationValidationRequest( return new Valid<InviteOrganizationUsersValidationRequest>(new InviteOrganizationUsersValidationRequest(
request, request,
subscriptionUpdate, subscriptionUpdate,
null)); null));
} }
private async Task<ValidationResult<InviteUserOrganizationValidationRequest>> ValidateSecretsManagerSubscriptionUpdateAsync( private async Task<ValidationResult<InviteOrganizationUsersValidationRequest>> ValidateSecretsManagerSubscriptionUpdateAsync(
InviteUserOrganizationValidationRequest request, InviteOrganizationUsersValidationRequest request,
PasswordManagerSubscriptionUpdate subscriptionUpdate) PasswordManagerSubscriptionUpdate subscriptionUpdate)
{ {
try try
@ -63,24 +62,24 @@ public class InviteUsersValidator(
organization: await organizationRepository.GetByIdAsync(request.InviteOrganization.OrganizationId), organization: await organizationRepository.GetByIdAsync(request.InviteOrganization.OrganizationId),
plan: request.InviteOrganization.Plan, plan: request.InviteOrganization.Plan,
autoscaling: true) autoscaling: true)
.AdjustSeats(GetSecretManagerSeatAdjustment( .AdjustSeats(GetSecretManagerSeatAdjustment(request));
occupiedSeats: request.OccupiedSmSeats,
organization: request.InviteOrganization,
invitesToSend: request.Invites.Count(x => x.AccessSecretsManager)));
await secretsManagerSubscriptionCommand.ValidateUpdateAsync(smSubscriptionUpdate); await secretsManagerSubscriptionCommand.ValidateUpdateAsync(smSubscriptionUpdate);
return new Valid<InviteUserOrganizationValidationRequest>(new InviteUserOrganizationValidationRequest( return new Valid<InviteOrganizationUsersValidationRequest>(new InviteOrganizationUsersValidationRequest(
request, request,
subscriptionUpdate, subscriptionUpdate,
smSubscriptionUpdate)); smSubscriptionUpdate));
} }
catch (Exception ex) catch (Exception ex)
{ {
return new Invalid<InviteUserOrganizationValidationRequest>(new Error<InviteUserOrganizationValidationRequest>(ex.Message, request)); return new Invalid<InviteOrganizationUsersValidationRequest>(new Error<InviteOrganizationUsersValidationRequest>(ex.Message, request));
} }
int GetSecretManagerSeatAdjustment(int occupiedSeats, InviteOrganization organization, int invitesToSend) => int GetSecretManagerSeatAdjustment(InviteOrganizationUsersValidationRequest request) =>
organization.SmSeats - (occupiedSeats + invitesToSend) ?? 0; Math.Abs(
request.InviteOrganization.SmSeats -
request.OccupiedSmSeats -
request.Invites.Count(x => x.AccessSecretsManager) ?? 0);
} }
} }

View File

@ -77,13 +77,13 @@ public class PasswordManagerSubscriptionUpdate
inviteOrganization: inviteOrganization) inviteOrganization: inviteOrganization)
{ } { }
public PasswordManagerSubscriptionUpdate(InviteUserOrganizationValidationRequest validationRequest) : public PasswordManagerSubscriptionUpdate(InviteOrganizationUsersValidationRequest usersValidationRequest) :
this( this(
organizationSeats: validationRequest.InviteOrganization.Seats, organizationSeats: usersValidationRequest.InviteOrganization.Seats,
organizationAutoScaleSeatLimit: validationRequest.InviteOrganization.MaxAutoScaleSeats, organizationAutoScaleSeatLimit: usersValidationRequest.InviteOrganization.MaxAutoScaleSeats,
currentSeats: validationRequest.OccupiedPmSeats, currentSeats: usersValidationRequest.OccupiedPmSeats,
newUsersToAdd: validationRequest.Invites.Length, newUsersToAdd: usersValidationRequest.Invites.Length,
plan: validationRequest.InviteOrganization.Plan.PasswordManager, plan: usersValidationRequest.InviteOrganization.Plan.PasswordManager,
inviteOrganization: validationRequest.InviteOrganization) inviteOrganization: usersValidationRequest.InviteOrganization)
{ } { }
} }

View File

@ -183,7 +183,7 @@ public static class OrganizationServiceCollectionExtensions
services.AddScoped<IInviteOrganizationUsersCommand, InviteOrganizationUsersCommand>(); services.AddScoped<IInviteOrganizationUsersCommand, InviteOrganizationUsersCommand>();
services.AddScoped<ISendOrganizationInvitesCommand, SendOrganizationInvitesCommand>(); services.AddScoped<ISendOrganizationInvitesCommand, SendOrganizationInvitesCommand>();
services.AddScoped<IInviteUsersValidator, InviteUsersValidator>(); services.AddScoped<IInviteUsersValidator, InviteOrganizationUsersValidator>();
services.AddScoped<IInviteUsersOrganizationValidator, InviteUsersOrganizationValidator>(); services.AddScoped<IInviteUsersOrganizationValidator, InviteUsersOrganizationValidator>();
services.AddScoped<IInviteUsersPasswordManagerValidator, InviteUsersPasswordManagerValidator>(); services.AddScoped<IInviteUsersPasswordManagerValidator, InviteUsersPasswordManagerValidator>();
services.AddScoped<IInviteUsersEnvironmentValidator, InviteUsersEnvironmentValidator>(); services.AddScoped<IInviteUsersEnvironmentValidator, InviteUsersEnvironmentValidator>();

View File

@ -8,7 +8,7 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.Invi
public static class InviteUserOrganizationValidationRequestHelpers public static class InviteUserOrganizationValidationRequestHelpers
{ {
public static InviteUserOrganizationValidationRequest GetInviteValidationRequestMock(InviteOrganizationUsersRequest request, public static InviteOrganizationUsersValidationRequest GetInviteValidationRequestMock(InviteOrganizationUsersRequest request,
InviteOrganization inviteOrganization, Organization organization) => InviteOrganization inviteOrganization, Organization organization) =>
new() new()
{ {
@ -23,7 +23,7 @@ public static class InviteUserOrganizationValidationRequestHelpers
.AdjustSeats(request.Invites.Count(x => x.AccessSecretsManager)) .AdjustSeats(request.Invites.Count(x => x.AccessSecretsManager))
}; };
public static InviteUserOrganizationValidationRequest WithPasswordManagerUpdate(this InviteUserOrganizationValidationRequest request, PasswordManagerSubscriptionUpdate passwordManagerSubscriptionUpdate) => public static InviteOrganizationUsersValidationRequest WithPasswordManagerUpdate(this InviteOrganizationUsersValidationRequest request, PasswordManagerSubscriptionUpdate passwordManagerSubscriptionUpdate) =>
new() new()
{ {
Invites = request.Invites, Invites = request.Invites,
@ -36,7 +36,7 @@ public static class InviteUserOrganizationValidationRequestHelpers
SecretsManagerSubscriptionUpdate = request.SecretsManagerSubscriptionUpdate SecretsManagerSubscriptionUpdate = request.SecretsManagerSubscriptionUpdate
}; };
public static InviteUserOrganizationValidationRequest WithSecretsManagerUpdate(this InviteUserOrganizationValidationRequest request, SecretsManagerSubscriptionUpdate secretsManagerSubscriptionUpdate) => public static InviteOrganizationUsersValidationRequest WithSecretsManagerUpdate(this InviteOrganizationUsersValidationRequest request, SecretsManagerSubscriptionUpdate secretsManagerSubscriptionUpdate) =>
new() new()
{ {
Invites = request.Invites, Invites = request.Invites,

View File

@ -72,8 +72,8 @@ public class InviteOrganizationUserCommandTests
.Returns([user.Email]); .Returns([user.Email]);
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteOrganizationUsersValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization))); .Returns(new Valid<InviteOrganizationUsersValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)));
// Act // Act
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request); var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
@ -134,8 +134,8 @@ public class InviteOrganizationUserCommandTests
.Returns(organization); .Returns(organization);
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteOrganizationUsersValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization))); .Returns(new Valid<InviteOrganizationUsersValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)));
// Act // Act
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request); var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
@ -198,9 +198,9 @@ public class InviteOrganizationUserCommandTests
.Returns(organization); .Returns(organization);
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteOrganizationUsersValidationRequest>())
.Returns(new Invalid<InviteUserOrganizationValidationRequest>( .Returns(new Invalid<InviteOrganizationUsersValidationRequest>(
new Error<InviteUserOrganizationValidationRequest>(errorMessage, validationRequest))); new Error<InviteOrganizationUsersValidationRequest>(errorMessage, validationRequest)));
// Act // Act
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request); var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
@ -268,8 +268,8 @@ public class InviteOrganizationUserCommandTests
.Returns(organization); .Returns(organization);
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteOrganizationUsersValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization) .Returns(new Valid<InviteOrganizationUsersValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)
.WithPasswordManagerUpdate(new PasswordManagerSubscriptionUpdate(inviteOrganization, organization.Seats.Value, 1)))); .WithPasswordManagerUpdate(new PasswordManagerSubscriptionUpdate(inviteOrganization, organization.Seats.Value, 1))));
// Act // Act
@ -338,8 +338,8 @@ public class InviteOrganizationUserCommandTests
.Returns(organization); .Returns(organization);
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteOrganizationUsersValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization) .Returns(new Valid<InviteOrganizationUsersValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)
.WithPasswordManagerUpdate(passwordManagerUpdate))); .WithPasswordManagerUpdate(passwordManagerUpdate)));
// Act // Act
@ -414,8 +414,8 @@ public class InviteOrganizationUserCommandTests
.Returns(organization); .Returns(organization);
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteOrganizationUsersValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization) .Returns(new Valid<InviteOrganizationUsersValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)
.WithSecretsManagerUpdate(secretsManagerSubscriptionUpdate))); .WithSecretsManagerUpdate(secretsManagerSubscriptionUpdate)));
// Act // Act
@ -486,8 +486,8 @@ public class InviteOrganizationUserCommandTests
.Returns(organization); .Returns(organization);
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteOrganizationUsersValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization) .Returns(new Valid<InviteOrganizationUsersValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)
.WithPasswordManagerUpdate(passwordManagerSubscriptionUpdate) .WithPasswordManagerUpdate(passwordManagerSubscriptionUpdate)
.WithSecretsManagerUpdate(secretsManagerSubscriptionUpdate))); .WithSecretsManagerUpdate(secretsManagerSubscriptionUpdate)));
@ -581,8 +581,8 @@ public class InviteOrganizationUserCommandTests
.Returns(organization); .Returns(organization);
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteOrganizationUsersValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization) .Returns(new Valid<InviteOrganizationUsersValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization, organization)
.WithPasswordManagerUpdate(passwordManagerSubscriptionUpdate) .WithPasswordManagerUpdate(passwordManagerSubscriptionUpdate)
.WithSecretsManagerUpdate(secretsManagerSubscriptionUpdate))); .WithSecretsManagerUpdate(secretsManagerSubscriptionUpdate)));

View File

@ -0,0 +1,66 @@
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;
using Bit.Core.Billing.Models.StaticStore.Plans;
using Bit.Core.Models.Business;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite;
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
[SutProviderCustomize]
public class InviteOrganizationUsersValidatorTests
{
[Theory]
[BitAutoData]
public async Task ValidateAsync_WhenOrganizationHasSecretsManagerInvites_ThenShouldCorrectlyCalculateSeatsToAdd(
Organization organization,
SutProvider<InviteOrganizationUsersValidator> sutProvider
)
{
organization.Seats = null;
organization.SmSeats = 10;
organization.UseSecretsManager = true;
var request = new InviteOrganizationUsersValidationRequest
{
Invites =
[
new OrganizationUserInvite(
email: "test@email.com",
externalId: "test-external-id"),
new OrganizationUserInvite(
email: "test2@email.com",
externalId: "test-external-id2"),
new OrganizationUserInvite(
email: "test3@email.com",
externalId: "test-external-id3")
],
InviteOrganization = new InviteOrganization(organization, new Enterprise2023Plan(true)),
OccupiedPmSeats = 0,
OccupiedSmSeats = 9
};
sutProvider.GetDependency<IPaymentService>()
.HasSecretsManagerStandalone(request.InviteOrganization)
.Returns(true);
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization.Id)
.Returns(organization);
_ = await sutProvider.Sut.ValidateAsync(request);
sutProvider.GetDependency<IUpdateSecretsManagerSubscriptionCommand>()
.Received(1)
.ValidateUpdateAsync(Arg.Is<SecretsManagerSubscriptionUpdate>(x =>
x.SmSeatsChanged == true && x.SmSeats == 12));
}
}