1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-21 12:05:42 +01:00

[AC-1408] Update plan to include secrets manager (#2942)

* Adding the Secret manager to the Plan List

* Adding the unit test for the StaticStoreTests class

* Fix whitespace formatting

* Fix whitespace formatting

* Price update

* Resolving the PR comments

* Resolving PR comments

* Fixing the whitespace

* only password manager plans are return for now

* format whitespace

* Resolve the test issue

* Fixing the failing test

* Refactoring the Plan separation

* add a unit test for SingleOrDefault

* Fix the whitespace format

* Separate the PM and SM plans

* Fixing the whitespace

* Remove unnecessary directive

* Fix imports ordering

* Fix imports ordering

* Resolve imports ordering

* Fixing imports ordering

* Fix response model, add MaxProjects

* Fix filename

* Fix format

* Fix: seat price should match annual/monthly

* Fix service account annual pricing

* Name the sm service account planId properly

* Update the secrets manager plan

* correcting the wrong amount for the seats

---------

Co-authored-by: Thomas Rittson <trittson@bitwarden.com>
This commit is contained in:
cyprain-okeke 2023-06-16 11:12:38 +01:00 committed by GitHub
parent 904b2fe205
commit 5a12db18d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 736 additions and 415 deletions

View File

@ -487,7 +487,7 @@ public class OrganizationsController : Controller
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
var plan = StaticStore.GetPlan(organization.PlanType);
var plan = StaticStore.GetPasswordManagerPlan(organization.PlanType);
if (plan.Product != ProductType.Enterprise)
{
throw new NotFoundException();

View File

@ -19,12 +19,30 @@ public class PlansController : Controller
[HttpGet("")]
[AllowAnonymous]
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 responses = data.Select(plan => new PlanResponseModel(plan));
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")]
public async Task<ListResponseModel<TaxRateResponseModel>> GetTaxRates()
{

View File

@ -26,7 +26,7 @@ public class OrganizationResponseModel : ResponseModel
BusinessCountry = organization.BusinessCountry;
BusinessTaxNumber = organization.BusinessTaxNumber;
BillingEmail = organization.BillingEmail;
Plan = new PlanResponseModel(StaticStore.Plans.FirstOrDefault(plan => plan.Type == organization.PlanType));
Plan = new PlanResponseModel(StaticStore.PasswordManagerPlans.FirstOrDefault(plan => plan.Type == organization.PlanType));
PlanType = organization.PlanType;
Seats = organization.Seats;
MaxAutoscaleSeats = organization.MaxAutoscaleSeats;

View File

@ -52,6 +52,13 @@ public class PlanResponseModel : ResponseModel
SeatPrice = plan.SeatPrice;
AdditionalStoragePricePerGb = plan.AdditionalStoragePricePerGb;
PremiumAccessOptionPrice = plan.PremiumAccessOptionPrice;
AdditionalPricePerServiceAccount = plan.AdditionalPricePerServiceAccount;
BaseServiceAccount = plan.BaseServiceAccount;
MaxServiceAccount = plan.MaxServiceAccount;
HasAdditionalServiceAccountOption = plan.HasAdditionalServiceAccountOption;
MaxProjects = plan.MaxProjects;
BitwardenProduct = plan.BitwardenProduct;
}
public PlanType Type { get; set; }
@ -98,4 +105,11 @@ public class PlanResponseModel : ResponseModel
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? MaxServiceAccount { get; set; }
public bool HasAdditionalServiceAccountOption { get; set; }
public short? MaxProjects { get; set; }
public BitwardenProductType BitwardenProduct { get; set; }
}

View File

@ -54,7 +54,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
FamilySponsorshipAvailable = FamilySponsorshipFriendlyName == null &&
StaticStore.GetSponsoredPlan(PlanSponsorshipType.FamiliesForEnterprise)
.UsersCanSponsor(organization);
PlanProductType = StaticStore.GetPlan(organization.PlanType).Product;
PlanProductType = StaticStore.GetPasswordManagerPlan(organization.PlanType).Product;
FamilySponsorshipLastSyncDate = organization.FamilySponsorshipLastSyncDate;
FamilySponsorshipToDelete = organization.FamilySponsorshipToDelete;
FamilySponsorshipValidUntil = organization.FamilySponsorshipValidUntil;

View File

@ -42,6 +42,6 @@ public class ProfileProviderOrganizationResponseModel : ProfileOrganizationRespo
UserId = organization.UserId?.ToString();
ProviderId = organization.ProviderId?.ToString();
ProviderName = organization.ProviderName;
PlanProductType = StaticStore.GetPlan(organization.PlanType).Product;
PlanProductType = StaticStore.GetPasswordManagerPlan(organization.PlanType).Product;
}
}

