diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index 70084a66b..5a1569c98 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -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(); diff --git a/src/Api/Controllers/PlansController.cs b/src/Api/Controllers/PlansController.cs index d738e60cf..a7ae7c0b3 100644 --- a/src/Api/Controllers/PlansController.cs +++ b/src/Api/Controllers/PlansController.cs @@ -19,12 +19,30 @@ public class PlansController : Controller [HttpGet("")] [AllowAnonymous] public ListResponseModel Get() + { + var data = StaticStore.PasswordManagerPlans; + var responses = data.Select(plan => new PlanResponseModel(plan)); + return new ListResponseModel(responses); + } + + [HttpGet("all")] + [AllowAnonymous] + public ListResponseModel GetAllPlans() { var data = StaticStore.Plans; var responses = data.Select(plan => new PlanResponseModel(plan)); return new ListResponseModel(responses); } + [HttpGet("sm-plans")] + [AllowAnonymous] + public ListResponseModel GetSecretsManagerPlans() + { + var data = StaticStore.SecretManagerPlans; + var responses = data.Select(plan => new PlanResponseModel(plan)); + return new ListResponseModel(responses); + } + [HttpGet("sales-tax-rates")] public async Task> GetTaxRates() { diff --git a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs index 8dbfc4b61..4a4edf5d5 100644 --- a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs +++ b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs @@ -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; diff --git a/src/Api/Models/Response/PlanResponseModel.cs b/src/Api/Models/Response/PlanResponseModel.cs index fd2934e73..ee86dde59 100644 --- a/src/Api/Models/Response/PlanResponseModel.cs +++ b/src/Api/Models/Response/PlanResponseModel.cs @@ -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; } } diff --git a/src/Api/Models/Response/ProfileOrganizationResponseModel.cs b/src/Api/Models/Response/ProfileOrganizationResponseModel.cs index 215fc7a23..bb57be69d 100644 --- a/src/Api/Models/Response/ProfileOrganizationResponseModel.cs +++ b/src/Api/Models/Response/ProfileOrganizationResponseModel.cs @@ -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; diff --git a/src/Api/Models/Response/ProfileProviderOrganizationResponseModel.cs b/src/Api/Models/Response/ProfileProviderOrganizationResponseModel.cs index d08e3b1ea..9dccad893 100644 --- a/src/Api/Models/Response/ProfileProviderOrganizationResponseModel.cs +++ b/src/Api/Models/Response/ProfileProviderOrganizationResponseModel.cs @@ -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; } } diff --git a/src/Billing/Controllers/StripeController.cs b/src/Billing/Controllers/StripeController.cs index fff638bf2..8562bd0a2 100644 --- a/src/Billing/Controllers/StripeController.cs +++ b/src/Billing/Controllers/StripeController.cs @@ -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); diff --git a/src/Core/Enums/BitwardenProductType.cs b/src/Core/Enums/BitwardenProductType.cs new file mode 100644 index 000000000..d0c358671 --- /dev/null +++ b/src/Core/Enums/BitwardenProductType.cs @@ -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, +} diff --git a/src/Core/Models/StaticStore/Plan.cs b/src/Core/Models/StaticStore/Plan.cs index 4a0313790..cc781a411 100644 --- a/src/Core/Models/StaticStore/Plan.cs +++ b/src/Core/Models/StaticStore/Plan.cs @@ -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; } } diff --git a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/CloudSyncSponsorshipsCommand.cs b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/CloudSyncSponsorshipsCommand.cs index d0569278b..5c0f1474a 100644 --- a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/CloudSyncSponsorshipsCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/CloudSyncSponsorshipsCommand.cs @@ -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 } diff --git a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/SetUpSponsorshipCommand.cs b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/SetUpSponsorshipCommand.cs index 9230e7d13..81a8bac96 100644 --- a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/SetUpSponsorshipCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/SetUpSponsorshipCommand.cs @@ -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."); } diff --git a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/ValidateSponsorshipCommand.cs b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/ValidateSponsorshipCommand.cs index 3f2d7af5e..af2f0af65 100644 --- a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/ValidateSponsorshipCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/ValidateSponsorshipCommand.cs @@ -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 || diff --git a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/CreateSponsorshipCommand.cs b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/CreateSponsorshipCommand.cs index 69e6c3232..bfc33ebe8 100644 --- a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/CreateSponsorshipCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/CreateSponsorshipCommand.cs @@ -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."); } diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 82d39a9d6..94f03c897 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -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> 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."); diff --git a/src/Core/Services/Implementations/StripePaymentService.cs b/src/Core/Services/Implementations/StripePaymentService.cs index 40d905a8b..ba9956153 100644 --- a/src/Core/Services/Implementations/StripePaymentService.cs +++ b/src/Core/Services/Implementations/StripePaymentService.cs @@ -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; diff --git a/src/Core/Utilities/PasswordManagerPlanStore.cs b/src/Core/Utilities/PasswordManagerPlanStore.cs new file mode 100644 index 000000000..1f9400eba --- /dev/null +++ b/src/Core/Utilities/PasswordManagerPlanStore.cs @@ -0,0 +1,398 @@ +using Bit.Core.Enums; +using Bit.Core.Models.StaticStore; + +namespace Bit.Core.Utilities; + +public static class PasswordManagerPlanStore +{ + public static IEnumerable CreatePlan() + { + return new List + { + 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, + }, + }; + } +} diff --git a/src/Core/Utilities/SecretsManagerPlanStore.cs b/src/Core/Utilities/SecretsManagerPlanStore.cs new file mode 100644 index 000000000..981acc3ab --- /dev/null +++ b/src/Core/Utilities/SecretsManagerPlanStore.cs @@ -0,0 +1,168 @@ +using Bit.Core.Enums; +using Bit.Core.Models.StaticStore; + +namespace Bit.Core.Utilities; + +public static class SecretsManagerPlanStore +{ + public static IEnumerable CreatePlan() + { + return new List + { + 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, + } + }; + } +} diff --git a/src/Core/Utilities/StaticStore.cs b/src/Core/Utilities/StaticStore.cs index 4b257a9cc..c5183296a 100644 --- a/src/Core/Utilities/StaticStore.cs +++ b/src/Core/Utilities/StaticStore.cs @@ -106,388 +106,19 @@ public class StaticStore #region Plans - Plans = new List - { - 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> GlobalDomains { get; set; } public static IEnumerable Plans { get; set; } + public static IEnumerable SecretManagerPlans { get; set; } + public static IEnumerable PasswordManagerPlans { get; set; } public static IEnumerable 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); } diff --git a/test/Api.Test/Controllers/OrganizationSponsorshipsControllerTests.cs b/test/Api.Test/Controllers/OrganizationSponsorshipsControllerTests.cs index e58add5ef..99bc1df63 100644 --- a/test/Api.Test/Controllers/OrganizationSponsorshipsControllerTests.cs +++ b/test/Api.Test/Controllers/OrganizationSponsorshipsControllerTests.cs @@ -20,11 +20,11 @@ namespace Bit.Api.Test.Controllers; public class OrganizationSponsorshipsControllerTests { public static IEnumerable EnterprisePlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p }); public static IEnumerable NonEnterprisePlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p }); public static IEnumerable NonFamiliesPlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Families).Select(p => new object[] { p }); public static IEnumerable NonConfirmedOrganizationUsersStatuses => Enum.GetValues() diff --git a/test/Api.Test/Vault/Controllers/SyncControllerTests.cs b/test/Api.Test/Vault/Controllers/SyncControllerTests.cs index d27a41ece..0e1629192 100644 --- a/test/Api.Test/Vault/Controllers/SyncControllerTests.cs +++ b/test/Api.Test/Vault/Controllers/SyncControllerTests.cs @@ -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); } } diff --git a/test/Core.Test/AutoFixture/OrganizationFixtures.cs b/test/Core.Test/AutoFixture/OrganizationFixtures.cs index 7d0044c36..ee0c13dd2 100644 --- a/test/Core.Test/AutoFixture/OrganizationFixtures.cs +++ b/test/Core.Test/AutoFixture/OrganizationFixtures.cs @@ -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.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(composer => composer .With(ou => ou.Plan, selectedPlan.Type) diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/FamiliesForEnterpriseTestsBase.cs b/test/Core.Test/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/FamiliesForEnterpriseTestsBase.cs index e49b095d7..a3fce8b3d 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/FamiliesForEnterpriseTestsBase.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/FamiliesForEnterpriseTestsBase.cs @@ -6,16 +6,16 @@ namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesFo public abstract class FamiliesForEnterpriseTestsBase { public static IEnumerable EnterprisePlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p }); public static IEnumerable NonEnterprisePlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p }); public static IEnumerable FamiliesPlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product == ProductType.Families).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Families).Select(p => new object[] { p }); public static IEnumerable NonFamiliesPlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Families).Select(p => new object[] { p }); public static IEnumerable NonConfirmedOrganizationUsersStatuses => Enum.GetValues() diff --git a/test/Core.Test/Services/StripePaymentServiceTests.cs b/test/Core.Test/Services/StripePaymentServiceTests.cs index 82131d874..c43e99fb9 100644 --- a/test/Core.Test/Services/StripePaymentServiceTests.cs +++ b/test/Core.Test/Services/StripePaymentServiceTests.cs @@ -39,7 +39,7 @@ public class StripePaymentServiceTests [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Stripe_ProviderOrg_Coupon_Add(SutProvider 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(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -89,7 +89,7 @@ public class StripePaymentServiceTests [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Stripe(SutProvider 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(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -141,7 +141,7 @@ public class StripePaymentServiceTests [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Stripe_PM(SutProvider 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(); @@ -194,7 +194,7 @@ public class StripePaymentServiceTests [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Stripe_TaxRate(SutProvider 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(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -223,7 +223,7 @@ public class StripePaymentServiceTests [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Stripe_Declined(SutProvider 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(); @@ -256,7 +256,7 @@ public class StripePaymentServiceTests [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Stripe_RequiresAction(SutProvider 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(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -287,7 +287,7 @@ public class StripePaymentServiceTests [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Paypal(SutProvider 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(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -346,7 +346,7 @@ public class StripePaymentServiceTests [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider 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>(); customerResult.IsSuccess().Returns(false); @@ -363,7 +363,7 @@ public class StripePaymentServiceTests [Theory, BitAutoData] public async void PurchaseOrganizationAsync_PayPal_Declined(SutProvider 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(); @@ -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); diff --git a/test/Core.Test/Utilities/StaticStoreTests.cs b/test/Core.Test/Utilities/StaticStoreTests.cs new file mode 100644 index 000000000..d30a0e6c7 --- /dev/null +++ b/test/Core.Test/Utilities/StaticStoreTests.cs @@ -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 + { + new Plan { Type = PlanType.EnterpriseAnnually, BitwardenProduct = BitwardenProductType.PasswordManager }, + new Plan { Type = PlanType.EnterpriseAnnually, BitwardenProduct = BitwardenProductType.PasswordManager } + }; + + Assert.Throws(() => plansStore.SingleOrDefault(p => p.Type == planType && p.BitwardenProduct == bitwardenProductType)); + } +}