1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-29 15:24:51 -05:00

Added plan validation to PasswordManagerInviteUserValidation. Cleaned up a few things.

This commit is contained in:
jrmccannon 2025-02-13 10:33:20 -06:00
parent 930419a502
commit bf8d6fb3ba
No known key found for this signature in database
GPG Key ID: CF03F3DB01CE96A6
4 changed files with 50 additions and 10 deletions

View File

@ -3,13 +3,17 @@
public static class InviteUserValidationErrorMessages public static class InviteUserValidationErrorMessages
{ {
public const string CannotAutoScaleOnSelfHostedError = "Cannot autoscale on self-hosted instance."; public const string CannotAutoScaleOnSelfHostedError = "Cannot autoscale on self-hosted instance.";
public const string SeatLimitHasBeenReachedError = "Seat limit has been reached.";
public const string ProviderBillableSeatLimitError = "Seat limit has been reached. Please contact your provider to add more seats."; public const string ProviderBillableSeatLimitError = "Seat limit has been reached. Please contact your provider to add more seats.";
public const string ProviderResellerSeatLimitError = "Seat limit has been reached. Contact your provider to purchase additional seats."; public const string ProviderResellerSeatLimitError = "Seat limit has been reached. Contact your provider to purchase additional seats.";
public const string CancelledSubscriptionError = "Cannot autoscale with a canceled subscription."; public const string CancelledSubscriptionError = "Cannot autoscale with a canceled subscription.";
public const string NoPaymentMethodFoundError = "No payment method found."; public const string NoPaymentMethodFoundError = "No payment method found.";
public const string NoSubscriptionFoundError = "No subscription found."; public const string NoSubscriptionFoundError = "No subscription found.";
// Password Manger Invite Users Error Messages
public const string SeatLimitHasBeenReachedError = "Seat limit has been reached.";
public const string PlanDoesNotAllowAdditionalSeats = "Plan does not allow additional seats.";
public const string PlanOnlyAllowsMaxAdditionalSeats = "Organization plan allows a maximum of {0} additional seats.";
// Secrets Manager Invite Users Error Messages // Secrets Manager Invite Users Error Messages
public const string OrganizationNoSecretsManager = "Organization has no access to Secrets Manager"; public const string OrganizationNoSecretsManager = "Organization has no access to Secrets Manager";
public const string SecretsManagerSeatLimitReached = "Secrets Manager seat limit has been reached."; public const string SecretsManagerSeatLimitReached = "Secrets Manager seat limit has been reached.";

View File

@ -1,5 +1,6 @@
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.Models.StaticStore;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
@ -8,13 +9,13 @@ public class PasswordManagerSubscriptionUpdate
/// <summary> /// <summary>
/// Seats the organization has /// Seats the organization has
/// </summary> /// </summary>
public int? Seats { get; private init; } public int? Seats { get; }
public int? MaxAutoScaleSeats { get; private init; } public int? MaxAutoScaleSeats { get; }
public int OccupiedSeats { get; private init; } public int OccupiedSeats { get; }
public int AdditionalSeats { get; private init; } public int AdditionalSeats { get; }
public int? AvailableSeats => Seats - OccupiedSeats; public int? AvailableSeats => Seats - OccupiedSeats;
@ -22,22 +23,25 @@ public class PasswordManagerSubscriptionUpdate
public int? UpdatedSeatTotal => Seats + SeatsRequiredToAdd; public int? UpdatedSeatTotal => Seats + SeatsRequiredToAdd;
private PasswordManagerSubscriptionUpdate(int? organizationSeats, int? organizationAutoScaleSeatLimit, int currentSeats, int seatsToAdd) public Plan Plan { get; }
private PasswordManagerSubscriptionUpdate(int? organizationSeats, int? organizationAutoScaleSeatLimit, int currentSeats, int seatsToAdd, Plan plan)
{ {
Seats = organizationSeats; Seats = organizationSeats;
MaxAutoScaleSeats = organizationAutoScaleSeatLimit; MaxAutoScaleSeats = organizationAutoScaleSeatLimit;
OccupiedSeats = currentSeats; OccupiedSeats = currentSeats;
AdditionalSeats = seatsToAdd; AdditionalSeats = seatsToAdd;
Plan = plan;
} }
public static PasswordManagerSubscriptionUpdate Create(OrganizationDto organizationDto, int occupiedSeats, int seatsToAdd) public static PasswordManagerSubscriptionUpdate Create(OrganizationDto organizationDto, int occupiedSeats, int seatsToAdd)
{ {
return new PasswordManagerSubscriptionUpdate(organizationDto.Seats, organizationDto.MaxAutoScaleSeats, occupiedSeats, seatsToAdd); return new PasswordManagerSubscriptionUpdate(organizationDto.Seats, organizationDto.MaxAutoScaleSeats, occupiedSeats, seatsToAdd, organizationDto.Plan);
} }
public static PasswordManagerSubscriptionUpdate Create(InviteUserOrganizationValidationRequest refined) public static PasswordManagerSubscriptionUpdate Create(InviteUserOrganizationValidationRequest refined)
{ {
return new PasswordManagerSubscriptionUpdate(refined.Organization.Seats, refined.Organization.MaxAutoScaleSeats, return new PasswordManagerSubscriptionUpdate(refined.Organization.Seats, refined.Organization.MaxAutoScaleSeats,
refined.OccupiedPmSeats, refined.Invites.Length); refined.OccupiedPmSeats, refined.Invites.Length, refined.Organization.Plan);
} }
} }