View File

@ -417,7 +417,7 @@ public class StripeController : Controller
// org
if (ids.Item1.HasValue)
{
if (subscription.Items.Any(i => StaticStore.Plans.Any(p => p.StripePlanId == i.Plan.Id)))
if (subscription.Items.Any(i => StaticStore.PasswordManagerPlans.Any(p => p.StripePlanId == i.Plan.Id)))
{
await _organizationService.EnableAsync(ids.Item1.Value, subscription.CurrentPeriodEnd);

View File

@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Enums;
public enum BitwardenProductType : byte
{
[Display(Name = "Password Manager")]
PasswordManager = 0,
[Display(Name = "Secrets Manager")]
SecretsManager = 1,
}

View File

@ -47,9 +47,16 @@ public class Plan
public string StripePlanId { get; set; }
public string StripeSeatPlanId { get; set; }
public string StripeStoragePlanId { get; set; }
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? MaxServiceAccount { get; set; }
public bool HasAdditionalServiceAccountOption { get; set; }
public short? MaxProjects { get; set; }
public BitwardenProductType BitwardenProduct { get; set; }
}

View File

@ -54,7 +54,7 @@ public class CloudSyncSponsorshipsCommand : ICloudSyncSponsorshipsCommand
{
var requiredSponsoringProductType = StaticStore.GetSponsoredPlan(selfHostedSponsorship.PlanSponsorshipType)?.SponsoringProductType;
if (requiredSponsoringProductType == null
|| StaticStore.GetPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value)
|| StaticStore.GetPasswordManagerPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value)
{
continue; // prevent unsupported sponsorships
}

View File

@ -51,7 +51,7 @@ public class SetUpSponsorshipCommand : ISetUpSponsorshipCommand
var requiredSponsoredProductType = StaticStore.GetSponsoredPlan(sponsorship.PlanSponsorshipType.Value)?.SponsoredProductType;
if (requiredSponsoredProductType == null ||
sponsoredOrganization == null ||
StaticStore.GetPlan(sponsoredOrganization.PlanType).Product != requiredSponsoredProductType.Value)
StaticStore.GetPasswordManagerPlan(sponsoredOrganization.PlanType).Product != requiredSponsoredProductType.Value)
{
throw new BadRequestException("Can only redeem sponsorship offer on families organizations.");
}

View File

@ -56,7 +56,7 @@ public class ValidateSponsorshipCommand : CancelSponsorshipCommand, IValidateSpo
return false;
}
var sponsoringOrgPlan = Utilities.StaticStore.GetPlan(sponsoringOrganization.PlanType);
var sponsoringOrgPlan = Utilities.StaticStore.GetPasswordManagerPlan(sponsoringOrganization.PlanType);
if (OrgDisabledForMoreThanGracePeriod(sponsoringOrganization) ||
sponsoredPlan.SponsoringProductType != sponsoringOrgPlan.Product ||
existingSponsorship.ToDelete ||

View File

@ -32,7 +32,7 @@ public class CreateSponsorshipCommand : ICreateSponsorshipCommand
var requiredSponsoringProductType = StaticStore.GetSponsoredPlan(sponsorshipType)?.SponsoringProductType;
if (requiredSponsoringProductType == null ||
sponsoringOrg == null ||
StaticStore.GetPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value)
StaticStore.GetPasswordManagerPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value)
{
throw new BadRequestException("Specified Organization cannot sponsor other organizations.");
}

