mirror of
https://github.com/bitwarden/server.git
synced 2025-04-05 13:08:17 -05:00
Corrected logic and added more tests.
This commit is contained in:
parent
e80bbe1caa
commit
4e205b9d78
@ -18,11 +18,13 @@ public class InviteOrganizationUsersValidator(
|
|||||||
IUpdateSecretsManagerSubscriptionCommand secretsManagerSubscriptionCommand,
|
IUpdateSecretsManagerSubscriptionCommand secretsManagerSubscriptionCommand,
|
||||||
IPaymentService paymentService) : IInviteUsersValidator
|
IPaymentService paymentService) : IInviteUsersValidator
|
||||||
{
|
{
|
||||||
public async Task<ValidationResult<InviteOrganizationUsersValidationRequest>> ValidateAsync(InviteOrganizationUsersValidationRequest request)
|
public async Task<ValidationResult<InviteOrganizationUsersValidationRequest>> ValidateAsync(
|
||||||
|
InviteOrganizationUsersValidationRequest request)
|
||||||
{
|
{
|
||||||
var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(request);
|
var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(request);
|
||||||
|
|
||||||
var passwordManagerValidationResult = await inviteUsersPasswordManagerValidator.ValidateAsync(subscriptionUpdate);
|
var passwordManagerValidationResult =
|
||||||
|
await inviteUsersPasswordManagerValidator.ValidateAsync(subscriptionUpdate);
|
||||||
|
|
||||||
if (passwordManagerValidationResult is Invalid<PasswordManagerSubscriptionUpdate> invalidSubscriptionUpdate)
|
if (passwordManagerValidationResult is Invalid<PasswordManagerSubscriptionUpdate> invalidSubscriptionUpdate)
|
||||||
{
|
{
|
||||||
@ -53,18 +55,25 @@ public class InviteOrganizationUsersValidator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ValidationResult<InviteOrganizationUsersValidationRequest>> ValidateSecretsManagerSubscriptionUpdateAsync(
|
private async Task<ValidationResult<InviteOrganizationUsersValidationRequest>> ValidateSecretsManagerSubscriptionUpdateAsync(
|
||||||
InviteOrganizationUsersValidationRequest request,
|
InviteOrganizationUsersValidationRequest request,
|
||||||
PasswordManagerSubscriptionUpdate subscriptionUpdate)
|
PasswordManagerSubscriptionUpdate subscriptionUpdate)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var smSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(
|
|
||||||
organization: await organizationRepository.GetByIdAsync(request.InviteOrganization.OrganizationId),
|
|
||||||
plan: request.InviteOrganization.Plan,
|
|
||||||
autoscaling: true)
|
|
||||||
.AdjustSeats(GetSecretManagerSeatAdjustment(request));
|
|
||||||
|
|
||||||
await secretsManagerSubscriptionCommand.ValidateUpdateAsync(smSubscriptionUpdate);
|
var smSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(
|
||||||
|
organization: await organizationRepository.GetByIdAsync(request.InviteOrganization.OrganizationId),
|
||||||
|
plan: request.InviteOrganization.Plan,
|
||||||
|
autoscaling: true);
|
||||||
|
|
||||||
|
var seatsToAdd = GetSecretManagerSeatAdjustment(request);
|
||||||
|
|
||||||
|
if (seatsToAdd > 0)
|
||||||
|
{
|
||||||
|
smSubscriptionUpdate.AdjustSeats(seatsToAdd);
|
||||||
|
|
||||||
|
await secretsManagerSubscriptionCommand.ValidateUpdateAsync(smSubscriptionUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
return new Valid<InviteOrganizationUsersValidationRequest>(new InviteOrganizationUsersValidationRequest(
|
return new Valid<InviteOrganizationUsersValidationRequest>(new InviteOrganizationUsersValidationRequest(
|
||||||
request,
|
request,
|
||||||
@ -73,13 +82,27 @@ public class InviteOrganizationUsersValidator(
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new Invalid<InviteOrganizationUsersValidationRequest>(new Error<InviteOrganizationUsersValidationRequest>(ex.Message, request));
|
return new Invalid<InviteOrganizationUsersValidationRequest>(
|
||||||
|
new Error<InviteOrganizationUsersValidationRequest>(ex.Message, request));
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetSecretManagerSeatAdjustment(InviteOrganizationUsersValidationRequest request) =>
|
|
||||||
Math.Abs(
|
|
||||||
request.InviteOrganization.SmSeats -
|
|
||||||
request.OccupiedSmSeats -
|
|
||||||
request.Invites.Count(x => x.AccessSecretsManager) ?? 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This calculates the number of SM seats to add to the organization seat total.
|
||||||
|
///
|
||||||
|
/// If they have a current seat limit (it can be null), we want to figure out how many are available (seats -
|
||||||
|
/// occupied seats). Then, we'll subtract the available seats from the number of users we're trying to invite.
|
||||||
|
///
|
||||||
|
/// If it's negative, we have available seats and do not need to increase, so we go with 0.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static int GetSecretManagerSeatAdjustment(InviteOrganizationUsersValidationRequest request) =>
|
||||||
|
request.InviteOrganization.SmSeats.HasValue
|
||||||
|
? Math.Max(
|
||||||
|
request.Invites.Count(x => x.AccessSecretsManager) -
|
||||||
|
(request.InviteOrganization.SmSeats.Value -
|
||||||
|
request.OccupiedSmSeats),
|
||||||
|
0)
|
||||||
|
: 0;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
using Bit.Core.AdminConsole.Models.Business;
|
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;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
||||||
|
using Bit.Core.AdminConsole.Shared.Validation;
|
||||||
using Bit.Core.Billing.Models.StaticStore.Plans;
|
using Bit.Core.Billing.Models.StaticStore.Plans;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
@ -10,6 +12,7 @@ using Bit.Core.Services;
|
|||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
|
using NSubstitute.ExceptionExtensions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite;
|
using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite;
|
||||||
|
|
||||||
@ -20,7 +23,7 @@ public class InviteOrganizationUsersValidatorTests
|
|||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task ValidateAsync_WhenOrganizationHasSecretsManagerInvites_ThenShouldCorrectlyCalculateSeatsToAdd(
|
public async Task ValidateAsync_WhenOrganizationHasSecretsManagerInvitesAndDoesNotHaveEnoughSeatsAvailable_ThenShouldCorrectlyCalculateSeatsToAdd(
|
||||||
Organization organization,
|
Organization organization,
|
||||||
SutProvider<InviteOrganizationUsersValidator> sutProvider
|
SutProvider<InviteOrganizationUsersValidator> sutProvider
|
||||||
)
|
)
|
||||||
@ -63,4 +66,96 @@ public class InviteOrganizationUsersValidatorTests
|
|||||||
.ValidateUpdateAsync(Arg.Is<SecretsManagerSubscriptionUpdate>(x =>
|
.ValidateUpdateAsync(Arg.Is<SecretsManagerSubscriptionUpdate>(x =>
|
||||||
x.SmSeatsChanged == true && x.SmSeats == 12));
|
x.SmSeatsChanged == true && x.SmSeats == 12));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task ValidateAsync_WhenOrganizationHasSecretsManagerInvitesAndHasSeatsAvailable_ThenShouldReturnValid(
|
||||||
|
Organization organization,
|
||||||
|
SutProvider<InviteOrganizationUsersValidator> sutProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
organization.Seats = null;
|
||||||
|
organization.SmSeats = 12;
|
||||||
|
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);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.ValidateAsync(request);
|
||||||
|
|
||||||
|
Assert.IsType<Valid<InviteOrganizationUsersValidationRequest>>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task ValidateAsync_WhenOrganizationHasSecretsManagerInvitesAndSmSeatUpdateFailsValidation_ThenShouldReturnInvalid(
|
||||||
|
Organization organization,
|
||||||
|
SutProvider<InviteOrganizationUsersValidator> sutProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
organization.Seats = null;
|
||||||
|
organization.SmSeats = 5;
|
||||||
|
organization.MaxAutoscaleSmSeats = 5;
|
||||||
|
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 = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IPaymentService>()
|
||||||
|
.HasSecretsManagerStandalone(request.InviteOrganization)
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(organization.Id)
|
||||||
|
.Returns(organization);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IUpdateSecretsManagerSubscriptionCommand>()
|
||||||
|
.ValidateUpdateAsync(Arg.Any<SecretsManagerSubscriptionUpdate>())
|
||||||
|
.Throws(new BadRequestException("Some Secrets Manager Failure"));
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.ValidateAsync(request);
|
||||||
|
|
||||||
|
Assert.IsType<Invalid<InviteOrganizationUsersValidationRequest>>(result);
|
||||||
|
Assert.Equal("Some Secrets Manager Failure", (result as Invalid<InviteOrganizationUsersValidationRequest>)!.ErrorMessageString);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user