mirror of
https://github.com/bitwarden/server.git
synced 2024-11-25 12:45:18 +01:00
Block org seat scaling when has Reseller provider (#3385)
This commit is contained in:
parent
cf38ff3c19
commit
07c202ecaf
@ -57,6 +57,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
private readonly IProviderUserRepository _providerUserRepository;
|
private readonly IProviderUserRepository _providerUserRepository;
|
||||||
private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery;
|
private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery;
|
||||||
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
|
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
|
||||||
|
private readonly IProviderRepository _providerRepository;
|
||||||
private readonly IOrgUserInviteTokenableFactory _orgUserInviteTokenableFactory;
|
private readonly IOrgUserInviteTokenableFactory _orgUserInviteTokenableFactory;
|
||||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
@ -90,6 +91,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
IOrgUserInviteTokenableFactory orgUserInviteTokenableFactory,
|
IOrgUserInviteTokenableFactory orgUserInviteTokenableFactory,
|
||||||
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
||||||
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
||||||
|
IProviderRepository providerRepository,
|
||||||
IFeatureService featureService)
|
IFeatureService featureService)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
@ -118,6 +120,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
_providerUserRepository = providerUserRepository;
|
_providerUserRepository = providerUserRepository;
|
||||||
_countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery;
|
_countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery;
|
||||||
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
||||||
|
_providerRepository = providerRepository;
|
||||||
_orgUserInviteTokenableFactory = orgUserInviteTokenableFactory;
|
_orgUserInviteTokenableFactory = orgUserInviteTokenableFactory;
|
||||||
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
@ -862,7 +865,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
|
|
||||||
if (newSeatsRequired > 0)
|
if (newSeatsRequired > 0)
|
||||||
{
|
{
|
||||||
var (canScale, failureReason) = CanScale(organization, newSeatsRequired);
|
var (canScale, failureReason) = await CanScaleAsync(organization, newSeatsRequired);
|
||||||
if (!canScale)
|
if (!canScale)
|
||||||
{
|
{
|
||||||
throw new BadRequestException(failureReason);
|
throw new BadRequestException(failureReason);
|
||||||
@ -1182,7 +1185,8 @@ public class OrganizationService : IOrganizationService
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal (bool canScale, string failureReason) CanScale(Organization organization,
|
internal async Task<(bool canScale, string failureReason)> CanScaleAsync(
|
||||||
|
Organization organization,
|
||||||
int seatsToAdd)
|
int seatsToAdd)
|
||||||
{
|
{
|
||||||
var failureReason = "";
|
var failureReason = "";
|
||||||
@ -1197,6 +1201,13 @@ public class OrganizationService : IOrganizationService
|
|||||||
return (true, failureReason);
|
return (true, failureReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var provider = await _providerRepository.GetByOrganizationIdAsync(organization.Id);
|
||||||
|
|
||||||
|
if (provider is { Enabled: true, Type: ProviderType.Reseller })
|
||||||
|
{
|
||||||
|
return (false, "Seat limit has been reached. Contact your provider to purchase additional seats.");
|
||||||
|
}
|
||||||
|
|
||||||
if (organization.Seats.HasValue &&
|
if (organization.Seats.HasValue &&
|
||||||
organization.MaxAutoscaleSeats.HasValue &&
|
organization.MaxAutoscaleSeats.HasValue &&
|
||||||
organization.MaxAutoscaleSeats.Value < organization.Seats.Value + seatsToAdd)
|
organization.MaxAutoscaleSeats.Value < organization.Seats.Value + seatsToAdd)
|
||||||
@ -1214,7 +1225,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var (canScale, failureMessage) = CanScale(organization, seatsToAdd);
|
var (canScale, failureMessage) = await CanScaleAsync(organization, seatsToAdd);
|
||||||
if (!canScale)
|
if (!canScale)
|
||||||
{
|
{
|
||||||
throw new BadRequestException(failureMessage);
|
throw new BadRequestException(failureMessage);
|
||||||
|
@ -34,6 +34,7 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
|||||||
using Bit.Test.Common.Fakes;
|
using Bit.Test.Common.Fakes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using NSubstitute.ExceptionExtensions;
|
using NSubstitute.ExceptionExtensions;
|
||||||
|
using NSubstitute.ReturnsExtensions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Organization = Bit.Core.Entities.Organization;
|
using Organization = Bit.Core.Entities.Organization;
|
||||||
using OrganizationUser = Bit.Core.Entities.OrganizationUser;
|
using OrganizationUser = Bit.Core.Entities.OrganizationUser;
|
||||||
@ -1606,15 +1607,16 @@ public class OrganizationServiceTests
|
|||||||
[BitAutoData(0, null, 100, true, "")]
|
[BitAutoData(0, null, 100, true, "")]
|
||||||
[BitAutoData(1, 100, null, true, "")]
|
[BitAutoData(1, 100, null, true, "")]
|
||||||
[BitAutoData(1, 100, 100, false, "Seat limit has been reached")]
|
[BitAutoData(1, 100, 100, false, "Seat limit has been reached")]
|
||||||
public void CanScale(int seatsToAdd, int? currentSeats, int? maxAutoscaleSeats,
|
public async Task CanScaleAsync(int seatsToAdd, int? currentSeats, int? maxAutoscaleSeats,
|
||||||
bool expectedResult, string expectedFailureMessage, Organization organization,
|
bool expectedResult, string expectedFailureMessage, Organization organization,
|
||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
organization.Seats = currentSeats;
|
organization.Seats = currentSeats;
|
||||||
organization.MaxAutoscaleSeats = maxAutoscaleSeats;
|
organization.MaxAutoscaleSeats = maxAutoscaleSeats;
|
||||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
||||||
|
sutProvider.GetDependency<IProviderRepository>().GetByOrganizationIdAsync(organization.Id).ReturnsNull();
|
||||||
|
|
||||||
var (result, failureMessage) = sutProvider.Sut.CanScale(organization, seatsToAdd);
|
var (result, failureMessage) = await sutProvider.Sut.CanScaleAsync(organization, seatsToAdd);
|
||||||
|
|
||||||
if (expectedFailureMessage == string.Empty)
|
if (expectedFailureMessage == string.Empty)
|
||||||
{
|
{
|
||||||
@ -1628,16 +1630,35 @@ public class OrganizationServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, PaidOrganizationCustomize, BitAutoData]
|
[Theory, PaidOrganizationCustomize, BitAutoData]
|
||||||
public void CanScale_FailsOnSelfHosted(Organization organization,
|
public async Task CanScaleAsync_FailsOnSelfHosted(Organization organization,
|
||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
||||||
var (result, failureMessage) = sutProvider.Sut.CanScale(organization, 10);
|
var (result, failureMessage) = await sutProvider.Sut.CanScaleAsync(organization, 10);
|
||||||
|
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
Assert.Contains("Cannot autoscale on self-hosted instance", failureMessage);
|
Assert.Contains("Cannot autoscale on self-hosted instance", failureMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory, PaidOrganizationCustomize, BitAutoData]
|
||||||
|
public async Task CanScaleAsync_FailsOnResellerManagedOrganization(
|
||||||
|
Organization organization,
|
||||||
|
SutProvider<OrganizationService> sutProvider)
|
||||||
|
{
|
||||||
|
var provider = new Provider
|
||||||
|
{
|
||||||
|
Enabled = true,
|
||||||
|
Type = ProviderType.Reseller
|
||||||
|
};
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IProviderRepository>().GetByOrganizationIdAsync(organization.Id).Returns(provider);
|
||||||
|
|
||||||
|
var (result, failureMessage) = await sutProvider.Sut.CanScaleAsync(organization, 10);
|
||||||
|
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Contains("Seat limit has been reached. Contact your provider to purchase additional seats.", failureMessage);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory, PaidOrganizationCustomize, BitAutoData]
|
[Theory, PaidOrganizationCustomize, BitAutoData]
|
||||||
public async Task Delete_Success(Organization organization, SutProvider<OrganizationService> sutProvider)
|
public async Task Delete_Success(Organization organization, SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user