View File

@ -179,13 +179,13 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException("Your account has no payment method available.");
}
var existingPlan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType);
var existingPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType);
if (existingPlan == null)
{
throw new BadRequestException("Existing plan not found.");
}
var newPlan = StaticStore.Plans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled);
var newPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled);
if (newPlan == null)
{
throw new BadRequestException("Plan not found.");
@ -379,7 +379,7 @@ public class OrganizationService : IOrganizationService
throw new NotFoundException();
}
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType);
var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType);
if (plan == null)
{
throw new BadRequestException("Existing plan not found.");
@ -437,7 +437,7 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException($"Cannot set max seat autoscaling below current seat count.");
}
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType);
var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType);
if (plan == null)
{
throw new BadRequestException("Existing plan not found.");
@ -489,7 +489,7 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException("No subscription found.");
}
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType);
var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType);
if (plan == null)
{
throw new BadRequestException("Existing plan not found.");
@ -607,7 +607,7 @@ public class OrganizationService : IOrganizationService
public async Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup signup,
bool provider = false)
{
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == signup.Plan);
var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == signup.Plan);
if (plan is not { LegacyYear: null })
{
throw new BadRequestException("Invalid plan selected.");
@ -712,7 +712,7 @@ public class OrganizationService : IOrganizationService
}
if (license.PlanType != PlanType.Custom &&
StaticStore.Plans.FirstOrDefault(p => p.Type == license.PlanType && !p.Disabled) == null)
StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == license.PlanType && !p.Disabled) == null)
{
throw new BadRequestException("Plan not found.");
}
@ -2519,7 +2519,7 @@ public class OrganizationService : IOrganizationService
public async Task CreatePendingOrganization(Organization organization, string ownerEmail, ClaimsPrincipal user, IUserService userService, bool salesAssistedTrialStarted)
{
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType);
var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType);
if (plan is not { LegacyYear: null })
{
throw new BadRequestException("Invalid plan selected.");

View File

@ -201,7 +201,7 @@ public class StripePaymentService : IPaymentService
private async Task ChangeOrganizationSponsorship(Organization org, OrganizationSponsorship sponsorship, bool applySponsorship)
{
var existingPlan = Utilities.StaticStore.GetPlan(org.PlanType);
var existingPlan = Utilities.StaticStore.GetPasswordManagerPlan(org.PlanType);
var sponsoredPlan = sponsorship != null ?
Utilities.StaticStore.GetSponsoredPlan(sponsorship.PlanSponsorshipType.Value) :
null;

View File

@ -0,0 +1,398 @@
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,
},
};
}
}

View File

@ -0,0 +1,168 @@
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 = "sm-enterprise-seat-monthly",
StripeServiceAccountPlanId = "service-account-monthly",
BasePrice = 0,
SeatPrice = 13,
AdditionalPricePerServiceAccount = 0.5M,
AllowSeatAutoscale = 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 = "sm-enterprise-seat-annually",
StripeServiceAccountPlanId = "service-account-annually",
BasePrice = 0,
SeatPrice = 144,
AdditionalPricePerServiceAccount = 6,
AllowSeatAutoscale = 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 = "sm-teams-seat-monthly",
StripeServiceAccountPlanId = "service-account-monthly",
BasePrice = 0,
SeatPrice = 7,
AdditionalPricePerServiceAccount = 0.5M,
AllowSeatAutoscale = 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 = "sm-teams-seat-annually",
StripeServiceAccountPlanId = "service-account-annually",
BasePrice = 0,
SeatPrice = 72,
AdditionalPricePerServiceAccount = 6,
AllowSeatAutoscale = 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,
MaxServiceAccount = 3,
UpgradeSortOrder = -1, // Always the lowest plan, cannot be upgraded to
DisplaySortOrder = -1,
AllowSeatAutoscale = false,
}
};
}
}

View File

