diff --git a/src/Api/Billing/Models/Responses/OrganizationMetadataResponse.cs b/src/Api/Billing/Models/Responses/OrganizationMetadataResponse.cs index 624eab1fe..b5f9ab2f5 100644 --- a/src/Api/Billing/Models/Responses/OrganizationMetadataResponse.cs +++ b/src/Api/Billing/Models/Responses/OrganizationMetadataResponse.cs @@ -3,8 +3,11 @@ namespace Bit.Api.Billing.Models.Responses; public record OrganizationMetadataResponse( + bool IsEligibleForSelfHost, bool IsOnSecretsManagerStandalone) { public static OrganizationMetadataResponse From(OrganizationMetadata metadata) - => new(metadata.IsOnSecretsManagerStandalone); + => new( + metadata.IsEligibleForSelfHost, + metadata.IsOnSecretsManagerStandalone); } diff --git a/src/Core/Billing/Models/OrganizationMetadata.cs b/src/Core/Billing/Models/OrganizationMetadata.cs index decc35ffd..136964d7c 100644 --- a/src/Core/Billing/Models/OrganizationMetadata.cs +++ b/src/Core/Billing/Models/OrganizationMetadata.cs @@ -1,8 +1,10 @@ namespace Bit.Core.Billing.Models; public record OrganizationMetadata( + bool IsEligibleForSelfHost, bool IsOnSecretsManagerStandalone) { public static OrganizationMetadata Default() => new( - IsOnSecretsManagerStandalone: default); + IsEligibleForSelfHost: false, + IsOnSecretsManagerStandalone: false); } diff --git a/src/Core/Billing/Services/Implementations/OrganizationBillingService.cs b/src/Core/Billing/Services/Implementations/OrganizationBillingService.cs index 3c5938cab..7db886203 100644 --- a/src/Core/Billing/Services/Implementations/OrganizationBillingService.cs +++ b/src/Core/Billing/Services/Implementations/OrganizationBillingService.cs @@ -1,4 +1,5 @@ using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Billing.Caches; using Bit.Core.Billing.Constants; using Bit.Core.Billing.Models; @@ -26,6 +27,7 @@ public class OrganizationBillingService( IGlobalSettings globalSettings, ILogger logger, IOrganizationRepository organizationRepository, + IProviderRepository providerRepository, ISetupIntentCache setupIntentCache, IStripeAdapter stripeAdapter, ISubscriberService subscriberService) : IOrganizationBillingService @@ -69,14 +71,11 @@ public class OrganizationBillingService( var subscription = await subscriberService.GetSubscription(organization); - if (customer == null || subscription == null) - { - return OrganizationMetadata.Default(); - } + var isEligibleForSelfHost = await IsEligibleForSelfHost(organization, subscription); var isOnSecretsManagerStandalone = IsOnSecretsManagerStandalone(organization, customer, subscription); - return new OrganizationMetadata(isOnSecretsManagerStandalone); + return new OrganizationMetadata(isEligibleForSelfHost, isOnSecretsManagerStandalone); } public async Task UpdatePaymentMethod( @@ -340,11 +339,38 @@ public class OrganizationBillingService( return await stripeAdapter.SubscriptionCreateAsync(subscriptionCreateOptions); } + private async Task IsEligibleForSelfHost( + Organization organization, + Subscription? organizationSubscription) + { + if (organization.Status != OrganizationStatusType.Managed) + { + return organization.Plan.Contains("Families") || + organization.Plan.Contains("Enterprise") && IsActive(organizationSubscription); + } + + var provider = await providerRepository.GetByOrganizationIdAsync(organization.Id); + + var providerSubscription = await subscriberService.GetSubscriptionOrThrow(provider); + + return organization.Plan.Contains("Enterprise") && IsActive(providerSubscription); + + bool IsActive(Subscription? subscription) => subscription?.Status is + StripeConstants.SubscriptionStatus.Active or + StripeConstants.SubscriptionStatus.Trialing or + StripeConstants.SubscriptionStatus.PastDue; + } + private static bool IsOnSecretsManagerStandalone( Organization organization, - Customer customer, - Subscription subscription) + Customer? customer, + Subscription? subscription) { + if (customer == null || subscription == null) + { + return false; + } + var plan = StaticStore.GetPlan(organization.PlanType); if (!plan.SupportsSecretsManager) diff --git a/test/Api.Test/Billing/Controllers/OrganizationBillingControllerTests.cs b/test/Api.Test/Billing/Controllers/OrganizationBillingControllerTests.cs index 70ca59940..b46fd307e 100644 --- a/test/Api.Test/Billing/Controllers/OrganizationBillingControllerTests.cs +++ b/test/Api.Test/Billing/Controllers/OrganizationBillingControllerTests.cs @@ -52,7 +52,7 @@ public class OrganizationBillingControllerTests { sutProvider.GetDependency().AccessMembersTab(organizationId).Returns(true); sutProvider.GetDependency().GetMetadata(organizationId) - .Returns(new OrganizationMetadata(true)); + .Returns(new OrganizationMetadata(true, true)); var result = await sutProvider.Sut.GetMetadataAsync(organizationId); @@ -60,6 +60,7 @@ public class OrganizationBillingControllerTests var organizationMetadataResponse = ((Ok)result).Value; + Assert.True(organizationMetadataResponse.IsEligibleForSelfHost); Assert.True(organizationMetadataResponse.IsOnSecretsManagerStandalone); }