1
0
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:
Jonas Hendrickx 2024-11-06 09:44:16 +01:00 committed by Cy Okeke
parent 319b13d87e
commit c5ae849b97
No known key found for this signature in database
GPG Key ID: 88B341B55C84B45C
5 changed files with 32 additions and 18 deletions

View File

@ -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))

View File

@ -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();
} }

View File

@ -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")]

View File

@ -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,

View File

@ -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;
} }