@ -106,388 +106,19 @@ public class StaticStore
#region Plans
Plans = new List<Plan>
{
new Plan
{
Type = PlanType.Free,
Product = ProductType.Free,
Name = "Free",
NameLocalizationKey = "planNameFree",
DescriptionLocalizationKey = "planDescFree",
BaseSeats = 2,
MaxCollections = 2,
MaxUsers = 2,
PasswordManagerPlans = PasswordManagerPlanStore.CreatePlan();
SecretManagerPlans = SecretsManagerPlanStore.CreatePlan();
UpgradeSortOrder = -1, // Always the lowest plan, cannot be upgraded to
DisplaySortOrder = -1,
Plans = PasswordManagerPlans.Concat(SecretManagerPlans);
AllowSeatAutoscale = false,
},
new Plan
{
Type = PlanType.FamiliesAnnually2019,
Product = ProductType.Families,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
},
};
#endregion
}
public static IDictionary<GlobalEquivalentDomainsType, IEnumerable<string>> GlobalDomains { get; set; }
public static IEnumerable<Plan> Plans { get; set; }
public static IEnumerable<Plan> SecretManagerPlans { get; set; }
public static IEnumerable<Plan> PasswordManagerPlans { get; set; }
public static IEnumerable<SponsoredPlan> SponsoredPlans { get; set; } = new[]
{
new SponsoredPlan
@ -497,11 +128,15 @@ public class StaticStore
SponsoringProductType = ProductType.Enterprise,
StripePlanId = "2021-family-for-enterprise-annually",
UsersCanSponsor = (OrganizationUserOrganizationDetails org) =>
GetPlan(org.PlanType).Product == ProductType.Enterprise,
GetPasswordManagerPlan(org.PlanType).Product == ProductType.Enterprise,
}
};
public static Plan GetPlan(PlanType planType) =>
Plans.FirstOrDefault(p => p.Type == planType);
public static Plan GetPasswordManagerPlan(PlanType planType) =>
PasswordManagerPlans.SingleOrDefault(p => p.Type == planType);
public static Plan GetSecretsManagerPlan(PlanType planType) =>
SecretManagerPlans.SingleOrDefault(p => p.Type == planType);
public static SponsoredPlan GetSponsoredPlan(PlanSponsorshipType planSponsorshipType) =>
SponsoredPlans.FirstOrDefault(p => p.PlanSponsorshipType == planSponsorshipType);
}

View File

@ -20,11 +20,11 @@ namespace Bit.Api.Test.Controllers;
public class OrganizationSponsorshipsControllerTests
{
public static IEnumerable<object[]> EnterprisePlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
public static IEnumerable<object[]> NonEnterprisePlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
public static IEnumerable<object[]> NonFamiliesPlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
public static IEnumerable<object[]> NonConfirmedOrganizationUsersStatuses =>
Enum.GetValues<OrganizationUserStatusType>()

View File

@ -305,7 +305,7 @@ public class SyncControllerTests
if (matchedProviderUserOrgDetails != null)
{
var providerOrgProductType = StaticStore.GetPlan(matchedProviderUserOrgDetails.PlanType).Product;
var providerOrgProductType = StaticStore.GetPasswordManagerPlan(matchedProviderUserOrgDetails.PlanType).Product;
Assert.Equal(providerOrgProductType, profProviderOrg.PlanProductType);
}
}

View File

