mirror of
https://github.com/bitwarden/server.git
synced 2024-12-01 13:43:23 +01:00
[PM-13470] Allow creating clients for Multi-organization enterprise (#4977)
This commit is contained in:
parent
319b13d87e
commit
c5ae849b97
@ -392,7 +392,9 @@ public class ProviderService : IProviderService
|
|||||||
|
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
||||||
|
|
||||||
ThrowOnInvalidPlanType(organization.PlanType);
|
var provider = await _providerRepository.GetByIdAsync(providerId);
|
||||||
|
|
||||||
|
ThrowOnInvalidPlanType(provider.Type, organization.PlanType);
|
||||||
|
|
||||||
if (organization.UseSecretsManager)
|
if (organization.UseSecretsManager)
|
||||||
{
|
{
|
||||||
@ -407,8 +409,6 @@ public class ProviderService : IProviderService
|
|||||||
Key = key,
|
Key = key,
|
||||||
};
|
};
|
||||||
|
|
||||||
var provider = await _providerRepository.GetByIdAsync(providerId);
|
|
||||||
|
|
||||||
await ApplyProviderPriceRateAsync(organization, provider);
|
await ApplyProviderPriceRateAsync(organization, provider);
|
||||||
await _providerOrganizationRepository.CreateAsync(providerOrganization);
|
await _providerOrganizationRepository.CreateAsync(providerOrganization);
|
||||||
|
|
||||||
@ -547,7 +547,7 @@ public class ProviderService : IProviderService
|
|||||||
|
|
||||||
var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) && provider.IsBillable();
|
var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) && provider.IsBillable();
|
||||||
|
|
||||||
ThrowOnInvalidPlanType(organizationSignup.Plan, consolidatedBillingEnabled);
|
ThrowOnInvalidPlanType(provider.Type, organizationSignup.Plan, consolidatedBillingEnabled);
|
||||||
|
|
||||||
var (organization, _, defaultCollection) = consolidatedBillingEnabled
|
var (organization, _, defaultCollection) = consolidatedBillingEnabled
|
||||||
? await _organizationService.SignupClientAsync(organizationSignup)
|
? await _organizationService.SignupClientAsync(organizationSignup)
|
||||||
@ -687,11 +687,27 @@ public class ProviderService : IProviderService
|
|||||||
return confirmedOwnersIds.Except(providerUserIds).Any();
|
return confirmedOwnersIds.Except(providerUserIds).Any();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThrowOnInvalidPlanType(PlanType requestedType, bool consolidatedBillingEnabled = false)
|
private void ThrowOnInvalidPlanType(ProviderType providerType, PlanType requestedType, bool consolidatedBillingEnabled = false)
|
||||||
{
|
{
|
||||||
if (consolidatedBillingEnabled && requestedType is not (PlanType.TeamsMonthly or PlanType.EnterpriseMonthly))
|
if (consolidatedBillingEnabled)
|
||||||
{
|
{
|
||||||
throw new BadRequestException($"Providers cannot manage organizations with the plan type {requestedType}. Only Teams (Monthly) and Enterprise (Monthly) are allowed.");
|
switch (providerType)
|
||||||
|
{
|
||||||
|
case ProviderType.Msp:
|
||||||
|
if (requestedType is not (PlanType.TeamsMonthly or PlanType.EnterpriseMonthly))
|
||||||
|
{
|
||||||
|
throw new BadRequestException($"Managed Service Providers cannot manage organizations with the plan type {requestedType}. Only Teams (Monthly) and Enterprise (Monthly) are allowed.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ProviderType.MultiOrganizationEnterprise:
|
||||||
|
if (requestedType is not (PlanType.EnterpriseMonthly or PlanType.EnterpriseAnnually))
|
||||||
|
{
|
||||||
|
throw new BadRequestException($"Multi-organization Enterprise Providers cannot manage organizations with the plan type {requestedType}. Only Enterprise (Monthly) and Enterprise (Annually) are allowed.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BadRequestException($"Unsupported provider type {providerType}.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ProviderDisallowedOrganizationTypes.Contains(requestedType))
|
if (ProviderDisallowedOrganizationTypes.Contains(requestedType))
|
||||||
|
@ -209,16 +209,9 @@ public class ProviderBillingService(
|
|||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(provider);
|
ArgumentNullException.ThrowIfNull(provider);
|
||||||
|
|
||||||
if (provider.Type != ProviderType.Msp)
|
if (!provider.SupportsConsolidatedBilling())
|
||||||
{
|
{
|
||||||
logger.LogError("Non-MSP provider ({ProviderID}) cannot scale their seats", provider.Id);
|
logger.LogError("Provider ({ProviderID}) cannot scale their seats", provider.Id);
|
||||||
|
|
||||||
throw new BillingException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!planType.SupportsConsolidatedBilling())
|
|
||||||
{
|
|
||||||
logger.LogError("Cannot scale provider ({ProviderID}) seats for plan type {PlanType} as it does not support consolidated billing", provider.Id, planType.ToString());
|
|
||||||
|
|
||||||
throw new BillingException();
|
throw new BillingException();
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ public class CreateClientOrganizationRequestBody
|
|||||||
[Required(ErrorMessage = "'ownerEmail' must be provided")]
|
[Required(ErrorMessage = "'ownerEmail' must be provided")]
|
||||||
public string OwnerEmail { get; set; }
|
public string OwnerEmail { get; set; }
|
||||||
|
|
||||||
[EnumMatches<PlanType>(PlanType.TeamsMonthly, PlanType.EnterpriseMonthly, ErrorMessage = "'planType' must be Teams (Monthly) or Enterprise (Monthly)")]
|
[EnumMatches<PlanType>(PlanType.TeamsMonthly, PlanType.EnterpriseMonthly, PlanType.EnterpriseAnnually, ErrorMessage = "'planType' must be Teams (Monthly), Enterprise (Monthly) or Enterprise (Annually)")]
|
||||||
public PlanType PlanType { get; set; }
|
public PlanType PlanType { get; set; }
|
||||||
|
|
||||||
[Range(1, int.MaxValue, ErrorMessage = "'seats' must be greater than 0")]
|
[Range(1, int.MaxValue, ErrorMessage = "'seats' must be greater than 0")]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Billing.Entities;
|
using Bit.Core.Billing.Entities;
|
||||||
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Models;
|
using Bit.Core.Billing.Models;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Stripe;
|
using Stripe;
|
||||||
@ -35,6 +36,8 @@ public record ProviderSubscriptionResponse(
|
|||||||
var cadence = plan.IsAnnual ? _annualCadence : _monthlyCadence;
|
var cadence = plan.IsAnnual ? _annualCadence : _monthlyCadence;
|
||||||
return new ProviderPlanResponse(
|
return new ProviderPlanResponse(
|
||||||
plan.Name,
|
plan.Name,
|
||||||
|
plan.Type,
|
||||||
|
plan.ProductTier,
|
||||||
configuredProviderPlan.SeatMinimum,
|
configuredProviderPlan.SeatMinimum,
|
||||||
configuredProviderPlan.PurchasedSeats,
|
configuredProviderPlan.PurchasedSeats,
|
||||||
configuredProviderPlan.AssignedSeats,
|
configuredProviderPlan.AssignedSeats,
|
||||||
@ -59,6 +62,8 @@ public record ProviderSubscriptionResponse(
|
|||||||
|
|
||||||
public record ProviderPlanResponse(
|
public record ProviderPlanResponse(
|
||||||
string PlanName,
|
string PlanName,
|
||||||
|
PlanType Type,
|
||||||
|
ProductTierType ProductTier,
|
||||||
int SeatMinimum,
|
int SeatMinimum,
|
||||||
int PurchasedSeats,
|
int PurchasedSeats,
|
||||||
int AssignedSeats,
|
int AssignedSeats,
|
||||||
|
@ -43,5 +43,5 @@ public static class BillingExtensions
|
|||||||
};
|
};
|
||||||
|
|
||||||
public static bool SupportsConsolidatedBilling(this PlanType planType)
|
public static bool SupportsConsolidatedBilling(this PlanType planType)
|
||||||
=> planType is PlanType.TeamsMonthly or PlanType.EnterpriseMonthly;
|
=> planType is PlanType.TeamsMonthly or PlanType.EnterpriseMonthly or PlanType.EnterpriseAnnually;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user