diff --git a/src/Core/Enums/PlanType.cs b/src/Core/Enums/PlanType.cs index 57fcd3090e..0fe72a4c45 100644 --- a/src/Core/Enums/PlanType.cs +++ b/src/Core/Enums/PlanType.cs @@ -28,14 +28,24 @@ public enum PlanType : byte EnterpriseMonthly2020 = 10, [Display(Name = "Enterprise (Annually) 2020")] EnterpriseAnnually2020 = 11, + [Display(Name = "Teams (Monthly) 2023")] + TeamsMonthly2023 = 12, + [Display(Name = "Teams (Annually) 2023")] + TeamsAnnually2023 = 13, + [Display(Name = "Enterprise (Monthly) 2023")] + EnterpriseMonthly2023 = 14, + [Display(Name = "Enterprise (Annually) 2023")] + EnterpriseAnnually2023 = 15, + [Display(Name = "Teams Starter 2023")] + TeamsStarter2023 = 16, [Display(Name = "Teams (Monthly)")] - TeamsMonthly = 12, + TeamsMonthly = 17, [Display(Name = "Teams (Annually)")] - TeamsAnnually = 13, + TeamsAnnually = 18, [Display(Name = "Enterprise (Monthly)")] - EnterpriseMonthly = 14, + EnterpriseMonthly = 19, [Display(Name = "Enterprise (Annually)")] - EnterpriseAnnually = 15, + EnterpriseAnnually = 20, [Display(Name = "Teams Starter")] - TeamsStarter = 16, + TeamsStarter = 21, } diff --git a/src/Core/Models/StaticStore/Plans/EnterprisePlan.cs b/src/Core/Models/StaticStore/Plans/EnterprisePlan.cs index 30242f49cf..4e256bff25 100644 --- a/src/Core/Models/StaticStore/Plans/EnterprisePlan.cs +++ b/src/Core/Models/StaticStore/Plans/EnterprisePlan.cs @@ -2,7 +2,7 @@ namespace Bit.Core.Models.StaticStore.Plans; -public record EnterprisePlan : Models.StaticStore.Plan +public record EnterprisePlan : Plan { public EnterprisePlan(bool isAnnual) { @@ -44,7 +44,7 @@ public record EnterprisePlan : Models.StaticStore.Plan { BaseSeats = 0; BasePrice = 0; - BaseServiceAccount = 200; + BaseServiceAccount = 50; HasAdditionalSeatsOption = true; HasAdditionalServiceAccountOption = true; @@ -55,16 +55,16 @@ public record EnterprisePlan : Models.StaticStore.Plan if (isAnnual) { StripeSeatPlanId = "secrets-manager-enterprise-seat-annually"; - StripeServiceAccountPlanId = "secrets-manager-service-account-annually"; + StripeServiceAccountPlanId = "secrets-manager-service-account-2024-annually"; SeatPrice = 144; - AdditionalPricePerServiceAccount = 6; + AdditionalPricePerServiceAccount = 12; } else { StripeSeatPlanId = "secrets-manager-enterprise-seat-monthly"; - StripeServiceAccountPlanId = "secrets-manager-service-account-monthly"; + StripeServiceAccountPlanId = "secrets-manager-service-account-2024-monthly"; SeatPrice = 13; - AdditionalPricePerServiceAccount = 0.5M; + AdditionalPricePerServiceAccount = 1; } } } diff --git a/src/Core/Models/StaticStore/Plans/EnterprisePlan2023.cs b/src/Core/Models/StaticStore/Plans/EnterprisePlan2023.cs new file mode 100644 index 0000000000..9e448199f6 --- /dev/null +++ b/src/Core/Models/StaticStore/Plans/EnterprisePlan2023.cs @@ -0,0 +1,102 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.StaticStore.Plans; + +public record Enterprise2023Plan : Plan +{ + public Enterprise2023Plan(bool isAnnual) + { + Type = isAnnual ? PlanType.EnterpriseAnnually2023 : PlanType.EnterpriseMonthly2023; + Product = ProductType.Enterprise; + Name = isAnnual ? "Enterprise (Annually)" : "Enterprise (Monthly)"; + IsAnnual = isAnnual; + NameLocalizationKey = "planNameEnterprise"; + DescriptionLocalizationKey = "planDescEnterprise"; + CanBeUsedByBusiness = true; + + TrialPeriodDays = 7; + + HasPolicies = true; + HasSelfHost = true; + HasGroups = true; + HasDirectory = true; + HasEvents = true; + HasTotp = true; + Has2fa = true; + HasApi = true; + HasSso = true; + HasKeyConnector = true; + HasScim = true; + HasResetPassword = true; + UsersGetPremium = true; + HasCustomPermissions = true; + + UpgradeSortOrder = 4; + DisplaySortOrder = 4; + + LegacyYear = 2024; + + PasswordManager = new Enterprise2023PasswordManagerFeatures(isAnnual); + SecretsManager = new Enterprise2023SecretsManagerFeatures(isAnnual); + } + + private record Enterprise2023SecretsManagerFeatures : SecretsManagerPlanFeatures + { + public Enterprise2023SecretsManagerFeatures(bool isAnnual) + { + BaseSeats = 0; + BasePrice = 0; + BaseServiceAccount = 200; + + HasAdditionalSeatsOption = true; + HasAdditionalServiceAccountOption = true; + + AllowSeatAutoscale = true; + AllowServiceAccountsAutoscale = true; + + if (isAnnual) + { + StripeSeatPlanId = "secrets-manager-enterprise-seat-annually"; + StripeServiceAccountPlanId = "secrets-manager-service-account-annually"; + SeatPrice = 144; + AdditionalPricePerServiceAccount = 6; + } + else + { + StripeSeatPlanId = "secrets-manager-enterprise-seat-monthly"; + StripeServiceAccountPlanId = "secrets-manager-service-account-monthly"; + SeatPrice = 13; + AdditionalPricePerServiceAccount = 0.5M; + } + } + } + + private record Enterprise2023PasswordManagerFeatures : PasswordManagerPlanFeatures + { + public Enterprise2023PasswordManagerFeatures(bool isAnnual) + { + BaseSeats = 0; + BaseStorageGb = 1; + + HasAdditionalStorageOption = true; + HasAdditionalSeatsOption = true; + + AllowSeatAutoscale = true; + + if (isAnnual) + { + AdditionalStoragePricePerGb = 4; + StripeStoragePlanId = "storage-gb-annually"; + StripeSeatPlanId = "2023-enterprise-org-seat-annually"; + SeatPrice = 72; + } + else + { + StripeSeatPlanId = "2023-enterprise-seat-monthly"; + StripeStoragePlanId = "storage-gb-monthly"; + SeatPrice = 7; + AdditionalStoragePricePerGb = 0.5M; + } + } + } +} diff --git a/src/Core/Models/StaticStore/Plans/TeamsPlan.cs b/src/Core/Models/StaticStore/Plans/TeamsPlan.cs index d181f62747..84ce6d4fde 100644 --- a/src/Core/Models/StaticStore/Plans/TeamsPlan.cs +++ b/src/Core/Models/StaticStore/Plans/TeamsPlan.cs @@ -2,7 +2,7 @@ namespace Bit.Core.Models.StaticStore.Plans; -public record TeamsPlan : Models.StaticStore.Plan +public record TeamsPlan : Plan { public TeamsPlan(bool isAnnual) { @@ -37,7 +37,7 @@ public record TeamsPlan : Models.StaticStore.Plan { BaseSeats = 0; BasePrice = 0; - BaseServiceAccount = 50; + BaseServiceAccount = 20; HasAdditionalSeatsOption = true; HasAdditionalServiceAccountOption = true; @@ -48,16 +48,16 @@ public record TeamsPlan : Models.StaticStore.Plan if (isAnnual) { StripeSeatPlanId = "secrets-manager-teams-seat-annually"; - StripeServiceAccountPlanId = "secrets-manager-service-account-annually"; + StripeServiceAccountPlanId = "secrets-manager-service-account-2024-annually"; SeatPrice = 72; - AdditionalPricePerServiceAccount = 6; + AdditionalPricePerServiceAccount = 12; } else { StripeSeatPlanId = "secrets-manager-teams-seat-monthly"; - StripeServiceAccountPlanId = "secrets-manager-service-account-monthly"; + StripeServiceAccountPlanId = "secrets-manager-service-account-2024-monthly"; SeatPrice = 7; - AdditionalPricePerServiceAccount = 0.5M; + AdditionalPricePerServiceAccount = 1; } } } diff --git a/src/Core/Models/StaticStore/Plans/TeamsPlan2023.cs b/src/Core/Models/StaticStore/Plans/TeamsPlan2023.cs new file mode 100644 index 0000000000..c0b3190104 --- /dev/null +++ b/src/Core/Models/StaticStore/Plans/TeamsPlan2023.cs @@ -0,0 +1,96 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.StaticStore.Plans; + +public record Teams2023Plan : Plan +{ + public Teams2023Plan(bool isAnnual) + { + Type = isAnnual ? PlanType.TeamsAnnually2023 : PlanType.TeamsMonthly2023; + Product = ProductType.Teams; + Name = isAnnual ? "Teams (Annually)" : "Teams (Monthly)"; + IsAnnual = isAnnual; + NameLocalizationKey = "planNameTeams"; + DescriptionLocalizationKey = "planDescTeams"; + CanBeUsedByBusiness = true; + + TrialPeriodDays = 7; + + HasGroups = true; + HasDirectory = true; + HasEvents = true; + HasTotp = true; + Has2fa = true; + HasApi = true; + UsersGetPremium = true; + + UpgradeSortOrder = 3; + DisplaySortOrder = 3; + + LegacyYear = 2024; + + PasswordManager = new Teams2023PasswordManagerFeatures(isAnnual); + SecretsManager = new Teams2023SecretsManagerFeatures(isAnnual); + } + + private record Teams2023SecretsManagerFeatures : SecretsManagerPlanFeatures + { + public Teams2023SecretsManagerFeatures(bool isAnnual) + { + BaseSeats = 0; + BasePrice = 0; + BaseServiceAccount = 50; + + HasAdditionalSeatsOption = true; + HasAdditionalServiceAccountOption = true; + + AllowSeatAutoscale = true; + AllowServiceAccountsAutoscale = true; + + if (isAnnual) + { + StripeSeatPlanId = "secrets-manager-teams-seat-annually"; + StripeServiceAccountPlanId = "secrets-manager-service-account-annually"; + SeatPrice = 72; + AdditionalPricePerServiceAccount = 6; + } + else + { + StripeSeatPlanId = "secrets-manager-teams-seat-monthly"; + StripeServiceAccountPlanId = "secrets-manager-service-account-monthly"; + SeatPrice = 7; + AdditionalPricePerServiceAccount = 0.5M; + } + } + } + + private record Teams2023PasswordManagerFeatures : PasswordManagerPlanFeatures + { + public Teams2023PasswordManagerFeatures(bool isAnnual) + { + BaseSeats = 0; + BaseStorageGb = 1; + BasePrice = 0; + + HasAdditionalStorageOption = true; + HasAdditionalSeatsOption = true; + + AllowSeatAutoscale = true; + + if (isAnnual) + { + StripeStoragePlanId = "storage-gb-annually"; + StripeSeatPlanId = "2023-teams-org-seat-annually"; + SeatPrice = 48; + AdditionalStoragePricePerGb = 4; + } + else + { + StripeSeatPlanId = "2023-teams-org-seat-monthly"; + StripeStoragePlanId = "storage-gb-monthly"; + SeatPrice = 5; + AdditionalStoragePricePerGb = 0.5M; + } + } + } +} diff --git a/src/Core/Models/StaticStore/Plans/TeamsStarterPlan.cs b/src/Core/Models/StaticStore/Plans/TeamsStarterPlan.cs index d00fec8f83..b1919376ea 100644 --- a/src/Core/Models/StaticStore/Plans/TeamsStarterPlan.cs +++ b/src/Core/Models/StaticStore/Plans/TeamsStarterPlan.cs @@ -36,7 +36,7 @@ public record TeamsStarterPlan : Plan { BaseSeats = 0; BasePrice = 0; - BaseServiceAccount = 50; + BaseServiceAccount = 20; HasAdditionalSeatsOption = true; HasAdditionalServiceAccountOption = true; @@ -45,9 +45,9 @@ public record TeamsStarterPlan : Plan AllowServiceAccountsAutoscale = true; StripeSeatPlanId = "secrets-manager-teams-seat-monthly"; - StripeServiceAccountPlanId = "secrets-manager-service-account-monthly"; + StripeServiceAccountPlanId = "secrets-manager-service-account-2024-monthly"; SeatPrice = 7; - AdditionalPricePerServiceAccount = 0.5M; + AdditionalPricePerServiceAccount = 1; } } diff --git a/src/Core/Models/StaticStore/Plans/TeamsStarterPlan2023.cs b/src/Core/Models/StaticStore/Plans/TeamsStarterPlan2023.cs new file mode 100644 index 0000000000..77b70b8317 --- /dev/null +++ b/src/Core/Models/StaticStore/Plans/TeamsStarterPlan2023.cs @@ -0,0 +1,72 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.StaticStore.Plans; + +public record TeamsStarterPlan2023 : Plan +{ + public TeamsStarterPlan2023() + { + Type = PlanType.TeamsStarter2023; + Product = ProductType.TeamsStarter; + Name = "Teams (Starter)"; + NameLocalizationKey = "planNameTeamsStarter"; + DescriptionLocalizationKey = "planDescTeams"; + CanBeUsedByBusiness = true; + + TrialPeriodDays = 7; + + HasGroups = true; + HasDirectory = true; + HasEvents = true; + HasTotp = true; + Has2fa = true; + HasApi = true; + UsersGetPremium = true; + + UpgradeSortOrder = 2; + DisplaySortOrder = 2; + + PasswordManager = new TeamsStarter2023PasswordManagerFeatures(); + SecretsManager = new TeamsStarter2023SecretsManagerFeatures(); + LegacyYear = 2024; + } + + private record TeamsStarter2023SecretsManagerFeatures : SecretsManagerPlanFeatures + { + public TeamsStarter2023SecretsManagerFeatures() + { + BaseSeats = 0; + BasePrice = 0; + BaseServiceAccount = 50; + + HasAdditionalSeatsOption = true; + HasAdditionalServiceAccountOption = true; + + AllowSeatAutoscale = true; + AllowServiceAccountsAutoscale = true; + + StripeSeatPlanId = "secrets-manager-teams-seat-monthly"; + StripeServiceAccountPlanId = "secrets-manager-service-account-monthly"; + SeatPrice = 7; + AdditionalPricePerServiceAccount = 0.5M; + } + } + + private record TeamsStarter2023PasswordManagerFeatures : PasswordManagerPlanFeatures + { + public TeamsStarter2023PasswordManagerFeatures() + { + BaseSeats = 10; + BaseStorageGb = 1; + BasePrice = 20; + + MaxSeats = 10; + + HasAdditionalStorageOption = true; + + StripePlanId = "teams-org-starter"; + StripeStoragePlanId = "storage-gb-monthly"; + AdditionalStoragePricePerGb = 0.5M; + } + } +} diff --git a/src/Core/Utilities/StaticStore.cs b/src/Core/Utilities/StaticStore.cs index 007f3374e0..51c8fdd0ca 100644 --- a/src/Core/Utilities/StaticStore.cs +++ b/src/Core/Utilities/StaticStore.cs @@ -114,8 +114,13 @@ public static class StaticStore new TeamsPlan(true), new TeamsPlan(false), + new Enterprise2023Plan(true), + new Enterprise2023Plan(false), new Enterprise2020Plan(true), new Enterprise2020Plan(false), + new TeamsStarterPlan2023(), + new Teams2023Plan(true), + new Teams2023Plan(false), new Teams2020Plan(true), new Teams2020Plan(false), new FamiliesPlan(), diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs index 4b5037bcfa..fa457186bd 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs @@ -526,7 +526,7 @@ public class UpdateSecretsManagerSubscriptionCommandTests Organization organization, SutProvider sutProvider) { - const int newSmServiceAccounts = 199; + const int newSmServiceAccounts = 49; organization.SmServiceAccounts = newSmServiceAccounts - 10; @@ -537,7 +537,7 @@ public class UpdateSecretsManagerSubscriptionCommandTests var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.UpdateSubscriptionAsync(update)); - Assert.Contains("Plan has a minimum of 200 machine accounts", exception.Message); + Assert.Contains("Plan has a minimum of 50 machine accounts", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } diff --git a/test/Core.Test/Utilities/StaticStoreTests.cs b/test/Core.Test/Utilities/StaticStoreTests.cs index 4b16ec96f9..79cf7304b0 100644 --- a/test/Core.Test/Utilities/StaticStoreTests.cs +++ b/test/Core.Test/Utilities/StaticStoreTests.cs @@ -13,7 +13,7 @@ public class StaticStoreTests var plans = StaticStore.Plans.ToList(); Assert.NotNull(plans); Assert.NotEmpty(plans); - Assert.Equal(17, plans.Count); + Assert.Equal(22, plans.Count); } [Theory]