@ -65,7 +65,7 @@ internal class PaidOrganization : ICustomization
public PlanType CheckedPlanType { get; set; }
public void Customize(IFixture fixture)
{
var validUpgradePlans = StaticStore.Plans.Where(p => p.Type != PlanType.Free && p.LegacyYear == null).OrderBy(p => p.UpgradeSortOrder).Select(p => p.Type).ToList();
var validUpgradePlans = StaticStore.PasswordManagerPlans.Where(p => p.Type != PlanType.Free && p.LegacyYear == null).OrderBy(p => p.UpgradeSortOrder).Select(p => p.Type).ToList();
var lowestActivePaidPlan = validUpgradePlans.First();
CheckedPlanType = CheckedPlanType.Equals(PlanType.Free) ? lowestActivePaidPlan : CheckedPlanType;
validUpgradePlans.Remove(lowestActivePaidPlan);
@ -93,7 +93,7 @@ internal class FreeOrganizationUpgrade : ICustomization
.With(o => o.PlanType, PlanType.Free));
var plansToIgnore = new List<PlanType> { PlanType.Free, PlanType.Custom };
var selectedPlan = StaticStore.Plans.Last(p => !plansToIgnore.Contains(p.Type) && !p.Disabled);
var selectedPlan = StaticStore.PasswordManagerPlans.Last(p => !plansToIgnore.Contains(p.Type) && !p.Disabled);
fixture.Customize<OrganizationUpgrade>(composer => composer
.With(ou => ou.Plan, selectedPlan.Type)

View File

@ -6,16 +6,16 @@ namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesFo
public abstract class FamiliesForEnterpriseTestsBase
{
public static IEnumerable<object[]> EnterprisePlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
public static IEnumerable<object[]> NonEnterprisePlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
public static IEnumerable<object[]> FamiliesPlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Families).Select(p => new object[] { p });
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Families).Select(p => new object[] { p });
public static IEnumerable<object[]> NonFamiliesPlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
public static IEnumerable<object[]> NonConfirmedOrganizationUsersStatuses =>
Enum.GetValues<OrganizationUserStatusType>()

View File

@ -39,7 +39,7 @@ public class StripePaymentServiceTests
[Theory, BitAutoData]
public async void PurchaseOrganizationAsync_Stripe_ProviderOrg_Coupon_Add(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo, bool provider = true)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
@ -89,7 +89,7 @@ public class StripePaymentServiceTests
[Theory, BitAutoData]
public async void PurchaseOrganizationAsync_Stripe(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
@ -141,7 +141,7 @@ public class StripePaymentServiceTests
[Theory, BitAutoData]
public async void PurchaseOrganizationAsync_Stripe_PM(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
paymentToken = "pm_" + paymentToken;
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
@ -194,7 +194,7 @@ public class StripePaymentServiceTests
[Theory, BitAutoData]
public async void PurchaseOrganizationAsync_Stripe_TaxRate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
@ -223,7 +223,7 @@ public class StripePaymentServiceTests
[Theory, BitAutoData]
public async void PurchaseOrganizationAsync_Stripe_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
paymentToken = "pm_" + paymentToken;
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
@ -256,7 +256,7 @@ public class StripePaymentServiceTests
[Theory, BitAutoData]
public async void PurchaseOrganizationAsync_Stripe_RequiresAction(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
@ -287,7 +287,7 @@ public class StripePaymentServiceTests
[Theory, BitAutoData]
public async void PurchaseOrganizationAsync_Paypal(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
@ -346,7 +346,7 @@ public class StripePaymentServiceTests
[Theory, BitAutoData]
public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
var customerResult = Substitute.For<Result<Customer>>();
customerResult.IsSuccess().Returns(false);
@ -363,7 +363,7 @@ public class StripePaymentServiceTests
[Theory, BitAutoData]
public async void PurchaseOrganizationAsync_PayPal_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
paymentToken = "pm_" + paymentToken;
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
@ -425,7 +425,7 @@ public class StripePaymentServiceTests
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription { });
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plan, 0, 0, false, taxInfo);
Assert.Null(result);

View File

@ -0,0 +1,70 @@
using Bit.Core.Enums;
using Bit.Core.Models.StaticStore;
using Bit.Core.Utilities;
using Xunit;
namespace Bit.Core.Test.Utilities;
public class StaticStoreTests
{
[Fact]
public void StaticStore_Initialization_Success()
{
var plans = StaticStore.Plans;
Assert.NotNull(plans);
Assert.NotEmpty(plans);
Assert.Equal(17, plans.Count());
}
[Theory]
[InlineData(PlanType.EnterpriseAnnually)]
public void StaticStore_GetPasswordManagerPlanByPlanType_Success(PlanType planType)
{
var plan = StaticStore.GetPasswordManagerPlan(planType);
Assert.NotNull(plan);
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));
}
}