View File

@ -5,8 +5,7 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUse
public static class PasswordManagerInviteUserValidation public static class PasswordManagerInviteUserValidation
{ {
// NOTE This is only for validating adding users to an organization, not removing
// TODO need to add plan validation from AdjustSeatsAsync
public static ValidationResult<PasswordManagerSubscriptionUpdate> Validate(PasswordManagerSubscriptionUpdate subscriptionUpdate) public static ValidationResult<PasswordManagerSubscriptionUpdate> Validate(PasswordManagerSubscriptionUpdate subscriptionUpdate)
{ {
@ -26,6 +25,18 @@ public static class PasswordManagerInviteUserValidation
return new Invalid<PasswordManagerSubscriptionUpdate>(SeatLimitHasBeenReachedError); return new Invalid<PasswordManagerSubscriptionUpdate>(SeatLimitHasBeenReachedError);
} }
if (subscriptionUpdate.Plan.PasswordManager.HasAdditionalSeatsOption is false)
{
return new Invalid<PasswordManagerSubscriptionUpdate>(PlanDoesNotAllowAdditionalSeats);
}
// Apparently MaxAdditionalSeats is never set. Can probably be removed.
if (subscriptionUpdate.AdditionalSeats > subscriptionUpdate.Plan.PasswordManager.MaxAdditionalSeats)
{
return new Invalid<PasswordManagerSubscriptionUpdate>(string.Format(PlanOnlyAllowsMaxAdditionalSeats,
subscriptionUpdate.Plan.PasswordManager.MaxAdditionalSeats));
}
return new Valid<PasswordManagerSubscriptionUpdate>(subscriptionUpdate); return new Valid<PasswordManagerSubscriptionUpdate>(subscriptionUpdate);
} }
} }

View File

@ -2,6 +2,7 @@
using Bit.Core.AdminConsole.Models.Business; using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
using Bit.Core.Billing.Enums;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
using Xunit; using Xunit;
@ -30,6 +31,7 @@ public class PasswordManagerInviteUserValidationTests
public void Validate_NumberOfSeatsToAddMatchesSeatsAvailable_ShouldReturnValidResult(Organization organization) public void Validate_NumberOfSeatsToAddMatchesSeatsAvailable_ShouldReturnValidResult(Organization organization)
{ {
organization.Seats = 8; organization.Seats = 8;
organization.PlanType = PlanType.EnterpriseAnnually;
var seatsOccupiedByUsers = 4; var seatsOccupiedByUsers = 4;
var additionalSeats = 4; var additionalSeats = 4;
@ -61,4 +63,23 @@ public class PasswordManagerInviteUserValidationTests
Assert.Equal(InviteUserValidationErrorMessages.SeatLimitHasBeenReachedError, result.ErrorMessageString); Assert.Equal(InviteUserValidationErrorMessages.SeatLimitHasBeenReachedError, result.ErrorMessageString);
} }
[Theory]
[BitAutoData]
public void Validate_GivenThePlanDoesNotAllowAdditionalSeats_ShouldBeInvalidMessageOfPlanNotAllowingSeats(Organization organization)
{
organization.Seats = 8;
var seatsOccupiedByUsers = 4;
var additionalSeats = 4;
organization.PlanType = PlanType.Free;
var organizationDto = OrganizationDto.FromOrganization(organization);
var subscriptionUpdate = PasswordManagerSubscriptionUpdate.Create(organizationDto, seatsOccupiedByUsers, additionalSeats);
var result = PasswordManagerInviteUserValidation.Validate(subscriptionUpdate);
Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result);
Assert.Equal(InviteUserValidationErrorMessages.PlanDoesNotAllowAdditionalSeats, result.ErrorMessageString);
}
} }