mirror of
https://github.com/bitwarden/server.git
synced 2024-11-21 12:05:42 +01: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:
parent
1c3bd4d252
commit
8177821e8b
@ -28,8 +28,8 @@ public class MaxProjectsQuery : IMaxProjectsQuery
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var plan = StaticStore.GetSecretsManagerPlan(org.PlanType);
|
var plan = StaticStore.GetPlan(org.PlanType);
|
||||||
if (plan == null)
|
if (plan?.SecretsManager == null)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Existing plan not found.");
|
throw new BadRequestException("Existing plan not found.");
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ public class MaxProjectsQuery : IMaxProjectsQuery
|
|||||||
if (plan.Type == PlanType.Free)
|
if (plan.Type == PlanType.Free)
|
||||||
{
|
{
|
||||||
var projects = await _projectRepository.GetProjectCountByOrganizationIdAsync(organizationId);
|
var projects = await _projectRepository.GetProjectCountByOrganizationIdAsync(organizationId);
|
||||||
return projects + projectsToAdd > plan.MaxProjects ? (plan.MaxProjects, true) : (plan.MaxProjects, false);
|
return ((short? max, bool? overMax))(projects + projectsToAdd > plan.SecretsManager.MaxProjects ? (plan.SecretsManager.MaxProjects, true) : (plan.SecretsManager.MaxProjects, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (null, null);
|
return (null, null);
|
||||||
|
@ -205,9 +205,8 @@ public class OrganizationsController : Controller
|
|||||||
var organization = await GetOrganization(id, model);
|
var organization = await GetOrganization(id, model);
|
||||||
|
|
||||||
if (organization.UseSecretsManager &&
|
if (organization.UseSecretsManager &&
|
||||||
!organization.SecretsManagerBeta
|
!organization.SecretsManagerBeta &&
|
||||||
&& StaticStore.GetSecretsManagerPlan(organization.PlanType) == null
|
!StaticStore.GetPlan(organization.PlanType).SupportsSecretsManager)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Plan does not support Secrets Manager");
|
throw new BadRequestException("Plan does not support Secrets Manager");
|
||||||
}
|
}
|
||||||
|
@ -158,11 +158,12 @@ public class OrganizationEditModel : OrganizationViewModel
|
|||||||
* Add mappings for individual properties as you need them
|
* Add mappings for individual properties as you need them
|
||||||
*/
|
*/
|
||||||
public IEnumerable<Dictionary<string, object>> GetPlansHelper() =>
|
public IEnumerable<Dictionary<string, object>> GetPlansHelper() =>
|
||||||
StaticStore.SecretManagerPlans.Select(p =>
|
StaticStore.Plans
|
||||||
new Dictionary<string, object>
|
.Where(p => p.SupportsSecretsManager)
|
||||||
|
.Select(p => new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
{ "type", p.Type },
|
{ "type", p.Type },
|
||||||
{ "baseServiceAccount", p.BaseServiceAccount }
|
{ "baseServiceAccount", p.SecretsManager.BaseServiceAccount }
|
||||||
});
|
});
|
||||||
|
|
||||||
public Organization CreateOrganization(Provider provider)
|
public Organization CreateOrganization(Provider provider)
|
||||||
|
@ -540,7 +540,7 @@ public class OrganizationsController : Controller
|
|||||||
if (model.Type == OrganizationApiKeyType.BillingSync || model.Type == OrganizationApiKeyType.Scim)
|
if (model.Type == OrganizationApiKeyType.BillingSync || model.Type == OrganizationApiKeyType.Scim)
|
||||||
{
|
{
|
||||||
// Non-enterprise orgs should not be able to create or view an apikey of billing sync/scim key types
|
// Non-enterprise orgs should not be able to create or view an apikey of billing sync/scim key types
|
||||||
var plan = StaticStore.GetPasswordManagerPlan(organization.PlanType);
|
var plan = StaticStore.GetPlan(organization.PlanType);
|
||||||
if (plan.Product != ProductType.Enterprise)
|
if (plan.Product != ProductType.Enterprise)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
|
@ -19,30 +19,12 @@ public class PlansController : Controller
|
|||||||
[HttpGet("")]
|
[HttpGet("")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public ListResponseModel<PlanResponseModel> Get()
|
public ListResponseModel<PlanResponseModel> Get()
|
||||||
{
|
|
||||||
var data = StaticStore.PasswordManagerPlans;
|
|
||||||
var responses = data.Select(plan => new PlanResponseModel(plan));
|
|
||||||
return new ListResponseModel<PlanResponseModel>(responses);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("all")]
|
|
||||||
[AllowAnonymous]
|
|
||||||
public ListResponseModel<PlanResponseModel> GetAllPlans()
|
|
||||||
{
|
{
|
||||||
var data = StaticStore.Plans;
|
var data = StaticStore.Plans;
|
||||||
var responses = data.Select(plan => new PlanResponseModel(plan));
|
var responses = data.Select(plan => new PlanResponseModel(plan));
|
||||||
return new ListResponseModel<PlanResponseModel>(responses);
|
return new ListResponseModel<PlanResponseModel>(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("sm-plans")]
|
|
||||||
[AllowAnonymous]
|
|
||||||
public ListResponseModel<PlanResponseModel> GetSecretsManagerPlans()
|
|
||||||
{
|
|
||||||
var data = StaticStore.SecretManagerPlans;
|
|
||||||
var responses = data.Select(plan => new PlanResponseModel(plan));
|
|
||||||
return new ListResponseModel<PlanResponseModel>(responses);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("sales-tax-rates")]
|
[HttpGet("sales-tax-rates")]
|
||||||
public async Task<ListResponseModel<TaxRateResponseModel>> GetTaxRates()
|
public async Task<ListResponseModel<TaxRateResponseModel>> GetTaxRates()
|
||||||
{
|
{
|
||||||
|
@ -26,12 +26,7 @@ public class OrganizationResponseModel : ResponseModel
|
|||||||
BusinessCountry = organization.BusinessCountry;
|
BusinessCountry = organization.BusinessCountry;
|
||||||
BusinessTaxNumber = organization.BusinessTaxNumber;
|
BusinessTaxNumber = organization.BusinessTaxNumber;
|
||||||
BillingEmail = organization.BillingEmail;
|
BillingEmail = organization.BillingEmail;
|
||||||
Plan = new PlanResponseModel(StaticStore.PasswordManagerPlans.FirstOrDefault(plan => plan.Type == organization.PlanType));
|
Plan = new PlanResponseModel(StaticStore.GetPlan(organization.PlanType));
|
||||||
var matchingPlan = StaticStore.GetSecretsManagerPlan(organization.PlanType);
|
|
||||||
if (matchingPlan != null)
|
|
||||||
{
|
|
||||||
SecretsManagerPlan = new PlanResponseModel(matchingPlan);
|
|
||||||
}
|
|
||||||
PlanType = organization.PlanType;
|
PlanType = organization.PlanType;
|
||||||
Seats = organization.Seats;
|
Seats = organization.Seats;
|
||||||
MaxAutoscaleSeats = organization.MaxAutoscaleSeats;
|
MaxAutoscaleSeats = organization.MaxAutoscaleSeats;
|
||||||
|
@ -21,15 +21,6 @@ public class PlanResponseModel : ResponseModel
|
|||||||
NameLocalizationKey = plan.NameLocalizationKey;
|
NameLocalizationKey = plan.NameLocalizationKey;
|
||||||
DescriptionLocalizationKey = plan.DescriptionLocalizationKey;
|
DescriptionLocalizationKey = plan.DescriptionLocalizationKey;
|
||||||
CanBeUsedByBusiness = plan.CanBeUsedByBusiness;
|
CanBeUsedByBusiness = plan.CanBeUsedByBusiness;
|
||||||
BaseSeats = plan.BaseSeats;
|
|
||||||
BaseStorageGb = plan.BaseStorageGb;
|
|
||||||
MaxCollections = plan.MaxCollections;
|
|
||||||
MaxUsers = plan.MaxUsers;
|
|
||||||
HasAdditionalSeatsOption = plan.HasAdditionalSeatsOption;
|
|
||||||
HasAdditionalStorageOption = plan.HasAdditionalStorageOption;
|
|
||||||
MaxAdditionalSeats = plan.MaxAdditionalSeats;
|
|
||||||
MaxAdditionalStorage = plan.MaxAdditionalStorage;
|
|
||||||
HasPremiumAccessOption = plan.HasPremiumAccessOption;
|
|
||||||
TrialPeriodDays = plan.TrialPeriodDays;
|
TrialPeriodDays = plan.TrialPeriodDays;
|
||||||
HasSelfHost = plan.HasSelfHost;
|
HasSelfHost = plan.HasSelfHost;
|
||||||
HasPolicies = plan.HasPolicies;
|
HasPolicies = plan.HasPolicies;
|
||||||
@ -45,22 +36,12 @@ public class PlanResponseModel : ResponseModel
|
|||||||
DisplaySortOrder = plan.DisplaySortOrder;
|
DisplaySortOrder = plan.DisplaySortOrder;
|
||||||
LegacyYear = plan.LegacyYear;
|
LegacyYear = plan.LegacyYear;
|
||||||
Disabled = plan.Disabled;
|
Disabled = plan.Disabled;
|
||||||
StripePlanId = plan.StripePlanId;
|
if (plan.SecretsManager != null)
|
||||||
StripeSeatPlanId = plan.StripeSeatPlanId;
|
{
|
||||||
StripeStoragePlanId = plan.StripeStoragePlanId;
|
SecretsManager = new SecretsManagerPlanFeaturesResponseModel(plan.SecretsManager);
|
||||||
BasePrice = plan.BasePrice;
|
}
|
||||||
SeatPrice = plan.SeatPrice;
|
|
||||||
AdditionalStoragePricePerGb = plan.AdditionalStoragePricePerGb;
|
|
||||||
PremiumAccessOptionPrice = plan.PremiumAccessOptionPrice;
|
|
||||||
|
|
||||||
AdditionalPricePerServiceAccount = plan.AdditionalPricePerServiceAccount;
|
PasswordManager = new PasswordManagerPlanFeaturesResponseModel(plan.PasswordManager);
|
||||||
BaseServiceAccount = plan.BaseServiceAccount;
|
|
||||||
MaxServiceAccounts = plan.MaxServiceAccounts;
|
|
||||||
MaxAdditionalServiceAccounts = plan.MaxAdditionalServiceAccount;
|
|
||||||
HasAdditionalServiceAccountOption = plan.HasAdditionalServiceAccountOption;
|
|
||||||
MaxProjects = plan.MaxProjects;
|
|
||||||
BitwardenProduct = plan.BitwardenProduct;
|
|
||||||
StripeServiceAccountPlanId = plan.StripeServiceAccountPlanId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlanType Type { get; set; }
|
public PlanType Type { get; set; }
|
||||||
@ -70,16 +51,6 @@ public class PlanResponseModel : ResponseModel
|
|||||||
public string NameLocalizationKey { get; set; }
|
public string NameLocalizationKey { get; set; }
|
||||||
public string DescriptionLocalizationKey { get; set; }
|
public string DescriptionLocalizationKey { get; set; }
|
||||||
public bool CanBeUsedByBusiness { get; set; }
|
public bool CanBeUsedByBusiness { get; set; }
|
||||||
public int BaseSeats { get; set; }
|
|
||||||
public short? BaseStorageGb { get; set; }
|
|
||||||
public short? MaxCollections { get; set; }
|
|
||||||
public short? MaxUsers { get; set; }
|
|
||||||
|
|
||||||
public bool HasAdditionalSeatsOption { get; set; }
|
|
||||||
public int? MaxAdditionalSeats { get; set; }
|
|
||||||
public bool HasAdditionalStorageOption { get; set; }
|
|
||||||
public short? MaxAdditionalStorage { get; set; }
|
|
||||||
public bool HasPremiumAccessOption { get; set; }
|
|
||||||
public int? TrialPeriodDays { get; set; }
|
public int? TrialPeriodDays { get; set; }
|
||||||
|
|
||||||
public bool HasSelfHost { get; set; }
|
public bool HasSelfHost { get; set; }
|
||||||
@ -98,21 +69,95 @@ public class PlanResponseModel : ResponseModel
|
|||||||
public int DisplaySortOrder { get; set; }
|
public int DisplaySortOrder { get; set; }
|
||||||
public int? LegacyYear { get; set; }
|
public int? LegacyYear { get; set; }
|
||||||
public bool Disabled { get; set; }
|
public bool Disabled { get; set; }
|
||||||
|
public SecretsManagerPlanFeaturesResponseModel SecretsManager { get; protected init; }
|
||||||
|
public PasswordManagerPlanFeaturesResponseModel PasswordManager { get; protected init; }
|
||||||
|
|
||||||
public string StripePlanId { get; set; }
|
public class SecretsManagerPlanFeaturesResponseModel
|
||||||
public string StripeSeatPlanId { get; set; }
|
{
|
||||||
public string StripeStoragePlanId { get; set; }
|
public SecretsManagerPlanFeaturesResponseModel(Plan.SecretsManagerPlanFeatures plan)
|
||||||
public string StripePremiumAccessPlanId { get; set; }
|
{
|
||||||
public decimal BasePrice { get; set; }
|
MaxServiceAccounts = plan.MaxServiceAccounts;
|
||||||
public decimal SeatPrice { get; set; }
|
AllowServiceAccountsAutoscale = plan is { AllowServiceAccountsAutoscale: true };
|
||||||
public decimal AdditionalStoragePricePerGb { get; set; }
|
StripeServiceAccountPlanId = plan.StripeServiceAccountPlanId;
|
||||||
public decimal PremiumAccessOptionPrice { get; set; }
|
AdditionalPricePerServiceAccount = plan.AdditionalPricePerServiceAccount;
|
||||||
public string StripeServiceAccountPlanId { get; set; }
|
BaseServiceAccount = plan.BaseServiceAccount;
|
||||||
public decimal? AdditionalPricePerServiceAccount { get; set; }
|
MaxAdditionalServiceAccount = plan.MaxAdditionalServiceAccount;
|
||||||
public short? BaseServiceAccount { get; set; }
|
HasAdditionalServiceAccountOption = plan is { HasAdditionalServiceAccountOption: true };
|
||||||
public short? MaxServiceAccounts { get; set; }
|
StripeSeatPlanId = plan.StripeSeatPlanId;
|
||||||
public short? MaxAdditionalServiceAccounts { get; set; }
|
HasAdditionalSeatsOption = plan is { HasAdditionalSeatsOption: true };
|
||||||
public bool HasAdditionalServiceAccountOption { get; set; }
|
BasePrice = plan.BasePrice;
|
||||||
public short? MaxProjects { get; set; }
|
SeatPrice = plan.SeatPrice;
|
||||||
public BitwardenProductType BitwardenProduct { get; set; }
|
BaseSeats = plan.BaseSeats;
|
||||||
|
MaxSeats = plan.MaxSeats;
|
||||||
|
MaxAdditionalSeats = plan.MaxAdditionalSeats;
|
||||||
|
AllowSeatAutoscale = plan.AllowSeatAutoscale;
|
||||||
|
MaxProjects = plan.MaxProjects;
|
||||||
|
}
|
||||||
|
// Service accounts
|
||||||
|
public short? MaxServiceAccounts { get; init; }
|
||||||
|
public bool AllowServiceAccountsAutoscale { get; init; }
|
||||||
|
public string StripeServiceAccountPlanId { get; init; }
|
||||||
|
public decimal? AdditionalPricePerServiceAccount { get; init; }
|
||||||
|
public short? BaseServiceAccount { get; init; }
|
||||||
|
public short? MaxAdditionalServiceAccount { get; init; }
|
||||||
|
public bool HasAdditionalServiceAccountOption { get; init; }
|
||||||
|
// Seats
|
||||||
|
public string StripeSeatPlanId { get; init; }
|
||||||
|
public bool HasAdditionalSeatsOption { get; init; }
|
||||||
|
public decimal BasePrice { get; init; }
|
||||||
|
public decimal SeatPrice { get; init; }
|
||||||
|
public int BaseSeats { get; init; }
|
||||||
|
public short? MaxSeats { get; init; }
|
||||||
|
public int? MaxAdditionalSeats { get; init; }
|
||||||
|
public bool AllowSeatAutoscale { get; init; }
|
||||||
|
|
||||||
|
// Features
|
||||||
|
public int MaxProjects { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public record PasswordManagerPlanFeaturesResponseModel
|
||||||
|
{
|
||||||
|
public PasswordManagerPlanFeaturesResponseModel(Plan.PasswordManagerPlanFeatures plan)
|
||||||
|
{
|
||||||
|
StripePlanId = plan.StripePlanId;
|
||||||
|
StripeSeatPlanId = plan.StripeSeatPlanId;
|
||||||
|
BasePrice = plan.BasePrice;
|
||||||
|
SeatPrice = plan.SeatPrice;
|
||||||
|
AllowSeatAutoscale = plan.AllowSeatAutoscale;
|
||||||
|
HasAdditionalSeatsOption = plan.HasAdditionalSeatsOption;
|
||||||
|
MaxAdditionalSeats = plan.MaxAdditionalSeats;
|
||||||
|
BaseSeats = plan.BaseSeats;
|
||||||
|
HasPremiumAccessOption = plan.HasPremiumAccessOption;
|
||||||
|
StripePremiumAccessPlanId = plan.StripePremiumAccessPlanId;
|
||||||
|
PremiumAccessOptionPrice = plan.PremiumAccessOptionPrice;
|
||||||
|
MaxSeats = plan.MaxSeats;
|
||||||
|
BaseStorageGb = plan.BaseStorageGb;
|
||||||
|
HasAdditionalStorageOption = plan.HasAdditionalStorageOption;
|
||||||
|
AdditionalStoragePricePerGb = plan.AdditionalStoragePricePerGb;
|
||||||
|
StripeStoragePlanId = plan.StripeStoragePlanId;
|
||||||
|
MaxAdditionalStorage = plan.MaxAdditionalStorage;
|
||||||
|
MaxCollections = plan.MaxCollections;
|
||||||
|
}
|
||||||
|
// Seats
|
||||||
|
public string StripePlanId { get; init; }
|
||||||
|
public string StripeSeatPlanId { get; init; }
|
||||||
|
public decimal BasePrice { get; init; }
|
||||||
|
public decimal SeatPrice { get; init; }
|
||||||
|
public bool AllowSeatAutoscale { get; init; }
|
||||||
|
public bool HasAdditionalSeatsOption { get; init; }
|
||||||
|
public int? MaxAdditionalSeats { get; init; }
|
||||||
|
public int BaseSeats { get; init; }
|
||||||
|
public bool HasPremiumAccessOption { get; init; }
|
||||||
|
public string StripePremiumAccessPlanId { get; init; }
|
||||||
|
public decimal PremiumAccessOptionPrice { get; init; }
|
||||||
|
public short? MaxSeats { get; init; }
|
||||||
|
// Storage
|
||||||
|
public short? BaseStorageGb { get; init; }
|
||||||
|
public bool HasAdditionalStorageOption { get; init; }
|
||||||
|
public decimal AdditionalStoragePricePerGb { get; init; }
|
||||||
|
public string StripeStoragePlanId { get; init; }
|
||||||
|
public short? MaxAdditionalStorage { get; init; }
|
||||||
|
// Feature
|
||||||
|
public short? MaxCollections { get; init; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
FamilySponsorshipAvailable = FamilySponsorshipFriendlyName == null &&
|
FamilySponsorshipAvailable = FamilySponsorshipFriendlyName == null &&
|
||||||
StaticStore.GetSponsoredPlan(PlanSponsorshipType.FamiliesForEnterprise)
|
StaticStore.GetSponsoredPlan(PlanSponsorshipType.FamiliesForEnterprise)
|
||||||
.UsersCanSponsor(organization);
|
.UsersCanSponsor(organization);
|
||||||
PlanProductType = StaticStore.GetPasswordManagerPlan(organization.PlanType).Product;
|
PlanProductType = StaticStore.GetPlan(organization.PlanType).Product;
|
||||||
FamilySponsorshipLastSyncDate = organization.FamilySponsorshipLastSyncDate;
|
FamilySponsorshipLastSyncDate = organization.FamilySponsorshipLastSyncDate;
|
||||||
FamilySponsorshipToDelete = organization.FamilySponsorshipToDelete;
|
FamilySponsorshipToDelete = organization.FamilySponsorshipToDelete;
|
||||||
FamilySponsorshipValidUntil = organization.FamilySponsorshipValidUntil;
|
FamilySponsorshipValidUntil = organization.FamilySponsorshipValidUntil;
|
||||||
|
@ -42,6 +42,6 @@ public class ProfileProviderOrganizationResponseModel : ProfileOrganizationRespo
|
|||||||
UserId = organization.UserId;
|
UserId = organization.UserId;
|
||||||
ProviderId = organization.ProviderId;
|
ProviderId = organization.ProviderId;
|
||||||
ProviderName = organization.ProviderName;
|
ProviderName = organization.ProviderName;
|
||||||
PlanProductType = StaticStore.GetPasswordManagerPlan(organization.PlanType).Product;
|
PlanProductType = StaticStore.GetPlan(organization.PlanType).Product;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Models.Api;
|
using Bit.Core.Models.Api;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@ -98,7 +97,6 @@ public class BillingSubscription
|
|||||||
Quantity = item.Quantity;
|
Quantity = item.Quantity;
|
||||||
SponsoredSubscriptionItem = item.SponsoredSubscriptionItem;
|
SponsoredSubscriptionItem = item.SponsoredSubscriptionItem;
|
||||||
AddonSubscriptionItem = item.AddonSubscriptionItem;
|
AddonSubscriptionItem = item.AddonSubscriptionItem;
|
||||||
BitwardenProduct = item.BitwardenProduct;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
@ -107,7 +105,6 @@ public class BillingSubscription
|
|||||||
public string Interval { get; set; }
|
public string Interval { get; set; }
|
||||||
public bool SponsoredSubscriptionItem { get; set; }
|
public bool SponsoredSubscriptionItem { get; set; }
|
||||||
public bool AddonSubscriptionItem { get; set; }
|
public bool AddonSubscriptionItem { get; set; }
|
||||||
public BitwardenProductType BitwardenProduct { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,7 +447,7 @@ public class StripeController : Controller
|
|||||||
// org
|
// org
|
||||||
if (ids.Item1.HasValue)
|
if (ids.Item1.HasValue)
|
||||||
{
|
{
|
||||||
if (subscription.Items.Any(i => StaticStore.PasswordManagerPlans.Any(p => p.StripePlanId == i.Plan.Id)))
|
if (subscription.Items.Any(i => StaticStore.Plans.Any(p => p.PasswordManager.StripePlanId == i.Plan.Id)))
|
||||||
{
|
{
|
||||||
await _organizationService.EnableAsync(ids.Item1.Value, subscription.CurrentPeriodEnd);
|
await _organizationService.EnableAsync(ids.Item1.Value, subscription.CurrentPeriodEnd);
|
||||||
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace Bit.Core.Enums;
|
|
||||||
|
|
||||||
public enum BitwardenProductType : byte
|
|
||||||
{
|
|
||||||
[Display(Name = "Password Manager")]
|
|
||||||
PasswordManager = 0,
|
|
||||||
[Display(Name = "Secrets Manager")]
|
|
||||||
SecretsManager = 1,
|
|
||||||
}
|
|
@ -43,18 +43,18 @@ public class SecretsManagerSubscriptionUpdate
|
|||||||
/// The seats the organization will have after the update, excluding the base seats included in the plan
|
/// The seats the organization will have after the update, excluding the base seats included in the plan
|
||||||
/// Usually this is what the organization is billed for
|
/// Usually this is what the organization is billed for
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int SmSeatsExcludingBase => SmSeats.HasValue ? SmSeats.Value - Plan.BaseSeats : 0;
|
public int SmSeatsExcludingBase => SmSeats.HasValue ? SmSeats.Value - Plan.SecretsManager.BaseSeats : 0;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The seats the organization will have after the update, excluding the base seats included in the plan
|
/// The seats the organization will have after the update, excluding the base seats included in the plan
|
||||||
/// Usually this is what the organization is billed for
|
/// Usually this is what the organization is billed for
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int SmServiceAccountsExcludingBase => SmServiceAccounts.HasValue ? SmServiceAccounts.Value - Plan.BaseServiceAccount.GetValueOrDefault() : 0;
|
public int SmServiceAccountsExcludingBase => SmServiceAccounts.HasValue ? SmServiceAccounts.Value - Plan.SecretsManager!.BaseServiceAccount : 0;
|
||||||
public bool SmSeatsChanged => SmSeats != Organization.SmSeats;
|
public bool SmSeatsChanged => SmSeats != Organization.SmSeats;
|
||||||
public bool SmServiceAccountsChanged => SmServiceAccounts != Organization.SmServiceAccounts;
|
public bool SmServiceAccountsChanged => SmServiceAccounts != Organization.SmServiceAccounts;
|
||||||
public bool MaxAutoscaleSmSeatsChanged => MaxAutoscaleSmSeats != Organization.MaxAutoscaleSmSeats;
|
public bool MaxAutoscaleSmSeatsChanged => MaxAutoscaleSmSeats != Organization.MaxAutoscaleSmSeats;
|
||||||
public bool MaxAutoscaleSmServiceAccountsChanged =>
|
public bool MaxAutoscaleSmServiceAccountsChanged =>
|
||||||
MaxAutoscaleSmServiceAccounts != Organization.MaxAutoscaleSmServiceAccounts;
|
MaxAutoscaleSmServiceAccounts != Organization.MaxAutoscaleSmServiceAccounts;
|
||||||
public Plan Plan => Utilities.StaticStore.GetSecretsManagerPlan(Organization.PlanType);
|
public Plan Plan => Utilities.StaticStore.GetPlan(Organization.PlanType);
|
||||||
public bool SmSeatAutoscaleLimitReached => SmSeats.HasValue && MaxAutoscaleSmSeats.HasValue && SmSeats == MaxAutoscaleSmSeats;
|
public bool SmSeatAutoscaleLimitReached => SmSeats.HasValue && MaxAutoscaleSmSeats.HasValue && SmSeats == MaxAutoscaleSmSeats;
|
||||||
|
|
||||||
public bool SmServiceAccountAutoscaleLimitReached => SmServiceAccounts.HasValue &&
|
public bool SmServiceAccountAutoscaleLimitReached => SmServiceAccounts.HasValue &&
|
||||||
@ -70,7 +70,7 @@ public class SecretsManagerSubscriptionUpdate
|
|||||||
|
|
||||||
Organization = organization;
|
Organization = organization;
|
||||||
|
|
||||||
if (Plan == null)
|
if (!Plan.SupportsSecretsManager)
|
||||||
{
|
{
|
||||||
throw new NotFoundException("Invalid Secrets Manager plan.");
|
throw new NotFoundException("Invalid Secrets Manager plan.");
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Stripe;
|
using Stripe;
|
||||||
|
using Plan = Bit.Core.Models.StaticStore.Plan;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Business;
|
namespace Bit.Core.Models.Business;
|
||||||
|
|
||||||
public class OrganizationSubscriptionOptionsBase : Stripe.SubscriptionCreateOptions
|
public class OrganizationSubscriptionOptionsBase : Stripe.SubscriptionCreateOptions
|
||||||
{
|
{
|
||||||
public OrganizationSubscriptionOptionsBase(Organization org, List<StaticStore.Plan> plans, TaxInfo taxInfo, int additionalSeats,
|
public OrganizationSubscriptionOptionsBase(Organization org, StaticStore.Plan plan, TaxInfo taxInfo, int additionalSeats,
|
||||||
int additionalStorageGb, bool premiumAccessAddon, int additionalSmSeats, int additionalServiceAccounts)
|
int additionalStorageGb, bool premiumAccessAddon, int additionalSmSeats, int additionalServiceAccounts)
|
||||||
{
|
{
|
||||||
Items = new List<SubscriptionItemOptions>();
|
Items = new List<SubscriptionItemOptions>();
|
||||||
@ -14,79 +14,80 @@ public class OrganizationSubscriptionOptionsBase : Stripe.SubscriptionCreateOpti
|
|||||||
{
|
{
|
||||||
[org.GatewayIdField()] = org.Id.ToString()
|
[org.GatewayIdField()] = org.Id.ToString()
|
||||||
};
|
};
|
||||||
foreach (var plan in plans)
|
|
||||||
{
|
|
||||||
AddPlanIdToSubscription(plan);
|
|
||||||
|
|
||||||
switch (plan.BitwardenProduct)
|
AddPlanIdToSubscription(plan);
|
||||||
{
|
|
||||||
case BitwardenProductType.PasswordManager:
|
if (org.UseSecretsManager)
|
||||||
{
|
{
|
||||||
AddPremiumAccessAddon(premiumAccessAddon, plan);
|
AddSecretsManagerSeat(plan, additionalSmSeats);
|
||||||
AddAdditionalSeatToSubscription(additionalSeats, plan);
|
AddServiceAccount(plan, additionalServiceAccounts);
|
||||||
AddAdditionalStorage(additionalStorageGb, plan);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BitwardenProductType.SecretsManager:
|
|
||||||
{
|
|
||||||
AddAdditionalSeatToSubscription(additionalSmSeats, plan);
|
|
||||||
AddServiceAccount(additionalServiceAccounts, plan);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddPremiumAccessAddon(plan, premiumAccessAddon);
|
||||||
|
AddPasswordManagerSeat(plan, additionalSeats);
|
||||||
|
AddAdditionalStorage(plan, additionalStorageGb);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(taxInfo?.StripeTaxRateId))
|
if (!string.IsNullOrWhiteSpace(taxInfo?.StripeTaxRateId))
|
||||||
{
|
{
|
||||||
DefaultTaxRates = new List<string> { taxInfo.StripeTaxRateId };
|
DefaultTaxRates = new List<string> { taxInfo.StripeTaxRateId };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddServiceAccount(int additionalServiceAccounts, StaticStore.Plan plan)
|
private void AddSecretsManagerSeat(Plan plan, int additionalSmSeats)
|
||||||
{
|
{
|
||||||
if (additionalServiceAccounts > 0 && plan.StripeServiceAccountPlanId != null)
|
if (additionalSmSeats > 0 && plan.SecretsManager.StripeSeatPlanId != null)
|
||||||
|
{
|
||||||
|
Items.Add(new SubscriptionItemOptions
|
||||||
|
{ Plan = plan.SecretsManager.StripeSeatPlanId, Quantity = additionalSmSeats });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddPasswordManagerSeat(Plan plan, int additionalSeats)
|
||||||
|
{
|
||||||
|
if (additionalSeats > 0 && plan.PasswordManager.StripeSeatPlanId != null)
|
||||||
|
{
|
||||||
|
Items.Add(new SubscriptionItemOptions
|
||||||
|
{ Plan = plan.PasswordManager.StripeSeatPlanId, Quantity = additionalSeats });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddServiceAccount(StaticStore.Plan plan, int additionalServiceAccounts)
|
||||||
|
{
|
||||||
|
if (additionalServiceAccounts > 0 && plan.SecretsManager.StripeServiceAccountPlanId != null)
|
||||||
{
|
{
|
||||||
Items.Add(new SubscriptionItemOptions
|
Items.Add(new SubscriptionItemOptions
|
||||||
{
|
{
|
||||||
Plan = plan.StripeServiceAccountPlanId,
|
Plan = plan.SecretsManager.StripeServiceAccountPlanId,
|
||||||
Quantity = additionalServiceAccounts
|
Quantity = additionalServiceAccounts
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddAdditionalStorage(int additionalStorageGb, StaticStore.Plan plan)
|
private void AddAdditionalStorage(StaticStore.Plan plan, int additionalStorageGb)
|
||||||
{
|
{
|
||||||
if (additionalStorageGb > 0)
|
if (additionalStorageGb > 0)
|
||||||
{
|
{
|
||||||
Items.Add(new SubscriptionItemOptions
|
Items.Add(new SubscriptionItemOptions
|
||||||
{
|
{
|
||||||
Plan = plan.StripeStoragePlanId,
|
Plan = plan.PasswordManager.StripeStoragePlanId,
|
||||||
Quantity = additionalStorageGb
|
Quantity = additionalStorageGb
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddPremiumAccessAddon(bool premiumAccessAddon, StaticStore.Plan plan)
|
private void AddPremiumAccessAddon(StaticStore.Plan plan, bool premiumAccessAddon)
|
||||||
{
|
{
|
||||||
if (premiumAccessAddon && plan.StripePremiumAccessPlanId != null)
|
if (premiumAccessAddon && plan.PasswordManager.StripePremiumAccessPlanId != null)
|
||||||
{
|
{
|
||||||
Items.Add(new SubscriptionItemOptions { Plan = plan.StripePremiumAccessPlanId, Quantity = 1 });
|
Items.Add(new SubscriptionItemOptions { Plan = plan.PasswordManager.StripePremiumAccessPlanId, Quantity = 1 });
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddAdditionalSeatToSubscription(int additionalSeats, StaticStore.Plan plan)
|
|
||||||
{
|
|
||||||
if (additionalSeats > 0 && plan.StripeSeatPlanId != null)
|
|
||||||
{
|
|
||||||
Items.Add(new SubscriptionItemOptions { Plan = plan.StripeSeatPlanId, Quantity = additionalSeats });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddPlanIdToSubscription(StaticStore.Plan plan)
|
private void AddPlanIdToSubscription(StaticStore.Plan plan)
|
||||||
{
|
{
|
||||||
if (plan.StripePlanId != null)
|
if (plan.PasswordManager.StripePlanId != null)
|
||||||
{
|
{
|
||||||
Items.Add(new SubscriptionItemOptions { Plan = plan.StripePlanId, Quantity = 1 });
|
Items.Add(new SubscriptionItemOptions { Plan = plan.PasswordManager.StripePlanId, Quantity = 1 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,14 +95,14 @@ public class OrganizationSubscriptionOptionsBase : Stripe.SubscriptionCreateOpti
|
|||||||
public class OrganizationPurchaseSubscriptionOptions : OrganizationSubscriptionOptionsBase
|
public class OrganizationPurchaseSubscriptionOptions : OrganizationSubscriptionOptionsBase
|
||||||
{
|
{
|
||||||
public OrganizationPurchaseSubscriptionOptions(
|
public OrganizationPurchaseSubscriptionOptions(
|
||||||
Organization org, List<StaticStore.Plan> plans,
|
Organization org, StaticStore.Plan plan,
|
||||||
TaxInfo taxInfo, int additionalSeats,
|
TaxInfo taxInfo, int additionalSeats,
|
||||||
int additionalStorageGb, bool premiumAccessAddon,
|
int additionalStorageGb, bool premiumAccessAddon,
|
||||||
int additionalSmSeats, int additionalServiceAccounts) :
|
int additionalSmSeats, int additionalServiceAccounts) :
|
||||||
base(org, plans, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon, additionalSmSeats, additionalServiceAccounts)
|
base(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon, additionalSmSeats, additionalServiceAccounts)
|
||||||
{
|
{
|
||||||
OffSession = true;
|
OffSession = true;
|
||||||
TrialPeriodDays = plans.FirstOrDefault(x => x.BitwardenProduct == BitwardenProductType.PasswordManager)!.TrialPeriodDays;
|
TrialPeriodDays = plan.TrialPeriodDays;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,8 +110,8 @@ public class OrganizationUpgradeSubscriptionOptions : OrganizationSubscriptionOp
|
|||||||
{
|
{
|
||||||
public OrganizationUpgradeSubscriptionOptions(
|
public OrganizationUpgradeSubscriptionOptions(
|
||||||
string customerId, Organization org,
|
string customerId, Organization org,
|
||||||
List<StaticStore.Plan> plans, OrganizationUpgrade upgrade) :
|
StaticStore.Plan plan, OrganizationUpgrade upgrade) :
|
||||||
base(org, plans, upgrade.TaxInfo, upgrade.AdditionalSeats, upgrade.AdditionalStorageGb,
|
base(org, plan, upgrade.TaxInfo, upgrade.AdditionalSeats, upgrade.AdditionalStorageGb,
|
||||||
upgrade.PremiumAccessAddon, upgrade.AdditionalSmSeats.GetValueOrDefault(),
|
upgrade.PremiumAccessAddon, upgrade.AdditionalSmSeats.GetValueOrDefault(),
|
||||||
upgrade.AdditionalServiceAccounts.GetValueOrDefault())
|
upgrade.AdditionalServiceAccounts.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Bit.Core.Enums;
|
using Stripe;
|
||||||
using Stripe;
|
|
||||||
|
|
||||||
namespace Bit.Core.Models.Business;
|
namespace Bit.Core.Models.Business;
|
||||||
|
|
||||||
@ -64,16 +63,12 @@ public class SubscriptionInfo
|
|||||||
Interval = item.Plan.Interval;
|
Interval = item.Plan.Interval;
|
||||||
AddonSubscriptionItem =
|
AddonSubscriptionItem =
|
||||||
Utilities.StaticStore.IsAddonSubscriptionItem(item.Plan.Id);
|
Utilities.StaticStore.IsAddonSubscriptionItem(item.Plan.Id);
|
||||||
BitwardenProduct =
|
|
||||||
Utilities.StaticStore.GetPlanByStripeId(item.Plan.Id)?.BitwardenProduct ?? BitwardenProductType.PasswordManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Quantity = (int)item.Quantity;
|
Quantity = (int)item.Quantity;
|
||||||
SponsoredSubscriptionItem = Utilities.StaticStore.SponsoredPlans.Any(p => p.StripePlanId == item.Plan.Id);
|
SponsoredSubscriptionItem = Utilities.StaticStore.SponsoredPlans.Any(p => p.StripePlanId == item.Plan.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BitwardenProductType BitwardenProduct { get; set; }
|
|
||||||
|
|
||||||
public bool AddonSubscriptionItem { get; set; }
|
public bool AddonSubscriptionItem { get; set; }
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Stripe;
|
using Stripe;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Business;
|
namespace Bit.Core.Models.Business;
|
||||||
@ -30,29 +29,23 @@ public abstract class SubscriptionUpdate
|
|||||||
planId == null ? null : subscription.Items?.Data?.FirstOrDefault(i => i.Plan.Id == planId);
|
planId == null ? null : subscription.Items?.Data?.FirstOrDefault(i => i.Plan.Id == planId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SeatSubscriptionUpdate : SubscriptionUpdate
|
public abstract class BaseSeatSubscriptionUpdate : SubscriptionUpdate
|
||||||
{
|
{
|
||||||
private readonly int _previousSeats;
|
private readonly int _previousSeats;
|
||||||
private readonly StaticStore.Plan _plan;
|
protected readonly StaticStore.Plan Plan;
|
||||||
private readonly long? _additionalSeats;
|
private readonly long? _additionalSeats;
|
||||||
protected override List<string> PlanIds => new() { _plan.StripeSeatPlanId };
|
|
||||||
|
|
||||||
|
protected BaseSeatSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats, int previousSeats)
|
||||||
public SeatSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats)
|
|
||||||
{
|
{
|
||||||
_plan = plan;
|
Plan = plan;
|
||||||
_additionalSeats = additionalSeats;
|
_additionalSeats = additionalSeats;
|
||||||
switch (plan.BitwardenProduct)
|
_previousSeats = previousSeats;
|
||||||
{
|
|
||||||
case BitwardenProductType.PasswordManager:
|
|
||||||
_previousSeats = organization.Seats.GetValueOrDefault();
|
|
||||||
break;
|
|
||||||
case BitwardenProductType.SecretsManager:
|
|
||||||
_previousSeats = organization.SmSeats.GetValueOrDefault();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract string GetPlanId();
|
||||||
|
|
||||||
|
protected override List<string> PlanIds => new() { GetPlanId() };
|
||||||
|
|
||||||
public override List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription)
|
public override List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription)
|
||||||
{
|
{
|
||||||
var item = SubscriptionItem(subscription, PlanIds.Single());
|
var item = SubscriptionItem(subscription, PlanIds.Single());
|
||||||
@ -85,12 +78,30 @@ public class SeatSubscriptionUpdate : SubscriptionUpdate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SeatSubscriptionUpdate : BaseSeatSubscriptionUpdate
|
||||||
|
{
|
||||||
|
public SeatSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats)
|
||||||
|
: base(organization, plan, additionalSeats, organization.Seats.GetValueOrDefault())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected override string GetPlanId() => Plan.PasswordManager.StripeSeatPlanId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SmSeatSubscriptionUpdate : BaseSeatSubscriptionUpdate
|
||||||
|
{
|
||||||
|
public SmSeatSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats)
|
||||||
|
: base(organization, plan, additionalSeats, organization.SmSeats.GetValueOrDefault())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected override string GetPlanId() => Plan.SecretsManager.StripeSeatPlanId;
|
||||||
|
}
|
||||||
|
|
||||||
public class ServiceAccountSubscriptionUpdate : SubscriptionUpdate
|
public class ServiceAccountSubscriptionUpdate : SubscriptionUpdate
|
||||||
{
|
{
|
||||||
private long? _prevServiceAccounts;
|
private long? _prevServiceAccounts;
|
||||||
private readonly StaticStore.Plan _plan;
|
private readonly StaticStore.Plan _plan;
|
||||||
private readonly long? _additionalServiceAccounts;
|
private readonly long? _additionalServiceAccounts;
|
||||||
protected override List<string> PlanIds => new() { _plan.StripeServiceAccountPlanId };
|
protected override List<string> PlanIds => new() { _plan.SecretsManager.StripeServiceAccountPlanId };
|
||||||
|
|
||||||
public ServiceAccountSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalServiceAccounts)
|
public ServiceAccountSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalServiceAccounts)
|
||||||
{
|
{
|
||||||
@ -190,7 +201,7 @@ public class SponsorOrganizationSubscriptionUpdate : SubscriptionUpdate
|
|||||||
|
|
||||||
public SponsorOrganizationSubscriptionUpdate(StaticStore.Plan existingPlan, StaticStore.SponsoredPlan sponsoredPlan, bool applySponsorship)
|
public SponsorOrganizationSubscriptionUpdate(StaticStore.Plan existingPlan, StaticStore.SponsoredPlan sponsoredPlan, bool applySponsorship)
|
||||||
{
|
{
|
||||||
_existingPlanStripeId = existingPlan.StripePlanId;
|
_existingPlanStripeId = existingPlan.PasswordManager.StripePlanId;
|
||||||
_sponsoredPlanStripeId = sponsoredPlan?.StripePlanId;
|
_sponsoredPlanStripeId = sponsoredPlan?.StripePlanId;
|
||||||
_applySponsorship = applySponsorship;
|
_applySponsorship = applySponsorship;
|
||||||
}
|
}
|
||||||
@ -269,7 +280,7 @@ public class SecretsManagerSubscribeUpdate : SubscriptionUpdate
|
|||||||
private readonly long? _additionalServiceAccounts;
|
private readonly long? _additionalServiceAccounts;
|
||||||
private readonly int _previousSeats;
|
private readonly int _previousSeats;
|
||||||
private readonly int _previousServiceAccounts;
|
private readonly int _previousServiceAccounts;
|
||||||
protected override List<string> PlanIds => new() { _plan.StripeSeatPlanId, _plan.StripeServiceAccountPlanId };
|
protected override List<string> PlanIds => new() { _plan.SecretsManager.StripeSeatPlanId, _plan.SecretsManager.StripeServiceAccountPlanId };
|
||||||
public SecretsManagerSubscribeUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats, long? additionalServiceAccounts)
|
public SecretsManagerSubscribeUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats, long? additionalServiceAccounts)
|
||||||
{
|
{
|
||||||
_plan = plan;
|
_plan = plan;
|
||||||
@ -303,7 +314,7 @@ public class SecretsManagerSubscribeUpdate : SubscriptionUpdate
|
|||||||
{
|
{
|
||||||
updatedItems.Add(new SubscriptionItemOptions
|
updatedItems.Add(new SubscriptionItemOptions
|
||||||
{
|
{
|
||||||
Price = _plan.StripeSeatPlanId,
|
Price = _plan.SecretsManager.StripeSeatPlanId,
|
||||||
Quantity = _additionalSeats
|
Quantity = _additionalSeats
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -312,7 +323,7 @@ public class SecretsManagerSubscribeUpdate : SubscriptionUpdate
|
|||||||
{
|
{
|
||||||
updatedItems.Add(new SubscriptionItemOptions
|
updatedItems.Add(new SubscriptionItemOptions
|
||||||
{
|
{
|
||||||
Price = _plan.StripeServiceAccountPlanId,
|
Price = _plan.SecretsManager.StripeServiceAccountPlanId,
|
||||||
Quantity = _additionalServiceAccounts
|
Quantity = _additionalServiceAccounts
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -322,14 +333,14 @@ public class SecretsManagerSubscribeUpdate : SubscriptionUpdate
|
|||||||
{
|
{
|
||||||
updatedItems.Add(new SubscriptionItemOptions
|
updatedItems.Add(new SubscriptionItemOptions
|
||||||
{
|
{
|
||||||
Price = _plan.StripeSeatPlanId,
|
Price = _plan.SecretsManager.StripeSeatPlanId,
|
||||||
Quantity = _previousSeats,
|
Quantity = _previousSeats,
|
||||||
Deleted = _previousSeats == 0 ? true : (bool?)null,
|
Deleted = _previousSeats == 0 ? true : (bool?)null,
|
||||||
});
|
});
|
||||||
|
|
||||||
updatedItems.Add(new SubscriptionItemOptions
|
updatedItems.Add(new SubscriptionItemOptions
|
||||||
{
|
{
|
||||||
Price = _plan.StripeServiceAccountPlanId,
|
Price = _plan.SecretsManager.StripeServiceAccountPlanId,
|
||||||
Quantity = _previousServiceAccounts,
|
Quantity = _previousServiceAccounts,
|
||||||
Deleted = _previousServiceAccounts == 0 ? true : (bool?)null,
|
Deleted = _previousServiceAccounts == 0 ? true : (bool?)null,
|
||||||
});
|
});
|
||||||
|
@ -2,64 +2,84 @@
|
|||||||
|
|
||||||
namespace Bit.Core.Models.StaticStore;
|
namespace Bit.Core.Models.StaticStore;
|
||||||
|
|
||||||
public class Plan
|
public abstract record Plan
|
||||||
{
|
{
|
||||||
public PlanType Type { get; set; }
|
public PlanType Type { get; protected init; }
|
||||||
public ProductType Product { get; set; }
|
public ProductType Product { get; protected init; }
|
||||||
public string Name { get; set; }
|
public string Name { get; protected init; }
|
||||||
public bool IsAnnual { get; set; }
|
public bool IsAnnual { get; protected init; }
|
||||||
public string NameLocalizationKey { get; set; }
|
public string NameLocalizationKey { get; protected init; }
|
||||||
public string DescriptionLocalizationKey { get; set; }
|
public string DescriptionLocalizationKey { get; protected init; }
|
||||||
public bool CanBeUsedByBusiness { get; set; }
|
public bool CanBeUsedByBusiness { get; protected init; }
|
||||||
public int BaseSeats { get; set; }
|
public int? TrialPeriodDays { get; protected init; }
|
||||||
public short? BaseStorageGb { get; set; }
|
public bool HasSelfHost { get; protected init; }
|
||||||
public short? MaxCollections { get; set; }
|
public bool HasPolicies { get; protected init; }
|
||||||
public short? MaxUsers { get; set; }
|
public bool HasGroups { get; protected init; }
|
||||||
public short? MaxServiceAccounts { get; set; }
|
public bool HasDirectory { get; protected init; }
|
||||||
public bool AllowSeatAutoscale { get; set; }
|
public bool HasEvents { get; protected init; }
|
||||||
|
public bool HasTotp { get; protected init; }
|
||||||
|
public bool Has2fa { get; protected init; }
|
||||||
|
public bool HasApi { get; protected init; }
|
||||||
|
public bool HasSso { get; protected init; }
|
||||||
|
public bool HasKeyConnector { get; protected init; }
|
||||||
|
public bool HasScim { get; protected init; }
|
||||||
|
public bool HasResetPassword { get; protected init; }
|
||||||
|
public bool UsersGetPremium { get; protected init; }
|
||||||
|
public bool HasCustomPermissions { get; protected init; }
|
||||||
|
public int UpgradeSortOrder { get; protected init; }
|
||||||
|
public int DisplaySortOrder { get; protected init; }
|
||||||
|
public int? LegacyYear { get; protected init; }
|
||||||
|
public bool Disabled { get; protected init; }
|
||||||
|
public PasswordManagerPlanFeatures PasswordManager { get; protected init; }
|
||||||
|
public SecretsManagerPlanFeatures SecretsManager { get; protected init; }
|
||||||
|
public bool SupportsSecretsManager => SecretsManager != null;
|
||||||
|
|
||||||
public bool AllowServiceAccountsAutoscale { get; set; }
|
public record SecretsManagerPlanFeatures
|
||||||
|
{
|
||||||
|
// Service accounts
|
||||||
|
public short? MaxServiceAccounts { get; init; }
|
||||||
|
public bool AllowServiceAccountsAutoscale { get; init; }
|
||||||
|
public string StripeServiceAccountPlanId { get; init; }
|
||||||
|
public decimal? AdditionalPricePerServiceAccount { get; init; }
|
||||||
|
public short BaseServiceAccount { get; init; }
|
||||||
|
public short? MaxAdditionalServiceAccount { get; init; }
|
||||||
|
public bool HasAdditionalServiceAccountOption { get; init; }
|
||||||
|
// Seats
|
||||||
|
public string StripeSeatPlanId { get; init; }
|
||||||
|
public bool HasAdditionalSeatsOption { get; init; }
|
||||||
|
public decimal BasePrice { get; init; }
|
||||||
|
public decimal SeatPrice { get; init; }
|
||||||
|
public int BaseSeats { get; init; }
|
||||||
|
public short? MaxSeats { get; init; }
|
||||||
|
public int? MaxAdditionalSeats { get; init; }
|
||||||
|
public bool AllowSeatAutoscale { get; init; }
|
||||||
|
|
||||||
public bool HasAdditionalSeatsOption { get; set; }
|
// Features
|
||||||
public int? MaxAdditionalSeats { get; set; }
|
public int MaxProjects { get; init; }
|
||||||
public bool HasAdditionalStorageOption { get; set; }
|
}
|
||||||
public short? MaxAdditionalStorage { get; set; }
|
|
||||||
public bool HasPremiumAccessOption { get; set; }
|
|
||||||
public int? TrialPeriodDays { get; set; }
|
|
||||||
|
|
||||||
public bool HasSelfHost { get; set; }
|
public record PasswordManagerPlanFeatures
|
||||||
public bool HasPolicies { get; set; }
|
{
|
||||||
public bool HasGroups { get; set; }
|
// Seats
|
||||||
public bool HasDirectory { get; set; }
|
public string StripePlanId { get; init; }
|
||||||
public bool HasEvents { get; set; }
|
public string StripeSeatPlanId { get; init; }
|
||||||
public bool HasTotp { get; set; }
|
public decimal BasePrice { get; init; }
|
||||||
public bool Has2fa { get; set; }
|
public decimal SeatPrice { get; init; }
|
||||||
public bool HasApi { get; set; }
|
public bool AllowSeatAutoscale { get; init; }
|
||||||
public bool HasSso { get; set; }
|
public bool HasAdditionalSeatsOption { get; init; }
|
||||||
public bool HasKeyConnector { get; set; }
|
public int? MaxAdditionalSeats { get; init; }
|
||||||
public bool HasScim { get; set; }
|
public int BaseSeats { get; init; }
|
||||||
public bool HasResetPassword { get; set; }
|
public bool HasPremiumAccessOption { get; init; }
|
||||||
public bool UsersGetPremium { get; set; }
|
public string StripePremiumAccessPlanId { get; init; }
|
||||||
public bool HasCustomPermissions { get; set; }
|
public decimal PremiumAccessOptionPrice { get; init; }
|
||||||
|
public short? MaxSeats { get; init; }
|
||||||
public int UpgradeSortOrder { get; set; }
|
// Storage
|
||||||
public int DisplaySortOrder { get; set; }
|
public short? BaseStorageGb { get; init; }
|
||||||
public int? LegacyYear { get; set; }
|
public bool HasAdditionalStorageOption { get; init; }
|
||||||
public bool Disabled { get; set; }
|
public decimal AdditionalStoragePricePerGb { get; init; }
|
||||||
|
public string StripeStoragePlanId { get; init; }
|
||||||
public string StripePlanId { get; set; }
|
public short? MaxAdditionalStorage { get; init; }
|
||||||
public string StripeSeatPlanId { get; set; }
|
// Feature
|
||||||
public string StripeStoragePlanId { get; set; }
|
public short? MaxCollections { get; init; }
|
||||||
public string StripeServiceAccountPlanId { get; set; }
|
}
|
||||||
public string StripePremiumAccessPlanId { get; set; }
|
|
||||||
public decimal BasePrice { get; set; }
|
|
||||||
public decimal SeatPrice { get; set; }
|
|
||||||
public decimal AdditionalStoragePricePerGb { get; set; }
|
|
||||||
public decimal PremiumAccessOptionPrice { get; set; }
|
|
||||||
public decimal? AdditionalPricePerServiceAccount { get; set; }
|
|
||||||
public short? BaseServiceAccount { get; set; }
|
|
||||||
public short? MaxAdditionalServiceAccount { get; set; }
|
|
||||||
public bool HasAdditionalServiceAccountOption { get; set; }
|
|
||||||
public short? MaxProjects { get; set; }
|
|
||||||
public BitwardenProductType BitwardenProduct { get; set; }
|
|
||||||
}
|
}
|
||||||
|
20
src/Core/Models/StaticStore/Plans/CustomPlan.cs
Normal file
20
src/Core/Models/StaticStore/Plans/CustomPlan.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.StaticStore.Plans;
|
||||||
|
|
||||||
|
public record CustomPlan : Models.StaticStore.Plan
|
||||||
|
{
|
||||||
|
public CustomPlan()
|
||||||
|
{
|
||||||
|
Type = PlanType.Custom;
|
||||||
|
PasswordManager = new CustomPasswordManagerFeatures();
|
||||||
|
}
|
||||||
|
|
||||||
|
private record CustomPasswordManagerFeatures : PasswordManagerPlanFeatures
|
||||||
|
{
|
||||||
|
public CustomPasswordManagerFeatures()
|
||||||
|
{
|
||||||
|
AllowSeatAutoscale = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
src/Core/Models/StaticStore/Plans/Enterprise2019Plan.cs
Normal file
65
src/Core/Models/StaticStore/Plans/Enterprise2019Plan.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.StaticStore.Plans;
|
||||||
|
|
||||||
|
public record Enterprise2019Plan : Models.StaticStore.Plan
|
||||||
|
{
|
||||||
|
public Enterprise2019Plan(bool isAnnual)
|
||||||
|
{
|
||||||
|
Type = isAnnual ? PlanType.EnterpriseAnnually2019 : PlanType.EnterpriseMonthly2019;
|
||||||
|
Product = ProductType.Enterprise;
|
||||||
|
Name = isAnnual ? "Enterprise (Annually) 2019" : "Enterprise (Monthly) 2019";
|
||||||
|
IsAnnual = isAnnual;
|
||||||
|
NameLocalizationKey = "planNameEnterprise";
|
||||||
|
DescriptionLocalizationKey = "planDescEnterprise";
|
||||||
|
CanBeUsedByBusiness = true;
|
||||||
|
|
||||||
|
TrialPeriodDays = 7;
|
||||||
|
|
||||||
|
HasPolicies = true;
|
||||||
|
HasSelfHost = true;
|
||||||
|
HasGroups = true;
|
||||||
|
HasDirectory = true;
|
||||||
|
HasEvents = true;
|
||||||
|
HasTotp = true;
|
||||||
|
Has2fa = true;
|
||||||
|
HasApi = true;
|
||||||
|
UsersGetPremium = true;
|
||||||
|
HasCustomPermissions = true;
|
||||||
|
|
||||||
|
UpgradeSortOrder = 3;
|
||||||
|
DisplaySortOrder = 3;
|
||||||
|
LegacyYear = 2020;
|
||||||
|
|
||||||
|
PasswordManager = new Enterprise2019PasswordManagerFeatures(isAnnual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Enterprise2019PasswordManagerFeatures : PasswordManagerPlanFeatures
|
||||||
|
{
|
||||||
|
public Enterprise2019PasswordManagerFeatures(bool isAnnual)
|
||||||
|
{
|
||||||
|
BaseSeats = 0;
|
||||||
|
BaseStorageGb = 1;
|
||||||
|
|
||||||
|
HasAdditionalStorageOption = true;
|
||||||
|
HasAdditionalSeatsOption = true;
|
||||||
|
|
||||||
|
AllowSeatAutoscale = true;
|
||||||
|
|
||||||
|
if (isAnnual)
|
||||||
|
{
|
||||||
|
StripeStoragePlanId = "storage-gb-annually";
|
||||||
|
StripeSeatPlanId = "enterprise-org-seat-annually";
|
||||||
|
SeatPrice = 36;
|
||||||
|
AdditionalStoragePricePerGb = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StripeSeatPlanId = "enterprise-org-seat-monthly";
|
||||||
|
StripeStoragePlanId = "storage-gb-monthly";
|
||||||
|
SeatPrice = 4M;
|
||||||
|
AdditionalStoragePricePerGb = 0.5M;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
src/Core/Models/StaticStore/Plans/EnterprisePlan.cs
Normal file
100
src/Core/Models/StaticStore/Plans/EnterprisePlan.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.StaticStore.Plans;
|
||||||
|
|
||||||
|
public record EnterprisePlan : Models.StaticStore.Plan
|
||||||
|
{
|
||||||
|
public EnterprisePlan(bool isAnnual)
|
||||||
|
{
|
||||||
|
Type = isAnnual ? PlanType.EnterpriseAnnually : PlanType.EnterpriseMonthly;
|
||||||
|
Product = ProductType.Enterprise;
|
||||||
|
Name = isAnnual ? "Enterprise (Annually)" : "Enterprise (Monthly)";
|
||||||
|
IsAnnual = isAnnual;
|
||||||
|
NameLocalizationKey = "planNameEnterprise";
|
||||||
|
DescriptionLocalizationKey = "planDescEnterprise";
|
||||||
|
CanBeUsedByBusiness = true;
|
||||||
|
|
||||||
|
TrialPeriodDays = 7;
|
||||||
|
|
||||||
|
HasPolicies = true;
|
||||||
|
HasSelfHost = true;
|
||||||
|
HasGroups = true;
|
||||||
|
HasDirectory = true;
|
||||||
|
HasEvents = true;
|
||||||
|
HasTotp = true;
|
||||||
|
Has2fa = true;
|
||||||
|
HasApi = true;
|
||||||
|
HasSso = true;
|
||||||
|
HasKeyConnector = true;
|
||||||
|
HasScim = true;
|
||||||
|
HasResetPassword = true;
|
||||||
|
UsersGetPremium = true;
|
||||||
|
HasCustomPermissions = true;
|
||||||
|
|
||||||
|
UpgradeSortOrder = 3;
|
||||||
|
DisplaySortOrder = 3;
|
||||||
|
|
||||||
|
PasswordManager = new EnterprisePasswordManagerFeatures(isAnnual);
|
||||||
|
SecretsManager = new EnterpriseSecretsManagerFeatures(isAnnual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record EnterpriseSecretsManagerFeatures : SecretsManagerPlanFeatures
|
||||||
|
{
|
||||||
|
public EnterpriseSecretsManagerFeatures(bool isAnnual)
|
||||||
|
{
|
||||||
|
BaseSeats = 0;
|
||||||
|
BasePrice = 0;
|
||||||
|
BaseServiceAccount = 200;
|
||||||
|
|
||||||
|
HasAdditionalSeatsOption = true;
|
||||||
|
HasAdditionalServiceAccountOption = true;
|
||||||
|
|
||||||
|
AllowSeatAutoscale = true;
|
||||||
|
AllowServiceAccountsAutoscale = true;
|
||||||
|
|
||||||
|
if (isAnnual)
|
||||||
|
{
|
||||||
|
StripeSeatPlanId = "secrets-manager-enterprise-seat-annually";
|
||||||
|
StripeServiceAccountPlanId = "secrets-manager-service-account-annually";
|
||||||
|
SeatPrice = 144;
|
||||||
|
AdditionalPricePerServiceAccount = 6;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StripeSeatPlanId = "secrets-manager-enterprise-seat-monthly";
|
||||||
|
StripeServiceAccountPlanId = "secrets-manager-service-account-monthly";
|
||||||
|
SeatPrice = 13;
|
||||||
|
AdditionalPricePerServiceAccount = 0.5M;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record EnterprisePasswordManagerFeatures : PasswordManagerPlanFeatures
|
||||||
|
{
|
||||||
|
public EnterprisePasswordManagerFeatures(bool isAnnual)
|
||||||
|
{
|
||||||
|
BaseSeats = 0;
|
||||||
|
BaseStorageGb = 1;
|
||||||
|
|
||||||
|
HasAdditionalStorageOption = true;
|
||||||
|
HasAdditionalSeatsOption = true;
|
||||||
|
|
||||||
|
AllowSeatAutoscale = true;
|
||||||
|
|
||||||
|
if (isAnnual)
|
||||||
|
{
|
||||||
|
AdditionalStoragePricePerGb = 4;
|
||||||
|
StripeStoragePlanId = "storage-gb-annually";
|
||||||
|
StripeSeatPlanId = "2020-enterprise-org-seat-annually";
|
||||||
|
SeatPrice = 60;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StripeSeatPlanId = "2020-enterprise-seat-monthly";
|
||||||
|
StripeStoragePlanId = "storage-gb-monthly";
|
||||||
|
SeatPrice = 6;
|
||||||
|
AdditionalStoragePricePerGb = 0.5M;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
src/Core/Models/StaticStore/Plans/Families2019Plan.cs
Normal file
49
src/Core/Models/StaticStore/Plans/Families2019Plan.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.StaticStore.Plans;
|
||||||
|
|
||||||
|
public record Families2019Plan : Models.StaticStore.Plan
|
||||||
|
{
|
||||||
|
public Families2019Plan()
|
||||||
|
{
|
||||||
|
Type = PlanType.FamiliesAnnually2019;
|
||||||
|
Product = ProductType.Families;
|
||||||
|
Name = "Families 2019";
|
||||||
|
IsAnnual = true;
|
||||||
|
NameLocalizationKey = "planNameFamilies";
|
||||||
|
DescriptionLocalizationKey = "planDescFamilies";
|
||||||
|
|
||||||
|
TrialPeriodDays = 7;
|
||||||
|
|
||||||
|
HasSelfHost = true;
|
||||||
|
HasTotp = true;
|
||||||
|
|
||||||
|
UpgradeSortOrder = 1;
|
||||||
|
DisplaySortOrder = 1;
|
||||||
|
LegacyYear = 2020;
|
||||||
|
|
||||||
|
PasswordManager = new Families2019PasswordManagerFeatures();
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Families2019PasswordManagerFeatures : PasswordManagerPlanFeatures
|
||||||
|
{
|
||||||
|
public Families2019PasswordManagerFeatures()
|
||||||
|
{
|
||||||
|
BaseSeats = 5;
|
||||||
|
BaseStorageGb = 1;
|
||||||
|
MaxSeats = 5;
|
||||||
|
|
||||||
|
HasAdditionalStorageOption = true;
|
||||||
|
HasPremiumAccessOption = true;
|
||||||
|
|
||||||
|
StripePlanId = "personal-org-annually";
|
||||||
|
StripeStoragePlanId = "storage-gb-annually";
|
||||||
|
StripePremiumAccessPlanId = "personal-org-premium-access-annually";
|
||||||
|
BasePrice = 12;
|
||||||
|
AdditionalStoragePricePerGb = 4;
|
||||||
|
PremiumAccessOptionPrice = 40;
|
||||||
|
|
||||||
|
AllowSeatAutoscale = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
src/Core/Models/StaticStore/Plans/FamiliesPlan.cs
Normal file
46
src/Core/Models/StaticStore/Plans/FamiliesPlan.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.StaticStore.Plans;
|
||||||
|
|
||||||
|
public record FamiliesPlan : Models.StaticStore.Plan
|
||||||
|
{
|
||||||
|
public FamiliesPlan()
|
||||||
|
{
|
||||||
|
Type = PlanType.FamiliesAnnually;
|
||||||
|
Product = ProductType.Families;
|
||||||
|
Name = "Families";
|
||||||
|
IsAnnual = true;
|
||||||
|
NameLocalizationKey = "planNameFamilies";
|
||||||
|
DescriptionLocalizationKey = "planDescFamilies";
|
||||||
|
|
||||||
|
TrialPeriodDays = 7;
|
||||||
|
|
||||||
|
HasSelfHost = true;
|
||||||
|
HasTotp = true;
|
||||||
|
UsersGetPremium = true;
|
||||||
|
|
||||||
|
UpgradeSortOrder = 1;
|
||||||
|
DisplaySortOrder = 1;
|
||||||
|
|
||||||
|
PasswordManager = new TeamsPasswordManagerFeatures();
|
||||||
|
}
|
||||||
|
|
||||||
|
private record TeamsPasswordManagerFeatures : PasswordManagerPlanFeatures
|
||||||
|
{
|
||||||
|
public TeamsPasswordManagerFeatures()
|
||||||
|
{
|
||||||
|
BaseSeats = 6;
|
||||||
|
BaseStorageGb = 1;
|
||||||
|
MaxSeats = 6;
|
||||||
|
|
||||||
|
HasAdditionalStorageOption = true;
|
||||||
|
|
||||||
|
StripePlanId = "2020-families-org-annually";
|
||||||
|
StripeStoragePlanId = "storage-gb-annually";
|
||||||
|
BasePrice = 40;
|
||||||
|
AdditionalStoragePricePerGb = 4;
|
||||||
|
|
||||||
|
AllowSeatAutoscale = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
src/Core/Models/StaticStore/Plans/FreePlan.cs
Normal file
47
src/Core/Models/StaticStore/Plans/FreePlan.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.StaticStore.Plans;
|
||||||
|
|
||||||
|
public record FreePlan : Models.StaticStore.Plan
|
||||||
|
{
|
||||||
|
public FreePlan()
|
||||||
|
{
|
||||||
|
Type = PlanType.Free;
|
||||||
|
Product = ProductType.Free;
|
||||||
|
Name = "Free";
|
||||||
|
NameLocalizationKey = "planNameFree";
|
||||||
|
DescriptionLocalizationKey = "planDescFree";
|
||||||
|
|
||||||
|
UpgradeSortOrder = -1; // Always the lowest plan, cannot be upgraded to
|
||||||
|
DisplaySortOrder = -1;
|
||||||
|
|
||||||
|
PasswordManager = new FreePasswordManagerFeatures();
|
||||||
|
SecretsManager = new FreeSecretsManagerFeatures();
|
||||||
|
}
|
||||||
|
|
||||||
|
private record FreeSecretsManagerFeatures : SecretsManagerPlanFeatures
|
||||||
|
{
|
||||||
|
public FreeSecretsManagerFeatures()
|
||||||
|
{
|
||||||
|
BaseSeats = 2;
|
||||||
|
BaseServiceAccount = 3;
|
||||||
|
MaxProjects = 3;
|
||||||
|
MaxSeats = 2;
|
||||||
|
MaxServiceAccounts = 3;
|
||||||
|
|
||||||
|
AllowSeatAutoscale = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record FreePasswordManagerFeatures : PasswordManagerPlanFeatures
|
||||||
|
{
|
||||||
|
public FreePasswordManagerFeatures()
|
||||||
|
{
|
||||||
|
BaseSeats = 2;
|
||||||
|
MaxCollections = 2;
|
||||||
|
MaxSeats = 2;
|
||||||
|
|
||||||
|
AllowSeatAutoscale = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
src/Core/Models/StaticStore/Plans/Teams2019Plan.cs
Normal file
60
src/Core/Models/StaticStore/Plans/Teams2019Plan.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.StaticStore.Plans;
|
||||||
|
|
||||||
|
public record Teams2019Plan : Models.StaticStore.Plan
|
||||||
|
{
|
||||||
|
public Teams2019Plan(bool isAnnual)
|
||||||
|
{
|
||||||
|
Type = isAnnual ? PlanType.TeamsAnnually2019 : PlanType.TeamsMonthly2019;
|
||||||
|
Product = ProductType.Teams;
|
||||||
|
Name = isAnnual ? "Teams (Annually) 2019" : "Teams (Monthly) 2019";
|
||||||
|
IsAnnual = isAnnual;
|
||||||
|
NameLocalizationKey = "planNameTeams";
|
||||||
|
DescriptionLocalizationKey = "planDescTeams";
|
||||||
|
CanBeUsedByBusiness = true;
|
||||||
|
|
||||||
|
TrialPeriodDays = 7;
|
||||||
|
|
||||||
|
HasTotp = true;
|
||||||
|
|
||||||
|
UpgradeSortOrder = 2;
|
||||||
|
DisplaySortOrder = 2;
|
||||||
|
LegacyYear = 2020;
|
||||||
|
|
||||||
|
PasswordManager = new Teams2019PasswordManagerFeatures(isAnnual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Teams2019PasswordManagerFeatures : PasswordManagerPlanFeatures
|
||||||
|
{
|
||||||
|
public Teams2019PasswordManagerFeatures(bool isAnnual)
|
||||||
|
{
|
||||||
|
BaseSeats = 5;
|
||||||
|
BaseStorageGb = 1;
|
||||||
|
|
||||||
|
HasAdditionalStorageOption = true;
|
||||||
|
HasAdditionalSeatsOption = true;
|
||||||
|
|
||||||
|
AllowSeatAutoscale = true;
|
||||||
|
|
||||||
|
if (isAnnual)
|
||||||
|
{
|
||||||
|
StripePlanId = "teams-org-annually";
|
||||||
|
StripeStoragePlanId = "storage-gb-annually";
|
||||||
|
StripeSeatPlanId = "teams-org-seat-annually";
|
||||||
|
SeatPrice = 24;
|
||||||
|
BasePrice = 60;
|
||||||
|
AdditionalStoragePricePerGb = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StripePlanId = "teams-org-monthly";
|
||||||
|
StripeSeatPlanId = "teams-org-seat-monthly";
|
||||||
|
StripeStoragePlanId = "storage-gb-monthly";
|
||||||
|
BasePrice = 8;
|
||||||
|
SeatPrice = 2.5M;
|
||||||
|
AdditionalStoragePricePerGb = 0.5M;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
src/Core/Models/StaticStore/Plans/TeamsPlan.cs
Normal file
94
src/Core/Models/StaticStore/Plans/TeamsPlan.cs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.StaticStore.Plans;
|
||||||
|
|
||||||
|
public record TeamsPlan : Models.StaticStore.Plan
|
||||||
|
{
|
||||||
|
public TeamsPlan(bool isAnnual)
|
||||||
|
{
|
||||||
|
Type = isAnnual ? PlanType.TeamsAnnually : PlanType.TeamsMonthly;
|
||||||
|
Product = ProductType.Teams;
|
||||||
|
Name = isAnnual ? "Teams (Annually)" : "Teams (Monthly)";
|
||||||
|
IsAnnual = isAnnual;
|
||||||
|
NameLocalizationKey = "planNameTeams";
|
||||||
|
DescriptionLocalizationKey = "planDescTeams";
|
||||||
|
CanBeUsedByBusiness = true;
|
||||||
|
|
||||||
|
TrialPeriodDays = 7;
|
||||||
|
|
||||||
|
HasGroups = true;
|
||||||
|
HasDirectory = true;
|
||||||
|
HasEvents = true;
|
||||||
|
HasTotp = true;
|
||||||
|
Has2fa = true;
|
||||||
|
HasApi = true;
|
||||||
|
UsersGetPremium = true;
|
||||||
|
|
||||||
|
UpgradeSortOrder = 2;
|
||||||
|
DisplaySortOrder = 2;
|
||||||
|
|
||||||
|
PasswordManager = new TeamsPasswordManagerFeatures(isAnnual);
|
||||||
|
SecretsManager = new TeamsSecretsManagerFeatures(isAnnual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record TeamsSecretsManagerFeatures : SecretsManagerPlanFeatures
|
||||||
|
{
|
||||||
|
public TeamsSecretsManagerFeatures(bool isAnnual)
|
||||||
|
{
|
||||||
|
BaseSeats = 0;
|
||||||
|
BasePrice = 0;
|
||||||
|
BaseServiceAccount = 50;
|
||||||
|
|
||||||
|
HasAdditionalSeatsOption = true;
|
||||||
|
HasAdditionalServiceAccountOption = true;
|
||||||
|
|
||||||
|
AllowSeatAutoscale = true;
|
||||||
|
AllowServiceAccountsAutoscale = true;
|
||||||
|
|
||||||
|
if (isAnnual)
|
||||||
|
{
|
||||||
|
StripeSeatPlanId = "secrets-manager-teams-seat-annually";
|
||||||
|
StripeServiceAccountPlanId = "secrets-manager-service-account-annually";
|
||||||
|
SeatPrice = 72;
|
||||||
|
AdditionalPricePerServiceAccount = 6;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StripeSeatPlanId = "secrets-manager-teams-seat-monthly";
|
||||||
|
StripeServiceAccountPlanId = "secrets-manager-service-account-monthly";
|
||||||
|
SeatPrice = 7;
|
||||||
|
AdditionalPricePerServiceAccount = 0.5M;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record TeamsPasswordManagerFeatures : PasswordManagerPlanFeatures
|
||||||
|
{
|
||||||
|
public TeamsPasswordManagerFeatures(bool isAnnual)
|
||||||
|
{
|
||||||
|
BaseSeats = 0;
|
||||||
|
BaseStorageGb = 1;
|
||||||
|
BasePrice = 0;
|
||||||
|
|
||||||
|
HasAdditionalStorageOption = true;
|
||||||
|
HasAdditionalSeatsOption = true;
|
||||||
|
|
||||||
|
AllowSeatAutoscale = true;
|
||||||
|
|
||||||
|
if (isAnnual)
|
||||||
|
{
|
||||||
|
StripeStoragePlanId = "storage-gb-annually";
|
||||||
|
StripeSeatPlanId = "2020-teams-org-seat-annually";
|
||||||
|
SeatPrice = 36;
|
||||||
|
AdditionalStoragePricePerGb = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StripeSeatPlanId = "2020-teams-org-seat-monthly";
|
||||||
|
StripeStoragePlanId = "storage-gb-monthly";
|
||||||
|
SeatPrice = 4;
|
||||||
|
AdditionalStoragePricePerGb = 0.5M;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -54,7 +54,7 @@ public class CloudSyncSponsorshipsCommand : ICloudSyncSponsorshipsCommand
|
|||||||
{
|
{
|
||||||
var requiredSponsoringProductType = StaticStore.GetSponsoredPlan(selfHostedSponsorship.PlanSponsorshipType)?.SponsoringProductType;
|
var requiredSponsoringProductType = StaticStore.GetSponsoredPlan(selfHostedSponsorship.PlanSponsorshipType)?.SponsoringProductType;
|
||||||
if (requiredSponsoringProductType == null
|
if (requiredSponsoringProductType == null
|
||||||
|| StaticStore.GetPasswordManagerPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value)
|
|| StaticStore.GetPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value)
|
||||||
{
|
{
|
||||||
continue; // prevent unsupported sponsorships
|
continue; // prevent unsupported sponsorships
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ public class SetUpSponsorshipCommand : ISetUpSponsorshipCommand
|
|||||||
var requiredSponsoredProductType = StaticStore.GetSponsoredPlan(sponsorship.PlanSponsorshipType.Value)?.SponsoredProductType;
|
var requiredSponsoredProductType = StaticStore.GetSponsoredPlan(sponsorship.PlanSponsorshipType.Value)?.SponsoredProductType;
|
||||||
if (requiredSponsoredProductType == null ||
|
if (requiredSponsoredProductType == null ||
|
||||||
sponsoredOrganization == null ||
|
sponsoredOrganization == null ||
|
||||||
StaticStore.GetPasswordManagerPlan(sponsoredOrganization.PlanType).Product != requiredSponsoredProductType.Value)
|
StaticStore.GetPlan(sponsoredOrganization.PlanType).Product != requiredSponsoredProductType.Value)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Can only redeem sponsorship offer on families organizations.");
|
throw new BadRequestException("Can only redeem sponsorship offer on families organizations.");
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ public class ValidateSponsorshipCommand : CancelSponsorshipCommand, IValidateSpo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sponsoringOrgPlan = Utilities.StaticStore.GetPasswordManagerPlan(sponsoringOrganization.PlanType);
|
var sponsoringOrgPlan = Utilities.StaticStore.GetPlan(sponsoringOrganization.PlanType);
|
||||||
if (OrgDisabledForMoreThanGracePeriod(sponsoringOrganization) ||
|
if (OrgDisabledForMoreThanGracePeriod(sponsoringOrganization) ||
|
||||||
sponsoredPlan.SponsoringProductType != sponsoringOrgPlan.Product ||
|
sponsoredPlan.SponsoringProductType != sponsoringOrgPlan.Product ||
|
||||||
existingSponsorship.ToDelete ||
|
existingSponsorship.ToDelete ||
|
||||||
|
@ -32,7 +32,7 @@ public class CreateSponsorshipCommand : ICreateSponsorshipCommand
|
|||||||
var requiredSponsoringProductType = StaticStore.GetSponsoredPlan(sponsorshipType)?.SponsoringProductType;
|
var requiredSponsoringProductType = StaticStore.GetSponsoredPlan(sponsorshipType)?.SponsoringProductType;
|
||||||
if (requiredSponsoringProductType == null ||
|
if (requiredSponsoringProductType == null ||
|
||||||
sponsoringOrg == null ||
|
sponsoringOrg == null ||
|
||||||
StaticStore.GetPasswordManagerPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value)
|
StaticStore.GetPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Specified Organization cannot sponsor other organizations.");
|
throw new BadRequestException("Specified Organization cannot sponsor other organizations.");
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ public class AddSecretsManagerSubscriptionCommand : IAddSecretsManagerSubscripti
|
|||||||
{
|
{
|
||||||
await ValidateOrganization(organization);
|
await ValidateOrganization(organization);
|
||||||
|
|
||||||
var plan = StaticStore.GetSecretsManagerPlan(organization.PlanType);
|
var plan = StaticStore.GetPlan(organization.PlanType);
|
||||||
var signup = SetOrganizationUpgrade(organization, additionalSmSeats, additionalServiceAccounts);
|
var signup = SetOrganizationUpgrade(organization, additionalSmSeats, additionalServiceAccounts);
|
||||||
_organizationService.ValidateSecretsManagerPlan(plan, signup);
|
_organizationService.ValidateSecretsManagerPlan(plan, signup);
|
||||||
|
|
||||||
@ -39,8 +39,8 @@ public class AddSecretsManagerSubscriptionCommand : IAddSecretsManagerSubscripti
|
|||||||
await _paymentService.AddSecretsManagerToSubscription(organization, plan, additionalSmSeats, additionalServiceAccounts);
|
await _paymentService.AddSecretsManagerToSubscription(organization, plan, additionalSmSeats, additionalServiceAccounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
organization.SmSeats = plan.BaseSeats + additionalSmSeats;
|
organization.SmSeats = plan.SecretsManager.BaseSeats + additionalSmSeats;
|
||||||
organization.SmServiceAccounts = plan.BaseServiceAccount.GetValueOrDefault() + additionalServiceAccounts;
|
organization.SmServiceAccounts = plan.SecretsManager.BaseServiceAccount + additionalServiceAccounts;
|
||||||
organization.UseSecretsManager = true;
|
organization.UseSecretsManager = true;
|
||||||
|
|
||||||
await _organizationService.ReplaceAndUpdateCacheAsync(organization);
|
await _organizationService.ReplaceAndUpdateCacheAsync(organization);
|
||||||
@ -79,7 +79,7 @@ public class AddSecretsManagerSubscriptionCommand : IAddSecretsManagerSubscripti
|
|||||||
throw new BadRequestException("Organization already uses Secrets Manager.");
|
throw new BadRequestException("Organization already uses Secrets Manager.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var plan = StaticStore.GetSecretsManagerPlan(organization.PlanType);
|
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType && p.SupportsSecretsManager);
|
||||||
if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId) && plan.Product != ProductType.Free)
|
if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId) && plan.Product != ProductType.Free)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("No payment method found.");
|
throw new BadRequestException("No payment method found.");
|
||||||
|
@ -205,10 +205,10 @@ public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubs
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check plan maximum seats
|
// Check plan maximum seats
|
||||||
if (!plan.HasAdditionalSeatsOption ||
|
if (!plan.SecretsManager.HasAdditionalSeatsOption ||
|
||||||
(plan.MaxAdditionalSeats.HasValue && update.SmSeatsExcludingBase > plan.MaxAdditionalSeats.Value))
|
(plan.SecretsManager.MaxAdditionalSeats.HasValue && update.SmSeatsExcludingBase > plan.SecretsManager.MaxAdditionalSeats.Value))
|
||||||
{
|
{
|
||||||
var planMaxSeats = plan.BaseSeats + plan.MaxAdditionalSeats.GetValueOrDefault();
|
var planMaxSeats = plan.SecretsManager.BaseSeats + plan.SecretsManager.MaxAdditionalSeats.GetValueOrDefault();
|
||||||
throw new BadRequestException($"You have reached the maximum number of Secrets Manager seats ({planMaxSeats}) for this plan.");
|
throw new BadRequestException($"You have reached the maximum number of Secrets Manager seats ({planMaxSeats}) for this plan.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,9 +222,9 @@ public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubs
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check minimum seats included with plan
|
// Check minimum seats included with plan
|
||||||
if (plan.BaseSeats > update.SmSeats.Value)
|
if (plan.SecretsManager.BaseSeats > update.SmSeats.Value)
|
||||||
{
|
{
|
||||||
throw new BadRequestException($"Plan has a minimum of {plan.BaseSeats} Secrets Manager seats.");
|
throw new BadRequestException($"Plan has a minimum of {plan.SecretsManager.BaseSeats} Secrets Manager seats.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check minimum seats required by business logic
|
// Check minimum seats required by business logic
|
||||||
@ -262,11 +262,11 @@ public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubs
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check plan maximum service accounts
|
// Check plan maximum service accounts
|
||||||
if (!plan.HasAdditionalServiceAccountOption ||
|
if (!plan.SecretsManager.HasAdditionalServiceAccountOption ||
|
||||||
(plan.MaxAdditionalServiceAccount.HasValue && update.SmServiceAccountsExcludingBase > plan.MaxAdditionalServiceAccount.Value))
|
(plan.SecretsManager.MaxAdditionalServiceAccount.HasValue && update.SmServiceAccountsExcludingBase > plan.SecretsManager.MaxAdditionalServiceAccount.Value))
|
||||||
{
|
{
|
||||||
var planMaxServiceAccounts = plan.BaseServiceAccount.GetValueOrDefault() +
|
var planMaxServiceAccounts = plan.SecretsManager.BaseServiceAccount +
|
||||||
plan.MaxAdditionalServiceAccount.GetValueOrDefault();
|
plan.SecretsManager.MaxAdditionalServiceAccount.GetValueOrDefault();
|
||||||
throw new BadRequestException($"You have reached the maximum number of service accounts ({planMaxServiceAccounts}) for this plan.");
|
throw new BadRequestException($"You have reached the maximum number of service accounts ({planMaxServiceAccounts}) for this plan.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,9 +281,9 @@ public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubs
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check minimum service accounts included with plan
|
// Check minimum service accounts included with plan
|
||||||
if (plan.BaseServiceAccount.HasValue && plan.BaseServiceAccount.Value > update.SmServiceAccounts.Value)
|
if (plan.SecretsManager.BaseServiceAccount > update.SmServiceAccounts.Value)
|
||||||
{
|
{
|
||||||
throw new BadRequestException($"Plan has a minimum of {plan.BaseServiceAccount} service accounts.");
|
throw new BadRequestException($"Plan has a minimum of {plan.SecretsManager.BaseServiceAccount} service accounts.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check minimum service accounts required by business logic
|
// Check minimum service accounts required by business logic
|
||||||
@ -319,15 +319,15 @@ public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubs
|
|||||||
throw new BadRequestException($"Cannot set max Secrets Manager seat autoscaling below current Secrets Manager seat count.");
|
throw new BadRequestException($"Cannot set max Secrets Manager seat autoscaling below current Secrets Manager seat count.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plan.MaxUsers.HasValue && update.MaxAutoscaleSmSeats.Value > plan.MaxUsers)
|
if (plan.SecretsManager.MaxSeats.HasValue && update.MaxAutoscaleSmSeats.Value > plan.SecretsManager.MaxSeats)
|
||||||
{
|
{
|
||||||
throw new BadRequestException(string.Concat(
|
throw new BadRequestException(string.Concat(
|
||||||
$"Your plan has a Secrets Manager seat limit of {plan.MaxUsers}, ",
|
$"Your plan has a Secrets Manager seat limit of {plan.SecretsManager.MaxSeats}, ",
|
||||||
$"but you have specified a max autoscale count of {update.MaxAutoscaleSmSeats}.",
|
$"but you have specified a max autoscale count of {update.MaxAutoscaleSmSeats}.",
|
||||||
"Reduce your max autoscale count."));
|
"Reduce your max autoscale count."));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!plan.AllowSeatAutoscale)
|
if (!plan.SecretsManager.AllowSeatAutoscale)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Your plan does not allow Secrets Manager seat autoscaling.");
|
throw new BadRequestException("Your plan does not allow Secrets Manager seat autoscaling.");
|
||||||
}
|
}
|
||||||
@ -349,15 +349,15 @@ public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubs
|
|||||||
$"Cannot set max service accounts autoscaling below current service accounts count.");
|
$"Cannot set max service accounts autoscaling below current service accounts count.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!plan.AllowServiceAccountsAutoscale)
|
if (!plan.SecretsManager.AllowServiceAccountsAutoscale)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Your plan does not allow service accounts autoscaling.");
|
throw new BadRequestException("Your plan does not allow service accounts autoscaling.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plan.MaxServiceAccounts.HasValue && update.MaxAutoscaleSmServiceAccounts.Value > plan.MaxServiceAccounts)
|
if (plan.SecretsManager.MaxServiceAccounts.HasValue && update.MaxAutoscaleSmServiceAccounts.Value > plan.SecretsManager.MaxServiceAccounts)
|
||||||
{
|
{
|
||||||
throw new BadRequestException(string.Concat(
|
throw new BadRequestException(string.Concat(
|
||||||
$"Your plan has a service account limit of {plan.MaxServiceAccounts}, ",
|
$"Your plan has a service account limit of {plan.SecretsManager.MaxServiceAccounts}, ",
|
||||||
$"but you have specified a max autoscale count of {update.MaxAutoscaleSmServiceAccounts}.",
|
$"but you have specified a max autoscale count of {update.MaxAutoscaleSmServiceAccounts}.",
|
||||||
"Reduce your max autoscale count."));
|
"Reduce your max autoscale count."));
|
||||||
}
|
}
|
||||||
|
@ -73,69 +73,67 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
throw new BadRequestException("Your account has no payment method available.");
|
throw new BadRequestException("Your account has no payment method available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingPasswordManagerPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType);
|
var existingPlan = StaticStore.GetPlan(organization.PlanType);
|
||||||
if (existingPasswordManagerPlan == null)
|
if (existingPlan == null)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Existing plan not found.");
|
throw new BadRequestException("Existing plan not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var newPasswordManagerPlan =
|
var newPlan = StaticStore.Plans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled);
|
||||||
StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled);
|
if (newPlan == null)
|
||||||
if (newPasswordManagerPlan == null)
|
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Plan not found.");
|
throw new BadRequestException("Plan not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingPasswordManagerPlan.Type == newPasswordManagerPlan.Type)
|
if (existingPlan.Type == newPlan.Type)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Organization is already on this plan.");
|
throw new BadRequestException("Organization is already on this plan.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingPasswordManagerPlan.UpgradeSortOrder >= newPasswordManagerPlan.UpgradeSortOrder)
|
if (existingPlan.UpgradeSortOrder >= newPlan.UpgradeSortOrder)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("You cannot upgrade to this plan.");
|
throw new BadRequestException("You cannot upgrade to this plan.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingPasswordManagerPlan.Type != PlanType.Free)
|
if (existingPlan.Type != PlanType.Free)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("You can only upgrade from the free plan. Contact support.");
|
throw new BadRequestException("You can only upgrade from the free plan. Contact support.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_organizationService.ValidatePasswordManagerPlan(newPasswordManagerPlan, upgrade);
|
_organizationService.ValidatePasswordManagerPlan(newPlan, upgrade);
|
||||||
var newSecretsManagerPlan =
|
|
||||||
StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled);
|
|
||||||
if (upgrade.UseSecretsManager)
|
if (upgrade.UseSecretsManager)
|
||||||
{
|
{
|
||||||
_organizationService.ValidateSecretsManagerPlan(newSecretsManagerPlan, upgrade);
|
_organizationService.ValidateSecretsManagerPlan(newPlan, upgrade);
|
||||||
}
|
}
|
||||||
|
|
||||||
var newPasswordManagerPlanSeats = (short)(newPasswordManagerPlan.BaseSeats +
|
var updatedPasswordManagerSeats = (short)(newPlan.PasswordManager.BaseSeats +
|
||||||
(newPasswordManagerPlan.HasAdditionalSeatsOption ? upgrade.AdditionalSeats : 0));
|
(newPlan.PasswordManager.HasAdditionalSeatsOption ? upgrade.AdditionalSeats : 0));
|
||||||
if (!organization.Seats.HasValue || organization.Seats.Value > newPasswordManagerPlanSeats)
|
if (!organization.Seats.HasValue || organization.Seats.Value > updatedPasswordManagerSeats)
|
||||||
{
|
{
|
||||||
var occupiedSeats =
|
var occupiedSeats =
|
||||||
await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
||||||
if (occupiedSeats > newPasswordManagerPlanSeats)
|
if (occupiedSeats > updatedPasswordManagerSeats)
|
||||||
{
|
{
|
||||||
throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " +
|
throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " +
|
||||||
$"Your new plan only has ({newPasswordManagerPlanSeats}) seats. Remove some users.");
|
$"Your new plan only has ({updatedPasswordManagerSeats}) seats. Remove some users.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newPasswordManagerPlan.MaxCollections.HasValue && (!organization.MaxCollections.HasValue ||
|
if (newPlan.PasswordManager.MaxCollections.HasValue && (!organization.MaxCollections.HasValue ||
|
||||||
organization.MaxCollections.Value >
|
organization.MaxCollections.Value >
|
||||||
newPasswordManagerPlan.MaxCollections.Value))
|
newPlan.PasswordManager.MaxCollections.Value))
|
||||||
{
|
{
|
||||||
var collectionCount = await _collectionRepository.GetCountByOrganizationIdAsync(organization.Id);
|
var collectionCount = await _collectionRepository.GetCountByOrganizationIdAsync(organization.Id);
|
||||||
if (collectionCount > newPasswordManagerPlan.MaxCollections.Value)
|
if (collectionCount > newPlan.PasswordManager.MaxCollections.Value)
|
||||||
{
|
{
|
||||||
throw new BadRequestException($"Your organization currently has {collectionCount} collections. " +
|
throw new BadRequestException($"Your organization currently has {collectionCount} collections. " +
|
||||||
$"Your new plan allows for a maximum of ({newPasswordManagerPlan.MaxCollections.Value}) collections. " +
|
$"Your new plan allows for a maximum of ({newPlan.PasswordManager.MaxCollections.Value}) collections. " +
|
||||||
"Remove some collections.");
|
"Remove some collections.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newPasswordManagerPlan.HasGroups && organization.UseGroups)
|
if (!newPlan.HasGroups && organization.UseGroups)
|
||||||
{
|
{
|
||||||
var groups = await _groupRepository.GetManyByOrganizationIdAsync(organization.Id);
|
var groups = await _groupRepository.GetManyByOrganizationIdAsync(organization.Id);
|
||||||
if (groups.Any())
|
if (groups.Any())
|
||||||
@ -145,7 +143,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newPasswordManagerPlan.HasPolicies && organization.UsePolicies)
|
if (!newPlan.HasPolicies && organization.UsePolicies)
|
||||||
{
|
{
|
||||||
var policies = await _policyRepository.GetManyByOrganizationIdAsync(organization.Id);
|
var policies = await _policyRepository.GetManyByOrganizationIdAsync(organization.Id);
|
||||||
if (policies.Any(p => p.Enabled))
|
if (policies.Any(p => p.Enabled))
|
||||||
@ -155,7 +153,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newPasswordManagerPlan.HasSso && organization.UseSso)
|
if (!newPlan.HasSso && organization.UseSso)
|
||||||
{
|
{
|
||||||
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id);
|
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id);
|
||||||
if (ssoConfig != null && ssoConfig.Enabled)
|
if (ssoConfig != null && ssoConfig.Enabled)
|
||||||
@ -165,7 +163,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newPasswordManagerPlan.HasKeyConnector && organization.UseKeyConnector)
|
if (!newPlan.HasKeyConnector && organization.UseKeyConnector)
|
||||||
{
|
{
|
||||||
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id);
|
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id);
|
||||||
if (ssoConfig != null && ssoConfig.GetData().MemberDecryptionType == MemberDecryptionType.KeyConnector)
|
if (ssoConfig != null && ssoConfig.GetData().MemberDecryptionType == MemberDecryptionType.KeyConnector)
|
||||||
@ -175,7 +173,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newPasswordManagerPlan.HasResetPassword && organization.UseResetPassword)
|
if (!newPlan.HasResetPassword && organization.UseResetPassword)
|
||||||
{
|
{
|
||||||
var resetPasswordPolicy =
|
var resetPasswordPolicy =
|
||||||
await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword);
|
await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword);
|
||||||
@ -186,7 +184,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newPasswordManagerPlan.HasScim && organization.UseScim)
|
if (!newPlan.HasScim && organization.UseScim)
|
||||||
{
|
{
|
||||||
var scimConnections = await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organization.Id,
|
var scimConnections = await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organization.Id,
|
||||||
OrganizationConnectionType.Scim);
|
OrganizationConnectionType.Scim);
|
||||||
@ -197,7 +195,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newPasswordManagerPlan.HasCustomPermissions && organization.UseCustomPermissions)
|
if (!newPlan.HasCustomPermissions && organization.UseCustomPermissions)
|
||||||
{
|
{
|
||||||
var organizationCustomUsers =
|
var organizationCustomUsers =
|
||||||
await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id,
|
await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id,
|
||||||
@ -209,9 +207,9 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (upgrade.UseSecretsManager && newSecretsManagerPlan != null)
|
if (upgrade.UseSecretsManager)
|
||||||
{
|
{
|
||||||
await ValidateSecretsManagerSeatsAndServiceAccountAsync(upgrade, organization, newSecretsManagerPlan);
|
await ValidateSecretsManagerSeatsAndServiceAccountAsync(upgrade, organization, newPlan);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check storage?
|
// TODO: Check storage?
|
||||||
@ -220,12 +218,8 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId))
|
if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId))
|
||||||
{
|
{
|
||||||
var organizationUpgradePlan = upgrade.UseSecretsManager
|
|
||||||
? StaticStore.Plans.Where(p => p.Type == upgrade.Plan).ToList()
|
|
||||||
: StaticStore.Plans.Where(p => p.Type == upgrade.Plan && p.BitwardenProduct == BitwardenProductType.PasswordManager).ToList();
|
|
||||||
|
|
||||||
paymentIntentClientSecret = await _paymentService.UpgradeFreeOrganizationAsync(organization,
|
paymentIntentClientSecret = await _paymentService.UpgradeFreeOrganizationAsync(organization,
|
||||||
organizationUpgradePlan, upgrade);
|
newPlan, upgrade);
|
||||||
success = string.IsNullOrWhiteSpace(paymentIntentClientSecret);
|
success = string.IsNullOrWhiteSpace(paymentIntentClientSecret);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -235,34 +229,34 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
organization.BusinessName = upgrade.BusinessName;
|
organization.BusinessName = upgrade.BusinessName;
|
||||||
organization.PlanType = newPasswordManagerPlan.Type;
|
organization.PlanType = newPlan.Type;
|
||||||
organization.Seats = (short)(newPasswordManagerPlan.BaseSeats + upgrade.AdditionalSeats);
|
organization.Seats = (short)(newPlan.PasswordManager.BaseSeats + upgrade.AdditionalSeats);
|
||||||
organization.MaxCollections = newPasswordManagerPlan.MaxCollections;
|
organization.MaxCollections = newPlan.PasswordManager.MaxCollections;
|
||||||
organization.UseGroups = newPasswordManagerPlan.HasGroups;
|
organization.UseGroups = newPlan.HasGroups;
|
||||||
organization.UseDirectory = newPasswordManagerPlan.HasDirectory;
|
organization.UseDirectory = newPlan.HasDirectory;
|
||||||
organization.UseEvents = newPasswordManagerPlan.HasEvents;
|
organization.UseEvents = newPlan.HasEvents;
|
||||||
organization.UseTotp = newPasswordManagerPlan.HasTotp;
|
organization.UseTotp = newPlan.HasTotp;
|
||||||
organization.Use2fa = newPasswordManagerPlan.Has2fa;
|
organization.Use2fa = newPlan.Has2fa;
|
||||||
organization.UseApi = newPasswordManagerPlan.HasApi;
|
organization.UseApi = newPlan.HasApi;
|
||||||
organization.SelfHost = newPasswordManagerPlan.HasSelfHost;
|
organization.SelfHost = newPlan.HasSelfHost;
|
||||||
organization.UsePolicies = newPasswordManagerPlan.HasPolicies;
|
organization.UsePolicies = newPlan.HasPolicies;
|
||||||
organization.MaxStorageGb = !newPasswordManagerPlan.BaseStorageGb.HasValue
|
organization.MaxStorageGb = !newPlan.PasswordManager.BaseStorageGb.HasValue
|
||||||
? (short?)null
|
? (short?)null
|
||||||
: (short)(newPasswordManagerPlan.BaseStorageGb.Value + upgrade.AdditionalStorageGb);
|
: (short)(newPlan.PasswordManager.BaseStorageGb.Value + upgrade.AdditionalStorageGb);
|
||||||
organization.UseGroups = newPasswordManagerPlan.HasGroups;
|
organization.UseGroups = newPlan.HasGroups;
|
||||||
organization.UseDirectory = newPasswordManagerPlan.HasDirectory;
|
organization.UseDirectory = newPlan.HasDirectory;
|
||||||
organization.UseEvents = newPasswordManagerPlan.HasEvents;
|
organization.UseEvents = newPlan.HasEvents;
|
||||||
organization.UseTotp = newPasswordManagerPlan.HasTotp;
|
organization.UseTotp = newPlan.HasTotp;
|
||||||
organization.Use2fa = newPasswordManagerPlan.Has2fa;
|
organization.Use2fa = newPlan.Has2fa;
|
||||||
organization.UseApi = newPasswordManagerPlan.HasApi;
|
organization.UseApi = newPlan.HasApi;
|
||||||
organization.UseSso = newPasswordManagerPlan.HasSso;
|
organization.UseSso = newPlan.HasSso;
|
||||||
organization.UseKeyConnector = newPasswordManagerPlan.HasKeyConnector;
|
organization.UseKeyConnector = newPlan.HasKeyConnector;
|
||||||
organization.UseScim = newPasswordManagerPlan.HasScim;
|
organization.UseScim = newPlan.HasScim;
|
||||||
organization.UseResetPassword = newPasswordManagerPlan.HasResetPassword;
|
organization.UseResetPassword = newPlan.HasResetPassword;
|
||||||
organization.SelfHost = newPasswordManagerPlan.HasSelfHost;
|
organization.SelfHost = newPlan.HasSelfHost;
|
||||||
organization.UsersGetPremium = newPasswordManagerPlan.UsersGetPremium || upgrade.PremiumAccessAddon;
|
organization.UsersGetPremium = newPlan.UsersGetPremium || upgrade.PremiumAccessAddon;
|
||||||
organization.UseCustomPermissions = newPasswordManagerPlan.HasCustomPermissions;
|
organization.UseCustomPermissions = newPlan.HasCustomPermissions;
|
||||||
organization.Plan = newPasswordManagerPlan.Name;
|
organization.Plan = newPlan.Name;
|
||||||
organization.Enabled = success;
|
organization.Enabled = success;
|
||||||
organization.PublicKey = upgrade.PublicKey;
|
organization.PublicKey = upgrade.PublicKey;
|
||||||
organization.PrivateKey = upgrade.PrivateKey;
|
organization.PrivateKey = upgrade.PrivateKey;
|
||||||
@ -271,8 +265,8 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
|
|
||||||
if (upgrade.UseSecretsManager)
|
if (upgrade.UseSecretsManager)
|
||||||
{
|
{
|
||||||
organization.SmSeats = newSecretsManagerPlan.BaseSeats + upgrade.AdditionalSmSeats.GetValueOrDefault();
|
organization.SmSeats = newPlan.SecretsManager.BaseSeats + upgrade.AdditionalSmSeats.GetValueOrDefault();
|
||||||
organization.SmServiceAccounts = newSecretsManagerPlan.BaseServiceAccount.GetValueOrDefault() +
|
organization.SmServiceAccounts = newPlan.SecretsManager.BaseServiceAccount +
|
||||||
upgrade.AdditionalServiceAccounts.GetValueOrDefault();
|
upgrade.AdditionalServiceAccounts.GetValueOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,10 +277,10 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
await _referenceEventService.RaiseEventAsync(
|
await _referenceEventService.RaiseEventAsync(
|
||||||
new ReferenceEvent(ReferenceEventType.UpgradePlan, organization, _currentContext)
|
new ReferenceEvent(ReferenceEventType.UpgradePlan, organization, _currentContext)
|
||||||
{
|
{
|
||||||
PlanName = newPasswordManagerPlan.Name,
|
PlanName = newPlan.Name,
|
||||||
PlanType = newPasswordManagerPlan.Type,
|
PlanType = newPlan.Type,
|
||||||
OldPlanName = existingPasswordManagerPlan.Name,
|
OldPlanName = existingPlan.Name,
|
||||||
OldPlanType = existingPasswordManagerPlan.Type,
|
OldPlanType = existingPlan.Type,
|
||||||
Seats = organization.Seats,
|
Seats = organization.Seats,
|
||||||
Storage = organization.MaxStorageGb,
|
Storage = organization.MaxStorageGb,
|
||||||
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
|
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
|
||||||
@ -299,8 +293,8 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
private async Task ValidateSecretsManagerSeatsAndServiceAccountAsync(OrganizationUpgrade upgrade, Organization organization,
|
private async Task ValidateSecretsManagerSeatsAndServiceAccountAsync(OrganizationUpgrade upgrade, Organization organization,
|
||||||
Models.StaticStore.Plan newSecretsManagerPlan)
|
Models.StaticStore.Plan newSecretsManagerPlan)
|
||||||
{
|
{
|
||||||
var newPlanSmSeats = (short)(newSecretsManagerPlan.BaseSeats +
|
var newPlanSmSeats = (short)(newSecretsManagerPlan.SecretsManager.BaseSeats +
|
||||||
(newSecretsManagerPlan.HasAdditionalSeatsOption
|
(newSecretsManagerPlan.SecretsManager.HasAdditionalSeatsOption
|
||||||
? upgrade.AdditionalSmSeats
|
? upgrade.AdditionalSmSeats
|
||||||
: 0));
|
: 0));
|
||||||
var occupiedSmSeats =
|
var occupiedSmSeats =
|
||||||
@ -316,10 +310,10 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var additionalServiceAccounts = newSecretsManagerPlan.HasAdditionalServiceAccountOption
|
var additionalServiceAccounts = newSecretsManagerPlan.SecretsManager.HasAdditionalServiceAccountOption
|
||||||
? upgrade.AdditionalServiceAccounts
|
? upgrade.AdditionalServiceAccounts
|
||||||
: 0;
|
: 0;
|
||||||
var newPlanServiceAccounts = newSecretsManagerPlan.BaseServiceAccount + additionalServiceAccounts;
|
var newPlanServiceAccounts = newSecretsManagerPlan.SecretsManager.BaseServiceAccount + additionalServiceAccounts;
|
||||||
|
|
||||||
if (!organization.SmServiceAccounts.HasValue || organization.SmServiceAccounts.Value > newPlanServiceAccounts)
|
if (!organization.SmServiceAccounts.HasValue || organization.SmServiceAccounts.Value > newPlanServiceAccounts)
|
||||||
{
|
{
|
||||||
@ -329,7 +323,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
|
|||||||
{
|
{
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
$"Your organization currently has {currentServiceAccounts} service accounts. " +
|
$"Your organization currently has {currentServiceAccounts} service accounts. " +
|
||||||
$"Your new plan only allows {newSecretsManagerPlan.MaxServiceAccounts} service accounts. " +
|
$"Your new plan only allows {newSecretsManagerPlan.SecretsManager.MaxServiceAccounts} service accounts. " +
|
||||||
"Remove some service accounts or increase your subscription.");
|
"Remove some service accounts or increase your subscription.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,12 @@ public interface IPaymentService
|
|||||||
{
|
{
|
||||||
Task CancelAndRecoverChargesAsync(ISubscriber subscriber);
|
Task CancelAndRecoverChargesAsync(ISubscriber subscriber);
|
||||||
Task<string> PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType,
|
Task<string> PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType,
|
||||||
string paymentToken, List<Plan> plans, short additionalStorageGb, int additionalSeats,
|
string paymentToken, Plan plan, short additionalStorageGb, int additionalSeats,
|
||||||
bool premiumAccessAddon, TaxInfo taxInfo, bool provider = false, int additionalSmSeats = 0,
|
bool premiumAccessAddon, TaxInfo taxInfo, bool provider = false, int additionalSmSeats = 0,
|
||||||
int additionalServiceAccount = 0);
|
int additionalServiceAccount = 0);
|
||||||
Task SponsorOrganizationAsync(Organization org, OrganizationSponsorship sponsorship);
|
Task SponsorOrganizationAsync(Organization org, OrganizationSponsorship sponsorship);
|
||||||
Task RemoveOrganizationSponsorshipAsync(Organization org, OrganizationSponsorship sponsorship);
|
Task RemoveOrganizationSponsorshipAsync(Organization org, OrganizationSponsorship sponsorship);
|
||||||
Task<string> UpgradeFreeOrganizationAsync(Organization org, List<Plan> plans, OrganizationUpgrade upgrade);
|
Task<string> UpgradeFreeOrganizationAsync(Organization org, Plan plan, OrganizationUpgrade upgrade);
|
||||||
Task<string> PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken,
|
Task<string> PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken,
|
||||||
short additionalStorageGb, TaxInfo taxInfo);
|
short additionalStorageGb, TaxInfo taxInfo);
|
||||||
Task<string> AdjustSeatsAsync(Organization organization, Plan plan, int additionalSeats, DateTime? prorationDate = null);
|
Task<string> AdjustSeatsAsync(Organization organization, Plan plan, int additionalSeats, DateTime? prorationDate = null);
|
||||||
|
@ -175,19 +175,19 @@ public class OrganizationService : IOrganizationService
|
|||||||
throw new NotFoundException();
|
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)
|
if (plan == null)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Existing plan not found.");
|
throw new BadRequestException("Existing plan not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!plan.HasAdditionalStorageOption)
|
if (!plan.PasswordManager.HasAdditionalStorageOption)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Plan does not allow additional storage.");
|
throw new BadRequestException("Plan does not allow additional storage.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var secret = await BillingHelpers.AdjustStorageAsync(_paymentService, organization, storageAdjustmentGb,
|
var secret = await BillingHelpers.AdjustStorageAsync(_paymentService, organization, storageAdjustmentGb,
|
||||||
plan.StripeStoragePlanId);
|
plan.PasswordManager.StripeStoragePlanId);
|
||||||
await _referenceEventService.RaiseEventAsync(
|
await _referenceEventService.RaiseEventAsync(
|
||||||
new ReferenceEvent(ReferenceEventType.AdjustStorage, organization, _currentContext)
|
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.");
|
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)
|
if (plan == null)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Existing plan not found.");
|
throw new BadRequestException("Existing plan not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!plan.AllowSeatAutoscale)
|
if (!plan.PasswordManager.AllowSeatAutoscale)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Your plan does not allow seat autoscaling.");
|
throw new BadRequestException("Your plan does not allow seat autoscaling.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plan.MaxUsers.HasValue && maxAutoscaleSeats.HasValue &&
|
if (plan.PasswordManager.MaxSeats.HasValue && maxAutoscaleSeats.HasValue &&
|
||||||
maxAutoscaleSeats > plan.MaxUsers)
|
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}.",
|
$"but you have specified a max autoscale count of {maxAutoscaleSeats}.",
|
||||||
"Reduce your max autoscale seat count."));
|
"Reduce your max autoscale seat count."));
|
||||||
}
|
}
|
||||||
@ -285,21 +285,21 @@ public class OrganizationService : IOrganizationService
|
|||||||
throw new BadRequestException("No subscription found.");
|
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)
|
if (plan == null)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Existing plan not found.");
|
throw new BadRequestException("Existing plan not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!plan.HasAdditionalSeatsOption)
|
if (!plan.PasswordManager.HasAdditionalSeatsOption)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Plan does not allow additional seats.");
|
throw new BadRequestException("Plan does not allow additional seats.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var newSeatTotal = organization.Seats.Value + seatAdjustment;
|
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)
|
if (newSeatTotal <= 0)
|
||||||
@ -307,11 +307,11 @@ public class OrganizationService : IOrganizationService
|
|||||||
throw new BadRequestException("You must have at least 1 seat.");
|
throw new BadRequestException("You must have at least 1 seat.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var additionalSeats = newSeatTotal - plan.BaseSeats;
|
var additionalSeats = newSeatTotal - plan.PasswordManager.BaseSeats;
|
||||||
if (plan.MaxAdditionalSeats.HasValue && additionalSeats > plan.MaxAdditionalSeats.Value)
|
if (plan.PasswordManager.MaxAdditionalSeats.HasValue && additionalSeats > plan.PasswordManager.MaxAdditionalSeats.Value)
|
||||||
{
|
{
|
||||||
throw new BadRequestException($"Organization plan allows a maximum of " +
|
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)
|
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,
|
public async Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup signup,
|
||||||
bool provider = false)
|
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 (signup.UseSecretsManager)
|
||||||
{
|
{
|
||||||
if (provider)
|
if (provider)
|
||||||
@ -415,7 +414,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
"Organizations with a Managed Service Provider do not support Secrets Manager.");
|
"Organizations with a Managed Service Provider do not support Secrets Manager.");
|
||||||
}
|
}
|
||||||
ValidateSecretsManagerPlan(secretsManagerPlan, signup);
|
ValidateSecretsManagerPlan(plan, signup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!provider)
|
if (!provider)
|
||||||
@ -430,25 +429,25 @@ public class OrganizationService : IOrganizationService
|
|||||||
Name = signup.Name,
|
Name = signup.Name,
|
||||||
BillingEmail = signup.BillingEmail,
|
BillingEmail = signup.BillingEmail,
|
||||||
BusinessName = signup.BusinessName,
|
BusinessName = signup.BusinessName,
|
||||||
PlanType = passwordManagerPlan.Type,
|
PlanType = plan!.Type,
|
||||||
Seats = (short)(passwordManagerPlan.BaseSeats + signup.AdditionalSeats),
|
Seats = (short)(plan.PasswordManager.BaseSeats + signup.AdditionalSeats),
|
||||||
MaxCollections = passwordManagerPlan.MaxCollections,
|
MaxCollections = plan.PasswordManager.MaxCollections,
|
||||||
MaxStorageGb = !passwordManagerPlan.BaseStorageGb.HasValue ?
|
MaxStorageGb = !plan.PasswordManager.BaseStorageGb.HasValue ?
|
||||||
(short?)null : (short)(passwordManagerPlan.BaseStorageGb.Value + signup.AdditionalStorageGb),
|
(short?)null : (short)(plan.PasswordManager.BaseStorageGb.Value + signup.AdditionalStorageGb),
|
||||||
UsePolicies = passwordManagerPlan.HasPolicies,
|
UsePolicies = plan.HasPolicies,
|
||||||
UseSso = passwordManagerPlan.HasSso,
|
UseSso = plan.HasSso,
|
||||||
UseGroups = passwordManagerPlan.HasGroups,
|
UseGroups = plan.HasGroups,
|
||||||
UseEvents = passwordManagerPlan.HasEvents,
|
UseEvents = plan.HasEvents,
|
||||||
UseDirectory = passwordManagerPlan.HasDirectory,
|
UseDirectory = plan.HasDirectory,
|
||||||
UseTotp = passwordManagerPlan.HasTotp,
|
UseTotp = plan.HasTotp,
|
||||||
Use2fa = passwordManagerPlan.Has2fa,
|
Use2fa = plan.Has2fa,
|
||||||
UseApi = passwordManagerPlan.HasApi,
|
UseApi = plan.HasApi,
|
||||||
UseResetPassword = passwordManagerPlan.HasResetPassword,
|
UseResetPassword = plan.HasResetPassword,
|
||||||
SelfHost = passwordManagerPlan.HasSelfHost,
|
SelfHost = plan.HasSelfHost,
|
||||||
UsersGetPremium = passwordManagerPlan.UsersGetPremium || signup.PremiumAccessAddon,
|
UsersGetPremium = plan.UsersGetPremium || signup.PremiumAccessAddon,
|
||||||
UseCustomPermissions = passwordManagerPlan.HasCustomPermissions,
|
UseCustomPermissions = plan.HasCustomPermissions,
|
||||||
UseScim = passwordManagerPlan.HasScim,
|
UseScim = plan.HasScim,
|
||||||
Plan = passwordManagerPlan.Name,
|
Plan = plan.Name,
|
||||||
Gateway = null,
|
Gateway = null,
|
||||||
ReferenceData = signup.Owner.ReferenceData,
|
ReferenceData = signup.Owner.ReferenceData,
|
||||||
Enabled = true,
|
Enabled = true,
|
||||||
@ -464,12 +463,12 @@ public class OrganizationService : IOrganizationService
|
|||||||
|
|
||||||
if (signup.UseSecretsManager)
|
if (signup.UseSecretsManager)
|
||||||
{
|
{
|
||||||
organization.SmSeats = secretsManagerPlan.BaseSeats + signup.AdditionalSmSeats.GetValueOrDefault();
|
organization.SmSeats = plan.SecretsManager.BaseSeats + signup.AdditionalSmSeats.GetValueOrDefault();
|
||||||
organization.SmServiceAccounts = secretsManagerPlan.BaseServiceAccount.GetValueOrDefault() +
|
organization.SmServiceAccounts = plan.SecretsManager.BaseServiceAccount +
|
||||||
signup.AdditionalServiceAccounts.GetValueOrDefault();
|
signup.AdditionalServiceAccounts.GetValueOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passwordManagerPlan.Type == PlanType.Free && !provider)
|
if (plan.Type == PlanType.Free && !provider)
|
||||||
{
|
{
|
||||||
var adminCount =
|
var adminCount =
|
||||||
await _organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(signup.Owner.Id);
|
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.");
|
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,
|
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.PremiumAccessAddon, signup.TaxInfo, provider, signup.AdditionalSmSeats.GetValueOrDefault(),
|
||||||
signup.AdditionalServiceAccounts.GetValueOrDefault());
|
signup.AdditionalServiceAccounts.GetValueOrDefault());
|
||||||
}
|
}
|
||||||
@ -495,8 +490,8 @@ public class OrganizationService : IOrganizationService
|
|||||||
await _referenceEventService.RaiseEventAsync(
|
await _referenceEventService.RaiseEventAsync(
|
||||||
new ReferenceEvent(ReferenceEventType.Signup, organization, _currentContext)
|
new ReferenceEvent(ReferenceEventType.Signup, organization, _currentContext)
|
||||||
{
|
{
|
||||||
PlanName = passwordManagerPlan.Name,
|
PlanName = plan.Name,
|
||||||
PlanType = passwordManagerPlan.Type,
|
PlanType = plan.Type,
|
||||||
Seats = returnValue.Item1.Seats,
|
Seats = returnValue.Item1.Seats,
|
||||||
Storage = returnValue.Item1.MaxStorageGb,
|
Storage = returnValue.Item1.MaxStorageGb,
|
||||||
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
|
// 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 &&
|
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.");
|
throw new BadRequestException("Plan not found.");
|
||||||
}
|
}
|
||||||
@ -1955,11 +1950,6 @@ public class OrganizationService : IOrganizationService
|
|||||||
throw new BadRequestException($"{productType} Plan not found.");
|
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)
|
if (additionalSeats < 0)
|
||||||
{
|
{
|
||||||
throw new BadRequestException($"You can't subtract {productType} seats!");
|
throw new BadRequestException($"You can't subtract {productType} seats!");
|
||||||
@ -1970,7 +1960,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
{
|
{
|
||||||
ValidatePlan(plan, upgrade.AdditionalSeats, "Password Manager");
|
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!");
|
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!");
|
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.");
|
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!");
|
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.");
|
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.");
|
throw new BadRequestException("Plan does not allow additional users.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plan.HasAdditionalSeatsOption && plan.MaxAdditionalSeats.HasValue &&
|
if (plan.PasswordManager.HasAdditionalSeatsOption && plan.PasswordManager.MaxAdditionalSeats.HasValue &&
|
||||||
upgrade.AdditionalSeats > plan.MaxAdditionalSeats.Value)
|
upgrade.AdditionalSeats > plan.PasswordManager.MaxAdditionalSeats.Value)
|
||||||
{
|
{
|
||||||
throw new BadRequestException($"Selected plan allows a maximum of " +
|
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)
|
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");
|
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.");
|
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!");
|
throw new BadRequestException("You can't subtract Service Accounts!");
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (plan.HasAdditionalSeatsOption)
|
switch (plan.SecretsManager.HasAdditionalSeatsOption)
|
||||||
{
|
{
|
||||||
case false when upgrade.AdditionalSmSeats > 0:
|
case false when upgrade.AdditionalSmSeats > 0:
|
||||||
throw new BadRequestException("Plan does not allow additional users.");
|
throw new BadRequestException("Plan does not allow additional users.");
|
||||||
case true when plan.MaxAdditionalSeats.HasValue &&
|
case true when plan.SecretsManager.MaxAdditionalSeats.HasValue &&
|
||||||
upgrade.AdditionalSmSeats > plan.MaxAdditionalSeats.Value:
|
upgrade.AdditionalSmSeats > plan.SecretsManager.MaxAdditionalSeats.Value:
|
||||||
throw new BadRequestException($"Selected plan allows a maximum of " +
|
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)
|
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 })
|
if (plan is not { LegacyYear: null })
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Invalid plan selected.");
|
throw new BadRequestException("Invalid plan selected.");
|
||||||
|
@ -49,7 +49,7 @@ public class StripePaymentService : IPaymentService
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType,
|
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 additionalSeats, bool premiumAccessAddon, TaxInfo taxInfo, bool provider = false,
|
||||||
int additionalSmSeats = 0, int additionalServiceAccount = 0)
|
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);
|
, additionalSmSeats, additionalServiceAccount);
|
||||||
|
|
||||||
Stripe.Customer customer = null;
|
Stripe.Customer customer = null;
|
||||||
@ -211,7 +211,7 @@ public class StripePaymentService : IPaymentService
|
|||||||
|
|
||||||
private async Task ChangeOrganizationSponsorship(Organization org, OrganizationSponsorship sponsorship, bool applySponsorship)
|
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 ?
|
var sponsoredPlan = sponsorship != null ?
|
||||||
Utilities.StaticStore.GetSponsoredPlan(sponsorship.PlanSponsorshipType.Value) :
|
Utilities.StaticStore.GetSponsoredPlan(sponsorship.PlanSponsorshipType.Value) :
|
||||||
null;
|
null;
|
||||||
@ -231,7 +231,7 @@ public class StripePaymentService : IPaymentService
|
|||||||
public Task RemoveOrganizationSponsorshipAsync(Organization org, OrganizationSponsorship sponsorship) =>
|
public Task RemoveOrganizationSponsorshipAsync(Organization org, OrganizationSponsorship sponsorship) =>
|
||||||
ChangeOrganizationSponsorship(org, sponsorship, false);
|
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)
|
OrganizationUpgrade upgrade)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(org.GatewaySubscriptionId))
|
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 (stripePaymentMethod, paymentMethodType) = IdentifyPaymentMethod(customer, subCreateOptions);
|
||||||
|
|
||||||
var subscription = await ChargeForNewSubscriptionAsync(org, customer, false,
|
var subscription = await ChargeForNewSubscriptionAsync(org, customer, false,
|
||||||
|
@ -1,398 +0,0 @@
|
|||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Models.StaticStore;
|
|
||||||
|
|
||||||
namespace Bit.Core.Utilities;
|
|
||||||
|
|
||||||
public static class PasswordManagerPlanStore
|
|
||||||
{
|
|
||||||
public static IEnumerable<Plan> CreatePlan()
|
|
||||||
{
|
|
||||||
return new List<Plan>
|
|
||||||
{
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.Free,
|
|
||||||
Product = ProductType.Free,
|
|
||||||
BitwardenProduct = BitwardenProductType.PasswordManager,
|
|
||||||
Name = "Free",
|
|
||||||
NameLocalizationKey = "planNameFree",
|
|
||||||
DescriptionLocalizationKey = "planDescFree",
|
|
||||||
BaseSeats = 2,
|
|
||||||
MaxCollections = 2,
|
|
||||||
MaxUsers = 2,
|
|
||||||
|
|
||||||
UpgradeSortOrder = -1, // Always the lowest plan, cannot be upgraded to
|
|
||||||
DisplaySortOrder = -1,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = false,
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.FamiliesAnnually2019,
|
|
||||||
Product = ProductType.Families,
|
|
||||||
BitwardenProduct = BitwardenProductType.PasswordManager,
|
|
||||||
Name = "Families 2019",
|
|
||||||
IsAnnual = true,
|
|
||||||
NameLocalizationKey = "planNameFamilies",
|
|
||||||
DescriptionLocalizationKey = "planDescFamilies",
|
|
||||||
BaseSeats = 5,
|
|
||||||
BaseStorageGb = 1,
|
|
||||||
MaxUsers = 5,
|
|
||||||
|
|
||||||
HasAdditionalStorageOption = true,
|
|
||||||
HasPremiumAccessOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
|
|
||||||
HasSelfHost = true,
|
|
||||||
HasTotp = true,
|
|
||||||
|
|
||||||
UpgradeSortOrder = 1,
|
|
||||||
DisplaySortOrder = 1,
|
|
||||||
LegacyYear = 2020,
|
|
||||||
|
|
||||||
StripePlanId = "personal-org-annually",
|
|
||||||
StripeStoragePlanId = "storage-gb-annually",
|
|
||||||
StripePremiumAccessPlanId = "personal-org-premium-access-annually",
|
|
||||||
BasePrice = 12,
|
|
||||||
AdditionalStoragePricePerGb = 4,
|
|
||||||
PremiumAccessOptionPrice = 40,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = false,
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.TeamsAnnually2019,
|
|
||||||
Product = ProductType.Teams,
|
|
||||||
BitwardenProduct = BitwardenProductType.PasswordManager,
|
|
||||||
Name = "Teams (Annually) 2019",
|
|
||||||
IsAnnual = true,
|
|
||||||
NameLocalizationKey = "planNameTeams",
|
|
||||||
DescriptionLocalizationKey = "planDescTeams",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseSeats = 5,
|
|
||||||
BaseStorageGb = 1,
|
|
||||||
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalStorageOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
|
|
||||||
HasTotp = true,
|
|
||||||
|
|
||||||
UpgradeSortOrder = 2,
|
|
||||||
DisplaySortOrder = 2,
|
|
||||||
LegacyYear = 2020,
|
|
||||||
|
|
||||||
StripePlanId = "teams-org-annually",
|
|
||||||
StripeSeatPlanId = "teams-org-seat-annually",
|
|
||||||
StripeStoragePlanId = "storage-gb-annually",
|
|
||||||
BasePrice = 60,
|
|
||||||
SeatPrice = 24,
|
|
||||||
AdditionalStoragePricePerGb = 4,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.TeamsMonthly2019,
|
|
||||||
Product = ProductType.Teams,
|
|
||||||
BitwardenProduct = BitwardenProductType.PasswordManager,
|
|
||||||
Name = "Teams (Monthly) 2019",
|
|
||||||
NameLocalizationKey = "planNameTeams",
|
|
||||||
DescriptionLocalizationKey = "planDescTeams",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseSeats = 5,
|
|
||||||
BaseStorageGb = 1,
|
|
||||||
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalStorageOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
|
|
||||||
HasTotp = true,
|
|
||||||
|
|
||||||
UpgradeSortOrder = 2,
|
|
||||||
DisplaySortOrder = 2,
|
|
||||||
LegacyYear = 2020,
|
|
||||||
|
|
||||||
StripePlanId = "teams-org-monthly",
|
|
||||||
StripeSeatPlanId = "teams-org-seat-monthly",
|
|
||||||
StripeStoragePlanId = "storage-gb-monthly",
|
|
||||||
BasePrice = 8,
|
|
||||||
SeatPrice = 2.5M,
|
|
||||||
AdditionalStoragePricePerGb = 0.5M,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.EnterpriseAnnually2019,
|
|
||||||
Name = "Enterprise (Annually) 2019",
|
|
||||||
IsAnnual = true,
|
|
||||||
Product = ProductType.Enterprise,
|
|
||||||
BitwardenProduct = BitwardenProductType.PasswordManager,
|
|
||||||
NameLocalizationKey = "planNameEnterprise",
|
|
||||||
DescriptionLocalizationKey = "planDescEnterprise",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseSeats = 0,
|
|
||||||
BaseStorageGb = 1,
|
|
||||||
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalStorageOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
|
|
||||||
HasPolicies = true,
|
|
||||||
HasSelfHost = true,
|
|
||||||
HasGroups = true,
|
|
||||||
HasDirectory = true,
|
|
||||||
HasEvents = true,
|
|
||||||
HasTotp = true,
|
|
||||||
Has2fa = true,
|
|
||||||
HasApi = true,
|
|
||||||
UsersGetPremium = true,
|
|
||||||
HasCustomPermissions = true,
|
|
||||||
|
|
||||||
UpgradeSortOrder = 3,
|
|
||||||
DisplaySortOrder = 3,
|
|
||||||
LegacyYear = 2020,
|
|
||||||
|
|
||||||
StripePlanId = null,
|
|
||||||
StripeSeatPlanId = "enterprise-org-seat-annually",
|
|
||||||
StripeStoragePlanId = "storage-gb-annually",
|
|
||||||
BasePrice = 0,
|
|
||||||
SeatPrice = 36,
|
|
||||||
AdditionalStoragePricePerGb = 4,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.EnterpriseMonthly2019,
|
|
||||||
Product = ProductType.Enterprise,
|
|
||||||
BitwardenProduct = BitwardenProductType.PasswordManager,
|
|
||||||
Name = "Enterprise (Monthly) 2019",
|
|
||||||
NameLocalizationKey = "planNameEnterprise",
|
|
||||||
DescriptionLocalizationKey = "planDescEnterprise",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseSeats = 0,
|
|
||||||
BaseStorageGb = 1,
|
|
||||||
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalStorageOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
|
|
||||||
HasPolicies = true,
|
|
||||||
HasGroups = true,
|
|
||||||
HasDirectory = true,
|
|
||||||
HasEvents = true,
|
|
||||||
HasTotp = true,
|
|
||||||
Has2fa = true,
|
|
||||||
HasApi = true,
|
|
||||||
HasSelfHost = true,
|
|
||||||
UsersGetPremium = true,
|
|
||||||
HasCustomPermissions = true,
|
|
||||||
|
|
||||||
UpgradeSortOrder = 3,
|
|
||||||
DisplaySortOrder = 3,
|
|
||||||
LegacyYear = 2020,
|
|
||||||
|
|
||||||
StripePlanId = null,
|
|
||||||
StripeSeatPlanId = "enterprise-org-seat-monthly",
|
|
||||||
StripeStoragePlanId = "storage-gb-monthly",
|
|
||||||
BasePrice = 0,
|
|
||||||
SeatPrice = 4M,
|
|
||||||
AdditionalStoragePricePerGb = 0.5M,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.FamiliesAnnually,
|
|
||||||
Product = ProductType.Families,
|
|
||||||
BitwardenProduct = BitwardenProductType.PasswordManager,
|
|
||||||
Name = "Families",
|
|
||||||
IsAnnual = true,
|
|
||||||
NameLocalizationKey = "planNameFamilies",
|
|
||||||
DescriptionLocalizationKey = "planDescFamilies",
|
|
||||||
BaseSeats = 6,
|
|
||||||
BaseStorageGb = 1,
|
|
||||||
MaxUsers = 6,
|
|
||||||
|
|
||||||
HasAdditionalStorageOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
|
|
||||||
HasSelfHost = true,
|
|
||||||
HasTotp = true,
|
|
||||||
UsersGetPremium = true,
|
|
||||||
|
|
||||||
UpgradeSortOrder = 1,
|
|
||||||
DisplaySortOrder = 1,
|
|
||||||
|
|
||||||
StripePlanId = "2020-families-org-annually",
|
|
||||||
StripeStoragePlanId = "storage-gb-annually",
|
|
||||||
BasePrice = 40,
|
|
||||||
AdditionalStoragePricePerGb = 4,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = false,
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.TeamsAnnually,
|
|
||||||
Product = ProductType.Teams,
|
|
||||||
BitwardenProduct = BitwardenProductType.PasswordManager,
|
|
||||||
Name = "Teams (Annually)",
|
|
||||||
IsAnnual = true,
|
|
||||||
NameLocalizationKey = "planNameTeams",
|
|
||||||
DescriptionLocalizationKey = "planDescTeams",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseStorageGb = 1,
|
|
||||||
BaseSeats = 0,
|
|
||||||
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalStorageOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
|
|
||||||
Has2fa = true,
|
|
||||||
HasApi = true,
|
|
||||||
HasDirectory = true,
|
|
||||||
HasEvents = true,
|
|
||||||
HasGroups = true,
|
|
||||||
HasTotp = true,
|
|
||||||
UsersGetPremium = true,
|
|
||||||
|
|
||||||
UpgradeSortOrder = 2,
|
|
||||||
DisplaySortOrder = 2,
|
|
||||||
|
|
||||||
StripeSeatPlanId = "2020-teams-org-seat-annually",
|
|
||||||
StripeStoragePlanId = "storage-gb-annually",
|
|
||||||
SeatPrice = 36,
|
|
||||||
AdditionalStoragePricePerGb = 4,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.TeamsMonthly,
|
|
||||||
Product = ProductType.Teams,
|
|
||||||
BitwardenProduct = BitwardenProductType.PasswordManager,
|
|
||||||
Name = "Teams (Monthly)",
|
|
||||||
NameLocalizationKey = "planNameTeams",
|
|
||||||
DescriptionLocalizationKey = "planDescTeams",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseStorageGb = 1,
|
|
||||||
BaseSeats = 0,
|
|
||||||
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalStorageOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
|
|
||||||
Has2fa = true,
|
|
||||||
HasApi = true,
|
|
||||||
HasDirectory = true,
|
|
||||||
HasEvents = true,
|
|
||||||
HasGroups = true,
|
|
||||||
HasTotp = true,
|
|
||||||
UsersGetPremium = true,
|
|
||||||
|
|
||||||
UpgradeSortOrder = 2,
|
|
||||||
DisplaySortOrder = 2,
|
|
||||||
|
|
||||||
StripeSeatPlanId = "2020-teams-org-seat-monthly",
|
|
||||||
StripeStoragePlanId = "storage-gb-monthly",
|
|
||||||
SeatPrice = 4,
|
|
||||||
AdditionalStoragePricePerGb = 0.5M,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.EnterpriseAnnually,
|
|
||||||
Name = "Enterprise (Annually)",
|
|
||||||
Product = ProductType.Enterprise,
|
|
||||||
BitwardenProduct = BitwardenProductType.PasswordManager,
|
|
||||||
IsAnnual = true,
|
|
||||||
NameLocalizationKey = "planNameEnterprise",
|
|
||||||
DescriptionLocalizationKey = "planDescEnterprise",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseSeats = 0,
|
|
||||||
BaseStorageGb = 1,
|
|
||||||
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalStorageOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
|
|
||||||
HasPolicies = true,
|
|
||||||
HasSelfHost = true,
|
|
||||||
HasGroups = true,
|
|
||||||
HasDirectory = true,
|
|
||||||
HasEvents = true,
|
|
||||||
HasTotp = true,
|
|
||||||
Has2fa = true,
|
|
||||||
HasApi = true,
|
|
||||||
HasSso = true,
|
|
||||||
HasKeyConnector = true,
|
|
||||||
HasScim = true,
|
|
||||||
HasResetPassword = true,
|
|
||||||
UsersGetPremium = true,
|
|
||||||
HasCustomPermissions = true,
|
|
||||||
|
|
||||||
UpgradeSortOrder = 3,
|
|
||||||
DisplaySortOrder = 3,
|
|
||||||
|
|
||||||
StripeSeatPlanId = "2020-enterprise-org-seat-annually",
|
|
||||||
StripeStoragePlanId = "storage-gb-annually",
|
|
||||||
BasePrice = 0,
|
|
||||||
SeatPrice = 60,
|
|
||||||
AdditionalStoragePricePerGb = 4,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.EnterpriseMonthly,
|
|
||||||
Product = ProductType.Enterprise,
|
|
||||||
BitwardenProduct = BitwardenProductType.PasswordManager,
|
|
||||||
Name = "Enterprise (Monthly)",
|
|
||||||
NameLocalizationKey = "planNameEnterprise",
|
|
||||||
DescriptionLocalizationKey = "planDescEnterprise",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseSeats = 0,
|
|
||||||
BaseStorageGb = 1,
|
|
||||||
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalStorageOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
|
|
||||||
HasPolicies = true,
|
|
||||||
HasGroups = true,
|
|
||||||
HasDirectory = true,
|
|
||||||
HasEvents = true,
|
|
||||||
HasTotp = true,
|
|
||||||
Has2fa = true,
|
|
||||||
HasApi = true,
|
|
||||||
HasSelfHost = true,
|
|
||||||
HasSso = true,
|
|
||||||
HasKeyConnector = true,
|
|
||||||
HasScim = true,
|
|
||||||
HasResetPassword = true,
|
|
||||||
UsersGetPremium = true,
|
|
||||||
HasCustomPermissions = true,
|
|
||||||
|
|
||||||
UpgradeSortOrder = 3,
|
|
||||||
DisplaySortOrder = 3,
|
|
||||||
|
|
||||||
StripeSeatPlanId = "2020-enterprise-seat-monthly",
|
|
||||||
StripeStoragePlanId = "storage-gb-monthly",
|
|
||||||
BasePrice = 0,
|
|
||||||
SeatPrice = 6,
|
|
||||||
AdditionalStoragePricePerGb = 0.5M,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.Custom,
|
|
||||||
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,172 +0,0 @@
|
|||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Models.StaticStore;
|
|
||||||
|
|
||||||
namespace Bit.Core.Utilities;
|
|
||||||
|
|
||||||
public static class SecretsManagerPlanStore
|
|
||||||
{
|
|
||||||
public static IEnumerable<Plan> CreatePlan()
|
|
||||||
{
|
|
||||||
return new List<Plan>
|
|
||||||
{
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.EnterpriseMonthly,
|
|
||||||
Product = ProductType.Enterprise,
|
|
||||||
BitwardenProduct = BitwardenProductType.SecretsManager,
|
|
||||||
Name = "Enterprise (Monthly)",
|
|
||||||
NameLocalizationKey = "planNameEnterprise",
|
|
||||||
DescriptionLocalizationKey = "planDescEnterprise",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseSeats = 0,
|
|
||||||
BaseServiceAccount = 200,
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalServiceAccountOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
HasPolicies = true,
|
|
||||||
HasGroups = true,
|
|
||||||
HasDirectory = true,
|
|
||||||
HasEvents = true,
|
|
||||||
HasTotp = true,
|
|
||||||
Has2fa = true,
|
|
||||||
HasApi = true,
|
|
||||||
HasSelfHost = true,
|
|
||||||
HasSso = true,
|
|
||||||
HasKeyConnector = true,
|
|
||||||
HasScim = true,
|
|
||||||
HasResetPassword = true,
|
|
||||||
UsersGetPremium = true,
|
|
||||||
HasCustomPermissions = true,
|
|
||||||
UpgradeSortOrder = 3,
|
|
||||||
DisplaySortOrder = 3,
|
|
||||||
StripeSeatPlanId = "secrets-manager-enterprise-seat-monthly",
|
|
||||||
StripeServiceAccountPlanId = "secrets-manager-service-account-monthly",
|
|
||||||
BasePrice = 0,
|
|
||||||
SeatPrice = 13,
|
|
||||||
AdditionalPricePerServiceAccount = 0.5M,
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
AllowServiceAccountsAutoscale = true
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.EnterpriseAnnually,
|
|
||||||
Name = "Enterprise (Annually)",
|
|
||||||
Product = ProductType.Enterprise,
|
|
||||||
BitwardenProduct = BitwardenProductType.SecretsManager,
|
|
||||||
IsAnnual = true,
|
|
||||||
NameLocalizationKey = "planNameEnterprise",
|
|
||||||
DescriptionLocalizationKey = "planDescEnterprise",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseSeats = 0,
|
|
||||||
BaseServiceAccount = 200,
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalServiceAccountOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
HasPolicies = true,
|
|
||||||
HasSelfHost = true,
|
|
||||||
HasGroups = true,
|
|
||||||
HasDirectory = true,
|
|
||||||
HasEvents = true,
|
|
||||||
HasTotp = true,
|
|
||||||
Has2fa = true,
|
|
||||||
HasApi = true,
|
|
||||||
HasSso = true,
|
|
||||||
HasKeyConnector = true,
|
|
||||||
HasScim = true,
|
|
||||||
HasResetPassword = true,
|
|
||||||
UsersGetPremium = true,
|
|
||||||
HasCustomPermissions = true,
|
|
||||||
UpgradeSortOrder = 3,
|
|
||||||
DisplaySortOrder = 3,
|
|
||||||
StripeSeatPlanId = "secrets-manager-enterprise-seat-annually",
|
|
||||||
StripeServiceAccountPlanId = "secrets-manager-service-account-annually",
|
|
||||||
BasePrice = 0,
|
|
||||||
SeatPrice = 144,
|
|
||||||
AdditionalPricePerServiceAccount = 6,
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
AllowServiceAccountsAutoscale = true
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.TeamsMonthly,
|
|
||||||
Name = "Teams (Monthly)",
|
|
||||||
Product = ProductType.Teams,
|
|
||||||
BitwardenProduct = BitwardenProductType.SecretsManager,
|
|
||||||
NameLocalizationKey = "planNameTeams",
|
|
||||||
DescriptionLocalizationKey = "planDescTeams",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseSeats = 0,
|
|
||||||
BaseServiceAccount = 50,
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalServiceAccountOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
Has2fa = true,
|
|
||||||
HasApi = true,
|
|
||||||
HasDirectory = true,
|
|
||||||
HasEvents = true,
|
|
||||||
HasGroups = true,
|
|
||||||
HasTotp = true,
|
|
||||||
UsersGetPremium = true,
|
|
||||||
UpgradeSortOrder = 2,
|
|
||||||
DisplaySortOrder = 2,
|
|
||||||
StripeSeatPlanId = "secrets-manager-teams-seat-monthly",
|
|
||||||
StripeServiceAccountPlanId = "secrets-manager-service-account-monthly",
|
|
||||||
BasePrice = 0,
|
|
||||||
SeatPrice = 7,
|
|
||||||
AdditionalPricePerServiceAccount = 0.5M,
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
AllowServiceAccountsAutoscale = true
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.TeamsAnnually,
|
|
||||||
Name = "Teams (Annually)",
|
|
||||||
Product = ProductType.Teams,
|
|
||||||
BitwardenProduct = BitwardenProductType.SecretsManager,
|
|
||||||
IsAnnual = true,
|
|
||||||
NameLocalizationKey = "planNameTeams",
|
|
||||||
DescriptionLocalizationKey = "planDescTeams",
|
|
||||||
CanBeUsedByBusiness = true,
|
|
||||||
BaseSeats = 0,
|
|
||||||
BaseServiceAccount = 50,
|
|
||||||
HasAdditionalSeatsOption = true,
|
|
||||||
HasAdditionalServiceAccountOption = true,
|
|
||||||
TrialPeriodDays = 7,
|
|
||||||
Has2fa = true,
|
|
||||||
HasApi = true,
|
|
||||||
HasDirectory = true,
|
|
||||||
HasEvents = true,
|
|
||||||
HasGroups = true,
|
|
||||||
HasTotp = true,
|
|
||||||
UsersGetPremium = true,
|
|
||||||
|
|
||||||
UpgradeSortOrder = 2,
|
|
||||||
DisplaySortOrder = 2,
|
|
||||||
StripeSeatPlanId = "secrets-manager-teams-seat-annually",
|
|
||||||
StripeServiceAccountPlanId = "secrets-manager-service-account-annually",
|
|
||||||
BasePrice = 0,
|
|
||||||
SeatPrice = 72,
|
|
||||||
AdditionalPricePerServiceAccount = 6,
|
|
||||||
AllowSeatAutoscale = true,
|
|
||||||
AllowServiceAccountsAutoscale = true
|
|
||||||
},
|
|
||||||
new Plan
|
|
||||||
{
|
|
||||||
Type = PlanType.Free,
|
|
||||||
Product = ProductType.Free,
|
|
||||||
BitwardenProduct = BitwardenProductType.SecretsManager,
|
|
||||||
Name = "Free",
|
|
||||||
NameLocalizationKey = "planNameFree",
|
|
||||||
DescriptionLocalizationKey = "planDescFree",
|
|
||||||
BaseSeats = 2,
|
|
||||||
BaseServiceAccount = 3,
|
|
||||||
MaxProjects = 3,
|
|
||||||
MaxUsers = 2,
|
|
||||||
MaxServiceAccounts = 3,
|
|
||||||
UpgradeSortOrder = -1, // Always the lowest plan, cannot be upgraded to
|
|
||||||
DisplaySortOrder = -1,
|
|
||||||
AllowSeatAutoscale = false,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,8 @@
|
|||||||
using Bit.Core.Enums;
|
using System.Collections.Immutable;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Models.StaticStore;
|
using Bit.Core.Models.StaticStore;
|
||||||
|
using Bit.Core.Models.StaticStore.Plans;
|
||||||
|
|
||||||
namespace Bit.Core.Utilities;
|
namespace Bit.Core.Utilities;
|
||||||
|
|
||||||
@ -104,21 +106,26 @@ public class StaticStore
|
|||||||
GlobalDomains.Add(GlobalEquivalentDomainsType.Pinterest, new List<string> { "pinterest.com", "pinterest.com.au", "pinterest.cl", "pinterest.de", "pinterest.dk", "pinterest.es", "pinterest.fr", "pinterest.co.uk", "pinterest.jp", "pinterest.co.kr", "pinterest.nz", "pinterest.pt", "pinterest.se" });
|
GlobalDomains.Add(GlobalEquivalentDomainsType.Pinterest, new List<string> { "pinterest.com", "pinterest.com.au", "pinterest.cl", "pinterest.de", "pinterest.dk", "pinterest.es", "pinterest.fr", "pinterest.co.uk", "pinterest.jp", "pinterest.co.kr", "pinterest.nz", "pinterest.pt", "pinterest.se" });
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Plans
|
Plans = new List<Models.StaticStore.Plan>
|
||||||
|
{
|
||||||
|
new EnterprisePlan(true),
|
||||||
|
new EnterprisePlan(false),
|
||||||
|
new TeamsPlan(true),
|
||||||
|
new TeamsPlan(false),
|
||||||
|
new FamiliesPlan(),
|
||||||
|
new FreePlan(),
|
||||||
|
new CustomPlan(),
|
||||||
|
|
||||||
PasswordManagerPlans = PasswordManagerPlanStore.CreatePlan();
|
new Enterprise2019Plan(true),
|
||||||
SecretManagerPlans = SecretsManagerPlanStore.CreatePlan();
|
new Enterprise2019Plan(false),
|
||||||
|
new Teams2019Plan(true),
|
||||||
Plans = PasswordManagerPlans.Concat(SecretManagerPlans);
|
new Teams2019Plan(false),
|
||||||
|
new Families2019Plan(),
|
||||||
|
}.ToImmutableList();
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IDictionary<GlobalEquivalentDomainsType, IEnumerable<string>> GlobalDomains { get; set; }
|
public static IDictionary<GlobalEquivalentDomainsType, IEnumerable<string>> GlobalDomains { get; set; }
|
||||||
public static IEnumerable<Plan> Plans { get; set; }
|
public static IEnumerable<Models.StaticStore.Plan> Plans { get; }
|
||||||
public static IEnumerable<Plan> SecretManagerPlans { get; set; }
|
|
||||||
public static IEnumerable<Plan> PasswordManagerPlans { get; set; }
|
|
||||||
public static IEnumerable<SponsoredPlan> SponsoredPlans { get; set; } = new[]
|
public static IEnumerable<SponsoredPlan> SponsoredPlans { get; set; } = new[]
|
||||||
{
|
{
|
||||||
new SponsoredPlan
|
new SponsoredPlan
|
||||||
@ -128,21 +135,20 @@ public class StaticStore
|
|||||||
SponsoringProductType = ProductType.Enterprise,
|
SponsoringProductType = ProductType.Enterprise,
|
||||||
StripePlanId = "2021-family-for-enterprise-annually",
|
StripePlanId = "2021-family-for-enterprise-annually",
|
||||||
UsersCanSponsor = (OrganizationUserOrganizationDetails org) =>
|
UsersCanSponsor = (OrganizationUserOrganizationDetails org) =>
|
||||||
GetPasswordManagerPlan(org.PlanType).Product == ProductType.Enterprise,
|
GetPlan(org.PlanType).Product == ProductType.Enterprise,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
public static Plan GetPasswordManagerPlan(PlanType planType) =>
|
|
||||||
PasswordManagerPlans.SingleOrDefault(p => p.Type == planType);
|
|
||||||
|
|
||||||
public static Plan GetSecretsManagerPlan(PlanType planType) =>
|
public static Models.StaticStore.Plan GetPlan(PlanType planType) =>
|
||||||
SecretManagerPlans.SingleOrDefault(p => p.Type == planType);
|
Plans.SingleOrDefault(p => p.Type == planType);
|
||||||
|
|
||||||
|
|
||||||
public static SponsoredPlan GetSponsoredPlan(PlanSponsorshipType planSponsorshipType) =>
|
public static SponsoredPlan GetSponsoredPlan(PlanSponsorshipType planSponsorshipType) =>
|
||||||
SponsoredPlans.FirstOrDefault(p => p.PlanSponsorshipType == planSponsorshipType);
|
SponsoredPlans.FirstOrDefault(p => p.PlanSponsorshipType == planSponsorshipType);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines if the stripe plan id is an addon item by checking if the provided stripe plan id
|
/// Determines if the stripe plan id is an addon item by checking if the provided stripe plan id
|
||||||
/// matches either the <see cref="Plan.StripeStoragePlanId"/> or <see cref="Plan.StripeServiceAccountPlanId"/>
|
/// matches either the <see cref="Plan.PasswordManagerPlanFeatures.StripeStoragePlanId"/> or <see cref="Plan.SecretsManagerPlanFeatures.StripeServiceAccountPlanId"/>
|
||||||
/// in any <see cref="Plans"/>.
|
/// in any <see cref="Plans"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stripePlanId"></param>
|
/// <param name="stripePlanId"></param>
|
||||||
@ -151,41 +157,8 @@ public class StaticStore
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public static bool IsAddonSubscriptionItem(string stripePlanId)
|
public static bool IsAddonSubscriptionItem(string stripePlanId)
|
||||||
{
|
{
|
||||||
if (PasswordManagerPlans.Select(p => p.StripeStoragePlanId).Contains(stripePlanId))
|
return Plans.Any(p =>
|
||||||
{
|
p.PasswordManager.StripeStoragePlanId == stripePlanId ||
|
||||||
return true;
|
(p.SecretsManager?.StripeServiceAccountPlanId == stripePlanId));
|
||||||
}
|
|
||||||
|
|
||||||
if (SecretManagerPlans.Select(p => p.StripeServiceAccountPlanId).Contains(stripePlanId))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a <see cref="Plan"/> by comparing the provided stripeId to the various
|
|
||||||
/// Stripe plan ids within a <see cref="Plan"/>.
|
|
||||||
/// The following <see cref="Plan"/> properties are checked:
|
|
||||||
/// <list type="bullet">
|
|
||||||
/// <item><see cref="Plan.StripePlanId"/></item>
|
|
||||||
/// <item><see cref="Plan.StripeSeatPlanId"/></item>
|
|
||||||
/// <item><see cref="Plan.StripeStoragePlanId"/></item>
|
|
||||||
/// <item><see cref="Plan.StripeServiceAccountPlanId"/></item>
|
|
||||||
/// <item><see cref="Plan.StripePremiumAccessPlanId"/></item>
|
|
||||||
/// </list>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stripeId"></param>
|
|
||||||
/// <returns>The plan if a matching stripeId was found, null otherwise</returns>
|
|
||||||
public static Plan GetPlanByStripeId(string stripeId)
|
|
||||||
{
|
|
||||||
return Plans.FirstOrDefault(p =>
|
|
||||||
p.StripePlanId == stripeId ||
|
|
||||||
p.StripeSeatPlanId == stripeId ||
|
|
||||||
p.StripeStoragePlanId == stripeId ||
|
|
||||||
p.StripeServiceAccountPlanId == stripeId ||
|
|
||||||
p.StripePremiumAccessPlanId == stripeId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,11 @@ namespace Bit.Api.Test.Controllers;
|
|||||||
public class OrganizationSponsorshipsControllerTests
|
public class OrganizationSponsorshipsControllerTests
|
||||||
{
|
{
|
||||||
public static IEnumerable<object[]> EnterprisePlanTypes =>
|
public static IEnumerable<object[]> EnterprisePlanTypes =>
|
||||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
|
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
|
||||||
public static IEnumerable<object[]> NonEnterprisePlanTypes =>
|
public static IEnumerable<object[]> NonEnterprisePlanTypes =>
|
||||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
|
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
|
||||||
public static IEnumerable<object[]> NonFamiliesPlanTypes =>
|
public static IEnumerable<object[]> NonFamiliesPlanTypes =>
|
||||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
|
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
|
||||||
|
|
||||||
public static IEnumerable<object[]> NonConfirmedOrganizationUsersStatuses =>
|
public static IEnumerable<object[]> NonConfirmedOrganizationUsersStatuses =>
|
||||||
Enum.GetValues<OrganizationUserStatusType>()
|
Enum.GetValues<OrganizationUserStatusType>()
|
||||||
|
@ -73,7 +73,7 @@ public class SyncControllerTests
|
|||||||
user.EquivalentDomains = JsonSerializer.Serialize(userEquivalentDomains);
|
user.EquivalentDomains = JsonSerializer.Serialize(userEquivalentDomains);
|
||||||
user.ExcludedGlobalEquivalentDomains = JsonSerializer.Serialize(userExcludedGlobalEquivalentDomains);
|
user.ExcludedGlobalEquivalentDomains = JsonSerializer.Serialize(userExcludedGlobalEquivalentDomains);
|
||||||
|
|
||||||
// At least 1 org needs to be enabled to fully test
|
// At least 1 org needs to be enabled to fully test
|
||||||
if (!organizationUserDetails.Any(o => o.Enabled))
|
if (!organizationUserDetails.Any(o => o.Enabled))
|
||||||
{
|
{
|
||||||
// We need at least 1 enabled org
|
// We need at least 1 enabled org
|
||||||
@ -165,7 +165,7 @@ public class SyncControllerTests
|
|||||||
user.EquivalentDomains = JsonSerializer.Serialize(userEquivalentDomains);
|
user.EquivalentDomains = JsonSerializer.Serialize(userEquivalentDomains);
|
||||||
user.ExcludedGlobalEquivalentDomains = JsonSerializer.Serialize(userExcludedGlobalEquivalentDomains);
|
user.ExcludedGlobalEquivalentDomains = JsonSerializer.Serialize(userExcludedGlobalEquivalentDomains);
|
||||||
|
|
||||||
// All orgs disabled
|
// All orgs disabled
|
||||||
if (organizationUserDetails.Count > 0)
|
if (organizationUserDetails.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (var orgUserDetails in organizationUserDetails)
|
foreach (var orgUserDetails in organizationUserDetails)
|
||||||
@ -218,7 +218,7 @@ public class SyncControllerTests
|
|||||||
|
|
||||||
Assert.IsType<SyncResponseModel>(result);
|
Assert.IsType<SyncResponseModel>(result);
|
||||||
|
|
||||||
// Collections should be empty when all standard orgs are disabled.
|
// Collections should be empty when all standard orgs are disabled.
|
||||||
Assert.Empty(result.Collections);
|
Assert.Empty(result.Collections);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,7 +297,7 @@ public class SyncControllerTests
|
|||||||
Assert.IsType<SyncResponseModel>(result);
|
Assert.IsType<SyncResponseModel>(result);
|
||||||
|
|
||||||
// Look up ProviderOrg output and compare to ProviderOrg method inputs to ensure
|
// Look up ProviderOrg output and compare to ProviderOrg method inputs to ensure
|
||||||
// product type is set correctly.
|
// product type is set correctly.
|
||||||
foreach (var profProviderOrg in result.Profile.ProviderOrganizations)
|
foreach (var profProviderOrg in result.Profile.ProviderOrganizations)
|
||||||
{
|
{
|
||||||
var matchedProviderUserOrgDetails =
|
var matchedProviderUserOrgDetails =
|
||||||
@ -305,7 +305,7 @@ public class SyncControllerTests
|
|||||||
|
|
||||||
if (matchedProviderUserOrgDetails != null)
|
if (matchedProviderUserOrgDetails != null)
|
||||||
{
|
{
|
||||||
var providerOrgProductType = StaticStore.GetPasswordManagerPlan(matchedProviderUserOrgDetails.PlanType).Product;
|
var providerOrgProductType = StaticStore.GetPlan(matchedProviderUserOrgDetails.PlanType).Product;
|
||||||
Assert.Equal(providerOrgProductType, profProviderOrg.PlanProductType);
|
Assert.Equal(providerOrgProductType, profProviderOrg.PlanProductType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,7 +337,7 @@ public class SyncControllerTests
|
|||||||
await sendRepository.ReceivedWithAnyArgs(1)
|
await sendRepository.ReceivedWithAnyArgs(1)
|
||||||
.GetManyByUserIdAsync(default);
|
.GetManyByUserIdAsync(default);
|
||||||
|
|
||||||
// These two are only called when at least 1 enabled org.
|
// These two are only called when at least 1 enabled org.
|
||||||
if (hasEnabledOrgs)
|
if (hasEnabledOrgs)
|
||||||
{
|
{
|
||||||
await collectionRepository.ReceivedWithAnyArgs(1)
|
await collectionRepository.ReceivedWithAnyArgs(1)
|
||||||
@ -347,7 +347,7 @@ public class SyncControllerTests
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// all disabled orgs
|
// all disabled orgs
|
||||||
await collectionRepository.ReceivedWithAnyArgs(0)
|
await collectionRepository.ReceivedWithAnyArgs(0)
|
||||||
.GetManyByUserIdAsync(default);
|
.GetManyByUserIdAsync(default);
|
||||||
await collectionCipherRepository.ReceivedWithAnyArgs(0)
|
await collectionCipherRepository.ReceivedWithAnyArgs(0)
|
||||||
|
@ -66,7 +66,7 @@ internal class PaidOrganization : ICustomization
|
|||||||
public PlanType CheckedPlanType { get; set; }
|
public PlanType CheckedPlanType { get; set; }
|
||||||
public void Customize(IFixture fixture)
|
public void Customize(IFixture fixture)
|
||||||
{
|
{
|
||||||
var validUpgradePlans = StaticStore.PasswordManagerPlans.Where(p => p.Type != PlanType.Free && p.LegacyYear == null).OrderBy(p => p.UpgradeSortOrder).Select(p => p.Type).ToList();
|
var validUpgradePlans = StaticStore.Plans.Where(p => p.Type != PlanType.Free && p.LegacyYear == null).OrderBy(p => p.UpgradeSortOrder).Select(p => p.Type).ToList();
|
||||||
var lowestActivePaidPlan = validUpgradePlans.First();
|
var lowestActivePaidPlan = validUpgradePlans.First();
|
||||||
CheckedPlanType = CheckedPlanType.Equals(PlanType.Free) ? lowestActivePaidPlan : CheckedPlanType;
|
CheckedPlanType = CheckedPlanType.Equals(PlanType.Free) ? lowestActivePaidPlan : CheckedPlanType;
|
||||||
validUpgradePlans.Remove(lowestActivePaidPlan);
|
validUpgradePlans.Remove(lowestActivePaidPlan);
|
||||||
@ -94,11 +94,11 @@ internal class FreeOrganizationUpgrade : ICustomization
|
|||||||
.With(o => o.PlanType, PlanType.Free));
|
.With(o => o.PlanType, PlanType.Free));
|
||||||
|
|
||||||
var plansToIgnore = new List<PlanType> { PlanType.Free, PlanType.Custom };
|
var plansToIgnore = new List<PlanType> { PlanType.Free, PlanType.Custom };
|
||||||
var selectedPlan = StaticStore.PasswordManagerPlans.Last(p => !plansToIgnore.Contains(p.Type) && !p.Disabled);
|
var selectedPlan = StaticStore.Plans.Last(p => !plansToIgnore.Contains(p.Type) && !p.Disabled);
|
||||||
|
|
||||||
fixture.Customize<OrganizationUpgrade>(composer => composer
|
fixture.Customize<OrganizationUpgrade>(composer => composer
|
||||||
.With(ou => ou.Plan, selectedPlan.Type)
|
.With(ou => ou.Plan, selectedPlan.Type)
|
||||||
.With(ou => ou.PremiumAccessAddon, selectedPlan.HasPremiumAccessOption));
|
.With(ou => ou.PremiumAccessAddon, selectedPlan.PasswordManager.HasPremiumAccessOption));
|
||||||
fixture.Customize<Organization>(composer => composer
|
fixture.Customize<Organization>(composer => composer
|
||||||
.Without(o => o.GatewaySubscriptionId));
|
.Without(o => o.GatewaySubscriptionId));
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ public class SecretsManagerOrganizationCustomization : ICustomization
|
|||||||
.With(o => o.UseSecretsManager, true)
|
.With(o => o.UseSecretsManager, true)
|
||||||
.With(o => o.SecretsManagerBeta, false)
|
.With(o => o.SecretsManagerBeta, false)
|
||||||
.With(o => o.PlanType, planType)
|
.With(o => o.PlanType, planType)
|
||||||
.With(o => o.Plan, StaticStore.GetPasswordManagerPlan(planType).Name)
|
.With(o => o.Plan, StaticStore.GetPlan(planType).Name)
|
||||||
.With(o => o.MaxAutoscaleSmSeats, (int?)null)
|
.With(o => o.MaxAutoscaleSmSeats, (int?)null)
|
||||||
.With(o => o.MaxAutoscaleSmServiceAccounts, (int?)null)
|
.With(o => o.MaxAutoscaleSmServiceAccounts, (int?)null)
|
||||||
);
|
);
|
||||||
|
@ -6,16 +6,16 @@ namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesFo
|
|||||||
public abstract class FamiliesForEnterpriseTestsBase
|
public abstract class FamiliesForEnterpriseTestsBase
|
||||||
{
|
{
|
||||||
public static IEnumerable<object[]> EnterprisePlanTypes =>
|
public static IEnumerable<object[]> EnterprisePlanTypes =>
|
||||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
|
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
|
||||||
|
|
||||||
public static IEnumerable<object[]> NonEnterprisePlanTypes =>
|
public static IEnumerable<object[]> NonEnterprisePlanTypes =>
|
||||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
|
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
|
||||||
|
|
||||||
public static IEnumerable<object[]> FamiliesPlanTypes =>
|
public static IEnumerable<object[]> FamiliesPlanTypes =>
|
||||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Families).Select(p => new object[] { p });
|
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Families).Select(p => new object[] { p });
|
||||||
|
|
||||||
public static IEnumerable<object[]> NonFamiliesPlanTypes =>
|
public static IEnumerable<object[]> NonFamiliesPlanTypes =>
|
||||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
|
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
|
||||||
|
|
||||||
public static IEnumerable<object[]> NonConfirmedOrganizationUsersStatuses =>
|
public static IEnumerable<object[]> NonConfirmedOrganizationUsersStatuses =>
|
||||||
Enum.GetValues<OrganizationUserStatusType>()
|
Enum.GetValues<OrganizationUserStatusType>()
|
||||||
|
@ -32,7 +32,7 @@ public class AddSecretsManagerSubscriptionCommandTests
|
|||||||
{
|
{
|
||||||
organization.PlanType = planType;
|
organization.PlanType = planType;
|
||||||
|
|
||||||
var plan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType);
|
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType);
|
||||||
|
|
||||||
await sutProvider.Sut.SignUpAsync(organization, additionalSmSeats, additionalServiceAccounts);
|
await sutProvider.Sut.SignUpAsync(organization, additionalSmSeats, additionalServiceAccounts);
|
||||||
|
|
||||||
@ -49,8 +49,8 @@ public class AddSecretsManagerSubscriptionCommandTests
|
|||||||
// TODO: call ReferenceEventService - see AC-1481
|
// TODO: call ReferenceEventService - see AC-1481
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>().Received(1).ReplaceAndUpdateCacheAsync(Arg.Is<Organization>(c =>
|
sutProvider.GetDependency<IOrganizationService>().Received(1).ReplaceAndUpdateCacheAsync(Arg.Is<Organization>(c =>
|
||||||
c.SmSeats == plan.BaseSeats + additionalSmSeats &&
|
c.SmSeats == plan.SecretsManager.BaseSeats + additionalSmSeats &&
|
||||||
c.SmServiceAccounts == plan.BaseServiceAccount.GetValueOrDefault() + additionalServiceAccounts &&
|
c.SmServiceAccounts == plan.SecretsManager.BaseServiceAccount + additionalServiceAccounts &&
|
||||||
c.UseSecretsManager == true));
|
c.UseSecretsManager == true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ public class UpdateSecretsManagerSubscriptionCommandTests
|
|||||||
|
|
||||||
await sutProvider.Sut.UpdateSubscriptionAsync(update);
|
await sutProvider.Sut.UpdateSubscriptionAsync(update);
|
||||||
|
|
||||||
var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType);
|
var plan = StaticStore.GetPlan(organization.PlanType);
|
||||||
await sutProvider.GetDependency<IPaymentService>().Received(1)
|
await sutProvider.GetDependency<IPaymentService>().Received(1)
|
||||||
.AdjustSeatsAsync(organization, plan, update.SmSeatsExcludingBase);
|
.AdjustSeatsAsync(organization, plan, update.SmSeatsExcludingBase);
|
||||||
await sutProvider.GetDependency<IPaymentService>().Received(1)
|
await sutProvider.GetDependency<IPaymentService>().Received(1)
|
||||||
@ -96,7 +96,7 @@ public class UpdateSecretsManagerSubscriptionCommandTests
|
|||||||
|
|
||||||
await sutProvider.Sut.UpdateSubscriptionAsync(update);
|
await sutProvider.Sut.UpdateSubscriptionAsync(update);
|
||||||
|
|
||||||
var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType);
|
var plan = StaticStore.GetPlan(organization.PlanType);
|
||||||
await sutProvider.GetDependency<IPaymentService>().Received(1)
|
await sutProvider.GetDependency<IPaymentService>().Received(1)
|
||||||
.AdjustSeatsAsync(organization, plan, update.SmSeatsExcludingBase);
|
.AdjustSeatsAsync(organization, plan, update.SmSeatsExcludingBase);
|
||||||
await sutProvider.GetDependency<IPaymentService>().Received(1)
|
await sutProvider.GetDependency<IPaymentService>().Received(1)
|
||||||
@ -213,11 +213,11 @@ public class UpdateSecretsManagerSubscriptionCommandTests
|
|||||||
public async Task AdjustServiceAccountsAsync_WithEnterpriseOrTeamsPlans_Success(PlanType planType, Guid organizationId,
|
public async Task AdjustServiceAccountsAsync_WithEnterpriseOrTeamsPlans_Success(PlanType planType, Guid organizationId,
|
||||||
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider)
|
SutProvider<UpdateSecretsManagerSubscriptionCommand> sutProvider)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == planType);
|
var plan = StaticStore.GetPlan(planType);
|
||||||
|
|
||||||
var organizationSeats = plan.BaseSeats + 10;
|
var organizationSeats = plan.SecretsManager.BaseSeats + 10;
|
||||||
var organizationMaxAutoscaleSeats = 20;
|
var organizationMaxAutoscaleSeats = 20;
|
||||||
var organizationServiceAccounts = plan.BaseServiceAccount.GetValueOrDefault() + 10;
|
var organizationServiceAccounts = plan.SecretsManager.BaseServiceAccount + 10;
|
||||||
var organizationMaxAutoscaleServiceAccounts = 300;
|
var organizationMaxAutoscaleServiceAccounts = 300;
|
||||||
|
|
||||||
var organization = new Organization
|
var organization = new Organization
|
||||||
@ -235,7 +235,7 @@ public class UpdateSecretsManagerSubscriptionCommandTests
|
|||||||
|
|
||||||
var smServiceAccountsAdjustment = 10;
|
var smServiceAccountsAdjustment = 10;
|
||||||
var expectedSmServiceAccounts = organizationServiceAccounts + smServiceAccountsAdjustment;
|
var expectedSmServiceAccounts = organizationServiceAccounts + smServiceAccountsAdjustment;
|
||||||
var expectedSmServiceAccountsExcludingBase = expectedSmServiceAccounts - plan.BaseServiceAccount.GetValueOrDefault();
|
var expectedSmServiceAccountsExcludingBase = expectedSmServiceAccounts - plan.SecretsManager.BaseServiceAccount;
|
||||||
|
|
||||||
var update = new SecretsManagerSubscriptionUpdate(organization, false).AdjustServiceAccounts(10);
|
var update = new SecretsManagerSubscriptionUpdate(organization, false).AdjustServiceAccounts(10);
|
||||||
|
|
||||||
|
@ -94,6 +94,7 @@ public class UpgradeOrganizationPlanCommandTests
|
|||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
upgrade.AdditionalSmSeats = 10;
|
upgrade.AdditionalSmSeats = 10;
|
||||||
upgrade.AdditionalSeats = 10;
|
upgrade.AdditionalSeats = 10;
|
||||||
|
upgrade.Plan = PlanType.TeamsAnnually;
|
||||||
await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade);
|
await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade);
|
||||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).ReplaceAndUpdateCacheAsync(organization);
|
await sutProvider.GetDependency<IOrganizationService>().Received(1).ReplaceAndUpdateCacheAsync(organization);
|
||||||
}
|
}
|
||||||
@ -108,8 +109,7 @@ public class UpgradeOrganizationPlanCommandTests
|
|||||||
{
|
{
|
||||||
upgrade.Plan = planType;
|
upgrade.Plan = planType;
|
||||||
|
|
||||||
var passwordManagerPlan = StaticStore.GetPasswordManagerPlan(upgrade.Plan);
|
var plan = StaticStore.GetPlan(upgrade.Plan);
|
||||||
var secretsManagerPlan = StaticStore.GetSecretsManagerPlan(upgrade.Plan);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
|
||||||
@ -121,9 +121,9 @@ public class UpgradeOrganizationPlanCommandTests
|
|||||||
|
|
||||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).ReplaceAndUpdateCacheAsync(
|
await sutProvider.GetDependency<IOrganizationService>().Received(1).ReplaceAndUpdateCacheAsync(
|
||||||
Arg.Is<Organization>(o =>
|
Arg.Is<Organization>(o =>
|
||||||
o.Seats == passwordManagerPlan.BaseSeats + upgrade.AdditionalSeats
|
o.Seats == plan.PasswordManager.BaseSeats + upgrade.AdditionalSeats
|
||||||
&& o.SmSeats == secretsManagerPlan.BaseSeats + upgrade.AdditionalSmSeats
|
&& o.SmSeats == plan.SecretsManager.BaseSeats + upgrade.AdditionalSmSeats
|
||||||
&& o.SmServiceAccounts == secretsManagerPlan.BaseServiceAccount + upgrade.AdditionalServiceAccounts));
|
&& o.SmServiceAccounts == plan.SecretsManager.BaseServiceAccount + upgrade.AdditionalServiceAccounts));
|
||||||
|
|
||||||
Assert.True(result.Item1);
|
Assert.True(result.Item1);
|
||||||
Assert.NotNull(result.Item2);
|
Assert.NotNull(result.Item2);
|
||||||
|
@ -155,20 +155,20 @@ public class OrganizationServiceTests
|
|||||||
{
|
{
|
||||||
signup.Plan = planType;
|
signup.Plan = planType;
|
||||||
|
|
||||||
var passwordManagerPlan = StaticStore.GetPasswordManagerPlan(signup.Plan);
|
var plan = StaticStore.GetPlan(signup.Plan);
|
||||||
|
|
||||||
signup.AdditionalSeats = 0;
|
signup.AdditionalSeats = 0;
|
||||||
signup.PaymentMethodType = PaymentMethodType.Card;
|
signup.PaymentMethodType = PaymentMethodType.Card;
|
||||||
signup.PremiumAccessAddon = false;
|
signup.PremiumAccessAddon = false;
|
||||||
signup.UseSecretsManager = false;
|
signup.UseSecretsManager = false;
|
||||||
|
|
||||||
var purchaseOrganizationPlan = StaticStore.Plans.Where(x => x.Type == signup.Plan).ToList();
|
var purchaseOrganizationPlan = StaticStore.GetPlan(signup.Plan);
|
||||||
|
|
||||||
var result = await sutProvider.Sut.SignUpAsync(signup);
|
var result = await sutProvider.Sut.SignUpAsync(signup);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).CreateAsync(
|
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).CreateAsync(
|
||||||
Arg.Is<Organization>(o =>
|
Arg.Is<Organization>(o =>
|
||||||
o.Seats == passwordManagerPlan.BaseSeats + signup.AdditionalSeats
|
o.Seats == plan.PasswordManager.BaseSeats + signup.AdditionalSeats
|
||||||
&& o.SmSeats == null
|
&& o.SmSeats == null
|
||||||
&& o.SmServiceAccounts == null));
|
&& o.SmServiceAccounts == null));
|
||||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
|
||||||
@ -177,8 +177,8 @@ public class OrganizationServiceTests
|
|||||||
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
|
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
|
||||||
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
|
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
|
||||||
referenceEvent.Type == ReferenceEventType.Signup &&
|
referenceEvent.Type == ReferenceEventType.Signup &&
|
||||||
referenceEvent.PlanName == passwordManagerPlan.Name &&
|
referenceEvent.PlanName == plan.Name &&
|
||||||
referenceEvent.PlanType == passwordManagerPlan.Type &&
|
referenceEvent.PlanType == plan.Type &&
|
||||||
referenceEvent.Seats == result.Item1.Seats &&
|
referenceEvent.Seats == result.Item1.Seats &&
|
||||||
referenceEvent.Storage == result.Item1.MaxStorageGb));
|
referenceEvent.Storage == result.Item1.MaxStorageGb));
|
||||||
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
|
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
|
||||||
@ -192,7 +192,7 @@ public class OrganizationServiceTests
|
|||||||
Arg.Any<Organization>(),
|
Arg.Any<Organization>(),
|
||||||
signup.PaymentMethodType.Value,
|
signup.PaymentMethodType.Value,
|
||||||
signup.PaymentToken,
|
signup.PaymentToken,
|
||||||
Arg.Is<List<Plan>>(plan => plan.Single() == passwordManagerPlan),
|
plan,
|
||||||
signup.AdditionalStorageGb,
|
signup.AdditionalStorageGb,
|
||||||
signup.AdditionalSeats,
|
signup.AdditionalSeats,
|
||||||
signup.PremiumAccessAddon,
|
signup.PremiumAccessAddon,
|
||||||
@ -212,8 +212,7 @@ public class OrganizationServiceTests
|
|||||||
{
|
{
|
||||||
signup.Plan = planType;
|
signup.Plan = planType;
|
||||||
|
|
||||||
var passwordManagerPlan = StaticStore.GetPasswordManagerPlan(signup.Plan);
|
var plan = StaticStore.GetPlan(signup.Plan);
|
||||||
var secretsManagerPlan = StaticStore.GetSecretsManagerPlan(signup.Plan);
|
|
||||||
|
|
||||||
signup.UseSecretsManager = true;
|
signup.UseSecretsManager = true;
|
||||||
signup.AdditionalSeats = 15;
|
signup.AdditionalSeats = 15;
|
||||||
@ -222,23 +221,21 @@ public class OrganizationServiceTests
|
|||||||
signup.PaymentMethodType = PaymentMethodType.Card;
|
signup.PaymentMethodType = PaymentMethodType.Card;
|
||||||
signup.PremiumAccessAddon = false;
|
signup.PremiumAccessAddon = false;
|
||||||
|
|
||||||
var purchaseOrganizationPlan = StaticStore.Plans.Where(x => x.Type == signup.Plan).ToList();
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.SignUpAsync(signup);
|
var result = await sutProvider.Sut.SignUpAsync(signup);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).CreateAsync(
|
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).CreateAsync(
|
||||||
Arg.Is<Organization>(o =>
|
Arg.Is<Organization>(o =>
|
||||||
o.Seats == passwordManagerPlan.BaseSeats + signup.AdditionalSeats
|
o.Seats == plan.PasswordManager.BaseSeats + signup.AdditionalSeats
|
||||||
&& o.SmSeats == secretsManagerPlan.BaseSeats + signup.AdditionalSmSeats
|
&& o.SmSeats == plan.SecretsManager.BaseSeats + signup.AdditionalSmSeats
|
||||||
&& o.SmServiceAccounts == secretsManagerPlan.BaseServiceAccount + signup.AdditionalServiceAccounts));
|
&& o.SmServiceAccounts == plan.SecretsManager.BaseServiceAccount + signup.AdditionalServiceAccounts));
|
||||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
|
||||||
Arg.Is<OrganizationUser>(o => o.AccessSecretsManager == signup.UseSecretsManager));
|
Arg.Is<OrganizationUser>(o => o.AccessSecretsManager == signup.UseSecretsManager));
|
||||||
|
|
||||||
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
|
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
|
||||||
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
|
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
|
||||||
referenceEvent.Type == ReferenceEventType.Signup &&
|
referenceEvent.Type == ReferenceEventType.Signup &&
|
||||||
referenceEvent.PlanName == purchaseOrganizationPlan[0].Name &&
|
referenceEvent.PlanName == plan.Name &&
|
||||||
referenceEvent.PlanType == purchaseOrganizationPlan[0].Type &&
|
referenceEvent.PlanType == plan.Type &&
|
||||||
referenceEvent.Seats == result.Item1.Seats &&
|
referenceEvent.Seats == result.Item1.Seats &&
|
||||||
referenceEvent.Storage == result.Item1.MaxStorageGb));
|
referenceEvent.Storage == result.Item1.MaxStorageGb));
|
||||||
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
|
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
|
||||||
@ -252,7 +249,7 @@ public class OrganizationServiceTests
|
|||||||
Arg.Any<Organization>(),
|
Arg.Any<Organization>(),
|
||||||
signup.PaymentMethodType.Value,
|
signup.PaymentMethodType.Value,
|
||||||
signup.PaymentToken,
|
signup.PaymentToken,
|
||||||
Arg.Is<List<Plan>>(plan => plan.All(p => purchaseOrganizationPlan.Contains(p))),
|
Arg.Is<Plan>(plan),
|
||||||
signup.AdditionalStorageGb,
|
signup.AdditionalStorageGb,
|
||||||
signup.AdditionalSeats,
|
signup.AdditionalSeats,
|
||||||
signup.PremiumAccessAddon,
|
signup.PremiumAccessAddon,
|
||||||
@ -1706,7 +1703,7 @@ public class OrganizationServiceTests
|
|||||||
public void ValidateSecretsManagerPlan_ThrowsException_WhenInvalidPlanSelected(
|
public void ValidateSecretsManagerPlan_ThrowsException_WhenInvalidPlanSelected(
|
||||||
PlanType planType, SutProvider<OrganizationService> sutProvider)
|
PlanType planType, SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.Plans.FirstOrDefault(x => x.Type == planType);
|
var plan = StaticStore.GetPlan(planType);
|
||||||
|
|
||||||
var signup = new OrganizationUpgrade
|
var signup = new OrganizationUpgrade
|
||||||
{
|
{
|
||||||
@ -1727,7 +1724,7 @@ public class OrganizationServiceTests
|
|||||||
[BitAutoData(PlanType.EnterpriseMonthly)]
|
[BitAutoData(PlanType.EnterpriseMonthly)]
|
||||||
public void ValidateSecretsManagerPlan_ThrowsException_WhenNoSecretsManagerSeats(PlanType planType, SutProvider<OrganizationService> sutProvider)
|
public void ValidateSecretsManagerPlan_ThrowsException_WhenNoSecretsManagerSeats(PlanType planType, SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType);
|
var plan = StaticStore.GetPlan(planType);
|
||||||
var signup = new OrganizationUpgrade
|
var signup = new OrganizationUpgrade
|
||||||
{
|
{
|
||||||
UseSecretsManager = true,
|
UseSecretsManager = true,
|
||||||
@ -1744,7 +1741,7 @@ public class OrganizationServiceTests
|
|||||||
[BitAutoData(PlanType.Free)]
|
[BitAutoData(PlanType.Free)]
|
||||||
public void ValidateSecretsManagerPlan_ThrowsException_WhenSubtractingSeats(PlanType planType, SutProvider<OrganizationService> sutProvider)
|
public void ValidateSecretsManagerPlan_ThrowsException_WhenSubtractingSeats(PlanType planType, SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType);
|
var plan = StaticStore.GetPlan(planType);
|
||||||
var signup = new OrganizationUpgrade
|
var signup = new OrganizationUpgrade
|
||||||
{
|
{
|
||||||
UseSecretsManager = true,
|
UseSecretsManager = true,
|
||||||
@ -1761,7 +1758,7 @@ public class OrganizationServiceTests
|
|||||||
PlanType planType,
|
PlanType planType,
|
||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType);
|
var plan = StaticStore.GetPlan(planType);
|
||||||
var signup = new OrganizationUpgrade
|
var signup = new OrganizationUpgrade
|
||||||
{
|
{
|
||||||
UseSecretsManager = true,
|
UseSecretsManager = true,
|
||||||
@ -1780,7 +1777,7 @@ public class OrganizationServiceTests
|
|||||||
[BitAutoData(PlanType.EnterpriseMonthly)]
|
[BitAutoData(PlanType.EnterpriseMonthly)]
|
||||||
public void ValidateSecretsManagerPlan_ThrowsException_WhenMoreSeatsThanPasswordManagerSeats(PlanType planType, SutProvider<OrganizationService> sutProvider)
|
public void ValidateSecretsManagerPlan_ThrowsException_WhenMoreSeatsThanPasswordManagerSeats(PlanType planType, SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType);
|
var plan = StaticStore.GetPlan(planType);
|
||||||
var signup = new OrganizationUpgrade
|
var signup = new OrganizationUpgrade
|
||||||
{
|
{
|
||||||
UseSecretsManager = true,
|
UseSecretsManager = true,
|
||||||
@ -1801,7 +1798,7 @@ public class OrganizationServiceTests
|
|||||||
PlanType planType,
|
PlanType planType,
|
||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType);
|
var plan = StaticStore.GetPlan(planType);
|
||||||
var signup = new OrganizationUpgrade
|
var signup = new OrganizationUpgrade
|
||||||
{
|
{
|
||||||
UseSecretsManager = true,
|
UseSecretsManager = true,
|
||||||
@ -1819,7 +1816,7 @@ public class OrganizationServiceTests
|
|||||||
PlanType planType,
|
PlanType planType,
|
||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType);
|
var plan = StaticStore.GetPlan(planType);
|
||||||
var signup = new OrganizationUpgrade
|
var signup = new OrganizationUpgrade
|
||||||
{
|
{
|
||||||
UseSecretsManager = true,
|
UseSecretsManager = true,
|
||||||
@ -1840,7 +1837,7 @@ public class OrganizationServiceTests
|
|||||||
PlanType planType,
|
PlanType planType,
|
||||||
SutProvider<OrganizationService> sutProvider)
|
SutProvider<OrganizationService> sutProvider)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType);
|
var plan = StaticStore.GetPlan(planType);
|
||||||
var signup = new OrganizationUpgrade
|
var signup = new OrganizationUpgrade
|
||||||
{
|
{
|
||||||
UseSecretsManager = true,
|
UseSecretsManager = true,
|
||||||
|
@ -40,7 +40,7 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_Stripe_ProviderOrg_Coupon_Add(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo, bool provider = true)
|
public async void PurchaseOrganizationAsync_Stripe_ProviderOrg_Coupon_Add(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo, bool provider = true)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||||
@ -56,7 +56,7 @@ public class StripePaymentServiceTests
|
|||||||
.BaseServiceUri.CloudRegion
|
.BaseServiceUri.CloudRegion
|
||||||
.Returns("US");
|
.Returns("US");
|
||||||
|
|
||||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo, provider);
|
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo, provider);
|
||||||
|
|
||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
||||||
@ -95,8 +95,8 @@ public class StripePaymentServiceTests
|
|||||||
public async void PurchaseOrganizationAsync_SM_Stripe_ProviderOrg_Coupon_Add(SutProvider<StripePaymentService> sutProvider, Organization organization,
|
public async void PurchaseOrganizationAsync_SM_Stripe_ProviderOrg_Coupon_Add(SutProvider<StripePaymentService> sutProvider, Organization organization,
|
||||||
string paymentToken, TaxInfo taxInfo, bool provider = true)
|
string paymentToken, TaxInfo taxInfo, bool provider = true)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
|
organization.UseSecretsManager = true;
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||||
{
|
{
|
||||||
@ -112,7 +112,7 @@ public class StripePaymentServiceTests
|
|||||||
.BaseServiceUri.CloudRegion
|
.BaseServiceUri.CloudRegion
|
||||||
.Returns("US");
|
.Returns("US");
|
||||||
|
|
||||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 1, 1,
|
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 1, 1,
|
||||||
false, taxInfo, provider, 1, 1);
|
false, taxInfo, provider, 1, 1);
|
||||||
|
|
||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
@ -151,8 +151,8 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_Stripe(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_Stripe(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
|
organization.UseSecretsManager = true;
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||||
{
|
{
|
||||||
@ -167,7 +167,7 @@ public class StripePaymentServiceTests
|
|||||||
.BaseServiceUri.CloudRegion
|
.BaseServiceUri.CloudRegion
|
||||||
.Returns("US");
|
.Returns("US");
|
||||||
|
|
||||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0
|
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0
|
||||||
, false, taxInfo, false, 8, 10);
|
, false, taxInfo, false, 8, 10);
|
||||||
|
|
||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
@ -207,7 +207,7 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_Stripe_PM(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_Stripe_PM(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.PasswordManagerPlans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
paymentToken = "pm_" + paymentToken;
|
paymentToken = "pm_" + paymentToken;
|
||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
@ -224,7 +224,7 @@ public class StripePaymentServiceTests
|
|||||||
.BaseServiceUri.CloudRegion
|
.BaseServiceUri.CloudRegion
|
||||||
.Returns("US");
|
.Returns("US");
|
||||||
|
|
||||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo);
|
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
|
||||||
|
|
||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
||||||
@ -264,7 +264,7 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_Stripe_TaxRate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_Stripe_TaxRate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||||
@ -280,7 +280,7 @@ public class StripePaymentServiceTests
|
|||||||
t.Country == taxInfo.BillingAddressCountry && t.PostalCode == taxInfo.BillingAddressPostalCode))
|
t.Country == taxInfo.BillingAddressCountry && t.PostalCode == taxInfo.BillingAddressPostalCode))
|
||||||
.Returns(new List<TaxRate> { new() { Id = "T-1" } });
|
.Returns(new List<TaxRate> { new() { Id = "T-1" } });
|
||||||
|
|
||||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo);
|
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
|
||||||
|
|
||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_Stripe_TaxRate_SM(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_Stripe_TaxRate_SM(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||||
@ -309,7 +309,7 @@ public class StripePaymentServiceTests
|
|||||||
t.Country == taxInfo.BillingAddressCountry && t.PostalCode == taxInfo.BillingAddressPostalCode))
|
t.Country == taxInfo.BillingAddressCountry && t.PostalCode == taxInfo.BillingAddressPostalCode))
|
||||||
.Returns(new List<TaxRate> { new() { Id = "T-1" } });
|
.Returns(new List<TaxRate> { new() { Id = "T-1" } });
|
||||||
|
|
||||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 2, 2,
|
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 2, 2,
|
||||||
false, taxInfo, false, 2, 2);
|
false, taxInfo, false, 2, 2);
|
||||||
|
|
||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
@ -323,7 +323,7 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_Stripe_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_Stripe_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
paymentToken = "pm_" + paymentToken;
|
paymentToken = "pm_" + paymentToken;
|
||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
@ -356,7 +356,7 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_SM_Stripe_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_SM_Stripe_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
paymentToken = "pm_" + paymentToken;
|
paymentToken = "pm_" + paymentToken;
|
||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
@ -390,7 +390,7 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_Stripe_RequiresAction(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_Stripe_RequiresAction(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||||
@ -412,7 +412,7 @@ public class StripePaymentServiceTests
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo);
|
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
|
||||||
|
|
||||||
Assert.Equal("clientSecret", result);
|
Assert.Equal("clientSecret", result);
|
||||||
Assert.False(organization.Enabled);
|
Assert.False(organization.Enabled);
|
||||||
@ -421,7 +421,7 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_SM_Stripe_RequiresAction(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_SM_Stripe_RequiresAction(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||||
@ -443,7 +443,7 @@ public class StripePaymentServiceTests
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans,
|
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan,
|
||||||
10, 10, false, taxInfo, false, 10, 10);
|
10, 10, false, taxInfo, false, 10, 10);
|
||||||
|
|
||||||
Assert.Equal("clientSecret", result);
|
Assert.Equal("clientSecret", result);
|
||||||
@ -453,7 +453,7 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_Paypal(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_Paypal(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||||
@ -480,7 +480,7 @@ public class StripePaymentServiceTests
|
|||||||
var braintreeGateway = sutProvider.GetDependency<IBraintreeGateway>();
|
var braintreeGateway = sutProvider.GetDependency<IBraintreeGateway>();
|
||||||
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
|
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
|
||||||
|
|
||||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, 0, 0, false, taxInfo);
|
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo);
|
||||||
|
|
||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
||||||
@ -517,10 +517,8 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_SM_Paypal(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_SM_Paypal(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
var passwordManagerPlan = plans.Single(p => p.BitwardenProduct == BitwardenProductType.PasswordManager);
|
organization.UseSecretsManager = true;
|
||||||
var secretsManagerPlan = plans.Single(p => p.BitwardenProduct == BitwardenProductType.SecretsManager);
|
|
||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||||
{
|
{
|
||||||
@ -550,7 +548,7 @@ public class StripePaymentServiceTests
|
|||||||
var additionalSeats = 10;
|
var additionalSeats = 10;
|
||||||
var additionalSmSeats = 5;
|
var additionalSmSeats = 5;
|
||||||
var additionalServiceAccounts = 20;
|
var additionalServiceAccounts = 20;
|
||||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans,
|
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan,
|
||||||
additionalStorage, additionalSeats, false, taxInfo, false, additionalSmSeats, additionalServiceAccounts);
|
additionalStorage, additionalSeats, false, taxInfo, false, additionalSmSeats, additionalServiceAccounts);
|
||||||
|
|
||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
@ -582,17 +580,17 @@ public class StripePaymentServiceTests
|
|||||||
s.Expand[0] == "latest_invoice.payment_intent" &&
|
s.Expand[0] == "latest_invoice.payment_intent" &&
|
||||||
s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() &&
|
s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() &&
|
||||||
s.Items.Count == 4 &&
|
s.Items.Count == 4 &&
|
||||||
s.Items.Count(i => i.Plan == passwordManagerPlan.StripeSeatPlanId && i.Quantity == additionalSeats) == 1 &&
|
s.Items.Count(i => i.Plan == plan.PasswordManager.StripeSeatPlanId && i.Quantity == additionalSeats) == 1 &&
|
||||||
s.Items.Count(i => i.Plan == passwordManagerPlan.StripeStoragePlanId && i.Quantity == additionalStorage) == 1 &&
|
s.Items.Count(i => i.Plan == plan.PasswordManager.StripeStoragePlanId && i.Quantity == additionalStorage) == 1 &&
|
||||||
s.Items.Count(i => i.Plan == secretsManagerPlan.StripeSeatPlanId && i.Quantity == additionalSmSeats) == 1 &&
|
s.Items.Count(i => i.Plan == plan.SecretsManager.StripeSeatPlanId && i.Quantity == additionalSmSeats) == 1 &&
|
||||||
s.Items.Count(i => i.Plan == secretsManagerPlan.StripeServiceAccountPlanId && i.Quantity == additionalServiceAccounts) == 1
|
s.Items.Count(i => i.Plan == plan.SecretsManager.StripeServiceAccountPlanId && i.Quantity == additionalServiceAccounts) == 1
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
|
|
||||||
var customerResult = Substitute.For<Result<Customer>>();
|
var customerResult = Substitute.For<Result<Customer>>();
|
||||||
customerResult.IsSuccess().Returns(false);
|
customerResult.IsSuccess().Returns(false);
|
||||||
@ -601,7 +599,7 @@ public class StripePaymentServiceTests
|
|||||||
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
|
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<GatewayException>(
|
var exception = await Assert.ThrowsAsync<GatewayException>(
|
||||||
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, 0, 0, false, taxInfo));
|
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo));
|
||||||
|
|
||||||
Assert.Equal("Failed to create PayPal customer record.", exception.Message);
|
Assert.Equal("Failed to create PayPal customer record.", exception.Message);
|
||||||
}
|
}
|
||||||
@ -609,7 +607,7 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_SM_Paypal_FailedCreate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_SM_Paypal_FailedCreate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
|
|
||||||
var customerResult = Substitute.For<Result<Customer>>();
|
var customerResult = Substitute.For<Result<Customer>>();
|
||||||
customerResult.IsSuccess().Returns(false);
|
customerResult.IsSuccess().Returns(false);
|
||||||
@ -618,7 +616,7 @@ public class StripePaymentServiceTests
|
|||||||
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
|
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<GatewayException>(
|
var exception = await Assert.ThrowsAsync<GatewayException>(
|
||||||
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans,
|
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan,
|
||||||
1, 1, false, taxInfo, false, 8, 8));
|
1, 1, false, taxInfo, false, 8, 8));
|
||||||
|
|
||||||
Assert.Equal("Failed to create PayPal customer record.", exception.Message);
|
Assert.Equal("Failed to create PayPal customer record.", exception.Message);
|
||||||
@ -627,7 +625,7 @@ public class StripePaymentServiceTests
|
|||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async void PurchaseOrganizationAsync_PayPal_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
public async void PurchaseOrganizationAsync_PayPal_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plans = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
paymentToken = "pm_" + paymentToken;
|
paymentToken = "pm_" + paymentToken;
|
||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
@ -689,7 +687,7 @@ public class StripePaymentServiceTests
|
|||||||
});
|
});
|
||||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription { });
|
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription { });
|
||||||
|
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
|
|
||||||
var upgrade = new OrganizationUpgrade()
|
var upgrade = new OrganizationUpgrade()
|
||||||
{
|
{
|
||||||
@ -700,7 +698,7 @@ public class StripePaymentServiceTests
|
|||||||
AdditionalSmSeats = 0,
|
AdditionalSmSeats = 0,
|
||||||
AdditionalServiceAccounts = 0
|
AdditionalServiceAccounts = 0
|
||||||
};
|
};
|
||||||
var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plans, upgrade);
|
var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plan, upgrade);
|
||||||
|
|
||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
}
|
}
|
||||||
@ -736,8 +734,8 @@ public class StripePaymentServiceTests
|
|||||||
AdditionalServiceAccounts = 50
|
AdditionalServiceAccounts = 50
|
||||||
};
|
};
|
||||||
|
|
||||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||||
var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plans, upgrade);
|
var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plan, upgrade);
|
||||||
|
|
||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.StaticStore;
|
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -14,57 +13,18 @@ public class StaticStoreTests
|
|||||||
var plans = StaticStore.Plans;
|
var plans = StaticStore.Plans;
|
||||||
Assert.NotNull(plans);
|
Assert.NotNull(plans);
|
||||||
Assert.NotEmpty(plans);
|
Assert.NotEmpty(plans);
|
||||||
Assert.Equal(17, plans.Count());
|
Assert.Equal(12, plans.Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(PlanType.EnterpriseAnnually)]
|
[InlineData(PlanType.EnterpriseAnnually)]
|
||||||
public void StaticStore_GetPasswordManagerPlanByPlanType_Success(PlanType planType)
|
[InlineData(PlanType.EnterpriseMonthly)]
|
||||||
|
[InlineData(PlanType.TeamsMonthly)]
|
||||||
|
[InlineData(PlanType.TeamsAnnually)]
|
||||||
|
public void StaticStore_GetPlan_Success(PlanType planType)
|
||||||
{
|
{
|
||||||
var plan = StaticStore.GetPasswordManagerPlan(planType);
|
var plan = StaticStore.GetPlan(planType);
|
||||||
|
|
||||||
Assert.NotNull(plan);
|
Assert.NotNull(plan);
|
||||||
Assert.Equal(planType, plan.Type);
|
Assert.Equal(planType, plan.Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(PlanType.EnterpriseAnnually)]
|
|
||||||
public void StaticStore_GetSecretsManagerPlanByPlanType_Success(PlanType planType)
|
|
||||||
{
|
|
||||||
var plan = StaticStore.GetSecretsManagerPlan(planType);
|
|
||||||
|
|
||||||
Assert.NotNull(plan);
|
|
||||||
Assert.Equal(planType, plan.Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(PlanType.EnterpriseAnnually)]
|
|
||||||
public void StaticStore_GetPasswordManagerPlan_ReturnsPasswordManagerPlans(PlanType planType)
|
|
||||||
{
|
|
||||||
var plan = StaticStore.GetPasswordManagerPlan(planType);
|
|
||||||
Assert.NotNull(plan);
|
|
||||||
Assert.Equal(BitwardenProductType.PasswordManager, plan.BitwardenProduct);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(PlanType.EnterpriseAnnually)]
|
|
||||||
public void StaticStore_GetSecretsManagerPlan_ReturnsSecretManagerPlans(PlanType planType)
|
|
||||||
{
|
|
||||||
var plan = StaticStore.GetSecretsManagerPlan(planType);
|
|
||||||
Assert.NotNull(plan);
|
|
||||||
Assert.Equal(BitwardenProductType.SecretsManager, plan.BitwardenProduct);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(PlanType.EnterpriseAnnually, BitwardenProductType.PasswordManager)]
|
|
||||||
public void StaticStore_AddDuplicatePlans_SingleOrDefaultThrowsException(PlanType planType, BitwardenProductType bitwardenProductType)
|
|
||||||
{
|
|
||||||
var plansStore = new List<Plan>
|
|
||||||
{
|
|
||||||
new Plan { Type = PlanType.EnterpriseAnnually, BitwardenProduct = BitwardenProductType.PasswordManager },
|
|
||||||
new Plan { Type = PlanType.EnterpriseAnnually, BitwardenProduct = BitwardenProductType.PasswordManager }
|
|
||||||
};
|
|
||||||
|
|
||||||
Assert.Throws<InvalidOperationException>(() => plansStore.SingleOrDefault(p => p.Type == planType && p.BitwardenProduct == bitwardenProductType));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user