1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 16:12:49 -05:00

[AC 1451] Refactor staticstore plans and consuming logic (#3164)

* refactor the plan and create new objects

* initial commit

* Add new plan types

* continue the refactoring by adding new plantypes

* changes for plans

* Refactoring continues

* making changes for plan

* Fixing the failing test

* Fixing  whitespace

* Fix some in correct values

* Resolve the plan data

* rearranging the plan

* Make the plan more immutable

* Resolve the lint errors

* Fix the failing test

* Add custom plan

* Fix the failing test

* Fix the failing test

* resolve the failing addons after refactoring

* Refactoring

* Merge branch 'master' into ac-1451/refactor-staticstore-plans-and-consuming-logic

* merge from master

* Merge branch 'master' into ac-1451/refactor-staticstore-plans-and-consuming-logic

* format whitespace

* resolve the conflict

* Fix some pr comments

* Fixing some of the pr comments

* fixing some of the pr comments

* Resolve some pr comments

* Resolve pr comments

* Resolves some pr comments

* Resolving some or comments

* Resolve a failing test

* fix the failing test

* Resolving some pr comments

* Fix the failing test

* resolve pr comment

* add a using statement fir a failing test

---------

Co-authored-by: Thomas Rittson <trittson@bitwarden.com>
This commit is contained in:
cyprain-okeke
2023-10-17 15:56:35 +01:00
committed by GitHub
parent 1c3bd4d252
commit 8177821e8b
48 changed files with 1041 additions and 1173 deletions

View File

@ -175,19 +175,19 @@ public class OrganizationService : IOrganizationService
throw new NotFoundException();
}
var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType);
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType);
if (plan == null)
{
throw new BadRequestException("Existing plan not found.");
}
if (!plan.HasAdditionalStorageOption)
if (!plan.PasswordManager.HasAdditionalStorageOption)
{
throw new BadRequestException("Plan does not allow additional storage.");
}
var secret = await BillingHelpers.AdjustStorageAsync(_paymentService, organization, storageAdjustmentGb,
plan.StripeStoragePlanId);
plan.PasswordManager.StripeStoragePlanId);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.AdjustStorage, organization, _currentContext)
{
@ -233,21 +233,21 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException($"Cannot set max seat autoscaling below current seat count.");
}
var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType);
var plan = StaticStore.GetPlan(organization.PlanType);
if (plan == null)
{
throw new BadRequestException("Existing plan not found.");
}
if (!plan.AllowSeatAutoscale)
if (!plan.PasswordManager.AllowSeatAutoscale)
{
throw new BadRequestException("Your plan does not allow seat autoscaling.");
}
if (plan.MaxUsers.HasValue && maxAutoscaleSeats.HasValue &&
maxAutoscaleSeats > plan.MaxUsers)
if (plan.PasswordManager.MaxSeats.HasValue && maxAutoscaleSeats.HasValue &&
maxAutoscaleSeats > plan.PasswordManager.MaxSeats)
{
throw new BadRequestException(string.Concat($"Your plan has a seat limit of {plan.MaxUsers}, ",
throw new BadRequestException(string.Concat($"Your plan has a seat limit of {plan.PasswordManager.MaxSeats}, ",
$"but you have specified a max autoscale count of {maxAutoscaleSeats}.",
"Reduce your max autoscale seat count."));
}
@ -285,21 +285,21 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException("No subscription found.");
}
var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType);
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType);
if (plan == null)
{
throw new BadRequestException("Existing plan not found.");
}
if (!plan.HasAdditionalSeatsOption)
if (!plan.PasswordManager.HasAdditionalSeatsOption)
{
throw new BadRequestException("Plan does not allow additional seats.");
}
var newSeatTotal = organization.Seats.Value + seatAdjustment;
if (plan.BaseSeats > newSeatTotal)
if (plan.PasswordManager.BaseSeats > newSeatTotal)
{
throw new BadRequestException($"Plan has a minimum of {plan.BaseSeats} seats.");
throw new BadRequestException($"Plan has a minimum of {plan.PasswordManager.BaseSeats} seats.");
}
if (newSeatTotal <= 0)
@ -307,11 +307,11 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException("You must have at least 1 seat.");
}
var additionalSeats = newSeatTotal - plan.BaseSeats;
if (plan.MaxAdditionalSeats.HasValue && additionalSeats > plan.MaxAdditionalSeats.Value)
var additionalSeats = newSeatTotal - plan.PasswordManager.BaseSeats;
if (plan.PasswordManager.MaxAdditionalSeats.HasValue && additionalSeats > plan.PasswordManager.MaxAdditionalSeats.Value)
{
throw new BadRequestException($"Organization plan allows a maximum of " +
$"{plan.MaxAdditionalSeats.Value} additional seats.");
$"{plan.PasswordManager.MaxAdditionalSeats.Value} additional seats.");
}
if (!organization.Seats.HasValue || organization.Seats.Value > newSeatTotal)
@ -403,11 +403,10 @@ public class OrganizationService : IOrganizationService
public async Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup signup,
bool provider = false)
{
var passwordManagerPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == signup.Plan);
var plan = StaticStore.GetPlan(signup.Plan);
ValidatePasswordManagerPlan(passwordManagerPlan, signup);
ValidatePasswordManagerPlan(plan, signup);
var secretsManagerPlan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == signup.Plan);
if (signup.UseSecretsManager)
{
if (provider)
@ -415,7 +414,7 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException(
"Organizations with a Managed Service Provider do not support Secrets Manager.");
}
ValidateSecretsManagerPlan(secretsManagerPlan, signup);
ValidateSecretsManagerPlan(plan, signup);
}
if (!provider)
@ -430,25 +429,25 @@ public class OrganizationService : IOrganizationService
Name = signup.Name,
BillingEmail = signup.BillingEmail,
BusinessName = signup.BusinessName,
PlanType = passwordManagerPlan.Type,
Seats = (short)(passwordManagerPlan.BaseSeats + signup.AdditionalSeats),
MaxCollections = passwordManagerPlan.MaxCollections,
MaxStorageGb = !passwordManagerPlan.BaseStorageGb.HasValue ?
(short?)null : (short)(passwordManagerPlan.BaseStorageGb.Value + signup.AdditionalStorageGb),
UsePolicies = passwordManagerPlan.HasPolicies,
UseSso = passwordManagerPlan.HasSso,
UseGroups = passwordManagerPlan.HasGroups,
UseEvents = passwordManagerPlan.HasEvents,
UseDirectory = passwordManagerPlan.HasDirectory,
UseTotp = passwordManagerPlan.HasTotp,
Use2fa = passwordManagerPlan.Has2fa,
UseApi = passwordManagerPlan.HasApi,
UseResetPassword = passwordManagerPlan.HasResetPassword,
SelfHost = passwordManagerPlan.HasSelfHost,
UsersGetPremium = passwordManagerPlan.UsersGetPremium || signup.PremiumAccessAddon,
UseCustomPermissions = passwordManagerPlan.HasCustomPermissions,
UseScim = passwordManagerPlan.HasScim,
Plan = passwordManagerPlan.Name,
PlanType = plan!.Type,
Seats = (short)(plan.PasswordManager.BaseSeats + signup.AdditionalSeats),
MaxCollections = plan.PasswordManager.MaxCollections,
MaxStorageGb = !plan.PasswordManager.BaseStorageGb.HasValue ?
(short?)null : (short)(plan.PasswordManager.BaseStorageGb.Value + signup.AdditionalStorageGb),
UsePolicies = plan.HasPolicies,
UseSso = plan.HasSso,
UseGroups = plan.HasGroups,
UseEvents = plan.HasEvents,
UseDirectory = plan.HasDirectory,
UseTotp = plan.HasTotp,
Use2fa = plan.Has2fa,
UseApi = plan.HasApi,
UseResetPassword = plan.HasResetPassword,
SelfHost = plan.HasSelfHost,
UsersGetPremium = plan.UsersGetPremium || signup.PremiumAccessAddon,
UseCustomPermissions = plan.HasCustomPermissions,
UseScim = plan.HasScim,
Plan = plan.Name,
Gateway = null,
ReferenceData = signup.Owner.ReferenceData,
Enabled = true,
@ -464,12 +463,12 @@ public class OrganizationService : IOrganizationService
if (signup.UseSecretsManager)
{
organization.SmSeats = secretsManagerPlan.BaseSeats + signup.AdditionalSmSeats.GetValueOrDefault();
organization.SmServiceAccounts = secretsManagerPlan.BaseServiceAccount.GetValueOrDefault() +
organization.SmSeats = plan.SecretsManager.BaseSeats + signup.AdditionalSmSeats.GetValueOrDefault();
organization.SmServiceAccounts = plan.SecretsManager.BaseServiceAccount +
signup.AdditionalServiceAccounts.GetValueOrDefault();
}
if (passwordManagerPlan.Type == PlanType.Free && !provider)
if (plan.Type == PlanType.Free && !provider)
{
var adminCount =
await _organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(signup.Owner.Id);
@ -478,14 +477,10 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException("You can only be an admin of one free organization.");
}
}
else if (passwordManagerPlan.Type != PlanType.Free)
else if (plan.Type != PlanType.Free)
{
var purchaseOrganizationPlan = signup.UseSecretsManager
? StaticStore.Plans.Where(p => p.Type == signup.Plan).ToList()
: StaticStore.PasswordManagerPlans.Where(p => p.Type == signup.Plan).Take(1).ToList();
await _paymentService.PurchaseOrganizationAsync(organization, signup.PaymentMethodType.Value,
signup.PaymentToken, purchaseOrganizationPlan, signup.AdditionalStorageGb, signup.AdditionalSeats,
signup.PaymentToken, plan, signup.AdditionalStorageGb, signup.AdditionalSeats,
signup.PremiumAccessAddon, signup.TaxInfo, provider, signup.AdditionalSmSeats.GetValueOrDefault(),
signup.AdditionalServiceAccounts.GetValueOrDefault());
}
@ -495,8 +490,8 @@ public class OrganizationService : IOrganizationService
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.Signup, organization, _currentContext)
{
PlanName = passwordManagerPlan.Name,
PlanType = passwordManagerPlan.Type,
PlanName = plan.Name,
PlanType = plan.Type,
Seats = returnValue.Item1.Seats,
Storage = returnValue.Item1.MaxStorageGb,
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
@ -525,7 +520,7 @@ public class OrganizationService : IOrganizationService
}
if (license.PlanType != PlanType.Custom &&
StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == license.PlanType && !p.Disabled) == null)
StaticStore.Plans.FirstOrDefault(p => p.Type == license.PlanType && !p.Disabled) == null)
{
throw new BadRequestException("Plan not found.");
}
@ -1955,11 +1950,6 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException($"{productType} Plan not found.");
}
if (plan.BaseSeats + additionalSeats <= 0)
{
throw new BadRequestException($"You do not have any {productType} seats!");
}
if (additionalSeats < 0)
{
throw new BadRequestException($"You can't subtract {productType} seats!");
@ -1970,7 +1960,7 @@ public class OrganizationService : IOrganizationService
{
ValidatePlan(plan, upgrade.AdditionalSeats, "Password Manager");
if (plan.BaseSeats + upgrade.AdditionalSeats <= 0)
if (plan.PasswordManager.BaseSeats + upgrade.AdditionalSeats <= 0)
{
throw new BadRequestException($"You do not have any Password Manager seats!");
}
@ -1980,7 +1970,7 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException($"You can't subtract Password Manager seats!");
}
if (!plan.HasAdditionalStorageOption && upgrade.AdditionalStorageGb > 0)
if (!plan.PasswordManager.HasAdditionalStorageOption && upgrade.AdditionalStorageGb > 0)
{
throw new BadRequestException("Plan does not allow additional storage.");
}
@ -1990,29 +1980,39 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException("You can't subtract storage!");
}
if (!plan.HasPremiumAccessOption && upgrade.PremiumAccessAddon)
if (!plan.PasswordManager.HasPremiumAccessOption && upgrade.PremiumAccessAddon)
{
throw new BadRequestException("This plan does not allow you to buy the premium access addon.");
}
if (!plan.HasAdditionalSeatsOption && upgrade.AdditionalSeats > 0)
if (!plan.PasswordManager.HasAdditionalSeatsOption && upgrade.AdditionalSeats > 0)
{
throw new BadRequestException("Plan does not allow additional users.");
}
if (plan.HasAdditionalSeatsOption && plan.MaxAdditionalSeats.HasValue &&
upgrade.AdditionalSeats > plan.MaxAdditionalSeats.Value)
if (plan.PasswordManager.HasAdditionalSeatsOption && plan.PasswordManager.MaxAdditionalSeats.HasValue &&
upgrade.AdditionalSeats > plan.PasswordManager.MaxAdditionalSeats.Value)
{
throw new BadRequestException($"Selected plan allows a maximum of " +
$"{plan.MaxAdditionalSeats.GetValueOrDefault(0)} additional users.");
$"{plan.PasswordManager.MaxAdditionalSeats.GetValueOrDefault(0)} additional users.");
}
}
public void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade)
{
if (plan.SupportsSecretsManager == false)
{
throw new BadRequestException("Invalid Secrets Manager plan selected.");
}
ValidatePlan(plan, upgrade.AdditionalSmSeats.GetValueOrDefault(), "Secrets Manager");
if (!plan.HasAdditionalServiceAccountOption && upgrade.AdditionalServiceAccounts > 0)
if (plan.SecretsManager.BaseSeats + upgrade.AdditionalSmSeats <= 0)
{
throw new BadRequestException($"You do not have any Secrets Manager seats!");
}
if (!plan.SecretsManager.HasAdditionalServiceAccountOption && upgrade.AdditionalServiceAccounts > 0)
{
throw new BadRequestException("Plan does not allow additional Service Accounts.");
}
@ -2027,14 +2027,14 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException("You can't subtract Service Accounts!");
}
switch (plan.HasAdditionalSeatsOption)
switch (plan.SecretsManager.HasAdditionalSeatsOption)
{
case false when upgrade.AdditionalSmSeats > 0:
throw new BadRequestException("Plan does not allow additional users.");
case true when plan.MaxAdditionalSeats.HasValue &&
upgrade.AdditionalSmSeats > plan.MaxAdditionalSeats.Value:
case true when plan.SecretsManager.MaxAdditionalSeats.HasValue &&
upgrade.AdditionalSmSeats > plan.SecretsManager.MaxAdditionalSeats.Value:
throw new BadRequestException($"Selected plan allows a maximum of " +
$"{plan.MaxAdditionalSeats.GetValueOrDefault(0)} additional users.");
$"{plan.SecretsManager.MaxAdditionalSeats.GetValueOrDefault(0)} additional users.");
}
}
@ -2457,7 +2457,7 @@ public class OrganizationService : IOrganizationService
public async Task CreatePendingOrganization(Organization organization, string ownerEmail, ClaimsPrincipal user, IUserService userService, bool salesAssistedTrialStarted)
{
var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType);
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType);
if (plan is not { LegacyYear: null })
{
throw new BadRequestException("Invalid plan selected.");

View File

@ -49,7 +49,7 @@ public class StripePaymentService : IPaymentService
}
public async Task<string> PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType,
string paymentToken, List<StaticStore.Plan> plans, short additionalStorageGb,
string paymentToken, StaticStore.Plan plan, short additionalStorageGb,
int additionalSeats, bool premiumAccessAddon, TaxInfo taxInfo, bool provider = false,
int additionalSmSeats = 0, int additionalServiceAccount = 0)
{
@ -119,7 +119,7 @@ public class StripePaymentService : IPaymentService
}
}
var subCreateOptions = new OrganizationPurchaseSubscriptionOptions(org, plans, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon
var subCreateOptions = new OrganizationPurchaseSubscriptionOptions(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon
, additionalSmSeats, additionalServiceAccount);
Stripe.Customer customer = null;
@ -211,7 +211,7 @@ public class StripePaymentService : IPaymentService
private async Task ChangeOrganizationSponsorship(Organization org, OrganizationSponsorship sponsorship, bool applySponsorship)
{
var existingPlan = Utilities.StaticStore.GetPasswordManagerPlan(org.PlanType);
var existingPlan = Utilities.StaticStore.GetPlan(org.PlanType);
var sponsoredPlan = sponsorship != null ?
Utilities.StaticStore.GetSponsoredPlan(sponsorship.PlanSponsorshipType.Value) :
null;
@ -231,7 +231,7 @@ public class StripePaymentService : IPaymentService
public Task RemoveOrganizationSponsorshipAsync(Organization org, OrganizationSponsorship sponsorship) =>
ChangeOrganizationSponsorship(org, sponsorship, false);
public async Task<string> UpgradeFreeOrganizationAsync(Organization org, List<StaticStore.Plan> plans,
public async Task<string> UpgradeFreeOrganizationAsync(Organization org, StaticStore.Plan plan,
OrganizationUpgrade upgrade)
{
if (!string.IsNullOrWhiteSpace(org.GatewaySubscriptionId))
@ -266,7 +266,7 @@ public class StripePaymentService : IPaymentService
}
}
var subCreateOptions = new OrganizationUpgradeSubscriptionOptions(customer.Id, org, plans, upgrade);
var subCreateOptions = new OrganizationUpgradeSubscriptionOptions(customer.Id, org, plan, upgrade);
var (stripePaymentMethod, paymentMethodType) = IdentifyPaymentMethod(customer, subCreateOptions);
var subscription = await ChargeForNewSubscriptionAsync(org, customer, false,