diff --git a/bitwarden_license/src/Commercial.Core/Services/ProviderService.cs b/bitwarden_license/src/Commercial.Core/Services/ProviderService.cs index 9a5f924246..d186eb6d41 100644 --- a/bitwarden_license/src/Commercial.Core/Services/ProviderService.cs +++ b/bitwarden_license/src/Commercial.Core/Services/ProviderService.cs @@ -354,6 +354,12 @@ public class ProviderService : IProviderService var organization = await _organizationRepository.GetByIdAsync(organizationId); ThrowOnInvalidPlanType(organization.PlanType); + if (organization.UseSecretsManager) + { + throw new BadRequestException( + "The organization is subscribed to Secrets Manager. Please contact Customer Support to manage the subscription."); + } + var providerOrganization = new ProviderOrganization { ProviderId = providerId, diff --git a/bitwarden_license/test/Commercial.Core.Test/Services/ProviderServiceTests.cs b/bitwarden_license/test/Commercial.Core.Test/Services/ProviderServiceTests.cs index babfa9c074..2a6e68bfb4 100644 --- a/bitwarden_license/test/Commercial.Core.Test/Services/ProviderServiceTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/Services/ProviderServiceTests.cs @@ -431,6 +431,23 @@ public class ProviderServiceTests Assert.Equal("Organization already belongs to a provider.", exception.Message); } + [Theory, BitAutoData] + public async Task AddOrganization_OrganizationHasSecretsManager_Throws(Provider provider, Organization organization, string key, + SutProvider sutProvider) + { + organization.PlanType = PlanType.EnterpriseAnnually; + organization.UseSecretsManager = true; + + sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); + var providerOrganizationRepository = sutProvider.GetDependency(); + providerOrganizationRepository.GetByOrganizationId(organization.Id).ReturnsNull(); + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.AddOrganization(provider.Id, organization.Id, key)); + Assert.Equal("The organization is subscribed to Secrets Manager. Please contact Customer Support to manage the subscription.", exception.Message); + } + [Theory, BitAutoData] public async Task AddOrganization_Success(Provider provider, Organization organization, string key, SutProvider sutProvider) diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs index 3741148af4..52136bd1b5 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs @@ -1,8 +1,10 @@ using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.Enums.Provider; using Bit.Core.Exceptions; using Bit.Core.Models.Business; using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; +using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Utilities; @@ -12,17 +14,21 @@ public class AddSecretsManagerSubscriptionCommand : IAddSecretsManagerSubscripti { private readonly IPaymentService _paymentService; private readonly IOrganizationService _organizationService; + private readonly IProviderRepository _providerRepository; + public AddSecretsManagerSubscriptionCommand( IPaymentService paymentService, - IOrganizationService organizationService) + IOrganizationService organizationService, + IProviderRepository providerRepository) { _paymentService = paymentService; _organizationService = organizationService; + _providerRepository = providerRepository; } public async Task SignUpAsync(Organization organization, int additionalSmSeats, int additionalServiceAccounts) { - ValidateOrganization(organization); + await ValidateOrganization(organization); var plan = StaticStore.GetSecretsManagerPlan(organization.PlanType); var signup = SetOrganizationUpgrade(organization, additionalSmSeats, additionalServiceAccounts); @@ -55,7 +61,7 @@ public class AddSecretsManagerSubscriptionCommand : IAddSecretsManagerSubscripti return signup; } - private static void ValidateOrganization(Organization organization) + private async Task ValidateOrganization(Organization organization) { if (organization == null) { @@ -83,5 +89,12 @@ public class AddSecretsManagerSubscriptionCommand : IAddSecretsManagerSubscripti { throw new BadRequestException("No subscription found."); } + + var provider = await _providerRepository.GetByOrganizationIdAsync(organization.Id); + if (provider is { Type: ProviderType.Msp }) + { + throw new BadRequestException( + "Organizations with a Managed Service Provider do not support Secrets Manager."); + } } } diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 10dc2a2056..2388faded2 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -410,6 +410,11 @@ public class OrganizationService : IOrganizationService var secretsManagerPlan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == signup.Plan); if (signup.UseSecretsManager) { + if (provider) + { + throw new BadRequestException( + "Organizations with a Managed Service Provider do not support Secrets Manager."); + } ValidateSecretsManagerPlan(secretsManagerPlan, signup); } diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs index a09500cf67..ec83fa1022 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs @@ -1,9 +1,12 @@ using Bit.Core.Entities; +using Bit.Core.Entities.Provider; using Bit.Core.Enums; +using Bit.Core.Enums.Provider; using Bit.Core.Exceptions; using Bit.Core.Models.Business; using Bit.Core.Models.StaticStore; using Bit.Core.OrganizationFeatures.OrganizationSubscriptions; +using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Utilities; using Bit.Test.Common.AutoFixture; @@ -127,6 +130,25 @@ public class AddSecretsManagerSubscriptionCommandTests await VerifyDependencyNotCalledAsync(sutProvider); } + [Theory] + [BitAutoData] + public async Task SignUpAsync_ThrowsException_WhenOrganizationIsManagedByMSP( + SutProvider sutProvider, + Organization organization, + Provider provider) + { + organization.UseSecretsManager = false; + organization.SecretsManagerBeta = false; + provider.Type = ProviderType.Msp; + sutProvider.GetDependency().GetByOrganizationIdAsync(organization.Id).Returns(provider); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.SignUpAsync(organization, 10, 10)); + + Assert.Contains("Organizations with a Managed Service Provider do not support Secrets Manager.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + private static async Task VerifyDependencyNotCalledAsync(SutProvider sutProvider) { await sutProvider.GetDependency().DidNotReceive() diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index 4907efafcb..a4974d39af 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -263,6 +263,22 @@ public class OrganizationServiceTests ); } + [Theory] + [BitAutoData(PlanType.EnterpriseAnnually)] + public async Task SignUp_SM_Throws_WhenManagedByMSP(PlanType planType, OrganizationSignup signup, SutProvider sutProvider) + { + signup.Plan = planType; + signup.UseSecretsManager = true; + signup.AdditionalSeats = 15; + signup.AdditionalSmSeats = 10; + signup.AdditionalServiceAccounts = 20; + signup.PaymentMethodType = PaymentMethodType.Card; + signup.PremiumAccessAddon = false; + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.SignUpAsync(signup, true)); + Assert.Contains("Organizations with a Managed Service Provider do not support Secrets Manager.", exception.Message); + } + [Theory] [BitAutoData] public async Task SignUpAsync_SecretManager_AdditionalServiceAccounts_NotAllowedByPlan_ShouldThrowException(OrganizationSignup signup, SutProvider sutProvider)