1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-23 04:21:05 -05:00

Merge branch 'main' into ac/pm-18238/add-requiretwofactorpolicyrequirement

This commit is contained in:
Rui Tome 2025-05-22 10:07:27 +01:00
commit b20acfe99e
No known key found for this signature in database
GPG Key ID: 526239D96A8EC066
85 changed files with 582 additions and 259 deletions

View File

@ -636,7 +636,9 @@ jobs:
setup-ephemeral-environment: setup-ephemeral-environment:
name: Setup Ephemeral Environment name: Setup Ephemeral Environment
needs: build-docker needs:
- build-artifacts
- build-docker
if: | if: |
needs.build-artifacts.outputs.has_secrets == 'true' needs.build-artifacts.outputs.has_secrets == 'true'
&& github.event_name == 'pull_request' && github.event_name == 'pull_request'

View File

@ -3,9 +3,9 @@ using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Providers.Interfaces; using Bit.Core.AdminConsole.Providers.Interfaces;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Repositories; using Bit.Core.Repositories;

View File

@ -7,6 +7,7 @@ using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Constants; using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Extensions; using Bit.Core.Billing.Extensions;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Providers.Services;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;

View File

@ -5,12 +5,13 @@ using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Models.Business.Provider; using Bit.Core.AdminConsole.Models.Business.Provider;
using Bit.Core.AdminConsole.Models.Business.Tokenables; using Bit.Core.AdminConsole.Models.Business.Tokenables;
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models; using Bit.Core.Billing.Models;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Services;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
@ -53,6 +54,7 @@ public class ProviderService : IProviderService
private readonly IApplicationCacheService _applicationCacheService; private readonly IApplicationCacheService _applicationCacheService;
private readonly IProviderBillingService _providerBillingService; private readonly IProviderBillingService _providerBillingService;
private readonly IPricingClient _pricingClient; private readonly IPricingClient _pricingClient;
private readonly IProviderClientOrganizationSignUpCommand _providerClientOrganizationSignUpCommand;
public ProviderService(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository, public ProviderService(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository,
IProviderOrganizationRepository providerOrganizationRepository, IUserRepository userRepository, IProviderOrganizationRepository providerOrganizationRepository, IUserRepository userRepository,
@ -61,7 +63,8 @@ public class ProviderService : IProviderService
IOrganizationRepository organizationRepository, GlobalSettings globalSettings, IOrganizationRepository organizationRepository, GlobalSettings globalSettings,
ICurrentContext currentContext, IStripeAdapter stripeAdapter, IFeatureService featureService, ICurrentContext currentContext, IStripeAdapter stripeAdapter, IFeatureService featureService,
IDataProtectorTokenFactory<ProviderDeleteTokenable> providerDeleteTokenDataFactory, IDataProtectorTokenFactory<ProviderDeleteTokenable> providerDeleteTokenDataFactory,
IApplicationCacheService applicationCacheService, IProviderBillingService providerBillingService, IPricingClient pricingClient) IApplicationCacheService applicationCacheService, IProviderBillingService providerBillingService, IPricingClient pricingClient,
IProviderClientOrganizationSignUpCommand providerClientOrganizationSignUpCommand)
{ {
_providerRepository = providerRepository; _providerRepository = providerRepository;
_providerUserRepository = providerUserRepository; _providerUserRepository = providerUserRepository;
@ -81,6 +84,7 @@ public class ProviderService : IProviderService
_applicationCacheService = applicationCacheService; _applicationCacheService = applicationCacheService;
_providerBillingService = providerBillingService; _providerBillingService = providerBillingService;
_pricingClient = pricingClient; _pricingClient = pricingClient;
_providerClientOrganizationSignUpCommand = providerClientOrganizationSignUpCommand;
} }
public async Task<Provider> CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key, TaxInfo taxInfo, TokenizedPaymentSource tokenizedPaymentSource = null) public async Task<Provider> CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key, TaxInfo taxInfo, TokenizedPaymentSource tokenizedPaymentSource = null)
@ -560,12 +564,12 @@ public class ProviderService : IProviderService
ThrowOnInvalidPlanType(provider.Type, organizationSignup.Plan); ThrowOnInvalidPlanType(provider.Type, organizationSignup.Plan);
var (organization, _, defaultCollection) = await _organizationService.SignupClientAsync(organizationSignup); var signUpResponse = await _providerClientOrganizationSignUpCommand.SignUpClientOrganizationAsync(organizationSignup);
var providerOrganization = new ProviderOrganization var providerOrganization = new ProviderOrganization
{ {
ProviderId = providerId, ProviderId = providerId,
OrganizationId = organization.Id, OrganizationId = signUpResponse.Organization.Id,
Key = organizationSignup.OwnerKey, Key = organizationSignup.OwnerKey,
}; };
@ -574,12 +578,12 @@ public class ProviderService : IProviderService
// Give the owner Can Manage access over the default collection // Give the owner Can Manage access over the default collection
// The orgUser is not available when the org is created so we have to do it here as part of the invite // The orgUser is not available when the org is created so we have to do it here as part of the invite
var defaultOwnerAccess = defaultCollection != null var defaultOwnerAccess = signUpResponse.DefaultCollection != null
? ?
[ [
new CollectionAccessSelection new CollectionAccessSelection
{ {
Id = defaultCollection.Id, Id = signUpResponse.DefaultCollection.Id,
HidePasswords = false, HidePasswords = false,
ReadOnly = false, ReadOnly = false,
Manage = true Manage = true
@ -587,7 +591,7 @@ public class ProviderService : IProviderService
] ]
: Array.Empty<CollectionAccessSelection>(); : Array.Empty<CollectionAccessSelection>();
await _organizationService.InviteUsersAsync(organization.Id, user.Id, systemUser: null, await _organizationService.InviteUsersAsync(signUpResponse.Organization.Id, user.Id, systemUser: null,
new (OrganizationUserInvite, string)[] new (OrganizationUserInvite, string)[]
{ {
( (

View File

@ -1,8 +1,8 @@
using System.Globalization; using System.Globalization;
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
using CsvHelper.Configuration.Attributes; using CsvHelper.Configuration.Attributes;
namespace Bit.Commercial.Core.Billing.Models; namespace Bit.Commercial.Core.Billing.Providers.Models;
public class ProviderClientInvoiceReportRow public class ProviderClientInvoiceReportRow
{ {

View File

@ -7,11 +7,12 @@ using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing; using Bit.Core.Billing;
using Bit.Core.Billing.Constants; using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Extensions; using Bit.Core.Billing.Extensions;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Providers.Services;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
@ -24,7 +25,7 @@ using Microsoft.Extensions.Logging;
using OneOf; using OneOf;
using Stripe; using Stripe;
namespace Bit.Commercial.Core.Billing; namespace Bit.Commercial.Core.Billing.Providers.Services;
[RequireFeature(FeatureFlagKeys.PM18770_EnableOrganizationBusinessUnitConversion)] [RequireFeature(FeatureFlagKeys.PM18770_EnableOrganizationBusinessUnitConversion)]
public class BusinessUnitConverter( public class BusinessUnitConverter(

View File

@ -1,5 +1,5 @@
using System.Globalization; using System.Globalization;
using Bit.Commercial.Core.Billing.Models; using Bit.Commercial.Core.Billing.Providers.Models;
using Bit.Core; using Bit.Core;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
@ -8,14 +8,15 @@ using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing; using Bit.Core.Billing;
using Bit.Core.Billing.Caches; using Bit.Core.Billing.Caches;
using Bit.Core.Billing.Constants; using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Extensions; using Bit.Core.Billing.Extensions;
using Bit.Core.Billing.Models; using Bit.Core.Billing.Models;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Providers.Models;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Providers.Services;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
using Bit.Core.Billing.Services.Contracts;
using Bit.Core.Billing.Tax.Models; using Bit.Core.Billing.Tax.Models;
using Bit.Core.Billing.Tax.Services; using Bit.Core.Billing.Tax.Services;
using Bit.Core.Enums; using Bit.Core.Enums;
@ -28,12 +29,11 @@ using Braintree;
using CsvHelper; using CsvHelper;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Stripe; using Stripe;
using static Bit.Core.Billing.Utilities; using static Bit.Core.Billing.Utilities;
using Customer = Stripe.Customer; using Customer = Stripe.Customer;
using Subscription = Stripe.Subscription; using Subscription = Stripe.Subscription;
namespace Bit.Commercial.Core.Billing; namespace Bit.Commercial.Core.Billing.Providers.Services;
public class ProviderBillingService( public class ProviderBillingService(
IBraintreeGateway braintreeGateway, IBraintreeGateway braintreeGateway,

View File

@ -6,7 +6,7 @@ using Bit.Core.Billing;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Stripe; using Stripe;
namespace Bit.Commercial.Core.Billing; namespace Bit.Commercial.Core.Billing.Providers.Services;
public static class ProviderPriceAdapter public static class ProviderPriceAdapter
{ {

View File

@ -1,9 +1,9 @@
using Bit.Commercial.Core.AdminConsole.Providers; using Bit.Commercial.Core.AdminConsole.Providers;
using Bit.Commercial.Core.AdminConsole.Services; using Bit.Commercial.Core.AdminConsole.Services;
using Bit.Commercial.Core.Billing; using Bit.Commercial.Core.Billing.Providers.Services;
using Bit.Core.AdminConsole.Providers.Interfaces; using Bit.Core.AdminConsole.Providers.Interfaces;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Services;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Bit.Commercial.Core.Utilities; namespace Bit.Commercial.Core.Utilities;

View File

@ -8,6 +8,7 @@ using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Constants; using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Providers.Services;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;

View File

@ -6,11 +6,12 @@ using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Models.Business.Provider; using Bit.Core.AdminConsole.Models.Business.Provider;
using Bit.Core.AdminConsole.Models.Business.Tokenables; using Bit.Core.AdminConsole.Models.Business.Tokenables;
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models; using Bit.Core.Billing.Models;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Services;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
@ -717,8 +718,8 @@ public class ProviderServiceTests
sutProvider.GetDependency<IProviderRepository>().GetByIdAsync(provider.Id).Returns(provider); sutProvider.GetDependency<IProviderRepository>().GetByIdAsync(provider.Id).Returns(provider);
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>(); var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
sutProvider.GetDependency<IOrganizationService>().SignupClientAsync(organizationSignup) sutProvider.GetDependency<IProviderClientOrganizationSignUpCommand>().SignUpClientOrganizationAsync(organizationSignup)
.Returns((organization, null as OrganizationUser, new Collection())); .Returns(new ProviderClientOrganizationSignUpResponse(organization, new Collection()));
var providerOrganization = var providerOrganization =
await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user); await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user);
@ -755,8 +756,8 @@ public class ProviderServiceTests
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>(); var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
sutProvider.GetDependency<IOrganizationService>().SignupClientAsync(organizationSignup) sutProvider.GetDependency<IProviderClientOrganizationSignUpCommand>().SignUpClientOrganizationAsync(organizationSignup)
.Returns((organization, null as OrganizationUser, new Collection())); .Returns(new ProviderClientOrganizationSignUpResponse(organization, new Collection()));
await Assert.ThrowsAsync<BadRequestException>(() => await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user)); sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user));
@ -782,8 +783,8 @@ public class ProviderServiceTests
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>(); var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
sutProvider.GetDependency<IOrganizationService>().SignupClientAsync(organizationSignup) sutProvider.GetDependency<IProviderClientOrganizationSignUpCommand>().SignUpClientOrganizationAsync(organizationSignup)
.Returns((organization, null as OrganizationUser, new Collection())); .Returns(new ProviderClientOrganizationSignUpResponse(organization, new Collection()));
var providerOrganization = await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user); var providerOrganization = await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user);
@ -821,8 +822,8 @@ public class ProviderServiceTests
sutProvider.GetDependency<IProviderRepository>().GetByIdAsync(provider.Id).Returns(provider); sutProvider.GetDependency<IProviderRepository>().GetByIdAsync(provider.Id).Returns(provider);
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>(); var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
sutProvider.GetDependency<IOrganizationService>().SignupClientAsync(organizationSignup) sutProvider.GetDependency<IProviderClientOrganizationSignUpCommand>().SignUpClientOrganizationAsync(organizationSignup)
.Returns((organization, null as OrganizationUser, defaultCollection)); .Returns(new ProviderClientOrganizationSignUpResponse(organization, defaultCollection));
var providerOrganization = var providerOrganization =
await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user); await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user);

View File

@ -1,16 +1,16 @@
#nullable enable #nullable enable
using System.Text; using System.Text;
using Bit.Commercial.Core.Billing; using Bit.Commercial.Core.Billing.Providers.Services;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing; using Bit.Core.Billing;
using Bit.Core.Billing.Constants; using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
@ -25,7 +25,7 @@ using NSubstitute;
using Stripe; using Stripe;
using Xunit; using Xunit;
namespace Bit.Commercial.Core.Test.Billing; namespace Bit.Commercial.Core.Test.Billing.Providers;
public class BusinessUnitConverterTests public class BusinessUnitConverterTests
{ {

View File

@ -1,7 +1,7 @@
using System.Globalization; using System.Globalization;
using System.Net; using System.Net;
using Bit.Commercial.Core.Billing; using Bit.Commercial.Core.Billing.Providers.Models;
using Bit.Commercial.Core.Billing.Models; using Bit.Commercial.Core.Billing.Providers.Services;
using Bit.Core; using Bit.Core;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
@ -10,13 +10,13 @@ using Bit.Core.AdminConsole.Models.Data.Provider;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Caches; using Bit.Core.Billing.Caches;
using Bit.Core.Billing.Constants; using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models; using Bit.Core.Billing.Models;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Providers.Models;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
using Bit.Core.Billing.Services.Contracts;
using Bit.Core.Billing.Tax.Services; using Bit.Core.Billing.Tax.Services;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
@ -40,7 +40,7 @@ using Customer = Stripe.Customer;
using PaymentMethod = Stripe.PaymentMethod; using PaymentMethod = Stripe.PaymentMethod;
using Subscription = Stripe.Subscription; using Subscription = Stripe.Subscription;
namespace Bit.Commercial.Core.Test.Billing; namespace Bit.Commercial.Core.Test.Billing.Providers;
[SutProviderCustomize] [SutProviderCustomize]
public class ProviderBillingServiceTests public class ProviderBillingServiceTests

View File

@ -1,11 +1,11 @@
using Bit.Commercial.Core.Billing; using Bit.Commercial.Core.Billing.Providers.Services;
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Stripe; using Stripe;
using Xunit; using Xunit;
namespace Bit.Commercial.Core.Test.Billing; namespace Bit.Commercial.Core.Test.Billing.Providers;
public class ProviderPriceAdapterTests public class ProviderPriceAdapterTests
{ {

View File

@ -3,7 +3,7 @@ using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
using Xunit; using Xunit;
namespace Bit.Commercial.Core.Test.Billing; namespace Bit.Commercial.Core.Test.Billing.Tax;
[SutProviderCustomize] [SutProviderCustomize]
public class TaxServiceTests public class TaxServiceTests

View File

@ -11,7 +11,7 @@ using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Extensions; using Bit.Core.Billing.Extensions;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Services;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.OrganizationConnectionConfigs; using Bit.Core.Models.OrganizationConnectionConfigs;

View File

@ -10,13 +10,13 @@ using Bit.Core.AdminConsole.Providers.Interfaces;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Billing.Constants; using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Extensions; using Bit.Core.Billing.Extensions;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Models;
using Bit.Core.Billing.Services.Contracts; using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Providers.Services;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Repositories; using Bit.Core.Repositories;

View File

@ -2,8 +2,8 @@
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.AdminConsole.Models.Data.Provider;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.SharedWeb.Utilities; using Bit.SharedWeb.Utilities;

View File

@ -2,8 +2,8 @@
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.AdminConsole.Models.Data.Provider;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Providers.Entities;
namespace Bit.Admin.AdminConsole.Models; namespace Bit.Admin.AdminConsole.Models;

View File

@ -7,7 +7,7 @@ using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Services;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Utilities; using Bit.Core.Utilities;

View File

@ -1,8 +1,8 @@
using Bit.Admin.Billing.Models; using Bit.Admin.Billing.Models;
using Bit.Admin.Enums; using Bit.Admin.Enums;
using Bit.Admin.Utilities; using Bit.Admin.Utilities;
using Bit.Core.Billing.Migration.Models; using Bit.Core.Billing.Providers.Migration.Models;
using Bit.Core.Billing.Migration.Services; using Bit.Core.Billing.Providers.Migration.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;

View File

@ -1,4 +1,4 @@
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
namespace Bit.Admin.Billing.Models; namespace Bit.Admin.Billing.Models;

View File

@ -1,5 +1,5 @@
@using System.Text.Json @using System.Text.Json
@model Bit.Core.Billing.Migration.Models.ProviderMigrationResult @model Bit.Core.Billing.Providers.Migration.Models.ProviderMigrationResult
@{ @{
ViewData["Title"] = "Results"; ViewData["Title"] = "Results";
} }

View File

@ -1,4 +1,4 @@
@model Bit.Core.Billing.Migration.Models.ProviderMigrationResult[] @model Bit.Core.Billing.Providers.Migration.Models.ProviderMigrationResult[]
@{ @{
ViewData["Title"] = "Results"; ViewData["Title"] = "Results";
} }

View File

@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Bit.Admin.Services; using Bit.Admin.Services;
using Bit.Core.Billing.Extensions; using Bit.Core.Billing.Extensions;
using Bit.Core.Billing.Migration; using Bit.Core.Billing.Providers.Migration;
#if !OSS #if !OSS
using Bit.Commercial.Core.Utilities; using Bit.Commercial.Core.Utilities;

View File

@ -2,13 +2,11 @@
using Bit.Api.AdminConsole.Models.Request.Organizations; using Bit.Api.AdminConsole.Models.Request.Organizations;
using Bit.Api.AdminConsole.Models.Response.Organizations; using Bit.Api.AdminConsole.Models.Response.Organizations;
using Bit.Api.Models.Response; using Bit.Api.Models.Response;
using Bit.Core;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -137,7 +135,6 @@ public class OrganizationDomainController : Controller
[AllowAnonymous] [AllowAnonymous]
[HttpPost("domain/sso/verified")] [HttpPost("domain/sso/verified")]
[RequireFeature(FeatureFlagKeys.VerifiedSsoDomainEndpoint)]
public async Task<VerifiedOrganizationDomainSsoDetailsResponseModel> GetVerifiedOrgDomainSsoDetailsAsync( public async Task<VerifiedOrganizationDomainSsoDetailsResponseModel> GetVerifiedOrgDomainSsoDetailsAsync(
[FromBody] OrganizationDomainSsoDetailsRequestModel model) [FromBody] OrganizationDomainSsoDetailsRequestModel model)
{ {

View File

@ -25,7 +25,7 @@ using Bit.Core.Auth.Services;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Extensions; using Bit.Core.Billing.Extensions;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Services;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;

View File

@ -2,7 +2,7 @@
using Bit.Api.Billing.Models.Requests; using Bit.Api.Billing.Models.Requests;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Services;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Business; using Bit.Core.Models.Business;

View File

@ -8,6 +8,7 @@ using Bit.Core;
using Bit.Core.Billing.Models; using Bit.Core.Billing.Models;
using Bit.Core.Billing.Models.Sales; using Bit.Core.Billing.Models.Sales;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Providers.Services;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
using Bit.Core.Billing.Tax.Models; using Bit.Core.Billing.Tax.Models;
using Bit.Core.Context; using Bit.Core.Context;
@ -301,8 +302,12 @@ public class OrganizationBillingController(
Debug.Assert(org is not null, "This organization has already been found via this same ID, this should be fine."); Debug.Assert(org is not null, "This organization has already been found via this same ID, this should be fine.");
var paymentSource = new TokenizedPaymentSource(organizationSignup.PaymentMethodType.Value, organizationSignup.PaymentToken); var paymentSource = new TokenizedPaymentSource(organizationSignup.PaymentMethodType.Value, organizationSignup.PaymentToken);
var taxInformation = TaxInformation.From(organizationSignup.TaxInfo); var taxInformation = TaxInformation.From(organizationSignup.TaxInfo);
await organizationBillingService.UpdatePaymentMethod(org, paymentSource, taxInformation);
await organizationBillingService.Finalize(sale); await organizationBillingService.Finalize(sale);
var updatedOrg = await organizationRepository.GetByIdAsync(organizationId);
if (updatedOrg != null)
{
await organizationBillingService.UpdatePaymentMethod(updatedOrg, paymentSource, taxInformation);
}
return TypedResults.Ok(); return TypedResults.Ok();
} }

View File

@ -1,10 +1,12 @@
using Bit.Api.Billing.Models.Requests; using Bit.Api.Billing.Models.Requests;
using Bit.Api.Billing.Models.Responses; using Bit.Api.Billing.Models.Responses;
using Bit.Commercial.Core.Billing.Providers.Services;
using Bit.Core; using Bit.Core;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Models;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Models;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Providers.Services;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
using Bit.Core.Billing.Tax.Models; using Bit.Core.Billing.Tax.Models;
using Bit.Core.Context; using Bit.Core.Context;
@ -148,13 +150,33 @@ public class ProviderBillingController(
var providerPlans = await providerPlanRepository.GetByProviderId(provider.Id); var providerPlans = await providerPlanRepository.GetByProviderId(provider.Id);
var getProviderPriceFromStripe = featureService.IsEnabled(FeatureFlagKeys.PM21383_GetProviderPriceFromStripe);
var configuredProviderPlans = await Task.WhenAll(providerPlans.Select(async providerPlan => var configuredProviderPlans = await Task.WhenAll(providerPlans.Select(async providerPlan =>
{ {
var plan = await pricingClient.GetPlanOrThrow(providerPlan.PlanType); var plan = await pricingClient.GetPlanOrThrow(providerPlan.PlanType);
decimal unitAmount;
if (getProviderPriceFromStripe)
{
var priceId = ProviderPriceAdapter.GetPriceId(provider, subscription, plan.Type);
var price = await stripeAdapter.PriceGetAsync(priceId);
unitAmount = price.UnitAmountDecimal.HasValue
? price.UnitAmountDecimal.Value / 100M
: plan.PasswordManager.ProviderPortalSeatPrice;
}
else
{
unitAmount = plan.PasswordManager.ProviderPortalSeatPrice;
}
return new ConfiguredProviderPlan( return new ConfiguredProviderPlan(
providerPlan.Id, providerPlan.Id,
providerPlan.ProviderId, providerPlan.ProviderId,
plan, plan,
unitAmount,
providerPlan.SeatMinimum ?? 0, providerPlan.SeatMinimum ?? 0,
providerPlan.PurchasedSeats ?? 0, providerPlan.PurchasedSeats ?? 0,
providerPlan.AllocatedSeats ?? 0); providerPlan.AllocatedSeats ?? 0);

View File

@ -2,6 +2,7 @@
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models; using Bit.Core.Billing.Models;
using Bit.Core.Billing.Providers.Models;
using Bit.Core.Billing.Tax.Models; using Bit.Core.Billing.Tax.Models;
using Stripe; using Stripe;
@ -35,7 +36,7 @@ public record ProviderSubscriptionResponse(
.Select(providerPlan => .Select(providerPlan =>
{ {
var plan = providerPlan.Plan; var plan = providerPlan.Plan;
var cost = (providerPlan.SeatMinimum + providerPlan.PurchasedSeats) * plan.PasswordManager.ProviderPortalSeatPrice; var cost = (providerPlan.SeatMinimum + providerPlan.PurchasedSeats) * providerPlan.Price;
var cadence = plan.IsAnnual ? _annualCadence : _monthlyCadence; var cadence = plan.IsAnnual ? _annualCadence : _monthlyCadence;
return new ProviderPlanResponse( return new ProviderPlanResponse(
plan.Name, plan.Name,

View File

@ -1,8 +1,8 @@
using Bit.Billing.Constants; using Bit.Billing.Constants;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Stripe; using Stripe;

View File

@ -0,0 +1,187 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Billing.Pricing;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
using Bit.Core.Models.StaticStore;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
namespace Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
public record ProviderClientOrganizationSignUpResponse(
Organization Organization,
Collection DefaultCollection);
public interface IProviderClientOrganizationSignUpCommand
{
/// <summary>
/// Sign up a new client organization for a provider.
/// </summary>
/// <param name="signup">The signup information.</param>
/// <returns>A tuple containing the new organization and its default collection.</returns>
Task<ProviderClientOrganizationSignUpResponse> SignUpClientOrganizationAsync(OrganizationSignup signup);
}
public class ProviderClientOrganizationSignUpCommand : IProviderClientOrganizationSignUpCommand
{
public const string PlanNullErrorMessage = "Password Manager Plan was null.";
public const string PlanDisabledErrorMessage = "Password Manager Plan is disabled.";
public const string AdditionalSeatsNegativeErrorMessage = "You can't subtract Password Manager seats!";
private readonly ICurrentContext _currentContext;
private readonly IPricingClient _pricingClient;
private readonly IReferenceEventService _referenceEventService;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
private readonly IApplicationCacheService _applicationCacheService;
private readonly ICollectionRepository _collectionRepository;
public ProviderClientOrganizationSignUpCommand(
ICurrentContext currentContext,
IPricingClient pricingClient,
IReferenceEventService referenceEventService,
IOrganizationRepository organizationRepository,
IOrganizationApiKeyRepository organizationApiKeyRepository,
IApplicationCacheService applicationCacheService,
ICollectionRepository collectionRepository)
{
_currentContext = currentContext;
_pricingClient = pricingClient;
_referenceEventService = referenceEventService;
_organizationRepository = organizationRepository;
_organizationApiKeyRepository = organizationApiKeyRepository;
_applicationCacheService = applicationCacheService;
_collectionRepository = collectionRepository;
}
public async Task<ProviderClientOrganizationSignUpResponse> SignUpClientOrganizationAsync(OrganizationSignup signup)
{
var plan = await _pricingClient.GetPlanOrThrow(signup.Plan);
ValidatePlan(plan, signup.AdditionalSeats);
var organization = new Organization
{
// Pre-generate the org id so that we can save it with the Stripe subscription.
Id = CoreHelpers.GenerateComb(),
Name = signup.Name,
BillingEmail = signup.BillingEmail,
PlanType = plan!.Type,
Seats = signup.AdditionalSeats,
MaxCollections = plan.PasswordManager.MaxCollections,
MaxStorageGb = 1,
UsePolicies = plan.HasPolicies,
UseSso = plan.HasSso,
UseOrganizationDomains = plan.HasOrganizationDomains,
UseGroups = plan.HasGroups,
UseEvents = plan.HasEvents,
UseDirectory = plan.HasDirectory,
UseTotp = plan.HasTotp,
Use2fa = plan.Has2fa,
UseApi = plan.HasApi,
UseResetPassword = plan.HasResetPassword,
SelfHost = plan.HasSelfHost,
UsersGetPremium = plan.UsersGetPremium,
UseCustomPermissions = plan.HasCustomPermissions,
UseScim = plan.HasScim,
Plan = plan.Name,
Gateway = GatewayType.Stripe,
ReferenceData = signup.Owner.ReferenceData,
Enabled = true,
LicenseKey = CoreHelpers.SecureRandomString(20),
PublicKey = signup.PublicKey,
PrivateKey = signup.PrivateKey,
CreationDate = DateTime.UtcNow,
RevisionDate = DateTime.UtcNow,
Status = OrganizationStatusType.Created,
UsePasswordManager = true,
// Secrets Manager not available for purchase with Consolidated Billing.
UseSecretsManager = false,
};
var returnValue = await SignUpAsync(organization, signup.CollectionName);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.Signup, organization, _currentContext)
{
PlanName = plan.Name,
PlanType = plan.Type,
Seats = returnValue.Organization.Seats,
SignupInitiationPath = signup.InitiationPath,
Storage = returnValue.Organization.MaxStorageGb,
});
return returnValue;
}
private static void ValidatePlan(Plan plan, int additionalSeats)
{
if (plan is null)
{
throw new BadRequestException(PlanNullErrorMessage);
}
if (plan.Disabled)
{
throw new BadRequestException(PlanDisabledErrorMessage);
}
if (additionalSeats < 0)
{
throw new BadRequestException(AdditionalSeatsNegativeErrorMessage);
}
}
/// <summary>
/// Private helper method to create a new organization.
/// </summary>
private async Task<ProviderClientOrganizationSignUpResponse> SignUpAsync(
Organization organization, string collectionName)
{
try
{
await _organizationRepository.CreateAsync(organization);
await _organizationApiKeyRepository.CreateAsync(new OrganizationApiKey
{
OrganizationId = organization.Id,
ApiKey = CoreHelpers.SecureRandomString(30),
Type = OrganizationApiKeyType.Default,
RevisionDate = DateTime.UtcNow,
});
await _applicationCacheService.UpsertOrganizationAbilityAsync(organization);
Collection defaultCollection = null;
if (!string.IsNullOrWhiteSpace(collectionName))
{
defaultCollection = new Collection
{
Name = collectionName,
OrganizationId = organization.Id,
CreationDate = organization.CreationDate,
RevisionDate = organization.CreationDate
};
await _collectionRepository.CreateAsync(defaultCollection, null, null);
}
return new ProviderClientOrganizationSignUpResponse(organization, defaultCollection);
}
catch
{
if (organization.Id != default)
{
await _organizationRepository.DeleteAsync(organization);
await _applicationCacheService.DeleteOrganizationAbilityAsync(organization.Id);
}
throw;
}
}
}

View File

@ -18,9 +18,6 @@ public interface IOrganizationService
Task AutoAddSeatsAsync(Organization organization, int seatsToAdd); Task AutoAddSeatsAsync(Organization organization, int seatsToAdd);
Task<string> AdjustSeatsAsync(Guid organizationId, int seatAdjustment); Task<string> AdjustSeatsAsync(Guid organizationId, int seatAdjustment);
Task VerifyBankAsync(Guid organizationId, int amount1, int amount2); Task VerifyBankAsync(Guid organizationId, int amount1, int amount2);
#nullable enable
Task<(Organization organization, OrganizationUser organizationUser, Collection defaultCollection)> SignupClientAsync(OrganizationSignup signup);
#nullable disable
/// <summary> /// <summary>
/// Create a new organization on a self-hosted instance /// Create a new organization on a self-hosted instance
/// </summary> /// </summary>

View File

@ -403,66 +403,6 @@ public class OrganizationService : IOrganizationService
} }
} }
public async Task<(Organization organization, OrganizationUser organizationUser, Collection defaultCollection)> SignupClientAsync(OrganizationSignup signup)
{
var plan = await _pricingClient.GetPlanOrThrow(signup.Plan);
ValidatePlan(plan, signup.AdditionalSeats, "Password Manager");
var organization = new Organization
{
// Pre-generate the org id so that we can save it with the Stripe subscription.
Id = CoreHelpers.GenerateComb(),
Name = signup.Name,
BillingEmail = signup.BillingEmail,
PlanType = plan!.Type,
Seats = signup.AdditionalSeats,
MaxCollections = plan.PasswordManager.MaxCollections,
MaxStorageGb = 1,
UsePolicies = plan.HasPolicies,
UseSso = plan.HasSso,
UseOrganizationDomains = plan.HasOrganizationDomains,
UseGroups = plan.HasGroups,
UseEvents = plan.HasEvents,
UseDirectory = plan.HasDirectory,
UseTotp = plan.HasTotp,
Use2fa = plan.Has2fa,
UseApi = plan.HasApi,
UseResetPassword = plan.HasResetPassword,
SelfHost = plan.HasSelfHost,
UsersGetPremium = plan.UsersGetPremium,
UseCustomPermissions = plan.HasCustomPermissions,
UseScim = plan.HasScim,
Plan = plan.Name,
Gateway = GatewayType.Stripe,
ReferenceData = signup.Owner.ReferenceData,
Enabled = true,
LicenseKey = CoreHelpers.SecureRandomString(20),
PublicKey = signup.PublicKey,
PrivateKey = signup.PrivateKey,
CreationDate = DateTime.UtcNow,
RevisionDate = DateTime.UtcNow,
Status = OrganizationStatusType.Created,
UsePasswordManager = true,
// Secrets Manager not available for purchase with Consolidated Billing.
UseSecretsManager = false,
};
var returnValue = await SignUpAsync(organization, default, signup.OwnerKey, signup.CollectionName, false);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.Signup, organization, _currentContext)
{
PlanName = plan.Name,
PlanType = plan.Type,
Seats = returnValue.Item1.Seats,
SignupInitiationPath = signup.InitiationPath,
Storage = returnValue.Item1.MaxStorageGb,
});
return returnValue;
}
private async Task ValidateSignUpPoliciesAsync(Guid ownerId) private async Task ValidateSignUpPoliciesAsync(Guid ownerId)
{ {
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg); var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg);

View File

@ -1,12 +1,12 @@
using System.ComponentModel.DataAnnotations; #nullable enable
using System.ComponentModel.DataAnnotations;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Utilities; using Bit.Core.Utilities;
#nullable enable namespace Bit.Core.Billing.Providers.Entities;
namespace Bit.Core.Billing.Entities;
public class ClientOrganizationMigrationRecord : ITableObject<Guid> public class ClientOrganizationMigrationRecord : ITableObject<Guid>
{ {

View File

@ -1,10 +1,10 @@
using System.ComponentModel.DataAnnotations; #nullable enable
using System.ComponentModel.DataAnnotations;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Utilities; using Bit.Core.Utilities;
#nullable enable namespace Bit.Core.Billing.Providers.Entities;
namespace Bit.Core.Billing.Entities;
public class ProviderInvoiceItem : ITableObject<Guid> public class ProviderInvoiceItem : ITableObject<Guid>
{ {

View File

@ -1,10 +1,10 @@
using Bit.Core.Billing.Enums; #nullable enable
using Bit.Core.Billing.Enums;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Utilities; using Bit.Core.Utilities;
#nullable enable namespace Bit.Core.Billing.Providers.Entities;
namespace Bit.Core.Billing.Entities;
public class ProviderPlan : ITableObject<Guid> public class ProviderPlan : ITableObject<Guid>
{ {

View File

@ -1,4 +1,4 @@
namespace Bit.Core.Billing.Migration.Models; namespace Bit.Core.Billing.Providers.Migration.Models;
public enum ClientMigrationProgress public enum ClientMigrationProgress
{ {

View File

@ -1,6 +1,6 @@
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
namespace Bit.Core.Billing.Migration.Models; namespace Bit.Core.Billing.Providers.Migration.Models;
public class ProviderMigrationResult public class ProviderMigrationResult
{ {

View File

@ -1,4 +1,4 @@
namespace Bit.Core.Billing.Migration.Models; namespace Bit.Core.Billing.Providers.Migration.Models;
public enum ProviderMigrationProgress public enum ProviderMigrationProgress
{ {

View File

@ -1,8 +1,8 @@
using Bit.Core.Billing.Migration.Services; using Bit.Core.Billing.Providers.Migration.Services;
using Bit.Core.Billing.Migration.Services.Implementations; using Bit.Core.Billing.Providers.Migration.Services.Implementations;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.Billing.Migration; namespace Bit.Core.Billing.Providers.Migration;
public static class ServiceCollectionExtensions public static class ServiceCollectionExtensions
{ {

View File

@ -1,8 +1,8 @@
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.Billing.Migration.Models; using Bit.Core.Billing.Providers.Migration.Models;
namespace Bit.Core.Billing.Migration.Services; namespace Bit.Core.Billing.Providers.Migration.Services;
public interface IMigrationTrackerCache public interface IMigrationTrackerCache
{ {

View File

@ -1,6 +1,6 @@
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
namespace Bit.Core.Billing.Migration.Services; namespace Bit.Core.Billing.Providers.Migration.Services;
public interface IOrganizationMigrator public interface IOrganizationMigrator
{ {

View File

@ -1,6 +1,6 @@
using Bit.Core.Billing.Migration.Models; using Bit.Core.Billing.Providers.Migration.Models;
namespace Bit.Core.Billing.Migration.Services; namespace Bit.Core.Billing.Providers.Migration.Services;
public interface IProviderMigrator public interface IProviderMigrator
{ {

View File

@ -1,11 +1,11 @@
using System.Text.Json; using System.Text.Json;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.Billing.Migration.Models; using Bit.Core.Billing.Providers.Migration.Models;
using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.Billing.Migration.Services.Implementations; namespace Bit.Core.Billing.Providers.Migration.Services.Implementations;
public class MigrationTrackerDistributedCache( public class MigrationTrackerDistributedCache(
[FromKeyedServices("persistent")] [FromKeyedServices("persistent")]

View File

@ -1,10 +1,10 @@
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.Billing.Constants; using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Migration.Models;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Providers.Migration.Models;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging;
using Stripe; using Stripe;
using Plan = Bit.Core.Models.StaticStore.Plan; using Plan = Bit.Core.Models.StaticStore.Plan;
namespace Bit.Core.Billing.Migration.Services.Implementations; namespace Bit.Core.Billing.Providers.Migration.Services.Implementations;
public class OrganizationMigrator( public class OrganizationMigrator(
IClientOrganizationMigrationRecordRepository clientOrganizationMigrationRecordRepository, IClientOrganizationMigrationRecordRepository clientOrganizationMigrationRecordRepository,

View File

@ -3,18 +3,18 @@ using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Constants; using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Migration.Models; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Migration.Models;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Models;
using Bit.Core.Billing.Services.Contracts; using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Providers.Services;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Stripe; using Stripe;
namespace Bit.Core.Billing.Migration.Services.Implementations; namespace Bit.Core.Billing.Providers.Migration.Services.Implementations;
public class ProviderMigrator( public class ProviderMigrator(
IClientOrganizationMigrationRecordRepository clientOrganizationMigrationRecordRepository, IClientOrganizationMigrationRecordRepository clientOrganizationMigrationRecordRepository,

View File

@ -1,4 +1,4 @@
namespace Bit.Core.Billing.Models; namespace Bit.Core.Billing.Providers.Models;
public record AddableOrganization( public record AddableOrganization(
Guid Id, Guid Id,

View File

@ -1,7 +1,7 @@
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
namespace Bit.Core.Billing.Services.Contracts; namespace Bit.Core.Billing.Providers.Models;
public record ChangeProviderPlanCommand( public record ChangeProviderPlanCommand(
Provider Provider, Provider Provider,

View File

@ -1,11 +1,12 @@
using Bit.Core.Models.StaticStore; using Bit.Core.Models.StaticStore;
namespace Bit.Core.Billing.Models; namespace Bit.Core.Billing.Providers.Models;
public record ConfiguredProviderPlan( public record ConfiguredProviderPlan(
Guid Id, Guid Id,
Guid ProviderId, Guid ProviderId,
Plan Plan, Plan Plan,
decimal Price,
int SeatMinimum, int SeatMinimum,
int PurchasedSeats, int PurchasedSeats,
int AssignedSeats); int AssignedSeats);

View File

@ -1,7 +1,7 @@
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
namespace Bit.Core.Billing.Services.Contracts; namespace Bit.Core.Billing.Providers.Models;
/// <param name="Provider">The provider to update the seat minimums for.</param> /// <param name="Provider">The provider to update the seat minimums for.</param>
/// <param name="Configuration">The new seat minimums for the provider.</param> /// <param name="Configuration">The new seat minimums for the provider.</param>

View File

@ -1,7 +1,7 @@
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Repositories; using Bit.Core.Repositories;
namespace Bit.Core.Billing.Repositories; namespace Bit.Core.Billing.Providers.Repositories;
public interface IClientOrganizationMigrationRecordRepository : IRepository<ClientOrganizationMigrationRecord, Guid> public interface IClientOrganizationMigrationRecordRepository : IRepository<ClientOrganizationMigrationRecord, Guid>
{ {

View File

@ -1,7 +1,7 @@
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Repositories; using Bit.Core.Repositories;
namespace Bit.Core.Billing.Repositories; namespace Bit.Core.Billing.Providers.Repositories;
public interface IProviderInvoiceItemRepository : IRepository<ProviderInvoiceItem, Guid> public interface IProviderInvoiceItemRepository : IRepository<ProviderInvoiceItem, Guid>
{ {

View File

@ -1,7 +1,7 @@
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Repositories; using Bit.Core.Repositories;
namespace Bit.Core.Billing.Repositories; namespace Bit.Core.Billing.Providers.Repositories;
public interface IProviderPlanRepository : IRepository<ProviderPlan, Guid> public interface IProviderPlanRepository : IRepository<ProviderPlan, Guid>
{ {

View File

@ -3,7 +3,7 @@ using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using OneOf; using OneOf;
namespace Bit.Core.Billing.Services; namespace Bit.Core.Billing.Providers.Services;
public interface IBusinessUnitConverter public interface IBusinessUnitConverter
{ {

View File

@ -1,14 +1,14 @@
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models; using Bit.Core.Billing.Models;
using Bit.Core.Billing.Services.Contracts; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Providers.Models;
using Bit.Core.Billing.Tax.Models; using Bit.Core.Billing.Tax.Models;
using Bit.Core.Models.Business; using Bit.Core.Models.Business;
using Stripe; using Stripe;
namespace Bit.Core.Billing.Services; namespace Bit.Core.Billing.Providers.Services;
public interface IProviderBillingService public interface IProviderBillingService
{ {

View File

@ -2,7 +2,7 @@
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Entities; using Bit.Core.Entities;
namespace Bit.Core.Billing.Services.Contracts; namespace Bit.Core.Billing.Tax.Models;
public class AutomaticTaxFactoryParameters public class AutomaticTaxFactoryParameters
{ {

View File

@ -1,4 +1,4 @@
using Bit.Core.Billing.Services.Contracts; using Bit.Core.Billing.Tax.Models;
namespace Bit.Core.Billing.Tax.Services; namespace Bit.Core.Billing.Tax.Services;

View File

@ -1,7 +1,7 @@
#nullable enable #nullable enable
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services.Contracts; using Bit.Core.Billing.Tax.Models;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Services; using Bit.Core.Services;

View File

@ -150,6 +150,7 @@ public static class FeatureFlagKeys
public const string UseOrganizationWarningsService = "use-organization-warnings-service"; public const string UseOrganizationWarningsService = "use-organization-warnings-service";
public const string PM20322_AllowTrialLength0 = "pm-20322-allow-trial-length-0"; public const string PM20322_AllowTrialLength0 = "pm-20322-allow-trial-length-0";
public const string PM21092_SetNonUSBusinessUseToReverseCharge = "pm-21092-set-non-us-business-use-to-reverse-charge"; public const string PM21092_SetNonUSBusinessUseToReverseCharge = "pm-21092-set-non-us-business-use-to-reverse-charge";
public const string PM21383_GetProviderPriceFromStripe = "pm-21383-get-provider-price-from-stripe";
/* Data Insights and Reporting Team */ /* Data Insights and Reporting Team */
public const string RiskInsightsCriticalApplication = "pm-14466-risk-insights-critical-application"; public const string RiskInsightsCriticalApplication = "pm-14466-risk-insights-critical-application";

View File

@ -69,8 +69,11 @@ public static class OrganizationServiceCollectionExtensions
services.AddBaseOrganizationSubscriptionCommandsQueries(); services.AddBaseOrganizationSubscriptionCommandsQueries();
} }
private static IServiceCollection AddOrganizationSignUpCommands(this IServiceCollection services) => private static void AddOrganizationSignUpCommands(this IServiceCollection services)
{
services.AddScoped<ICloudOrganizationSignUpCommand, CloudOrganizationSignUpCommand>(); services.AddScoped<ICloudOrganizationSignUpCommand, CloudOrganizationSignUpCommand>();
services.AddScoped<IProviderClientOrganizationSignUpCommand, ProviderClientOrganizationSignUpCommand>();
}
private static void AddOrganizationDeleteCommands(this IServiceCollection services) private static void AddOrganizationDeleteCommands(this IServiceCollection services)
{ {

View File

@ -57,4 +57,5 @@ public interface IStripeAdapter
Task<SetupIntent> SetupIntentGet(string id, SetupIntentGetOptions options = null); Task<SetupIntent> SetupIntentGet(string id, SetupIntentGetOptions options = null);
Task SetupIntentVerifyMicroDeposit(string id, SetupIntentVerifyMicrodepositsOptions options); Task SetupIntentVerifyMicroDeposit(string id, SetupIntentVerifyMicrodepositsOptions options);
Task<List<Stripe.TestHelpers.TestClock>> TestClockListAsync(); Task<List<Stripe.TestHelpers.TestClock>> TestClockListAsync();
Task<Price> PriceGetAsync(string id, PriceGetOptions options = null);
} }

View File

@ -283,4 +283,7 @@ public class StripeAdapter : IStripeAdapter
} }
return items; return items;
} }
public Task<Price> PriceGetAsync(string id, PriceGetOptions options = null)
=> _priceService.GetAsync(id, options);
} }

View File

@ -7,7 +7,7 @@ using Bit.Core.Billing.Extensions;
using Bit.Core.Billing.Models; using Bit.Core.Billing.Models;
using Bit.Core.Billing.Models.Business; using Bit.Core.Billing.Models.Business;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services.Contracts; using Bit.Core.Billing.Tax.Models;
using Bit.Core.Billing.Tax.Requests; using Bit.Core.Billing.Tax.Requests;
using Bit.Core.Billing.Tax.Responses; using Bit.Core.Billing.Tax.Responses;
using Bit.Core.Billing.Tax.Services; using Bit.Core.Billing.Tax.Services;

View File

@ -1,6 +1,6 @@
using System.Data; using System.Data;
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Settings; using Bit.Core.Settings;
using Bit.Infrastructure.Dapper.Repositories; using Bit.Infrastructure.Dapper.Repositories;
using Dapper; using Dapper;

View File

@ -1,6 +1,6 @@
using System.Data; using System.Data;
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Settings; using Bit.Core.Settings;
using Bit.Infrastructure.Dapper.Repositories; using Bit.Infrastructure.Dapper.Repositories;
using Dapper; using Dapper;

View File

@ -1,6 +1,6 @@
using System.Data; using System.Data;
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Settings; using Bit.Core.Settings;
using Bit.Infrastructure.Dapper.Repositories; using Bit.Infrastructure.Dapper.Repositories;
using Dapper; using Dapper;

View File

@ -1,5 +1,6 @@
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.Repositories; using Bit.Core.Auth.Repositories;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Repositories;
using Bit.Core.KeyManagement.Repositories; using Bit.Core.KeyManagement.Repositories;
using Bit.Core.NotificationCenter.Repositories; using Bit.Core.NotificationCenter.Repositories;

View File

@ -2,7 +2,7 @@
namespace Bit.Infrastructure.EntityFramework.Billing.Models; namespace Bit.Infrastructure.EntityFramework.Billing.Models;
public class ClientOrganizationMigrationRecord : Core.Billing.Entities.ClientOrganizationMigrationRecord public class ClientOrganizationMigrationRecord : Core.Billing.Providers.Entities.ClientOrganizationMigrationRecord
{ {
} }
@ -11,6 +11,6 @@ public class ClientOrganizationMigrationRecordProfile : Profile
{ {
public ClientOrganizationMigrationRecordProfile() public ClientOrganizationMigrationRecordProfile()
{ {
CreateMap<Core.Billing.Entities.ClientOrganizationMigrationRecord, ClientOrganizationMigrationRecord>().ReverseMap(); CreateMap<Core.Billing.Providers.Entities.ClientOrganizationMigrationRecord, ClientOrganizationMigrationRecord>().ReverseMap();
} }
} }

View File

@ -4,7 +4,7 @@ using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider;
namespace Bit.Infrastructure.EntityFramework.Billing.Models; namespace Bit.Infrastructure.EntityFramework.Billing.Models;
// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global // ReSharper disable once ClassWithVirtualMembersNeverInherited.Global
public class ProviderInvoiceItem : Core.Billing.Entities.ProviderInvoiceItem public class ProviderInvoiceItem : Core.Billing.Providers.Entities.ProviderInvoiceItem
{ {
public virtual Provider Provider { get; set; } public virtual Provider Provider { get; set; }
} }
@ -13,6 +13,6 @@ public class ProviderInvoiceItemMapperProfile : Profile
{ {
public ProviderInvoiceItemMapperProfile() public ProviderInvoiceItemMapperProfile()
{ {
CreateMap<Core.Billing.Entities.ProviderInvoiceItem, ProviderInvoiceItem>().ReverseMap(); CreateMap<Core.Billing.Providers.Entities.ProviderInvoiceItem, ProviderInvoiceItem>().ReverseMap();
} }
} }

View File

@ -4,7 +4,7 @@ using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider;
namespace Bit.Infrastructure.EntityFramework.Billing.Models; namespace Bit.Infrastructure.EntityFramework.Billing.Models;
// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global // ReSharper disable once ClassWithVirtualMembersNeverInherited.Global
public class ProviderPlan : Core.Billing.Entities.ProviderPlan public class ProviderPlan : Core.Billing.Providers.Entities.ProviderPlan
{ {
public virtual Provider Provider { get; set; } public virtual Provider Provider { get; set; }
} }
@ -13,6 +13,6 @@ public class ProviderPlanMapperProfile : Profile
{ {
public ProviderPlanMapperProfile() public ProviderPlanMapperProfile()
{ {
CreateMap<Core.Billing.Entities.ProviderPlan, ProviderPlan>().ReverseMap(); CreateMap<Core.Billing.Providers.Entities.ProviderPlan, ProviderPlan>().ReverseMap();
} }
} }

View File

@ -1,6 +1,6 @@
using AutoMapper; using AutoMapper;
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Repositories;
using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Infrastructure.EntityFramework.Repositories;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

View File

@ -1,6 +1,6 @@
using AutoMapper; using AutoMapper;
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Repositories;
using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Infrastructure.EntityFramework.Repositories;
using LinqToDB; using LinqToDB;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

View File

@ -1,6 +1,6 @@
using AutoMapper; using AutoMapper;
using Bit.Core.Billing.Entities; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Repositories;
using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Infrastructure.EntityFramework.Repositories;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

View File

@ -1,5 +1,6 @@
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.Repositories; using Bit.Core.Auth.Repositories;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Repositories;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.KeyManagement.Repositories; using Bit.Core.KeyManagement.Repositories;

View File

@ -5,7 +5,7 @@ using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Services;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;

View File

@ -21,7 +21,7 @@ using Bit.Core.Auth.Repositories;
using Bit.Core.Auth.Services; using Bit.Core.Auth.Services;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Services;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;

View File

@ -6,7 +6,7 @@ using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Providers.Services;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;

View File

@ -1,14 +1,17 @@
using Bit.Api.Billing.Controllers; using Bit.Api.Billing.Controllers;
using Bit.Api.Billing.Models.Requests; using Bit.Api.Billing.Models.Requests;
using Bit.Api.Billing.Models.Responses; using Bit.Api.Billing.Models.Responses;
using Bit.Commercial.Core.Billing.Providers.Services;
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.Enums.Provider;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Constants; using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Billing.Providers.Services;
using Bit.Core.Billing.Services; using Bit.Core.Billing.Services;
using Bit.Core.Billing.Tax.Models; using Bit.Core.Billing.Tax.Models;
using Bit.Core.Context; using Bit.Core.Context;
@ -285,6 +288,19 @@ public class ProviderBillingControllerTests
Discount = new Discount { Coupon = new Coupon { PercentOff = 10 } }, Discount = new Discount { Coupon = new Coupon { PercentOff = 10 } },
TaxIds = new StripeList<TaxId> { Data = [new TaxId { Value = "123456789" }] } TaxIds = new StripeList<TaxId> { Data = [new TaxId { Value = "123456789" }] }
}, },
Items = new StripeList<SubscriptionItem>
{
Data = [
new SubscriptionItem
{
Price = new Price { Id = ProviderPriceAdapter.MSP.Active.Enterprise }
},
new SubscriptionItem
{
Price = new Price { Id = ProviderPriceAdapter.MSP.Active.Teams }
}
]
},
Status = "unpaid", Status = "unpaid",
}; };
@ -330,11 +346,21 @@ public class ProviderBillingControllerTests
} }
}; };
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.PM21383_GetProviderPriceFromStripe)
.Returns(true);
sutProvider.GetDependency<IProviderPlanRepository>().GetByProviderId(provider.Id).Returns(providerPlans); sutProvider.GetDependency<IProviderPlanRepository>().GetByProviderId(provider.Id).Returns(providerPlans);
foreach (var providerPlan in providerPlans) foreach (var providerPlan in providerPlans)
{ {
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(providerPlan.PlanType).Returns(StaticStore.GetPlan(providerPlan.PlanType)); var plan = StaticStore.GetPlan(providerPlan.PlanType);
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(providerPlan.PlanType).Returns(plan);
var priceId = ProviderPriceAdapter.GetPriceId(provider, subscription, providerPlan.PlanType);
sutProvider.GetDependency<IStripeAdapter>().PriceGetAsync(priceId)
.Returns(new Price
{
UnitAmountDecimal = plan.PasswordManager.ProviderPortalSeatPrice * 100
});
} }
var result = await sutProvider.Sut.GetSubscriptionAsync(provider.Id); var result = await sutProvider.Sut.GetSubscriptionAsync(provider.Id);

View File

@ -4,10 +4,10 @@ using Bit.Billing.Test.Utilities;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.AdminConsole.Models.Data.Provider;
using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Repositories; using Bit.Core.Billing.Providers.Entities;
using Bit.Core.Billing.Providers.Repositories;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Utilities; using Bit.Core.Utilities;

View File

@ -0,0 +1,169 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Pricing;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
using Bit.Core.Models.StaticStore;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Organizations.OrganizationSignUp;
[SutProviderCustomize]
public class ProviderClientOrganizationSignUpCommandTests
{
[Theory]
[BitAutoData(PlanType.TeamsAnnually)]
[BitAutoData(PlanType.TeamsMonthly)]
[BitAutoData(PlanType.EnterpriseAnnually)]
[BitAutoData(PlanType.EnterpriseMonthly)]
public async Task SignupClientAsync_ValidParameters_CreatesOrganizationSuccessfully(
PlanType planType,
OrganizationSignup signup,
string collectionName,
SutProvider<ProviderClientOrganizationSignUpCommand> sutProvider)
{
signup.Plan = planType;
signup.AdditionalSeats = 15;
signup.CollectionName = collectionName;
var plan = StaticStore.GetPlan(signup.Plan);
sutProvider.GetDependency<IPricingClient>()
.GetPlanOrThrow(signup.Plan)
.Returns(plan);
var result = await sutProvider.Sut.SignUpClientOrganizationAsync(signup);
Assert.NotNull(result.Organization);
Assert.NotNull(result.DefaultCollection);
Assert.Equal(collectionName, result.DefaultCollection.Name);
await sutProvider.GetDependency<IOrganizationRepository>()
.Received(1)
.CreateAsync(
Arg.Is<Organization>(o =>
o.Name == signup.Name &&
o.BillingEmail == signup.BillingEmail &&
o.PlanType == plan.Type &&
o.Seats == signup.AdditionalSeats &&
o.MaxCollections == plan.PasswordManager.MaxCollections &&
o.UsePasswordManager == true &&
o.UseSecretsManager == false &&
o.Status == OrganizationStatusType.Created
)
);
await sutProvider.GetDependency<IReferenceEventService>()
.Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
referenceEvent.Type == ReferenceEventType.Signup &&
referenceEvent.PlanName == plan.Name &&
referenceEvent.PlanType == plan.Type &&
referenceEvent.Seats == result.Organization.Seats &&
referenceEvent.Storage == result.Organization.MaxStorageGb &&
referenceEvent.SignupInitiationPath == signup.InitiationPath
));
await sutProvider.GetDependency<ICollectionRepository>()
.Received(1)
.CreateAsync(
Arg.Is<Collection>(c =>
c.Name == collectionName &&
c.OrganizationId == result.Organization.Id
),
Arg.Any<IEnumerable<CollectionAccessSelection>>(),
Arg.Any<IEnumerable<CollectionAccessSelection>>()
);
await sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.Received(1)
.CreateAsync(
Arg.Is<OrganizationApiKey>(k =>
k.OrganizationId == result.Organization.Id &&
k.Type == OrganizationApiKeyType.Default
)
);
await sutProvider.GetDependency<IApplicationCacheService>()
.Received(1)
.UpsertOrganizationAbilityAsync(Arg.Is<Organization>(o => o.Id == result.Organization.Id));
}
[Theory]
[BitAutoData]
public async Task SignupClientAsync_NullPlan_ThrowsBadRequestException(
OrganizationSignup signup,
SutProvider<ProviderClientOrganizationSignUpCommand> sutProvider)
{
sutProvider.GetDependency<IPricingClient>()
.GetPlanOrThrow(signup.Plan)
.Returns((Plan)null);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SignUpClientOrganizationAsync(signup));
Assert.Contains(ProviderClientOrganizationSignUpCommand.PlanNullErrorMessage, exception.Message);
}
[Theory]
[BitAutoData]
public async Task SignupClientAsync_NegativeAdditionalSeats_ThrowsBadRequestException(
OrganizationSignup signup,
SutProvider<ProviderClientOrganizationSignUpCommand> sutProvider)
{
signup.Plan = PlanType.TeamsMonthly;
signup.AdditionalSeats = -5;
var plan = StaticStore.GetPlan(signup.Plan);
sutProvider.GetDependency<IPricingClient>()
.GetPlanOrThrow(signup.Plan)
.Returns(plan);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SignUpClientOrganizationAsync(signup));
Assert.Contains(ProviderClientOrganizationSignUpCommand.AdditionalSeatsNegativeErrorMessage, exception.Message);
}
[Theory]
[BitAutoData(PlanType.TeamsAnnually)]
public async Task SignupClientAsync_WhenExceptionIsThrown_CleanupIsPerformed(
PlanType planType,
OrganizationSignup signup,
SutProvider<ProviderClientOrganizationSignUpCommand> sutProvider)
{
signup.Plan = planType;
var plan = StaticStore.GetPlan(signup.Plan);
sutProvider.GetDependency<IPricingClient>()
.GetPlanOrThrow(signup.Plan)
.Returns(plan);
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.When(x => x.CreateAsync(Arg.Any<OrganizationApiKey>()))
.Do(_ => throw new Exception());
var thrownException = await Assert.ThrowsAsync<Exception>(
() => sutProvider.Sut.SignUpClientOrganizationAsync(signup));
await sutProvider.GetDependency<IOrganizationRepository>()
.Received(1)
.DeleteAsync(Arg.Is<Organization>(o => o.Name == signup.Name));
await sutProvider.GetDependency<IApplicationCacheService>()
.Received(1)
.DeleteOrganizationAbilityAsync(Arg.Any<Guid>());
}
}

View File

@ -9,7 +9,6 @@ using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Business; using Bit.Core.Models.Business;
@ -177,47 +176,6 @@ public class OrganizationServiceTests
referenceEvent.Users == expectedNewUsersCount)); referenceEvent.Users == expectedNewUsersCount));
} }
[Theory, BitAutoData]
public async Task SignupClientAsync_Succeeds(
OrganizationSignup signup,
SutProvider<OrganizationService> sutProvider)
{
signup.Plan = PlanType.TeamsMonthly;
var plan = StaticStore.GetPlan(signup.Plan);
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(signup.Plan).Returns(plan);
var (organization, _, _) = await sutProvider.Sut.SignupClientAsync(signup);
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).CreateAsync(Arg.Is<Organization>(org =>
org.Id == organization.Id &&
org.Name == signup.Name &&
org.Plan == plan.Name &&
org.PlanType == plan.Type &&
org.UsePolicies == plan.HasPolicies &&
org.PublicKey == signup.PublicKey &&
org.PrivateKey == signup.PrivateKey &&
org.UseSecretsManager == false));
await sutProvider.GetDependency<IOrganizationApiKeyRepository>().Received(1)
.CreateAsync(Arg.Is<OrganizationApiKey>(orgApiKey =>
orgApiKey.OrganizationId == organization.Id));
await sutProvider.GetDependency<IApplicationCacheService>().Received(1)
.UpsertOrganizationAbilityAsync(organization);
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
await sutProvider.GetDependency<ICollectionRepository>().Received(1)
.CreateAsync(Arg.Is<Collection>(c => c.Name == signup.CollectionName && c.OrganizationId == organization.Id), null, null);
await sutProvider.GetDependency<IReferenceEventService>().Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(
re =>
re.Type == ReferenceEventType.Signup &&
re.PlanType == plan.Type));
}
[Theory] [Theory]
[OrganizationInviteCustomize(InviteeUserType = OrganizationUserType.User, [OrganizationInviteCustomize(InviteeUserType = OrganizationUserType.User,
InvitorUserType = OrganizationUserType.Owner), OrganizationCustomize, BitAutoData] InvitorUserType = OrganizationUserType.Owner), OrganizationCustomize, BitAutoData]

View File

@ -2,7 +2,7 @@
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models.StaticStore.Plans; using Bit.Core.Billing.Models.StaticStore.Plans;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services.Contracts; using Bit.Core.Billing.Tax.Models;
using Bit.Core.Billing.Tax.Services.Implementations; using Bit.Core.Billing.Tax.Services.Implementations;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;

View File

@ -1,7 +1,7 @@
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models.StaticStore.Plans; using Bit.Core.Billing.Models.StaticStore.Plans;
using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services.Contracts; using Bit.Core.Billing.Tax.Models;
using Bit.Core.Billing.Tax.Requests; using Bit.Core.Billing.Tax.Requests;
using Bit.Core.Billing.Tax.Services; using Bit.Core.Billing.Tax.Services;
using Bit.Core.Enums; using Bit.Core.Enums;