diff --git a/bitwarden_license/src/Commercial.Core/AdminConsole/Providers/CreateProviderCommand.cs b/bitwarden_license/src/Commercial.Core/AdminConsole/Providers/CreateProviderCommand.cs index 3b01370ef..69b7e67ec 100644 --- a/bitwarden_license/src/Commercial.Core/AdminConsole/Providers/CreateProviderCommand.cs +++ b/bitwarden_license/src/Commercial.Core/AdminConsole/Providers/CreateProviderCommand.cs @@ -1,5 +1,4 @@ -using Bit.Core; -using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Providers.Interfaces; using Bit.Core.AdminConsole.Repositories; @@ -10,7 +9,6 @@ using Bit.Core.Billing.Repositories; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Repositories; -using Bit.Core.Services; namespace Bit.Commercial.Core.AdminConsole.Providers; @@ -21,35 +19,28 @@ public class CreateProviderCommand : ICreateProviderCommand private readonly IProviderService _providerService; private readonly IUserRepository _userRepository; private readonly IProviderPlanRepository _providerPlanRepository; - private readonly IFeatureService _featureService; public CreateProviderCommand( IProviderRepository providerRepository, IProviderUserRepository providerUserRepository, IProviderService providerService, IUserRepository userRepository, - IProviderPlanRepository providerPlanRepository, - IFeatureService featureService) + IProviderPlanRepository providerPlanRepository) { _providerRepository = providerRepository; _providerUserRepository = providerUserRepository; _providerService = providerService; _userRepository = userRepository; _providerPlanRepository = providerPlanRepository; - _featureService = featureService; } public async Task CreateMspAsync(Provider provider, string ownerEmail, int teamsMinimumSeats, int enterpriseMinimumSeats) { var providerId = await CreateProviderAsync(provider, ownerEmail); - var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - - if (isConsolidatedBillingEnabled) - { - await CreateProviderPlanAsync(providerId, PlanType.TeamsMonthly, teamsMinimumSeats); - await CreateProviderPlanAsync(providerId, PlanType.EnterpriseMonthly, enterpriseMinimumSeats); - } + await Task.WhenAll( + CreateProviderPlanAsync(providerId, PlanType.TeamsMonthly, teamsMinimumSeats), + CreateProviderPlanAsync(providerId, PlanType.EnterpriseMonthly, enterpriseMinimumSeats)); } public async Task CreateResellerAsync(Provider provider) @@ -61,12 +52,7 @@ public class CreateProviderCommand : ICreateProviderCommand { var providerId = await CreateProviderAsync(provider, ownerEmail); - var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - - if (isConsolidatedBillingEnabled) - { - await CreateProviderPlanAsync(providerId, plan, minimumSeats); - } + await CreateProviderPlanAsync(providerId, plan, minimumSeats); } private async Task CreateProviderAsync(Provider provider, string ownerEmail) @@ -77,12 +63,7 @@ public class CreateProviderCommand : ICreateProviderCommand throw new BadRequestException("Invalid owner. Owner must be an existing Bitwarden user."); } - var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - - if (isConsolidatedBillingEnabled) - { - provider.Gateway = GatewayType.Stripe; - } + provider.Gateway = GatewayType.Stripe; await ProviderRepositoryCreateAsync(provider, ProviderStatusType.Pending); diff --git a/bitwarden_license/src/Commercial.Core/AdminConsole/Providers/RemoveOrganizationFromProviderCommand.cs b/bitwarden_license/src/Commercial.Core/AdminConsole/Providers/RemoveOrganizationFromProviderCommand.cs index 045fd5059..ce0c0c933 100644 --- a/bitwarden_license/src/Commercial.Core/AdminConsole/Providers/RemoveOrganizationFromProviderCommand.cs +++ b/bitwarden_license/src/Commercial.Core/AdminConsole/Providers/RemoveOrganizationFromProviderCommand.cs @@ -1,7 +1,5 @@ -using Bit.Core; -using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities.Provider; -using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.Providers.Interfaces; using Bit.Core.AdminConsole.Repositories; @@ -102,11 +100,8 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv Provider provider, IEnumerable organizationOwnerEmails) { - var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - - if (isConsolidatedBillingEnabled && - provider.Status == ProviderStatusType.Billable && - organization.Status == OrganizationStatusType.Managed && + if (provider.IsBillable() && + organization.IsValidClient() && !string.IsNullOrEmpty(organization.GatewayCustomerId)) { await _stripeAdapter.CustomerUpdateAsync(organization.GatewayCustomerId, new CustomerUpdateOptions diff --git a/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs b/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs index 48ea903ad..e384d71df 100644 --- a/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs +++ b/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs @@ -8,7 +8,6 @@ using Bit.Core.AdminConsole.Models.Business.Tokenables; using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Services; using Bit.Core.Billing.Enums; -using Bit.Core.Billing.Extensions; using Bit.Core.Billing.Services; using Bit.Core.Context; using Bit.Core.Entities; @@ -101,24 +100,16 @@ public class ProviderService : IProviderService throw new BadRequestException("Invalid owner."); } - if (!_featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling)) + if (taxInfo == null || string.IsNullOrEmpty(taxInfo.BillingAddressCountry) || string.IsNullOrEmpty(taxInfo.BillingAddressPostalCode)) { - provider.Status = ProviderStatusType.Created; - await _providerRepository.UpsertAsync(provider); - } - else - { - if (taxInfo == null || string.IsNullOrEmpty(taxInfo.BillingAddressCountry) || string.IsNullOrEmpty(taxInfo.BillingAddressPostalCode)) - { - throw new BadRequestException("Both address and postal code are required to set up your provider."); - } - var customer = await _providerBillingService.SetupCustomer(provider, taxInfo); - provider.GatewayCustomerId = customer.Id; - var subscription = await _providerBillingService.SetupSubscription(provider); - provider.GatewaySubscriptionId = subscription.Id; - provider.Status = ProviderStatusType.Billable; - await _providerRepository.UpsertAsync(provider); + throw new BadRequestException("Both address and postal code are required to set up your provider."); } + var customer = await _providerBillingService.SetupCustomer(provider, taxInfo); + provider.GatewayCustomerId = customer.Id; + var subscription = await _providerBillingService.SetupSubscription(provider); + provider.GatewaySubscriptionId = subscription.Id; + provider.Status = ProviderStatusType.Billable; + await _providerRepository.UpsertAsync(provider); providerUser.Key = key; await _providerUserRepository.ReplaceAsync(providerUser); @@ -545,13 +536,9 @@ public class ProviderService : IProviderService { var provider = await _providerRepository.GetByIdAsync(providerId); - var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) && provider.IsBillable(); + ThrowOnInvalidPlanType(provider.Type, organizationSignup.Plan); - ThrowOnInvalidPlanType(provider.Type, organizationSignup.Plan, consolidatedBillingEnabled); - - var (organization, _, defaultCollection) = consolidatedBillingEnabled - ? await _organizationService.SignupClientAsync(organizationSignup) - : await _organizationService.SignUpAsync(organizationSignup); + var (organization, _, defaultCollection) = await _organizationService.SignupClientAsync(organizationSignup); var providerOrganization = new ProviderOrganization { @@ -687,27 +674,24 @@ public class ProviderService : IProviderService return confirmedOwnersIds.Except(providerUserIds).Any(); } - private void ThrowOnInvalidPlanType(ProviderType providerType, PlanType requestedType, bool consolidatedBillingEnabled = false) + private void ThrowOnInvalidPlanType(ProviderType providerType, PlanType requestedType) { - if (consolidatedBillingEnabled) + switch (providerType) { - switch (providerType) - { - case ProviderType.Msp: - if (requestedType is not (PlanType.TeamsMonthly or PlanType.EnterpriseMonthly)) - { - throw new BadRequestException($"Managed Service Providers cannot manage organizations with the plan type {requestedType}. Only Teams (Monthly) and Enterprise (Monthly) are allowed."); - } - break; - case ProviderType.MultiOrganizationEnterprise: - if (requestedType is not (PlanType.EnterpriseMonthly or PlanType.EnterpriseAnnually)) - { - throw new BadRequestException($"Multi-organization Enterprise Providers cannot manage organizations with the plan type {requestedType}. Only Enterprise (Monthly) and Enterprise (Annually) are allowed."); - } - break; - default: - throw new BadRequestException($"Unsupported provider type {providerType}."); - } + case ProviderType.Msp: + if (requestedType is not (PlanType.TeamsMonthly or PlanType.EnterpriseMonthly)) + { + throw new BadRequestException($"Managed Service Providers cannot manage organizations with the plan type {requestedType}. Only Teams (Monthly) and Enterprise (Monthly) are allowed."); + } + break; + case ProviderType.MultiOrganizationEnterprise: + if (requestedType is not (PlanType.EnterpriseMonthly or PlanType.EnterpriseAnnually)) + { + throw new BadRequestException($"Multi-organization Enterprise Providers cannot manage organizations with the plan type {requestedType}. Only Enterprise (Monthly) and Enterprise (Annually) are allowed."); + } + break; + default: + throw new BadRequestException($"Unsupported provider type {providerType}."); } if (ProviderDisallowedOrganizationTypes.Contains(requestedType)) diff --git a/bitwarden_license/test/Commercial.Core.Test/AdminConsole/ProviderFeatures/RemoveOrganizationFromProviderCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/ProviderFeatures/RemoveOrganizationFromProviderCommandTests.cs index e984259e9..f45ab7504 100644 --- a/bitwarden_license/test/Commercial.Core.Test/AdminConsole/ProviderFeatures/RemoveOrganizationFromProviderCommandTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/ProviderFeatures/RemoveOrganizationFromProviderCommandTests.cs @@ -1,5 +1,4 @@ using Bit.Commercial.Core.AdminConsole.Providers; -using Bit.Core; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Enums.Provider; @@ -155,9 +154,6 @@ public class RemoveOrganizationFromProviderCommandTests "b@example.com" ]); - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(false); - sutProvider.GetDependency().SubscriptionGetAsync(organization.GatewaySubscriptionId) .Returns(GetSubscription(organization.GatewaySubscriptionId)); @@ -222,9 +218,6 @@ public class RemoveOrganizationFromProviderCommandTests "b@example.com" ]); - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(true); - var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.SubscriptionCreateAsync(Arg.Any()).Returns(new Subscription diff --git a/bitwarden_license/test/Commercial.Core.Test/AdminConsole/Services/ProviderServiceTests.cs b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/Services/ProviderServiceTests.cs index 4aac363b9..2883c9d7e 100644 --- a/bitwarden_license/test/Commercial.Core.Test/AdminConsole/Services/ProviderServiceTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/Services/ProviderServiceTests.cs @@ -1,6 +1,5 @@ using Bit.Commercial.Core.AdminConsole.Services; using Bit.Commercial.Core.Test.AdminConsole.AutoFixture; -using Bit.Core; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Enums.Provider; @@ -55,36 +54,8 @@ public class ProviderServiceTests } [Theory, BitAutoData] - public async Task CompleteSetupAsync_Success(User user, Provider provider, string key, - [ProviderUser(ProviderUserStatusType.Confirmed, ProviderUserType.ProviderAdmin)] ProviderUser providerUser, - SutProvider sutProvider) - { - providerUser.ProviderId = provider.Id; - providerUser.UserId = user.Id; - var userService = sutProvider.GetDependency(); - userService.GetUserByIdAsync(user.Id).Returns(user); - - var providerUserRepository = sutProvider.GetDependency(); - providerUserRepository.GetByProviderUserAsync(provider.Id, user.Id).Returns(providerUser); - - var dataProtectionProvider = DataProtectionProvider.Create("ApplicationName"); - var protector = dataProtectionProvider.CreateProtector("ProviderServiceDataProtector"); - sutProvider.GetDependency().CreateProtector("ProviderServiceDataProtector") - .Returns(protector); - sutProvider.Create(); - - var token = protector.Protect($"ProviderSetupInvite {provider.Id} {user.Email} {CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow)}"); - - await sutProvider.Sut.CompleteSetupAsync(provider, user.Id, token, key); - - await sutProvider.GetDependency().Received().UpsertAsync(provider); - await sutProvider.GetDependency().Received() - .ReplaceAsync(Arg.Is(pu => pu.UserId == user.Id && pu.ProviderId == provider.Id && pu.Key == key)); - } - - [Theory, BitAutoData] - public async Task CompleteSetupAsync_ConsolidatedBilling_Success(User user, Provider provider, string key, TaxInfo taxInfo, - [ProviderUser(ProviderUserStatusType.Confirmed, ProviderUserType.ProviderAdmin)] ProviderUser providerUser, + public async Task CompleteSetupAsync_Success(User user, Provider provider, string key, TaxInfo taxInfo, + [ProviderUser] ProviderUser providerUser, SutProvider sutProvider) { providerUser.ProviderId = provider.Id; @@ -100,9 +71,6 @@ public class ProviderServiceTests sutProvider.GetDependency().CreateProtector("ProviderServiceDataProtector") .Returns(protector); - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(true); - var providerBillingService = sutProvider.GetDependency(); var customer = new Customer { Id = "customer_id" }; @@ -489,7 +457,7 @@ public class ProviderServiceTests public async Task AddOrganization_OrganizationHasSecretsManager_Throws(Provider provider, Organization organization, string key, SutProvider sutProvider) { - organization.PlanType = PlanType.EnterpriseAnnually; + organization.PlanType = PlanType.EnterpriseMonthly; organization.UseSecretsManager = true; sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); @@ -506,7 +474,7 @@ public class ProviderServiceTests public async Task AddOrganization_Success(Provider provider, Organization organization, string key, SutProvider sutProvider) { - organization.PlanType = PlanType.EnterpriseAnnually; + organization.PlanType = PlanType.EnterpriseMonthly; var providerRepository = sutProvider.GetDependency(); providerRepository.GetByIdAsync(provider.Id).Returns(provider); @@ -549,8 +517,8 @@ public class ProviderServiceTests sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); var providerOrganizationRepository = sutProvider.GetDependency(); - var expectedPlanType = PlanType.EnterpriseAnnually; - organization.PlanType = PlanType.EnterpriseAnnually; + var expectedPlanType = PlanType.EnterpriseMonthly; + organization.PlanType = PlanType.EnterpriseMonthly; sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); await sutProvider.Sut.AddOrganization(provider.Id, organization.Id, key); @@ -579,12 +547,12 @@ public class ProviderServiceTests BackdateProviderCreationDate(provider, newCreationDate); provider.Type = ProviderType.Msp; - organization.PlanType = PlanType.EnterpriseAnnually; - organization.Plan = "Enterprise (Annually)"; + organization.PlanType = PlanType.EnterpriseMonthly; + organization.Plan = "Enterprise (Monthly)"; - var expectedPlanType = PlanType.EnterpriseAnnually2020; + var expectedPlanType = PlanType.EnterpriseMonthly2020; - var expectedPlanId = "2020-enterprise-org-seat-annually"; + var expectedPlanId = "2020-enterprise-org-seat-monthly"; sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); var providerOrganizationRepository = sutProvider.GetDependency(); @@ -663,11 +631,11 @@ public class ProviderServiceTests public async Task CreateOrganizationAsync_Success(Provider provider, OrganizationSignup organizationSignup, Organization organization, string clientOwnerEmail, User user, SutProvider sutProvider) { - organizationSignup.Plan = PlanType.EnterpriseAnnually; + organizationSignup.Plan = PlanType.EnterpriseMonthly; sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); var providerOrganizationRepository = sutProvider.GetDependency(); - sutProvider.GetDependency().SignUpAsync(organizationSignup) + sutProvider.GetDependency().SignupClientAsync(organizationSignup) .Returns((organization, null as OrganizationUser, new Collection())); var providerOrganization = @@ -688,7 +656,7 @@ public class ProviderServiceTests } [Theory, OrganizationCustomize, BitAutoData] - public async Task CreateOrganizationAsync_ConsolidatedBillingEnabled_InvalidPlanType_ThrowsBadRequestException( + public async Task CreateOrganizationAsync_InvalidPlanType_ThrowsBadRequestException( Provider provider, OrganizationSignup organizationSignup, Organization organization, @@ -696,8 +664,6 @@ public class ProviderServiceTests User user, SutProvider sutProvider) { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true); - provider.Type = ProviderType.Msp; provider.Status = ProviderStatusType.Billable; @@ -717,7 +683,7 @@ public class ProviderServiceTests } [Theory, OrganizationCustomize, BitAutoData] - public async Task CreateOrganizationAsync_ConsolidatedBillingEnabled_InvokeSignupClientAsync( + public async Task CreateOrganizationAsync_InvokeSignupClientAsync( Provider provider, OrganizationSignup organizationSignup, Organization organization, @@ -725,8 +691,6 @@ public class ProviderServiceTests User user, SutProvider sutProvider) { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true); - provider.Type = ProviderType.Msp; provider.Status = ProviderStatusType.Billable; @@ -771,11 +735,11 @@ public class ProviderServiceTests (Provider provider, OrganizationSignup organizationSignup, Organization organization, string clientOwnerEmail, User user, SutProvider sutProvider, Collection defaultCollection) { - organizationSignup.Plan = PlanType.EnterpriseAnnually; + organizationSignup.Plan = PlanType.EnterpriseMonthly; sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); var providerOrganizationRepository = sutProvider.GetDependency(); - sutProvider.GetDependency().SignUpAsync(organizationSignup) + sutProvider.GetDependency().SignupClientAsync(organizationSignup) .Returns((organization, null as OrganizationUser, defaultCollection)); var providerOrganization = diff --git a/src/Admin/AdminConsole/Controllers/OrganizationsController.cs b/src/Admin/AdminConsole/Controllers/OrganizationsController.cs index db41e9282..67a80a375 100644 --- a/src/Admin/AdminConsole/Controllers/OrganizationsController.cs +++ b/src/Admin/AdminConsole/Controllers/OrganizationsController.cs @@ -55,8 +55,8 @@ public class OrganizationsController : Controller private readonly IServiceAccountRepository _serviceAccountRepository; private readonly IProviderOrganizationRepository _providerOrganizationRepository; private readonly IRemoveOrganizationFromProviderCommand _removeOrganizationFromProviderCommand; - private readonly IFeatureService _featureService; private readonly IProviderBillingService _providerBillingService; + private readonly IFeatureService _featureService; public OrganizationsController( IOrganizationService organizationService, @@ -82,8 +82,8 @@ public class OrganizationsController : Controller IServiceAccountRepository serviceAccountRepository, IProviderOrganizationRepository providerOrganizationRepository, IRemoveOrganizationFromProviderCommand removeOrganizationFromProviderCommand, - IFeatureService featureService, - IProviderBillingService providerBillingService) + IProviderBillingService providerBillingService, + IFeatureService featureService) { _organizationService = organizationService; _organizationRepository = organizationRepository; @@ -108,8 +108,8 @@ public class OrganizationsController : Controller _serviceAccountRepository = serviceAccountRepository; _providerOrganizationRepository = providerOrganizationRepository; _removeOrganizationFromProviderCommand = removeOrganizationFromProviderCommand; - _featureService = featureService; _providerBillingService = providerBillingService; + _featureService = featureService; } [RequirePermission(Permission.Org_List_View)] @@ -285,9 +285,7 @@ public class OrganizationsController : Controller return RedirectToAction("Index"); } - var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - - if (consolidatedBillingEnabled && organization.IsValidClient()) + if (organization.IsValidClient()) { var provider = await _providerRepository.GetByOrganizationIdAsync(organization.Id); @@ -477,12 +475,10 @@ public class OrganizationsController : Controller Organization organization, OrganizationEditModel update) { - var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - var scaleMSPOnClientOrganizationUpdate = _featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate); - if (!consolidatedBillingEnabled || !scaleMSPOnClientOrganizationUpdate) + if (!scaleMSPOnClientOrganizationUpdate) { return; } diff --git a/src/Admin/AdminConsole/Controllers/ProvidersController.cs b/src/Admin/AdminConsole/Controllers/ProvidersController.cs index 83e4ce7d5..8a56483a6 100644 --- a/src/Admin/AdminConsole/Controllers/ProvidersController.cs +++ b/src/Admin/AdminConsole/Controllers/ProvidersController.cs @@ -282,9 +282,7 @@ public class ProvidersController : Controller await _providerRepository.ReplaceAsync(provider); await _applicationCacheService.UpsertProviderAbilityAsync(provider); - var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - - if (!isConsolidatedBillingEnabled || !provider.IsBillable()) + if (!provider.IsBillable()) { return RedirectToAction("Edit", new { id }); } @@ -340,10 +338,7 @@ public class ProvidersController : Controller var users = await _providerUserRepository.GetManyDetailsByProviderAsync(id); var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(id); - var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - - - if (!isConsolidatedBillingEnabled || !provider.IsBillable()) + if (!provider.IsBillable()) { return new ProviderEditModel(provider, users, providerOrganizations, new List()); } diff --git a/src/Admin/AdminConsole/Views/Providers/CreateMsp.cshtml b/src/Admin/AdminConsole/Views/Providers/CreateMsp.cshtml index dde62b58a..d28348196 100644 --- a/src/Admin/AdminConsole/Views/Providers/CreateMsp.cshtml +++ b/src/Admin/AdminConsole/Views/Providers/CreateMsp.cshtml @@ -1,10 +1,5 @@ -@using Bit.Core.AdminConsole.Enums.Provider -@using Bit.Core - @model CreateMspProviderModel -@inject Bit.Core.Services.IFeatureService FeatureService - @{ ViewData["Title"] = "Create Managed Service Provider"; } @@ -17,8 +12,6 @@ - @if (FeatureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling)) - {
@@ -33,7 +26,6 @@
- } diff --git a/src/Admin/AdminConsole/Views/Providers/Edit.cshtml b/src/Admin/AdminConsole/Views/Providers/Edit.cshtml index 53944d0fc..005c498aa 100644 --- a/src/Admin/AdminConsole/Views/Providers/Edit.cshtml +++ b/src/Admin/AdminConsole/Views/Providers/Edit.cshtml @@ -48,7 +48,7 @@ - @if (FeatureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) && Model.Provider.IsBillable()) + @if (Model.Provider.IsBillable()) { switch (Model.Provider.Type) { @@ -68,46 +68,6 @@ -
-
-
-
- - -
-
-
-
-
-
-
- -
- -
- - - -
-
-
-
-
-
- -
- -
- - - -
-
-
-
-
break; } case ProviderType.MultiOrganizationEnterprise: @@ -141,6 +101,46 @@ break; } } +
+
+
+
+ + +
+
+
+
+
+
+
+ +
+ +
+ + + +
+
+
+
+
+
+ +
+ +
+ + + +
+
+
+
+
} @await Html.PartialAsync("Organizations", Model) diff --git a/src/Api/AdminConsole/Controllers/OrganizationsController.cs b/src/Api/AdminConsole/Controllers/OrganizationsController.cs index e134adc04..88734a6c7 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationsController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationsController.cs @@ -289,9 +289,7 @@ public class OrganizationsController : Controller throw new BadRequestException(string.Empty, "User verification failed."); } - var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - - if (consolidatedBillingEnabled && organization.IsValidClient()) + if (organization.IsValidClient()) { var provider = await _providerRepository.GetByOrganizationIdAsync(organization.Id); @@ -322,8 +320,7 @@ public class OrganizationsController : Controller throw new BadRequestException("Invalid token."); } - var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - if (consolidatedBillingEnabled && organization.IsValidClient()) + if (organization.IsValidClient()) { var provider = await _providerRepository.GetByOrganizationIdAsync(organization.Id); if (provider.IsBillable()) diff --git a/src/Api/Billing/Controllers/BaseProviderController.cs b/src/Api/Billing/Controllers/BaseProviderController.cs index ecc9f23a7..038abfaa9 100644 --- a/src/Api/Billing/Controllers/BaseProviderController.cs +++ b/src/Api/Billing/Controllers/BaseProviderController.cs @@ -1,5 +1,4 @@ -using Bit.Core; -using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Billing.Extensions; using Bit.Core.Context; @@ -9,7 +8,6 @@ namespace Bit.Api.Billing.Controllers; public abstract class BaseProviderController( ICurrentContext currentContext, - IFeatureService featureService, ILogger logger, IProviderRepository providerRepository, IUserService userService) : BaseBillingController @@ -26,15 +24,6 @@ public abstract class BaseProviderController( Guid providerId, Func checkAuthorization) { - if (!featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling)) - { - logger.LogError( - "Cannot run Consolidated Billing operation for provider ({ProviderID}) while feature flag is disabled", - providerId); - - return (null, Error.NotFound()); - } - var provider = await providerRepository.GetByIdAsync(providerId); if (provider == null) diff --git a/src/Api/Billing/Controllers/ProviderBillingController.cs b/src/Api/Billing/Controllers/ProviderBillingController.cs index d2209685a..f7ddf0853 100644 --- a/src/Api/Billing/Controllers/ProviderBillingController.cs +++ b/src/Api/Billing/Controllers/ProviderBillingController.cs @@ -19,14 +19,13 @@ namespace Bit.Api.Billing.Controllers; [Authorize("Application")] public class ProviderBillingController( ICurrentContext currentContext, - IFeatureService featureService, ILogger logger, IProviderBillingService providerBillingService, IProviderPlanRepository providerPlanRepository, IProviderRepository providerRepository, ISubscriberService subscriberService, IStripeAdapter stripeAdapter, - IUserService userService) : BaseProviderController(currentContext, featureService, logger, providerRepository, userService) + IUserService userService) : BaseProviderController(currentContext, logger, providerRepository, userService) { [HttpGet("invoices")] public async Task GetInvoicesAsync([FromRoute] Guid providerId) diff --git a/src/Api/Billing/Controllers/ProviderClientsController.cs b/src/Api/Billing/Controllers/ProviderClientsController.cs index 700dd4a2e..0c09fa7ba 100644 --- a/src/Api/Billing/Controllers/ProviderClientsController.cs +++ b/src/Api/Billing/Controllers/ProviderClientsController.cs @@ -14,14 +14,13 @@ namespace Bit.Api.Billing.Controllers; [Route("providers/{providerId:guid}/clients")] public class ProviderClientsController( ICurrentContext currentContext, - IFeatureService featureService, ILogger logger, IOrganizationRepository organizationRepository, IProviderBillingService providerBillingService, IProviderOrganizationRepository providerOrganizationRepository, IProviderRepository providerRepository, IProviderService providerService, - IUserService userService) : BaseProviderController(currentContext, featureService, logger, providerRepository, userService) + IUserService userService) : BaseProviderController(currentContext, logger, providerRepository, userService) { [HttpPost] public async Task CreateAsync( diff --git a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs index f44ce686f..47c79aa13 100644 --- a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs +++ b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs @@ -15,6 +15,7 @@ using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Repositories; using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Billing.Enums; +using Bit.Core.Billing.Extensions; using Bit.Core.Billing.Models.Sales; using Bit.Core.Billing.Services; using Bit.Core.Context; @@ -444,13 +445,6 @@ public class OrganizationService : IOrganizationService public async Task<(Organization organization, OrganizationUser organizationUser, Collection defaultCollection)> SignupClientAsync(OrganizationSignup signup) { - var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - - if (!consolidatedBillingEnabled) - { - throw new InvalidOperationException($"{nameof(SignupClientAsync)} is only for use within Consolidated Billing"); - } - var plan = StaticStore.GetPlan(signup.Plan); ValidatePlan(plan, signup.AdditionalSeats, "Password Manager"); @@ -1443,10 +1437,7 @@ public class OrganizationService : IOrganizationService if (provider is { Enabled: true }) { - var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling); - - if (consolidatedBillingEnabled && provider.Type == ProviderType.Msp && - provider.Status == ProviderStatusType.Billable) + if (provider.IsBillable()) { return (false, "Seat limit has been reached. Please contact your provider to add more seats."); } diff --git a/src/Core/Billing/Extensions/BillingExtensions.cs b/src/Core/Billing/Extensions/BillingExtensions.cs index 93afbb971..39b92e95a 100644 --- a/src/Core/Billing/Extensions/BillingExtensions.cs +++ b/src/Core/Billing/Extensions/BillingExtensions.cs @@ -11,10 +11,11 @@ namespace Bit.Core.Billing.Extensions; public static class BillingExtensions { public static bool IsBillable(this Provider provider) => - provider.SupportsConsolidatedBilling() && provider.Status == ProviderStatusType.Billable; - - public static bool SupportsConsolidatedBilling(this Provider provider) - => provider.Type.SupportsConsolidatedBilling(); + provider is + { + Type: ProviderType.Msp or ProviderType.MultiOrganizationEnterprise, + Status: ProviderStatusType.Billable + }; public static bool SupportsConsolidatedBilling(this ProviderType providerType) => providerType is ProviderType.Msp or ProviderType.MultiOrganizationEnterprise; @@ -24,12 +25,15 @@ public static class BillingExtensions { Seats: not null, Status: OrganizationStatusType.Managed, - PlanType: PlanType.TeamsMonthly or PlanType.EnterpriseMonthly + PlanType: PlanType.TeamsMonthly or PlanType.EnterpriseMonthly or PlanType.EnterpriseAnnually }; public static bool IsStripeEnabled(this ISubscriber subscriber) - => !string.IsNullOrEmpty(subscriber.GatewayCustomerId) && - !string.IsNullOrEmpty(subscriber.GatewaySubscriptionId); + => subscriber is + { + GatewayCustomerId: not null and not "", + GatewaySubscriptionId: not null and not "" + }; public static bool IsUnverifiedBankAccount(this SetupIntent setupIntent) => setupIntent is diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index aed90a893..3e40eb7a1 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -107,7 +107,6 @@ public static class FeatureFlagKeys public const string ItemShare = "item-share"; public const string DuoRedirect = "duo-redirect"; public const string AC2101UpdateTrialInitiationEmail = "AC-2101-update-trial-initiation-email"; - public const string EnableConsolidatedBilling = "enable-consolidated-billing"; public const string AC1795_UpdatedSubscriptionStatusSection = "AC-1795_updated-subscription-status-section"; public const string EmailVerification = "email-verification"; public const string EmailVerificationDisableTimingDelays = "email-verification-disable-timing-delays"; diff --git a/test/Admin.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs b/test/Admin.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs index af98fef03..485126ebb 100644 --- a/test/Admin.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs +++ b/test/Admin.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs @@ -68,7 +68,6 @@ public class OrganizationsControllerTests var featureService = sutProvider.GetDependency(); - featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Created }; @@ -104,7 +103,6 @@ public class OrganizationsControllerTests var featureService = sutProvider.GetDependency(); - featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; @@ -147,7 +145,6 @@ public class OrganizationsControllerTests var featureService = sutProvider.GetDependency(); - featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; @@ -190,7 +187,6 @@ public class OrganizationsControllerTests var featureService = sutProvider.GetDependency(); - featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; @@ -233,7 +229,6 @@ public class OrganizationsControllerTests var featureService = sutProvider.GetDependency(); - featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; @@ -278,7 +273,6 @@ public class OrganizationsControllerTests var featureService = sutProvider.GetDependency(); - featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; @@ -322,7 +316,6 @@ public class OrganizationsControllerTests var featureService = sutProvider.GetDependency(); - featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true); featureService.IsEnabled(FeatureFlagKeys.PM14401_ScaleMSPOnClientOrganizationUpdate).Returns(true); var provider = new Provider { Type = ProviderType.Msp, Status = ProviderStatusType.Billable }; diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs index 13826888d..27c0f7a7c 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs @@ -218,8 +218,6 @@ public class OrganizationsControllerTests : IDisposable _userService.VerifySecretAsync(user, requestModel.Secret).Returns(true); - _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true); - _providerRepository.GetByOrganizationIdAsync(organization.Id).Returns(provider); await _sut.Delete(organizationId.ToString(), requestModel); diff --git a/test/Api.Test/Billing/Controllers/ProviderBillingControllerTests.cs b/test/Api.Test/Billing/Controllers/ProviderBillingControllerTests.cs index 73e3850c8..d46038ae9 100644 --- a/test/Api.Test/Billing/Controllers/ProviderBillingControllerTests.cs +++ b/test/Api.Test/Billing/Controllers/ProviderBillingControllerTests.cs @@ -1,7 +1,6 @@ using Bit.Api.Billing.Controllers; using Bit.Api.Billing.Models.Requests; using Bit.Api.Billing.Models.Responses; -using Bit.Core; using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Repositories; @@ -35,27 +34,11 @@ public class ProviderBillingControllerTests { #region GetInvoicesAsync & TryGetBillableProviderForAdminOperations - [Theory, BitAutoData] - public async Task GetInvoicesAsync_FFDisabled_NotFound( - Guid providerId, - SutProvider sutProvider) - { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(false); - - var result = await sutProvider.Sut.GetInvoicesAsync(providerId); - - AssertNotFound(result); - } - [Theory, BitAutoData] public async Task GetInvoicesAsync_NullProvider_NotFound( Guid providerId, SutProvider sutProvider) { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(true); - sutProvider.GetDependency().GetByIdAsync(providerId).ReturnsNull(); var result = await sutProvider.Sut.GetInvoicesAsync(providerId); @@ -68,9 +51,6 @@ public class ProviderBillingControllerTests Provider provider, SutProvider sutProvider) { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(true); - sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); sutProvider.GetDependency().ProviderProviderAdmin(provider.Id) @@ -86,9 +66,6 @@ public class ProviderBillingControllerTests Provider provider, SutProvider sutProvider) { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(true); - provider.Type = ProviderType.Reseller; provider.Status = ProviderStatusType.Created; @@ -229,27 +206,11 @@ public class ProviderBillingControllerTests #region GetSubscriptionAsync & TryGetBillableProviderForServiceUserOperation - [Theory, BitAutoData] - public async Task GetSubscriptionAsync_FFDisabled_NotFound( - Guid providerId, - SutProvider sutProvider) - { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(false); - - var result = await sutProvider.Sut.GetSubscriptionAsync(providerId); - - AssertNotFound(result); - } - [Theory, BitAutoData] public async Task GetSubscriptionAsync_NullProvider_NotFound( Guid providerId, SutProvider sutProvider) { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(true); - sutProvider.GetDependency().GetByIdAsync(providerId).ReturnsNull(); var result = await sutProvider.Sut.GetSubscriptionAsync(providerId); @@ -262,9 +223,6 @@ public class ProviderBillingControllerTests Provider provider, SutProvider sutProvider) { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(true); - sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); sutProvider.GetDependency().ProviderUser(provider.Id) @@ -280,9 +238,6 @@ public class ProviderBillingControllerTests Provider provider, SutProvider sutProvider) { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(true); - provider.Type = ProviderType.Reseller; provider.Status = ProviderStatusType.Created; diff --git a/test/Api.Test/Billing/Utilities.cs b/test/Api.Test/Billing/Utilities.cs index 3f5eee72c..fd79c71ca 100644 --- a/test/Api.Test/Billing/Utilities.cs +++ b/test/Api.Test/Billing/Utilities.cs @@ -1,11 +1,9 @@ using Bit.Api.Billing.Controllers; -using Bit.Core; using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Models.Api; -using Bit.Core.Services; using Bit.Test.Common.AutoFixture; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.HttpResults; @@ -59,9 +57,6 @@ public static class Utilities Provider provider, SutProvider sutProvider) where T : BaseProviderController { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) - .Returns(true); - provider.Type = ProviderType.Msp; provider.Status = ProviderStatusType.Billable; diff --git a/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs b/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs index 147162c66..e09293f32 100644 --- a/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs @@ -420,8 +420,6 @@ public class OrganizationServiceTests OrganizationSignup signup, SutProvider sutProvider) { - sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true); - signup.Plan = PlanType.TeamsMonthly; var (organization, _, _) = await sutProvider.Sut.SignupClientAsync(signup);