From fd51797420ef246386e9f3cfacf1de193350c7e5 Mon Sep 17 00:00:00 2001 From: Shane Melton Date: Wed, 28 Jun 2023 17:40:29 -0700 Subject: [PATCH 01/41] [AC-1423] Add AddonProduct and BitwardenProduct properties to BillingSubscriptionItem (#3037) * [AC-1423] Add AddonProduct and BitwardenProduct properties to BillingSubscriptionItem - Add a helper method to determine the appropriate addon type based on the subscription items StripeId * [AC-1423] Add helper to StaticStore.cs to find a Plan by StripePlanId * [AC-1423] Use the helper method to set SubscriptionInfo.BitwardenProduct --- .../Response/SubscriptionResponseModel.cs | 5 ++ src/Core/Models/Business/SubscriptionInfo.cs | 11 ++++- src/Core/Utilities/StaticStore.cs | 49 +++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/Api/Models/Response/SubscriptionResponseModel.cs b/src/Api/Models/Response/SubscriptionResponseModel.cs index 3ec1b975e3b7..4c0ee9338b34 100644 --- a/src/Api/Models/Response/SubscriptionResponseModel.cs +++ b/src/Api/Models/Response/SubscriptionResponseModel.cs @@ -1,4 +1,5 @@ using Bit.Core.Entities; +using Bit.Core.Enums; using Bit.Core.Models.Api; using Bit.Core.Models.Business; using Bit.Core.Utilities; @@ -82,6 +83,8 @@ public BillingSubscriptionItem(SubscriptionInfo.BillingSubscription.BillingSubsc Interval = item.Interval; Quantity = item.Quantity; SponsoredSubscriptionItem = item.SponsoredSubscriptionItem; + AddonSubscriptionItem = item.AddonSubscriptionItem; + BitwardenProduct = item.BitwardenProduct; } public string Name { get; set; } @@ -89,6 +92,8 @@ public BillingSubscriptionItem(SubscriptionInfo.BillingSubscription.BillingSubsc public int Quantity { get; set; } public string Interval { get; set; } public bool SponsoredSubscriptionItem { get; set; } + public bool AddonSubscriptionItem { get; set; } + public BitwardenProductType BitwardenProduct { get; set; } } } diff --git a/src/Core/Models/Business/SubscriptionInfo.cs b/src/Core/Models/Business/SubscriptionInfo.cs index 61aa060cd443..c72e291de0d6 100644 --- a/src/Core/Models/Business/SubscriptionInfo.cs +++ b/src/Core/Models/Business/SubscriptionInfo.cs @@ -1,4 +1,5 @@ -using Stripe; +using Bit.Core.Enums; +using Stripe; namespace Bit.Core.Models.Business; @@ -46,12 +47,20 @@ public BillingSubscriptionItem(SubscriptionItem item) Name = item.Plan.Nickname; Amount = item.Plan.Amount.GetValueOrDefault() / 100M; Interval = item.Plan.Interval; + AddonSubscriptionItem = + Utilities.StaticStore.IsAddonSubscriptionItem(item.Plan.Id); + BitwardenProduct = + Utilities.StaticStore.GetPlanByStripeId(item.Plan.Id)?.BitwardenProduct ?? BitwardenProductType.PasswordManager; } Quantity = (int)item.Quantity; SponsoredSubscriptionItem = Utilities.StaticStore.SponsoredPlans.Any(p => p.StripePlanId == item.Plan.Id); } + public BitwardenProductType BitwardenProduct { get; set; } + + public bool AddonSubscriptionItem { get; set; } + public string Name { get; set; } public decimal Amount { get; set; } public int Quantity { get; set; } diff --git a/src/Core/Utilities/StaticStore.cs b/src/Core/Utilities/StaticStore.cs index c5183296a9e6..ad11b2cfd092 100644 --- a/src/Core/Utilities/StaticStore.cs +++ b/src/Core/Utilities/StaticStore.cs @@ -139,4 +139,53 @@ public static Plan GetSecretsManagerPlan(PlanType planType) => public static SponsoredPlan GetSponsoredPlan(PlanSponsorshipType planSponsorshipType) => SponsoredPlans.FirstOrDefault(p => p.PlanSponsorshipType == planSponsorshipType); + + /// + /// Determines if the stripe plan id is an addon item by checking if the provided stripe plan id + /// matches either the or + /// in any . + /// + /// + /// + /// True if the stripePlanId is a addon product, false otherwise + /// + public static bool IsAddonSubscriptionItem(string stripePlanId) + { + if (PasswordManagerPlans.Select(p => p.StripeStoragePlanId).Contains(stripePlanId)) + { + return true; + } + + if (SecretManagerPlans.Select(p => p.StripeServiceAccountPlanId).Contains(stripePlanId)) + { + return true; + } + + return false; + } + + /// + /// Get a by comparing the provided stripeId to the various + /// Stripe plan ids within a . + /// The following properties are checked: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// The plan if a matching stripeId was found, null otherwise + public static Plan GetPlanByStripeId(string stripeId) + { + return Plans.FirstOrDefault(p => + p.StripePlanId == stripeId || + p.StripeSeatPlanId == stripeId || + p.StripeStoragePlanId == stripeId || + p.StripeServiceAccountPlanId == stripeId || + p.StripePremiumAccessPlanId == stripeId + ); + } } From 12e66968e7781935a7e6ee327f583e8307cb12a1 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Thu, 29 Jun 2023 11:05:05 +1000 Subject: [PATCH 02/41] Add SecretsManagerBilling feature flag to Constants --- src/Core/Constants.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 8ff378d00cb7..5cd0349291b4 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -36,6 +36,7 @@ public static class FeatureFlagKeys public const string DisplayEuEnvironment = "display-eu-environment"; public const string DisplayLowKdfIterationWarning = "display-kdf-iteration-warning"; public const string TrustedDeviceEncryption = "trusted-device-encryption"; + public const string SecretsManagerBilling = "sm-ga-billing"; public static List GetAllKeys() { From 46b22605d1b8fff04cfdc13b7a198e257e35ae11 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Sat, 1 Jul 2023 12:25:15 +0100 Subject: [PATCH 03/41] [AC 1409] Secrets Manager Subscription Stripe Integration (#3019) * Adding the Secret manager to the Plan List * Adding the unit test for the StaticStoreTests class * Fix whitespace formatting * Fix whitespace formatting * Price update * Resolving the PR comments * Resolving PR comments * Fixing the whitespace * only password manager plans are return for now * format whitespace * Resolve the test issue * Fixing the failing test * Refactoring the Plan separation * add a unit test for SingleOrDefault * Fix the whitespace format * Separate the PM and SM plans * Fixing the whitespace * Remove unnecessary directive * Fix imports ordering * Fix imports ordering * Resolve imports ordering * Fixing imports ordering * Fix response model, add MaxProjects * Fix filename * Fix format * Fix: seat price should match annual/monthly * Fix service account annual pricing * Changes for secret manager signup and upgradeplan * Changes for secrets manager signup and upgrade * refactoring the code * Format whitespace * remove unnecessary using directive * Resolve the PR comment on Subscription creation * Resolve PR comment * Add password manager to the error message * Add UseSecretsManager to the event log * Resolve PR comment on plan validation * Resolving pr comments for service account count * Resolving pr comments for service account count * Resolve the pr comments * Remove the store procedure that is no-longer needed * Rename a property properly * Resolving the PR comment * Resolve PR comments * Resolving PR comments * Resolving the Pr comments * Resolving some PR comments * Resolving the PR comments * Resolving the build identity build * Add additional Validation * Resolve the Lint issues * remove unnecessary using directive * Remove the white spaces * Adding unit test for the stripe payment * Remove the incomplete test * Fixing the failing test * Fix the failing test * Fix the fail test on organization service * Fix the failing unit test * Fix the whitespace format * Fix the failing test * Fix the whitespace format * resolve pr comments * Fix the lint message * Resolve the PR comments * resolve pr comments * Resolve pr comments * Resolve the pr comments * remove unused code * Added for sm validation test * Fix the whitespace format issues --------- Co-authored-by: Thomas Rittson Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> --- .../Repositories/ServiceAccountRepository.cs | 10 + bitwarden_license/src/Scim/Startup.cs | 2 + .../OrganizationCreateRequestModel.cs | 9 + .../OrganizationUpgradeRequestModel.cs | 9 + .../Models/Business/OrganizationUpgrade.cs | 3 + .../Business/SubscriptionCreateOptions.cs | 84 +++-- .../IOrganizationUserRepository.cs | 1 + .../Noop/NoopServiceAccountRepository.cs | 60 +++ .../Repositories/IServiceAccountRepository.cs | 1 + src/Core/Services/IPaymentService.cs | 10 +- .../Implementations/OrganizationService.cs | 356 ++++++++++++------ .../Implementations/StripePaymentService.cs | 15 +- .../Tools/Models/Business/ReferenceEvent.cs | 3 + src/Identity/Startup.cs | 3 + .../OrganizationUserRepository.cs | 13 + .../OrganizationUserRepository.cs | 7 + ...ccupiedSmSeatCountByOrganizationIdQuery.cs | 22 ++ .../Utilities/ServiceCollectionExtensions.cs | 2 + ...eadOccupiedSmSeatCountByOrganizationId.sql | 16 + .../Services/OrganizationServiceTests.cs | 148 ++++++++ .../Services/StripePaymentServiceTests.cs | 268 +++++++++++-- ..._OrgUserReadOccupiedSmSeatCountByOrgId.sql | 32 ++ 22 files changed, 896 insertions(+), 178 deletions(-) create mode 100644 src/Core/Repositories/Noop/NoopServiceAccountRepository.cs create mode 100644 src/Infrastructure.EntityFramework/Repositories/Queries/OrganizationUserReadOccupiedSmSeatCountByOrganizationIdQuery.cs create mode 100644 src/Sql/dbo/Stored Procedures/OrganizationUser_ReadOccupiedSmSeatCountByOrganizationId.sql create mode 100644 util/Migrator/DbScripts/2023-06-08_00_OrgUserReadOccupiedSmSeatCountByOrgId.sql diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ServiceAccountRepository.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ServiceAccountRepository.cs index 492c0c0d89e9..d880f5d02434 100644 --- a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ServiceAccountRepository.cs +++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ServiceAccountRepository.cs @@ -130,6 +130,16 @@ public async Task DeleteManyByIdAsync(IEnumerable ids) return policy == null ? (false, false) : (policy.Read, policy.Write); } + public async Task GetServiceAccountCountByOrganizationIdAsync(Guid organizationId) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + return await dbContext.ServiceAccount + .CountAsync(ou => ou.OrganizationId == organizationId); + } + } + private static Expression> UserHasReadAccessToServiceAccount(Guid userId) => sa => sa.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) || sa.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read)); diff --git a/bitwarden_license/src/Scim/Startup.cs b/bitwarden_license/src/Scim/Startup.cs index 4ef46459c3bd..0d87f48ff789 100644 --- a/bitwarden_license/src/Scim/Startup.cs +++ b/bitwarden_license/src/Scim/Startup.cs @@ -42,6 +42,8 @@ public void ConfigureServices(IServiceCollection services) // Repositories services.AddDatabaseRepositories(globalSettings); + services.AddOosServices(); + // Context services.AddScoped(); services.AddScoped(); diff --git a/src/Api/Models/Request/Organizations/OrganizationCreateRequestModel.cs b/src/Api/Models/Request/Organizations/OrganizationCreateRequestModel.cs index 3e4602179503..35151dc4f625 100644 --- a/src/Api/Models/Request/Organizations/OrganizationCreateRequestModel.cs +++ b/src/Api/Models/Request/Organizations/OrganizationCreateRequestModel.cs @@ -40,6 +40,12 @@ public class OrganizationCreateRequestModel : IValidatableObject [StringLength(2)] public string BillingAddressCountry { get; set; } public int? MaxAutoscaleSeats { get; set; } + [Range(0, int.MaxValue)] + public int? AdditionalSmSeats { get; set; } + [Range(0, int.MaxValue)] + public int? AdditionalServiceAccounts { get; set; } + [Required] + public bool UseSecretsManager { get; set; } public virtual OrganizationSignup ToOrganizationSignup(User user) { @@ -58,6 +64,9 @@ public virtual OrganizationSignup ToOrganizationSignup(User user) BillingEmail = BillingEmail, BusinessName = BusinessName, CollectionName = CollectionName, + AdditionalSmSeats = AdditionalSmSeats.GetValueOrDefault(), + AdditionalServiceAccounts = AdditionalServiceAccounts.GetValueOrDefault(), + UseSecretsManager = UseSecretsManager, TaxInfo = new TaxInfo { TaxIdNumber = TaxIdNumber, diff --git a/src/Api/Models/Request/Organizations/OrganizationUpgradeRequestModel.cs b/src/Api/Models/Request/Organizations/OrganizationUpgradeRequestModel.cs index fb2666cc1ed0..0c5277ec6e2f 100644 --- a/src/Api/Models/Request/Organizations/OrganizationUpgradeRequestModel.cs +++ b/src/Api/Models/Request/Organizations/OrganizationUpgradeRequestModel.cs @@ -13,6 +13,12 @@ public class OrganizationUpgradeRequestModel public int AdditionalSeats { get; set; } [Range(0, 99)] public short? AdditionalStorageGb { get; set; } + [Range(0, int.MaxValue)] + public int? AdditionalSmSeats { get; set; } + [Range(0, int.MaxValue)] + public int? AdditionalServiceAccounts { get; set; } + [Required] + public bool UseSecretsManager { get; set; } public bool PremiumAccessAddon { get; set; } public string BillingAddressCountry { get; set; } public string BillingAddressPostalCode { get; set; } @@ -24,6 +30,9 @@ public OrganizationUpgrade ToOrganizationUpgrade() { AdditionalSeats = AdditionalSeats, AdditionalStorageGb = AdditionalStorageGb.GetValueOrDefault(), + AdditionalServiceAccounts = AdditionalServiceAccounts.GetValueOrDefault(0), + AdditionalSmSeats = AdditionalSmSeats.GetValueOrDefault(0), + UseSecretsManager = UseSecretsManager, BusinessName = BusinessName, Plan = PlanType, PremiumAccessAddon = PremiumAccessAddon, diff --git a/src/Core/Models/Business/OrganizationUpgrade.cs b/src/Core/Models/Business/OrganizationUpgrade.cs index b77a9d012c2e..173502a24fa1 100644 --- a/src/Core/Models/Business/OrganizationUpgrade.cs +++ b/src/Core/Models/Business/OrganizationUpgrade.cs @@ -12,4 +12,7 @@ public class OrganizationUpgrade public TaxInfo TaxInfo { get; set; } public string PublicKey { get; set; } public string PrivateKey { get; set; } + public int? AdditionalSmSeats { get; set; } + public int? AdditionalServiceAccounts { get; set; } + public bool UseSecretsManager { get; set; } } diff --git a/src/Core/Models/Business/SubscriptionCreateOptions.cs b/src/Core/Models/Business/SubscriptionCreateOptions.cs index 4964a625c8c4..bd51f431f065 100644 --- a/src/Core/Models/Business/SubscriptionCreateOptions.cs +++ b/src/Core/Models/Business/SubscriptionCreateOptions.cs @@ -1,36 +1,61 @@ using Bit.Core.Entities; +using Bit.Core.Enums; using Stripe; namespace Bit.Core.Models.Business; public class OrganizationSubscriptionOptionsBase : Stripe.SubscriptionCreateOptions { - public OrganizationSubscriptionOptionsBase(Organization org, StaticStore.Plan plan, TaxInfo taxInfo, int additionalSeats, int additionalStorageGb, bool premiumAccessAddon) + public OrganizationSubscriptionOptionsBase(Organization org, List plans, TaxInfo taxInfo, int additionalSeats, + int additionalStorageGb, bool premiumAccessAddon, int additionalSmSeats = 0, int additionalServiceAccounts = 0) { Items = new List(); Metadata = new Dictionary { [org.GatewayIdField()] = org.Id.ToString() }; - - if (plan.StripePlanId != null) + foreach (var plan in plans) { - Items.Add(new SubscriptionItemOptions + AddPlanIdToSubscription(plan); + + switch (plan.BitwardenProduct) { - Plan = plan.StripePlanId, - Quantity = 1 - }); + case BitwardenProductType.PasswordManager: + { + AddPremiumAccessAddon(premiumAccessAddon, plan); + AddAdditionalSeatToSubscription(additionalSeats, plan); + AddAdditionalStorage(additionalStorageGb, plan); + break; + } + case BitwardenProductType.SecretsManager: + { + AddAdditionalSeatToSubscription(additionalSmSeats, plan); + AddServiceAccount(additionalServiceAccounts, plan); + break; + } + } } - if (additionalSeats > 0 && plan.StripeSeatPlanId != null) + if (!string.IsNullOrWhiteSpace(taxInfo?.StripeTaxRateId)) + { + DefaultTaxRates = new List { taxInfo.StripeTaxRateId }; + } + } + + private void AddServiceAccount(int additionalServiceAccounts, StaticStore.Plan plan) + { + if (additionalServiceAccounts > 0 && plan.StripeServiceAccountPlanId != null) { Items.Add(new SubscriptionItemOptions { - Plan = plan.StripeSeatPlanId, - Quantity = additionalSeats + Plan = plan.StripeServiceAccountPlanId, + Quantity = additionalServiceAccounts }); } + } + private void AddAdditionalStorage(int additionalStorageGb, StaticStore.Plan plan) + { if (additionalStorageGb > 0) { Items.Add(new SubscriptionItemOptions @@ -39,19 +64,29 @@ public OrganizationSubscriptionOptionsBase(Organization org, StaticStore.Plan pl Quantity = additionalStorageGb }); } + } + private void AddPremiumAccessAddon(bool premiumAccessAddon, StaticStore.Plan plan) + { if (premiumAccessAddon && plan.StripePremiumAccessPlanId != null) { - Items.Add(new SubscriptionItemOptions - { - Plan = plan.StripePremiumAccessPlanId, - Quantity = 1 - }); + Items.Add(new SubscriptionItemOptions { Plan = plan.StripePremiumAccessPlanId, Quantity = 1 }); } + } - if (!string.IsNullOrWhiteSpace(taxInfo?.StripeTaxRateId)) + private void AddAdditionalSeatToSubscription(int additionalSeats, StaticStore.Plan plan) + { + if (additionalSeats > 0 && plan.StripeSeatPlanId != null) { - DefaultTaxRates = new List { taxInfo.StripeTaxRateId }; + Items.Add(new SubscriptionItemOptions { Plan = plan.StripeSeatPlanId, Quantity = additionalSeats }); + } + } + + private void AddPlanIdToSubscription(StaticStore.Plan plan) + { + if (plan.StripePlanId != null) + { + Items.Add(new SubscriptionItemOptions { Plan = plan.StripePlanId, Quantity = 1 }); } } } @@ -59,13 +94,14 @@ public OrganizationSubscriptionOptionsBase(Organization org, StaticStore.Plan pl public class OrganizationPurchaseSubscriptionOptions : OrganizationSubscriptionOptionsBase { public OrganizationPurchaseSubscriptionOptions( - Organization org, StaticStore.Plan plan, + Organization org, List plans, TaxInfo taxInfo, int additionalSeats = 0, - int additionalStorageGb = 0, bool premiumAccessAddon = false) : - base(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon) + int additionalStorageGb = 0, bool premiumAccessAddon = false, + int additionalSmSeats = 0, int additionalServiceAccounts = 0) : + base(org, plans, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon, additionalSmSeats, additionalServiceAccounts) { OffSession = true; - TrialPeriodDays = plan.TrialPeriodDays; + TrialPeriodDays = plans.FirstOrDefault(x => x.BitwardenProduct == BitwardenProductType.PasswordManager)!.TrialPeriodDays; } } @@ -73,10 +109,10 @@ public class OrganizationUpgradeSubscriptionOptions : OrganizationSubscriptionOp { public OrganizationUpgradeSubscriptionOptions( string customerId, Organization org, - StaticStore.Plan plan, TaxInfo taxInfo, + List plans, TaxInfo taxInfo, int additionalSeats = 0, int additionalStorageGb = 0, - bool premiumAccessAddon = false) : - base(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon) + bool premiumAccessAddon = false, int additionalSmSeats = 0, int additionalServiceAccounts = 0) : + base(org, plans, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon, additionalSmSeats, additionalServiceAccounts) { Customer = customerId; } diff --git a/src/Core/Repositories/IOrganizationUserRepository.cs b/src/Core/Repositories/IOrganizationUserRepository.cs index 16d333f9e9fe..f9dfa12c2cbf 100644 --- a/src/Core/Repositories/IOrganizationUserRepository.cs +++ b/src/Core/Repositories/IOrganizationUserRepository.cs @@ -40,4 +40,5 @@ Task GetDetailsByUserAsync(Guid userId, Gui Task RevokeAsync(Guid id); Task RestoreAsync(Guid id, OrganizationUserStatusType status); Task> GetByUserIdWithPolicyDetailsAsync(Guid userId, PolicyType policyType); + Task GetOccupiedSmSeatCountByOrganizationIdAsync(Guid organizationId); } diff --git a/src/Core/Repositories/Noop/NoopServiceAccountRepository.cs b/src/Core/Repositories/Noop/NoopServiceAccountRepository.cs new file mode 100644 index 000000000000..526ff1cb8821 --- /dev/null +++ b/src/Core/Repositories/Noop/NoopServiceAccountRepository.cs @@ -0,0 +1,60 @@ +using Bit.Core.Enums; +using Bit.Core.SecretsManager.Entities; +using Bit.Core.SecretsManager.Repositories; + +namespace Bit.Core.Repositories.Noop; + +public class NoopServiceAccountRepository : IServiceAccountRepository +{ + public Task> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId, AccessClientType accessType) + { + return Task.FromResult(null as IEnumerable); + } + + public Task GetByIdAsync(Guid id) + { + return Task.FromResult(null as ServiceAccount); + } + + public Task> GetManyByIds(IEnumerable ids) + { + return Task.FromResult(null as IEnumerable); + } + + public Task CreateAsync(ServiceAccount serviceAccount) + { + return Task.FromResult(null as ServiceAccount); + } + + public Task ReplaceAsync(ServiceAccount serviceAccount) + { + return Task.FromResult(0); + } + + public Task DeleteManyByIdAsync(IEnumerable ids) + { + return Task.FromResult(0); + } + + public Task UserHasReadAccessToServiceAccount(Guid id, Guid userId) + { + return Task.FromResult(false); + } + + public Task UserHasWriteAccessToServiceAccount(Guid id, Guid userId) + { + return Task.FromResult(false); + } + + public Task> GetManyByOrganizationIdWriteAccessAsync(Guid organizationId, Guid userId, AccessClientType accessType) => throw new NotImplementedException(); + + public Task<(bool Read, bool Write)> AccessToServiceAccountAsync(Guid id, Guid userId, AccessClientType accessType) + { + return Task.FromResult((false, false)); + } + + public Task GetServiceAccountCountByOrganizationIdAsync(Guid organizationId) + { + return Task.FromResult(0); + } +} diff --git a/src/Core/SecretsManager/Repositories/IServiceAccountRepository.cs b/src/Core/SecretsManager/Repositories/IServiceAccountRepository.cs index d79ed020a372..b362a5676b89 100644 --- a/src/Core/SecretsManager/Repositories/IServiceAccountRepository.cs +++ b/src/Core/SecretsManager/Repositories/IServiceAccountRepository.cs @@ -15,4 +15,5 @@ public interface IServiceAccountRepository Task UserHasWriteAccessToServiceAccount(Guid id, Guid userId); Task> GetManyByOrganizationIdWriteAccessAsync(Guid organizationId, Guid userId, AccessClientType accessType); Task<(bool Read, bool Write)> AccessToServiceAccountAsync(Guid id, Guid userId, AccessClientType accessType); + Task GetServiceAccountCountByOrganizationIdAsync(Guid organizationId); } diff --git a/src/Core/Services/IPaymentService.cs b/src/Core/Services/IPaymentService.cs index 95c6f2590888..ba482e0d59c8 100644 --- a/src/Core/Services/IPaymentService.cs +++ b/src/Core/Services/IPaymentService.cs @@ -9,12 +9,14 @@ public interface IPaymentService { Task CancelAndRecoverChargesAsync(ISubscriber subscriber); Task PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType, - string paymentToken, Plan plan, short additionalStorageGb, int additionalSeats, - bool premiumAccessAddon, TaxInfo taxInfo, bool provider = false); + string paymentToken, List plans, short additionalStorageGb, int additionalSeats, + bool premiumAccessAddon, TaxInfo taxInfo, bool provider = false, int additionalSmSeats = 0, + int additionalServiceAccount = 0); Task SponsorOrganizationAsync(Organization org, OrganizationSponsorship sponsorship); Task RemoveOrganizationSponsorshipAsync(Organization org, OrganizationSponsorship sponsorship); - Task UpgradeFreeOrganizationAsync(Organization org, Plan plan, - short additionalStorageGb, int additionalSeats, bool premiumAccessAddon, TaxInfo taxInfo); + Task UpgradeFreeOrganizationAsync(Organization org, List plans, + short additionalStorageGb, int additionalSeats, bool premiumAccessAddon, TaxInfo taxInfo, + int additionalSmSeats = 0, int additionalServiceAccounts = 0); Task PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken, short additionalStorageGb, TaxInfo taxInfo); Task AdjustSeatsAsync(Organization organization, Plan plan, int additionalSeats, DateTime? prorationDate = null); diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 94f03c89716c..abf420392bf3 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -13,6 +13,7 @@ using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.Repositories; +using Bit.Core.SecretsManager.Repositories; using Bit.Core.Settings; using Bit.Core.Tools.Enums; using Bit.Core.Tools.Models.Business; @@ -53,6 +54,7 @@ public class OrganizationService : IOrganizationService private readonly ILogger _logger; private readonly IProviderOrganizationRepository _providerOrganizationRepository; private readonly IProviderUserRepository _providerUserRepository; + private readonly IServiceAccountRepository _serviceAccountRepository; public OrganizationService( IOrganizationRepository organizationRepository, @@ -81,7 +83,8 @@ public OrganizationService( ICurrentContext currentContext, ILogger logger, IProviderOrganizationRepository providerOrganizationRepository, - IProviderUserRepository providerUserRepository) + IProviderUserRepository providerUserRepository, + IServiceAccountRepository serviceAccountRepository) { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; @@ -110,6 +113,7 @@ public OrganizationService( _logger = logger; _providerOrganizationRepository = providerOrganizationRepository; _providerUserRepository = providerUserRepository; + _serviceAccountRepository = serviceAccountRepository; } public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken, @@ -179,90 +183,99 @@ public async Task> UpgradePlanAsync(Guid organizationId, Org throw new BadRequestException("Your account has no payment method available."); } - var existingPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); - if (existingPlan == null) + var existingPasswordManagerPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); + if (existingPasswordManagerPlan == null) { throw new BadRequestException("Existing plan not found."); } - var newPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled); - if (newPlan == null) + var newPasswordManagerPlan = + StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled); + if (newPasswordManagerPlan == null) { throw new BadRequestException("Plan not found."); } - if (existingPlan.Type == newPlan.Type) + if (existingPasswordManagerPlan.Type == newPasswordManagerPlan.Type) { throw new BadRequestException("Organization is already on this plan."); } - if (existingPlan.UpgradeSortOrder >= newPlan.UpgradeSortOrder) + if (existingPasswordManagerPlan.UpgradeSortOrder >= newPasswordManagerPlan.UpgradeSortOrder) { throw new BadRequestException("You cannot upgrade to this plan."); } - if (existingPlan.Type != PlanType.Free) + if (existingPasswordManagerPlan.Type != PlanType.Free) { throw new BadRequestException("You can only upgrade from the free plan. Contact support."); } - ValidateOrganizationUpgradeParameters(newPlan, upgrade); + ValidatePasswordManagerPlan(newPasswordManagerPlan, upgrade); + var newSecretsManagerPlan = + StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled); + if (upgrade.UseSecretsManager) + { + ValidateSecretsManagerPlan(newSecretsManagerPlan, upgrade); + } - var newPlanSeats = (short)(newPlan.BaseSeats + - (newPlan.HasAdditionalSeatsOption ? upgrade.AdditionalSeats : 0)); - if (!organization.Seats.HasValue || organization.Seats.Value > newPlanSeats) + var newPasswordManagerPlanSeats = (short)(newPasswordManagerPlan.BaseSeats + + (newPasswordManagerPlan.HasAdditionalSeatsOption ? upgrade.AdditionalSeats : 0)); + if (!organization.Seats.HasValue || organization.Seats.Value > newPasswordManagerPlanSeats) { - var occupiedSeats = await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); - if (occupiedSeats > newPlanSeats) + var occupiedSeats = + await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); + if (occupiedSeats > newPasswordManagerPlanSeats) { throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " + - $"Your new plan only has ({newPlanSeats}) seats. Remove some users."); + $"Your new plan only has ({newPasswordManagerPlanSeats}) seats. Remove some users."); } } - if (newPlan.MaxCollections.HasValue && (!organization.MaxCollections.HasValue || - organization.MaxCollections.Value > newPlan.MaxCollections.Value)) + if (newPasswordManagerPlan.MaxCollections.HasValue && (!organization.MaxCollections.HasValue || + organization.MaxCollections.Value > + newPasswordManagerPlan.MaxCollections.Value)) { var collectionCount = await _collectionRepository.GetCountByOrganizationIdAsync(organization.Id); - if (collectionCount > newPlan.MaxCollections.Value) + if (collectionCount > newPasswordManagerPlan.MaxCollections.Value) { throw new BadRequestException($"Your organization currently has {collectionCount} collections. " + - $"Your new plan allows for a maximum of ({newPlan.MaxCollections.Value}) collections. " + - "Remove some collections."); + $"Your new plan allows for a maximum of ({newPasswordManagerPlan.MaxCollections.Value}) collections. " + + "Remove some collections."); } } - if (!newPlan.HasGroups && organization.UseGroups) + if (!newPasswordManagerPlan.HasGroups && organization.UseGroups) { var groups = await _groupRepository.GetManyByOrganizationIdAsync(organization.Id); if (groups.Any()) { throw new BadRequestException($"Your new plan does not allow the groups feature. " + - $"Remove your groups."); + $"Remove your groups."); } } - if (!newPlan.HasPolicies && organization.UsePolicies) + if (!newPasswordManagerPlan.HasPolicies && organization.UsePolicies) { var policies = await _policyRepository.GetManyByOrganizationIdAsync(organization.Id); if (policies.Any(p => p.Enabled)) { throw new BadRequestException($"Your new plan does not allow the policies feature. " + - $"Disable your policies."); + $"Disable your policies."); } } - if (!newPlan.HasSso && organization.UseSso) + if (!newPasswordManagerPlan.HasSso && organization.UseSso) { var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id); if (ssoConfig != null && ssoConfig.Enabled) { throw new BadRequestException($"Your new plan does not allow the SSO feature. " + - $"Disable your SSO configuration."); + $"Disable your SSO configuration."); } } - if (!newPlan.HasKeyConnector && organization.UseKeyConnector) + if (!newPasswordManagerPlan.HasKeyConnector && organization.UseKeyConnector) { var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id); if (ssoConfig != null && ssoConfig.GetData().MemberDecryptionType == MemberDecryptionType.KeyConnector) @@ -272,29 +285,29 @@ public async Task> UpgradePlanAsync(Guid organizationId, Org } } - if (!newPlan.HasResetPassword && organization.UseResetPassword) + if (!newPasswordManagerPlan.HasResetPassword && organization.UseResetPassword) { var resetPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword); if (resetPasswordPolicy != null && resetPasswordPolicy.Enabled) { throw new BadRequestException("Your new plan does not allow the Password Reset feature. " + - "Disable your Password Reset policy."); + "Disable your Password Reset policy."); } } - if (!newPlan.HasScim && organization.UseScim) + if (!newPasswordManagerPlan.HasScim && organization.UseScim) { var scimConnections = await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organization.Id, OrganizationConnectionType.Scim); if (scimConnections != null && scimConnections.Any(c => c.GetConfig()?.Enabled == true)) { throw new BadRequestException("Your new plan does not allow the SCIM feature. " + - "Disable your SCIM configuration."); + "Disable your SCIM configuration."); } } - if (!newPlan.HasCustomPermissions && organization.UseCustomPermissions) + if (!newPasswordManagerPlan.HasCustomPermissions && organization.UseCustomPermissions) { var organizationCustomUsers = await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, @@ -306,14 +319,25 @@ await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, } } - // TODO: Check storage? + if (upgrade.UseSecretsManager && newSecretsManagerPlan != null) + { + await ValidateSecretsManagerSeatsAndServiceAccountAsync(upgrade, organization, newSecretsManagerPlan); + } + // TODO: Check storage? string paymentIntentClientSecret = null; var success = true; + if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId)) { - paymentIntentClientSecret = await _paymentService.UpgradeFreeOrganizationAsync(organization, newPlan, - upgrade.AdditionalStorageGb, upgrade.AdditionalSeats, upgrade.PremiumAccessAddon, upgrade.TaxInfo); + var organizationUpgradePlan = upgrade.UseSecretsManager + ? StaticStore.Plans.Where(p => p.Type == upgrade.Plan).ToList() + : StaticStore.Plans.Where(p => p.Type == upgrade.Plan && p.BitwardenProduct == BitwardenProductType.PasswordManager).ToList(); + + paymentIntentClientSecret = await _paymentService.UpgradeFreeOrganizationAsync(organization, + organizationUpgradePlan, + upgrade.AdditionalStorageGb, upgrade.AdditionalSeats, upgrade.PremiumAccessAddon, upgrade.TaxInfo + , upgrade.AdditionalSmSeats.GetValueOrDefault(), upgrade.AdditionalServiceAccounts.GetValueOrDefault()); success = string.IsNullOrWhiteSpace(paymentIntentClientSecret); } else @@ -323,54 +347,100 @@ await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, } organization.BusinessName = upgrade.BusinessName; - organization.PlanType = newPlan.Type; - organization.Seats = (short)(newPlan.BaseSeats + upgrade.AdditionalSeats); - organization.MaxCollections = newPlan.MaxCollections; - organization.UseGroups = newPlan.HasGroups; - organization.UseDirectory = newPlan.HasDirectory; - organization.UseEvents = newPlan.HasEvents; - organization.UseTotp = newPlan.HasTotp; - organization.Use2fa = newPlan.Has2fa; - organization.UseApi = newPlan.HasApi; - organization.SelfHost = newPlan.HasSelfHost; - organization.UsePolicies = newPlan.HasPolicies; - organization.MaxStorageGb = !newPlan.BaseStorageGb.HasValue ? - (short?)null : (short)(newPlan.BaseStorageGb.Value + upgrade.AdditionalStorageGb); - organization.UseGroups = newPlan.HasGroups; - organization.UseDirectory = newPlan.HasDirectory; - organization.UseEvents = newPlan.HasEvents; - organization.UseTotp = newPlan.HasTotp; - organization.Use2fa = newPlan.Has2fa; - organization.UseApi = newPlan.HasApi; - organization.UseSso = newPlan.HasSso; - organization.UseKeyConnector = newPlan.HasKeyConnector; - organization.UseScim = newPlan.HasScim; - organization.UseResetPassword = newPlan.HasResetPassword; - organization.SelfHost = newPlan.HasSelfHost; - organization.UsersGetPremium = newPlan.UsersGetPremium || upgrade.PremiumAccessAddon; - organization.UseCustomPermissions = newPlan.HasCustomPermissions; - organization.Plan = newPlan.Name; + organization.PlanType = newPasswordManagerPlan.Type; + organization.Seats = (short)(newPasswordManagerPlan.BaseSeats + upgrade.AdditionalSeats); + organization.MaxCollections = newPasswordManagerPlan.MaxCollections; + organization.UseGroups = newPasswordManagerPlan.HasGroups; + organization.UseDirectory = newPasswordManagerPlan.HasDirectory; + organization.UseEvents = newPasswordManagerPlan.HasEvents; + organization.UseTotp = newPasswordManagerPlan.HasTotp; + organization.Use2fa = newPasswordManagerPlan.Has2fa; + organization.UseApi = newPasswordManagerPlan.HasApi; + organization.SelfHost = newPasswordManagerPlan.HasSelfHost; + organization.UsePolicies = newPasswordManagerPlan.HasPolicies; + organization.MaxStorageGb = !newPasswordManagerPlan.BaseStorageGb.HasValue + ? (short?)null + : (short)(newPasswordManagerPlan.BaseStorageGb.Value + upgrade.AdditionalStorageGb); + organization.UseGroups = newPasswordManagerPlan.HasGroups; + organization.UseDirectory = newPasswordManagerPlan.HasDirectory; + organization.UseEvents = newPasswordManagerPlan.HasEvents; + organization.UseTotp = newPasswordManagerPlan.HasTotp; + organization.Use2fa = newPasswordManagerPlan.Has2fa; + organization.UseApi = newPasswordManagerPlan.HasApi; + organization.UseSso = newPasswordManagerPlan.HasSso; + organization.UseKeyConnector = newPasswordManagerPlan.HasKeyConnector; + organization.UseScim = newPasswordManagerPlan.HasScim; + organization.UseResetPassword = newPasswordManagerPlan.HasResetPassword; + organization.SelfHost = newPasswordManagerPlan.HasSelfHost; + organization.UsersGetPremium = newPasswordManagerPlan.UsersGetPremium || upgrade.PremiumAccessAddon; + organization.UseCustomPermissions = newPasswordManagerPlan.HasCustomPermissions; + organization.Plan = newPasswordManagerPlan.Name; organization.Enabled = success; organization.PublicKey = upgrade.PublicKey; organization.PrivateKey = upgrade.PrivateKey; + organization.UsePasswordManager = true; + organization.SmSeats = (short)(newSecretsManagerPlan.BaseSeats + upgrade.AdditionalSmSeats.GetValueOrDefault()); + organization.SmServiceAccounts = upgrade.AdditionalServiceAccounts.GetValueOrDefault(); + organization.UseSecretsManager = upgrade.UseSecretsManager; + await ReplaceAndUpdateCacheAsync(organization); if (success) { await _referenceEventService.RaiseEventAsync( new ReferenceEvent(ReferenceEventType.UpgradePlan, organization, _currentContext) { - PlanName = newPlan.Name, - PlanType = newPlan.Type, - OldPlanName = existingPlan.Name, - OldPlanType = existingPlan.Type, + PlanName = newPasswordManagerPlan.Name, + PlanType = newPasswordManagerPlan.Type, + OldPlanName = existingPasswordManagerPlan.Name, + OldPlanType = existingPasswordManagerPlan.Type, Seats = organization.Seats, Storage = organization.MaxStorageGb, + SmSeats = organization.SmSeats, + ServiceAccounts = organization.SmServiceAccounts, + UseSecretsManager = organization.UseSecretsManager }); } return new Tuple(success, paymentIntentClientSecret); } + private async Task ValidateSecretsManagerSeatsAndServiceAccountAsync(OrganizationUpgrade upgrade, Organization organization, + Models.StaticStore.Plan newSecretsManagerPlan) + { + var newPlanSmSeats = (short)(newSecretsManagerPlan.BaseSeats + + (newSecretsManagerPlan.HasAdditionalSeatsOption + ? upgrade.AdditionalSmSeats + : 0)); + var occupiedSmSeats = + await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id); + + if (!organization.SmSeats.HasValue || organization.SmSeats.Value > newPlanSmSeats) + { + if (occupiedSmSeats > newPlanSmSeats) + { + throw new BadRequestException( + $"Your organization currently has {occupiedSmSeats} Secrets Manager seats filled. " + + $"Your new plan only has ({newPlanSmSeats}) seats. Remove some users."); + } + } + + if (newSecretsManagerPlan.BaseServiceAccount != null) + { + if (!organization.SmServiceAccounts.HasValue || + organization.SmServiceAccounts.Value > newSecretsManagerPlan.MaxServiceAccount) + { + var currentServiceAccounts = + await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(organization.Id); + if (currentServiceAccounts > newSecretsManagerPlan.MaxServiceAccount) + { + throw new BadRequestException( + $"Your organization currently has {currentServiceAccounts} service account seats filled. " + + $"Your new plan only has ({newSecretsManagerPlan.MaxServiceAccount}) service accounts. Remove some service accounts."); + } + } + } + } + public async Task AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb) { var organization = await GetOrgById(organizationId); @@ -607,15 +677,14 @@ public async Task VerifyBankAsync(Guid organizationId, int amount1, int amount2) public async Task> SignUpAsync(OrganizationSignup signup, bool provider = false) { - var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == signup.Plan); - if (plan is not { LegacyYear: null }) - { - throw new BadRequestException("Invalid plan selected."); - } + var passwordManagerPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == signup.Plan); - if (plan.Disabled) + ValidatePasswordManagerPlan(passwordManagerPlan, signup); + + var secretsManagerPlan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == signup.Plan); + if (signup.UseSecretsManager) { - throw new BadRequestException("Plan not found."); + ValidateSecretsManagerPlan(secretsManagerPlan, signup); } if (!provider) @@ -623,8 +692,6 @@ public async Task> SignUpAsync(Organizatio await ValidateSignUpPoliciesAsync(signup.Owner.Id); } - ValidateOrganizationUpgradeParameters(plan, signup); - var organization = new Organization { // Pre-generate the org id so that we can save it with the Stripe subscription.. @@ -632,25 +699,25 @@ public async Task> SignUpAsync(Organizatio Name = signup.Name, BillingEmail = signup.BillingEmail, BusinessName = signup.BusinessName, - PlanType = plan.Type, - Seats = (short)(plan.BaseSeats + signup.AdditionalSeats), - MaxCollections = plan.MaxCollections, - MaxStorageGb = !plan.BaseStorageGb.HasValue ? - (short?)null : (short)(plan.BaseStorageGb.Value + signup.AdditionalStorageGb), - UsePolicies = plan.HasPolicies, - UseSso = plan.HasSso, - 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 || signup.PremiumAccessAddon, - UseCustomPermissions = plan.HasCustomPermissions, - UseScim = plan.HasScim, - Plan = plan.Name, + PlanType = passwordManagerPlan.Type, + Seats = (short)(passwordManagerPlan.BaseSeats + signup.AdditionalSeats), + MaxCollections = passwordManagerPlan.MaxCollections, + MaxStorageGb = !passwordManagerPlan.BaseStorageGb.HasValue ? + (short?)null : (short)(passwordManagerPlan.BaseStorageGb.Value + signup.AdditionalStorageGb), + UsePolicies = passwordManagerPlan.HasPolicies, + UseSso = passwordManagerPlan.HasSso, + UseGroups = passwordManagerPlan.HasGroups, + UseEvents = passwordManagerPlan.HasEvents, + UseDirectory = passwordManagerPlan.HasDirectory, + UseTotp = passwordManagerPlan.HasTotp, + Use2fa = passwordManagerPlan.Has2fa, + UseApi = passwordManagerPlan.HasApi, + UseResetPassword = passwordManagerPlan.HasResetPassword, + SelfHost = passwordManagerPlan.HasSelfHost, + UsersGetPremium = passwordManagerPlan.UsersGetPremium || signup.PremiumAccessAddon, + UseCustomPermissions = passwordManagerPlan.HasCustomPermissions, + UseScim = passwordManagerPlan.HasScim, + Plan = passwordManagerPlan.Name, Gateway = null, ReferenceData = signup.Owner.ReferenceData, Enabled = true, @@ -659,10 +726,14 @@ public async Task> SignUpAsync(Organizatio PrivateKey = signup.PrivateKey, CreationDate = DateTime.UtcNow, RevisionDate = DateTime.UtcNow, - Status = OrganizationStatusType.Created + Status = OrganizationStatusType.Created, + UsePasswordManager = true, + SmSeats = (short)(secretsManagerPlan.BaseSeats + signup.AdditionalSmSeats.GetValueOrDefault()), + SmServiceAccounts = signup.AdditionalServiceAccounts.GetValueOrDefault(), + UseSecretsManager = signup.UseSecretsManager }; - if (plan.Type == PlanType.Free && !provider) + if (passwordManagerPlan.Type == PlanType.Free && !provider) { var adminCount = await _organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(signup.Owner.Id); @@ -671,11 +742,16 @@ public async Task> SignUpAsync(Organizatio throw new BadRequestException("You can only be an admin of one free organization."); } } - else if (plan.Type != PlanType.Free) + else if (passwordManagerPlan.Type != PlanType.Free) { + var purchaseOrganizationPlan = signup.UseSecretsManager + ? StaticStore.Plans.Where(p => p.Type == signup.Plan).ToList() + : StaticStore.PasswordManagerPlans.Where(p => p.Type == signup.Plan).Take(1).ToList(); + await _paymentService.PurchaseOrganizationAsync(organization, signup.PaymentMethodType.Value, - signup.PaymentToken, plan, signup.AdditionalStorageGb, signup.AdditionalSeats, - signup.PremiumAccessAddon, signup.TaxInfo, provider); + signup.PaymentToken, purchaseOrganizationPlan, signup.AdditionalStorageGb, signup.AdditionalSeats, + signup.PremiumAccessAddon, signup.TaxInfo, provider, signup.AdditionalSmSeats.GetValueOrDefault(), + signup.AdditionalServiceAccounts.GetValueOrDefault()); } var ownerId = provider ? default : signup.Owner.Id; @@ -683,10 +759,13 @@ await _paymentService.PurchaseOrganizationAsync(organization, signup.PaymentMeth await _referenceEventService.RaiseEventAsync( new ReferenceEvent(ReferenceEventType.Signup, organization, _currentContext) { - PlanName = plan.Name, - PlanType = plan.Type, + PlanName = passwordManagerPlan.Name, + PlanType = passwordManagerPlan.Type, Seats = returnValue.Item1.Seats, Storage = returnValue.Item1.MaxStorageGb, + SmSeats = returnValue.Item1.SmSeats, + ServiceAccounts = returnValue.Item1.SmServiceAccounts, + UseSecretsManager = returnValue.Item1.UseSecretsManager }); return returnValue; } @@ -2060,31 +2139,56 @@ private async Task GetOrgById(Guid id) return await _organizationRepository.GetByIdAsync(id); } - private void ValidateOrganizationUpgradeParameters(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade) + private static void ValidatePlan(Models.StaticStore.Plan plan, int additionalSeats, string productType) { - if (!plan.HasAdditionalStorageOption && upgrade.AdditionalStorageGb > 0) + if (plan is not { LegacyYear: null }) { - throw new BadRequestException("Plan does not allow additional storage."); + throw new BadRequestException($"Invalid {productType} plan selected."); } - if (upgrade.AdditionalStorageGb < 0) + if (plan.Disabled) { - throw new BadRequestException("You can't subtract storage!"); + throw new BadRequestException($"{productType} Plan not found."); } - if (!plan.HasPremiumAccessOption && upgrade.PremiumAccessAddon) + if (plan.BaseSeats + additionalSeats <= 0) { - throw new BadRequestException("This plan does not allow you to buy the premium access addon."); + throw new BadRequestException($"You do not have any {productType} seats!"); + } + + if (additionalSeats < 0) + { + throw new BadRequestException($"You can't subtract {productType} seats!"); } + } + + private static void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade) + { + ValidatePlan(plan, upgrade.AdditionalSeats, "Password Manager"); if (plan.BaseSeats + upgrade.AdditionalSeats <= 0) { - throw new BadRequestException("You do not have any seats!"); + throw new BadRequestException($"You do not have any Password Manager seats!"); } if (upgrade.AdditionalSeats < 0) { - throw new BadRequestException("You can't subtract seats!"); + throw new BadRequestException($"You can't subtract Password Manager seats!"); + } + + if (!plan.HasAdditionalStorageOption && upgrade.AdditionalStorageGb > 0) + { + throw new BadRequestException("Plan does not allow additional storage."); + } + + if (upgrade.AdditionalStorageGb < 0) + { + throw new BadRequestException("You can't subtract storage!"); + } + + if (!plan.HasPremiumAccessOption && upgrade.PremiumAccessAddon) + { + throw new BadRequestException("This plan does not allow you to buy the premium access addon."); } if (!plan.HasAdditionalSeatsOption && upgrade.AdditionalSeats > 0) @@ -2096,7 +2200,37 @@ private void ValidateOrganizationUpgradeParameters(Models.StaticStore.Plan plan, upgrade.AdditionalSeats > plan.MaxAdditionalSeats.Value) { throw new BadRequestException($"Selected plan allows a maximum of " + - $"{plan.MaxAdditionalSeats.GetValueOrDefault(0)} additional users."); + $"{plan.MaxAdditionalSeats.GetValueOrDefault(0)} additional users."); + } + } + + private static void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade) + { + ValidatePlan(plan, upgrade.AdditionalSmSeats.GetValueOrDefault(), "Secrets Manager"); + + if (!plan.HasAdditionalServiceAccountOption && upgrade.AdditionalServiceAccounts > 0) + { + throw new BadRequestException("Plan does not allow additional Service Accounts."); + } + + if (upgrade.AdditionalSmSeats.GetValueOrDefault() > upgrade.AdditionalSeats) + { + throw new BadRequestException("You cannot have more Secrets Manager seats than Password Manager seats."); + } + + if (upgrade.AdditionalServiceAccounts.GetValueOrDefault() < 0) + { + throw new BadRequestException("You can't subtract Service Accounts!"); + } + + switch (plan.HasAdditionalSeatsOption) + { + case false when upgrade.AdditionalSmSeats > 0: + throw new BadRequestException("Plan does not allow additional users."); + case true when plan.MaxAdditionalSeats.HasValue && + upgrade.AdditionalSmSeats > plan.MaxAdditionalSeats.Value: + throw new BadRequestException($"Selected plan allows a maximum of " + + $"{plan.MaxAdditionalSeats.GetValueOrDefault(0)} additional users."); } } diff --git a/src/Core/Services/Implementations/StripePaymentService.cs b/src/Core/Services/Implementations/StripePaymentService.cs index ba995615349d..bc0507de2f16 100644 --- a/src/Core/Services/Implementations/StripePaymentService.cs +++ b/src/Core/Services/Implementations/StripePaymentService.cs @@ -45,8 +45,9 @@ public StripePaymentService( } public async Task PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType, - string paymentToken, StaticStore.Plan plan, short additionalStorageGb, - int additionalSeats, bool premiumAccessAddon, TaxInfo taxInfo, bool provider = false) + string paymentToken, List plans, short additionalStorageGb, + int additionalSeats, bool premiumAccessAddon, TaxInfo taxInfo, bool provider = false, + int additionalSmSeats = 0, int additionalServiceAccount = 0) { Braintree.Customer braintreeCustomer = null; string stipeCustomerSourceToken = null; @@ -110,7 +111,8 @@ public async Task PurchaseOrganizationAsync(Organization org, PaymentMet } } - var subCreateOptions = new OrganizationPurchaseSubscriptionOptions(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon); + var subCreateOptions = new OrganizationPurchaseSubscriptionOptions(org, plans, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon + , additionalSmSeats, additionalServiceAccount); Stripe.Customer customer = null; Stripe.Subscription subscription; @@ -221,8 +223,9 @@ public Task SponsorOrganizationAsync(Organization org, OrganizationSponsorship s public Task RemoveOrganizationSponsorshipAsync(Organization org, OrganizationSponsorship sponsorship) => ChangeOrganizationSponsorship(org, sponsorship, false); - public async Task UpgradeFreeOrganizationAsync(Organization org, StaticStore.Plan plan, - short additionalStorageGb, int additionalSeats, bool premiumAccessAddon, TaxInfo taxInfo) + public async Task UpgradeFreeOrganizationAsync(Organization org, List plans, + short additionalStorageGb, int additionalSeats, bool premiumAccessAddon, TaxInfo taxInfo, + int additionalSmSeats = 0, int additionalServiceAccounts = 0) { if (!string.IsNullOrWhiteSpace(org.GatewaySubscriptionId)) { @@ -255,7 +258,7 @@ public async Task UpgradeFreeOrganizationAsync(Organization org, StaticS } } - var subCreateOptions = new OrganizationUpgradeSubscriptionOptions(customer.Id, org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon); + var subCreateOptions = new OrganizationUpgradeSubscriptionOptions(customer.Id, org, plans, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon); var (stripePaymentMethod, paymentMethodType) = IdentifyPaymentMethod(customer, subCreateOptions); var subscription = await ChargeForNewSubscriptionAsync(org, customer, false, diff --git a/src/Core/Tools/Models/Business/ReferenceEvent.cs b/src/Core/Tools/Models/Business/ReferenceEvent.cs index 393708668a96..382d9cf1907b 100644 --- a/src/Core/Tools/Models/Business/ReferenceEvent.cs +++ b/src/Core/Tools/Models/Business/ReferenceEvent.cs @@ -70,4 +70,7 @@ public ReferenceEvent(ReferenceEventType type, IReferenceable source, ICurrentCo public string ClientId { get; set; } public Version ClientVersion { get; set; } + public int? SmSeats { get; set; } + public int? ServiceAccounts { get; set; } + public bool UseSecretsManager { get; set; } } diff --git a/src/Identity/Startup.cs b/src/Identity/Startup.cs index 6a0f274ce8bb..5ba20d905bb9 100644 --- a/src/Identity/Startup.cs +++ b/src/Identity/Startup.cs @@ -46,6 +46,8 @@ public void ConfigureServices(IServiceCollection services) // Repositories services.AddDatabaseRepositories(globalSettings); + services.AddOosServices(); + // Context services.AddScoped(); services.TryAddSingleton(); @@ -151,6 +153,7 @@ public void ConfigureServices(IServiceCollection services) }); } + public void Configure( IApplicationBuilder app, IWebHostEnvironment env, diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs index ca246949089d..008242c26c2b 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs @@ -99,6 +99,19 @@ public async Task GetOccupiedSeatCountByOrganizationIdAsync(Guid organizati } } + public async Task GetOccupiedSmSeatCountByOrganizationIdAsync(Guid organizationId) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var result = await connection.ExecuteScalarAsync( + "[dbo].[OrganizationUser_ReadOccupiedSmSeatCountByOrganizationId]", + new { OrganizationId = organizationId }, + commandType: CommandType.StoredProcedure); + + return result; + } + } + public async Task> SelectKnownEmailsAsync(Guid organizationId, IEnumerable emails, bool onlyRegisteredUsers) { diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs index 282304720fa8..8256696d9686 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs @@ -621,4 +621,11 @@ on p.OrganizationId equals ou.OrganizationId return await query.ToListAsync(); } } + + public async Task GetOccupiedSmSeatCountByOrganizationIdAsync(Guid organizationId) + { + var query = new OrganizationUserReadOccupiedSmSeatCountByOrganizationIdQuery(organizationId); + return await GetCountFromQuery(query); + } + } diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/OrganizationUserReadOccupiedSmSeatCountByOrganizationIdQuery.cs b/src/Infrastructure.EntityFramework/Repositories/Queries/OrganizationUserReadOccupiedSmSeatCountByOrganizationIdQuery.cs new file mode 100644 index 000000000000..0f21a80ba64f --- /dev/null +++ b/src/Infrastructure.EntityFramework/Repositories/Queries/OrganizationUserReadOccupiedSmSeatCountByOrganizationIdQuery.cs @@ -0,0 +1,22 @@ +using Bit.Core.Enums; +using Bit.Infrastructure.EntityFramework.Models; + +namespace Bit.Infrastructure.EntityFramework.Repositories.Queries; + +public class OrganizationUserReadOccupiedSmSeatCountByOrganizationIdQuery : IQuery +{ + private readonly Guid _organizationId; + + public OrganizationUserReadOccupiedSmSeatCountByOrganizationIdQuery(Guid organizationId) + { + _organizationId = organizationId; + } + + public IQueryable Run(DatabaseContext dbContext) + { + var query = from ou in dbContext.OrganizationUsers + where ou.OrganizationId == _organizationId && ou.Status >= OrganizationUserStatusType.Invited && ou.AccessSecretsManager == true + select ou; + return query; + } +} diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index c239be969a9c..355f4b46c4ca 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -18,6 +18,7 @@ using Bit.Core.OrganizationFeatures; using Bit.Core.Repositories; using Bit.Core.Resources; +using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; using Bit.Core.Settings; using Bit.Core.Tokens; @@ -329,6 +330,7 @@ public static void AddDefaultServices(this IServiceCollection services, GlobalSe public static void AddOosServices(this IServiceCollection services) { services.AddScoped(); + services.AddScoped(); } public static void AddNoopServices(this IServiceCollection services) diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadOccupiedSmSeatCountByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadOccupiedSmSeatCountByOrganizationId.sql new file mode 100644 index 000000000000..3c3792effe59 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadOccupiedSmSeatCountByOrganizationId.sql @@ -0,0 +1,16 @@ +CREATE PROCEDURE [dbo].[OrganizationUser_ReadOccupiedSmSeatCountByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + COUNT(1) + FROM + [dbo].[OrganizationUserView] + WHERE + OrganizationId = @OrganizationId + AND Status >= 0 --Invited + AND AccessSecretsManager = 1 +END +GO diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index b208ddecb0b4..b2c73c24fc9d 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -13,6 +13,7 @@ using Bit.Core.Models.Business; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.OrganizationUsers; +using Bit.Core.Models.StaticStore; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; @@ -22,6 +23,7 @@ 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; @@ -174,6 +176,20 @@ public async Task UpgradePlan_AlreadyInPlan_Throws(Organization organization, Or Assert.Contains("already on this plan", exception.Message); } + [Theory, BitAutoData] + public async Task UpgradePlan_SM_AlreadyInPlan_Throws(Organization organization, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + upgrade.Plan = organization.PlanType; + upgrade.UseSecretsManager = true; + upgrade.AdditionalSmSeats = 10; + upgrade.AdditionalServiceAccounts = 10; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); + Assert.Contains("already on this plan", exception.Message); + } + [Theory, PaidOrganizationCustomize(CheckedPlanType = PlanType.Free), BitAutoData] public async Task UpgradePlan_UpgradeFromPaidPlan_Throws(Organization organization, OrganizationUpgrade upgrade, SutProvider sutProvider) @@ -184,16 +200,147 @@ public async Task UpgradePlan_UpgradeFromPaidPlan_Throws(Organization organizati Assert.Contains("can only upgrade", exception.Message); } + [Theory, PaidOrganizationCustomize(CheckedPlanType = PlanType.Free), BitAutoData] + public async Task UpgradePlan_SM_UpgradeFromPaidPlan_Throws(Organization organization, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + upgrade.UseSecretsManager = true; + upgrade.AdditionalSmSeats = 10; + upgrade.AdditionalServiceAccounts = 10; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); + Assert.Contains("can only upgrade", exception.Message); + } + [Theory] [FreeOrganizationUpgradeCustomize, BitAutoData] public async Task UpgradePlan_Passes(Organization organization, OrganizationUpgrade upgrade, SutProvider sutProvider) { sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + upgrade.AdditionalSmSeats = 10; + upgrade.AdditionalSeats = 10; await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade); await sutProvider.GetDependency().Received(1).ReplaceAsync(organization); } + [Theory] + [BitAutoData] + public async Task SignUp_SM_Passes(OrganizationSignup signup, SutProvider sutProvider) + { + signup.AdditionalSmSeats = 10; + signup.AdditionalSeats = 10; + signup.Plan = PlanType.EnterpriseAnnually; + signup.UseSecretsManager = true; + signup.PaymentMethodType = PaymentMethodType.Card; + signup.PremiumAccessAddon = false; + + var purchaseOrganizationPlan = StaticStore.Plans.Where(x => x.Type == signup.Plan).ToList(); + + var result = await sutProvider.Sut.SignUpAsync(signup); + + await sutProvider.GetDependency().Received(1) + .RaiseEventAsync(Arg.Is(referenceEvent => + referenceEvent.Type == ReferenceEventType.Signup && + referenceEvent.PlanName == purchaseOrganizationPlan[0].Name && + referenceEvent.PlanType == purchaseOrganizationPlan[0].Type && + referenceEvent.Seats == result.Item1.Seats && + referenceEvent.SmSeats == result.Item1.SmSeats && + referenceEvent.ServiceAccounts == result.Item1.SmServiceAccounts && + referenceEvent.UseSecretsManager == result.Item1.UseSecretsManager && + referenceEvent.Storage == result.Item1.MaxStorageGb)); + + Assert.NotNull(result); + Assert.NotNull(result.Item1); + Assert.NotNull(result.Item2); + Assert.IsType>(result); + + await sutProvider.GetDependency().Received(1).PurchaseOrganizationAsync( + Arg.Any(), + signup.PaymentMethodType.Value, + signup.PaymentToken, + Arg.Is>(plan => plan.All(p => purchaseOrganizationPlan.Contains(p))), + signup.AdditionalStorageGb, + signup.AdditionalSeats, + signup.PremiumAccessAddon, + signup.TaxInfo, + false, + signup.AdditionalSmSeats.GetValueOrDefault(), + signup.AdditionalServiceAccounts.GetValueOrDefault() + ); + } + + [Theory] + [BitAutoData] + public async Task SignUpAsync_SecretManagerValidation_ShouldThrowException(OrganizationSignup signup, SutProvider sutProvider) + { + signup.AdditionalSmSeats = 10; + signup.AdditionalSeats = 10; + signup.Plan = PlanType.EnterpriseAnnually; + signup.UseSecretsManager = true; + signup.PaymentMethodType = PaymentMethodType.Card; + signup.PremiumAccessAddon = false; + signup.AdditionalServiceAccounts = 10; + + var purchaseOrganizationPlan = StaticStore.Plans.Where(x => x.Type == signup.Plan && x.BitwardenProduct == BitwardenProductType.PasswordManager).ToList(); + var secretsManagerPlan = StaticStore.GetSecretsManagerPlan(PlanType.EnterpriseAnnually); + secretsManagerPlan.HasAdditionalServiceAccountOption = false; + purchaseOrganizationPlan.Add(secretsManagerPlan); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.SignUpAsync(signup)); + Assert.Contains("Plan does not allow additional Service Accounts.", exception.Message); + } + + [Theory] + [BitAutoData] + public async Task SignUpAsync_SMSeatsGreatThanPMSeat_ShouldThrowException(OrganizationSignup signup, SutProvider sutProvider) + { + signup.AdditionalSmSeats = 100; + signup.AdditionalSeats = 10; + signup.Plan = PlanType.EnterpriseAnnually; + signup.UseSecretsManager = true; + signup.PaymentMethodType = PaymentMethodType.Card; + signup.PremiumAccessAddon = false; + signup.AdditionalServiceAccounts = 10; + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.SignUpAsync(signup)); + Assert.Contains("You cannot have more Secrets Manager seats than Password Manager seats", exception.Message); + } + + [Theory] + [BitAutoData] + public async Task SignUpAsync_InvalidateServiceAccount_ShouldThrowException(OrganizationSignup signup, SutProvider sutProvider) + { + signup.AdditionalSmSeats = 10; + signup.AdditionalSeats = 10; + signup.Plan = PlanType.EnterpriseAnnually; + signup.UseSecretsManager = true; + signup.PaymentMethodType = PaymentMethodType.Card; + signup.PremiumAccessAddon = false; + signup.AdditionalServiceAccounts = -10; + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.SignUpAsync(signup)); + Assert.Contains("You can't subtract Service Accounts!", exception.Message); + } + + [Theory] + [FreeOrganizationUpgradeCustomize, BitAutoData] + public async Task UpgradePlan_SM_Passes(Organization organization, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + upgrade.AdditionalSmSeats = 10; + upgrade.AdditionalSeats = 10; + var result = await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade); + await sutProvider.GetDependency().Received(1).ReplaceAsync(organization); + Assert.True(result.Item1); + Assert.NotNull(result.Item2); + } + [Theory] [OrganizationInviteCustomize(InviteeUserType = OrganizationUserType.User, InvitorUserType = OrganizationUserType.Owner), BitAutoData] @@ -1469,4 +1616,5 @@ public async Task HasConfirmedOwnersExcept_WithConfirmedProviderUser_IncludeProv Assert.Equal(includeProvider, result); } + } diff --git a/test/Core.Test/Services/StripePaymentServiceTests.cs b/test/Core.Test/Services/StripePaymentServiceTests.cs index c43e99fb9871..96f9a6a6f315 100644 --- a/test/Core.Test/Services/StripePaymentServiceTests.cs +++ b/test/Core.Test/Services/StripePaymentServiceTests.cs @@ -31,7 +31,7 @@ public class StripePaymentServiceTests public async void PurchaseOrganizationAsync_Invalid(PaymentMethodType paymentMethodType, SutProvider sutProvider) { var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.PurchaseOrganizationAsync(null, paymentMethodType, null, null, 0, 0, false, null)); + () => sutProvider.Sut.PurchaseOrganizationAsync(null, paymentMethodType, null, null, 0, 0, false, null, false, -1, -1)); Assert.Equal("Payment method is not supported at this time.", exception.Message); } @@ -39,7 +39,7 @@ public async void PurchaseOrganizationAsync_Invalid(PaymentMethodType paymentMet [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Stripe_ProviderOrg_Coupon_Add(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo, bool provider = true) { - var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually); + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -52,7 +52,7 @@ public async void PurchaseOrganizationAsync_Stripe_ProviderOrg_Coupon_Add(SutPro CurrentPeriodEnd = DateTime.Today.AddDays(10), }); - var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo, provider); + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo, provider); Assert.Null(result); Assert.Equal(GatewayType.Stripe, organization.Gateway); @@ -87,9 +87,10 @@ await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) + public async void PurchaseOrganizationAsync_SM_Stripe_ProviderOrg_Coupon_Add(SutProvider sutProvider, Organization organization, + string paymentToken, TaxInfo taxInfo, bool provider = true) { - var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually); + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -100,9 +101,11 @@ public async void PurchaseOrganizationAsync_Stripe(SutProvider(c => c.Description == organization.BusinessName && c.Email == organization.BillingEmail && c.Source == paymentToken && c.PaymentMethod == null && + c.Coupon == "msp-discount-35" && !c.Metadata.Any() && c.InvoiceSettings.DefaultPaymentMethod == null && - c.InvoiceSettings.CustomFields != null && - c.InvoiceSettings.CustomFields[0].Name == "Organization" && - c.InvoiceSettings.CustomFields[0].Value == organization.SubscriberName().Substring(0, 30) && c.Address.Country == taxInfo.BillingAddressCountry && c.Address.PostalCode == taxInfo.BillingAddressPostalCode && c.Address.Line1 == taxInfo.BillingAddressLine1 && @@ -134,15 +135,14 @@ await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) + public async void PurchaseOrganizationAsync_Stripe(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually); - paymentToken = "pm_" + paymentToken; + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -155,7 +155,8 @@ public async void PurchaseOrganizationAsync_Stripe_PM(SutProvider(c => c.Description == organization.BusinessName && c.Email == organization.BillingEmail && - c.Source == null && - c.PaymentMethod == paymentToken && + c.Source == paymentToken && + c.PaymentMethod == null && !c.Metadata.Any() && - c.InvoiceSettings.DefaultPaymentMethod == paymentToken && + c.InvoiceSettings.DefaultPaymentMethod == null && c.InvoiceSettings.CustomFields != null && c.InvoiceSettings.CustomFields[0].Name == "Organization" && c.InvoiceSettings.CustomFields[0].Value == organization.SubscriberName().Substring(0, 30) && @@ -187,14 +187,43 @@ await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually); + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + + var stripeAdapter = sutProvider.GetDependency(); + stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer + { + Id = "C-1", + }); + stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription + { + Id = "S-1", + CurrentPeriodEnd = DateTime.Today.AddDays(10), + }); + sutProvider.GetDependency().GetByLocationAsync(Arg.Is(t => + t.Country == taxInfo.BillingAddressCountry && t.PostalCode == taxInfo.BillingAddressPostalCode)) + .Returns(new List { new() { Id = "T-1" } }); + + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo); + + Assert.Null(result); + + await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is(s => + s.DefaultTaxRates.Count == 1 && + s.DefaultTaxRates[0] == "T-1" + )); + } + + [Theory, BitAutoData] + public async void PurchaseOrganizationAsync_Stripe_TaxRate_SM(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) + { + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -210,7 +239,8 @@ public async void PurchaseOrganizationAsync_Stripe_TaxRate(SutProvider { new() { Id = "T-1" } }); - var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo); + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 2, 2, + false, taxInfo, false, 2, 2); Assert.Null(result); @@ -223,7 +253,7 @@ await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually); + var plan = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); paymentToken = "pm_" + paymentToken; var stripeAdapter = sutProvider.GetDependency(); @@ -253,10 +283,75 @@ public async void PurchaseOrganizationAsync_Stripe_Declined(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) + { + var plan = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + paymentToken = "pm_" + paymentToken; + + var stripeAdapter = sutProvider.GetDependency(); + stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer + { + Id = "C-1", + }); + stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription + { + Id = "S-1", + CurrentPeriodEnd = DateTime.Today.AddDays(10), + Status = "incomplete", + LatestInvoice = new Stripe.Invoice + { + PaymentIntent = new Stripe.PaymentIntent + { + Status = "requires_payment_method", + }, + }, + }); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, + 1, 12, false, taxInfo, false, 10, 10)); + + Assert.Equal("Payment method was declined.", exception.Message); + + await stripeAdapter.Received(1).CustomerDeleteAsync("C-1"); + } + [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Stripe_RequiresAction(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually); + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + + var stripeAdapter = sutProvider.GetDependency(); + stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer + { + Id = "C-1", + }); + stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription + { + Id = "S-1", + CurrentPeriodEnd = DateTime.Today.AddDays(10), + Status = "incomplete", + LatestInvoice = new Stripe.Invoice + { + PaymentIntent = new Stripe.PaymentIntent + { + Status = "requires_action", + ClientSecret = "clientSecret", + }, + }, + }); + + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo); + + Assert.Equal("clientSecret", result); + Assert.False(organization.Enabled); + } + + [Theory, BitAutoData] + public async void PurchaseOrganizationAsync_SM_Stripe_RequiresAction(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) + { + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -278,7 +373,8 @@ public async void PurchaseOrganizationAsync_Stripe_RequiresAction(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually); + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -310,7 +406,7 @@ public async void PurchaseOrganizationAsync_Paypal(SutProvider(); braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult); - var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo); + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, 0, 0, false, taxInfo); Assert.Null(result); Assert.Equal(GatewayType.Stripe, organization.Gateway); @@ -343,10 +439,87 @@ await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) + { + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + + var stripeAdapter = sutProvider.GetDependency(); + stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer + { + Id = "C-1", + }); + stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription + { + Id = "S-1", + CurrentPeriodEnd = DateTime.Today.AddDays(10), + }); + + var customer = Substitute.For(); + customer.Id.ReturnsForAnyArgs("Braintree-Id"); + customer.PaymentMethods.ReturnsForAnyArgs(new[] { Substitute.For() }); + var customerResult = Substitute.For>(); + customerResult.IsSuccess().Returns(true); + customerResult.Target.ReturnsForAnyArgs(customer); + + var braintreeGateway = sutProvider.GetDependency(); + braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult); + + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, + 2, 10, false, taxInfo, false, 10, 10); + + Assert.Null(result); + Assert.Equal(GatewayType.Stripe, organization.Gateway); + Assert.Equal("C-1", organization.GatewayCustomerId); + Assert.Equal("S-1", organization.GatewaySubscriptionId); + Assert.True(organization.Enabled); + Assert.Equal(DateTime.Today.AddDays(10), organization.ExpirationDate); + + await stripeAdapter.Received().CustomerCreateAsync(Arg.Is(c => + c.Description == organization.BusinessName && + c.Email == organization.BillingEmail && + c.PaymentMethod == null && + c.Metadata.Count == 1 && + c.Metadata["btCustomerId"] == "Braintree-Id" && + c.InvoiceSettings.DefaultPaymentMethod == null && + c.Address.Country == taxInfo.BillingAddressCountry && + c.Address.PostalCode == taxInfo.BillingAddressPostalCode && + c.Address.Line1 == taxInfo.BillingAddressLine1 && + c.Address.Line2 == taxInfo.BillingAddressLine2 && + c.Address.City == taxInfo.BillingAddressCity && + c.Address.State == taxInfo.BillingAddressState && + c.TaxIdData == null + )); + + await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is(s => + s.Customer == "C-1" && + s.Expand[0] == "latest_invoice.payment_intent" && + s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() && + s.Items.Count > 0 + )); + } + [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually); + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + + var customerResult = Substitute.For>(); + customerResult.IsSuccess().Returns(false); + + var braintreeGateway = sutProvider.GetDependency(); + braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, 0, 0, false, taxInfo)); + + Assert.Equal("Failed to create PayPal customer record.", exception.Message); + } + + [Theory, BitAutoData] + public async void PurchaseOrganizationAsync_SM_Paypal_FailedCreate(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) + { + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); var customerResult = Substitute.For>(); customerResult.IsSuccess().Returns(false); @@ -355,7 +528,8 @@ public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider( - () => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo)); + () => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, + 1, 1, false, taxInfo, false, 8, 8)); Assert.Equal("Failed to create PayPal customer record.", exception.Message); } @@ -363,7 +537,7 @@ public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually); + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); paymentToken = "pm_" + paymentToken; var stripeAdapter = sutProvider.GetDependency(); @@ -396,7 +570,7 @@ public async void PurchaseOrganizationAsync_PayPal_Declined(SutProvider( - () => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo)); + () => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, 0, 0, false, taxInfo)); Assert.Equal("Payment method was declined.", exception.Message); @@ -425,8 +599,36 @@ public async void UpgradeFreeOrganizationAsync_Success(SutProvider p.Type == PlanType.EnterpriseAnnually); - var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plan, 0, 0, false, taxInfo); + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plans, 0, 0, false, taxInfo); + + Assert.Null(result); + } + + [Theory, BitAutoData] + public async void UpgradeFreeOrganizationAsync_SM_Success(SutProvider sutProvider, + Organization organization, TaxInfo taxInfo) + { + organization.GatewaySubscriptionId = null; + var stripeAdapter = sutProvider.GetDependency(); + stripeAdapter.CustomerGetAsync(default).ReturnsForAnyArgs(new Stripe.Customer + { + Id = "C-1", + Metadata = new Dictionary + { + { "btCustomerId", "B-123" }, + } + }); + stripeAdapter.InvoiceUpcomingAsync(default).ReturnsForAnyArgs(new Stripe.Invoice + { + PaymentIntent = new Stripe.PaymentIntent { Status = "requires_payment_method", }, + AmountDue = 0 + }); + stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription { }); + + var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plans, 0, 0, + false, taxInfo); Assert.Null(result); } diff --git a/util/Migrator/DbScripts/2023-06-08_00_OrgUserReadOccupiedSmSeatCountByOrgId.sql b/util/Migrator/DbScripts/2023-06-08_00_OrgUserReadOccupiedSmSeatCountByOrgId.sql new file mode 100644 index 000000000000..bb31b1e6007b --- /dev/null +++ b/util/Migrator/DbScripts/2023-06-08_00_OrgUserReadOccupiedSmSeatCountByOrgId.sql @@ -0,0 +1,32 @@ +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_ReadOccupiedSmSeatCountByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + COUNT(1) + FROM + [dbo].[OrganizationUserView] + WHERE + OrganizationId = @OrganizationId + AND Status >= 0 --Invited + AND AccessSecretsManager = 1 +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[ServiceAccount_ReadCountByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + COUNT(1) + FROM + [dbo].[ServiceAccount] + WHERE + OrganizationId = @OrganizationId +END +GO + From 906267242b1b2b2a90086f530fce52770b745fed Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Mon, 3 Jul 2023 14:47:47 -0400 Subject: [PATCH 04/41] SM-802: Add SecretsManagerBetaColumn SQL migration and Org table update --- src/Sql/dbo/Tables/Organization.sql | 1 + ...2023-07-03_00_SecretsManagerBetaColumn.sql | 391 ++++++++++++++++++ 2 files changed, 392 insertions(+) create mode 100644 util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql diff --git a/src/Sql/dbo/Tables/Organization.sql b/src/Sql/dbo/Tables/Organization.sql index d064401171fc..e38cd4b25953 100644 --- a/src/Sql/dbo/Tables/Organization.sql +++ b/src/Sql/dbo/Tables/Organization.sql @@ -50,6 +50,7 @@ [SmServiceAccounts] INT NULL, [MaxAutoscaleSmSeats] INT NULL, [MaxAutoscaleSmServiceAccounts] INT NULL, + [SecretsManagerBeta] BIT NOT NULL CONSTRAINT [DF_Organization_SecretsManagerBeta] DEFAULT (0), CONSTRAINT [PK_Organization] PRIMARY KEY CLUSTERED ([Id] ASC) ); diff --git a/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql b/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql new file mode 100644 index 000000000000..6ceb8e63a0ed --- /dev/null +++ b/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql @@ -0,0 +1,391 @@ +-- Add SecretsManagerBeta Column +IF COL_LENGTH('[dbo].[Organization]', 'SecretsManagerBeta') IS NULL +BEGIN +ALTER TABLE + [dbo].[Organization] + ADD [SecretsManagerBeta] BIT NOT NULL + CONSTRAINT [DF_Organization_SecretsManagerBeta] + DEFAULT (0); +END +GO + +-- Insert value in column 'SecretsManagerBeta' in Organization_Create +CREATE or ALTER PROCEDURE [dbo].[Organization_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @Identifier NVARCHAR(50), + @Name NVARCHAR(50), + @BusinessName NVARCHAR(50), + @BusinessAddress1 NVARCHAR(50), + @BusinessAddress2 NVARCHAR(50), + @BusinessAddress3 NVARCHAR(50), + @BusinessCountry VARCHAR(2), + @BusinessTaxNumber NVARCHAR(30), + @BillingEmail NVARCHAR(256), + @Plan NVARCHAR(50), + @PlanType TINYINT, + @Seats INT, + @MaxCollections SMALLINT, + @UsePolicies BIT, + @UseSso BIT, + @UseGroups BIT, + @UseDirectory BIT, + @UseEvents BIT, + @UseTotp BIT, + @Use2fa BIT, + @UseApi BIT, + @UseResetPassword BIT, + @SelfHost BIT, + @UsersGetPremium BIT, + @Storage BIGINT, + @MaxStorageGb SMALLINT, + @Gateway TINYINT, + @GatewayCustomerId VARCHAR(50), + @GatewaySubscriptionId VARCHAR(50), + @ReferenceData VARCHAR(MAX), + @Enabled BIT, + @LicenseKey VARCHAR(100), + @PublicKey VARCHAR(MAX), + @PrivateKey VARCHAR(MAX), + @TwoFactorProviders NVARCHAR(MAX), + @ExpirationDate DATETIME2(7), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @OwnersNotifiedOfAutoscaling DATETIME2(7), + @MaxAutoscaleSeats INT, + @UseKeyConnector BIT = 0, + @UseScim BIT = 0, + @UseCustomPermissions BIT = 0, + @UseSecretsManager BIT = 0, + @Status TINYINT = 0, + @UsePasswordManager BIT = 1, + @SmSeats INT = null, + @SmServiceAccounts INT = null, + @MaxAutoscaleSmSeats INT= null, + @MaxAutoscaleSmServiceAccounts INT = null, + @SecretsManagerBeta BIT = 0 +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[Organization] + ( + [Id], + [Identifier], + [Name], + [BusinessName], + [BusinessAddress1], + [BusinessAddress2], + [BusinessAddress3], + [BusinessCountry], + [BusinessTaxNumber], + [BillingEmail], + [Plan], + [PlanType], + [Seats], + [MaxCollections], + [UsePolicies], + [UseSso], + [UseGroups], + [UseDirectory], + [UseEvents], + [UseTotp], + [Use2fa], + [UseApi], + [UseResetPassword], + [SelfHost], + [UsersGetPremium], + [Storage], + [MaxStorageGb], + [Gateway], + [GatewayCustomerId], + [GatewaySubscriptionId], + [ReferenceData], + [Enabled], + [LicenseKey], + [PublicKey], + [PrivateKey], + [TwoFactorProviders], + [ExpirationDate], + [CreationDate], + [RevisionDate], + [OwnersNotifiedOfAutoscaling], + [MaxAutoscaleSeats], + [UseKeyConnector], + [UseScim], + [UseCustomPermissions], + [UseSecretsManager], + [Status], + [UsePasswordManager], + [SmSeats], + [SmServiceAccounts], + [MaxAutoscaleSmSeats], + [MaxAutoscaleSmServiceAccounts], + [SecretsManagerBeta] + ) + VALUES + ( + @Id, + @Identifier, + @Name, + @BusinessName, + @BusinessAddress1, + @BusinessAddress2, + @BusinessAddress3, + @BusinessCountry, + @BusinessTaxNumber, + @BillingEmail, + @Plan, + @PlanType, + @Seats, + @MaxCollections, + @UsePolicies, + @UseSso, + @UseGroups, + @UseDirectory, + @UseEvents, + @UseTotp, + @Use2fa, + @UseApi, + @UseResetPassword, + @SelfHost, + @UsersGetPremium, + @Storage, + @MaxStorageGb, + @Gateway, + @GatewayCustomerId, + @GatewaySubscriptionId, + @ReferenceData, + @Enabled, + @LicenseKey, + @PublicKey, + @PrivateKey, + @TwoFactorProviders, + @ExpirationDate, + @CreationDate, + @RevisionDate, + @OwnersNotifiedOfAutoscaling, + @MaxAutoscaleSeats, + @UseKeyConnector, + @UseScim, + @UseCustomPermissions, + @UseSecretsManager, + @Status, + @UsePasswordManager, + @SmSeats, + @SmServiceAccounts, + @MaxAutoscaleSmSeats, + @MaxAutoscaleSmServiceAccounts, + @SecretsManagerBeta + ) +END +GO + +-- Insert value in column `SecretsManagerBeta` in Organization_Update +CREATE OR ALTER PROCEDURE [dbo].[Organization_Update] + @Id UNIQUEIDENTIFIER, + @Identifier NVARCHAR(50), + @Name NVARCHAR(50), + @BusinessName NVARCHAR(50), + @BusinessAddress1 NVARCHAR(50), + @BusinessAddress2 NVARCHAR(50), + @BusinessAddress3 NVARCHAR(50), + @BusinessCountry VARCHAR(2), + @BusinessTaxNumber NVARCHAR(30), + @BillingEmail NVARCHAR(256), + @Plan NVARCHAR(50), + @PlanType TINYINT, + @Seats INT, + @MaxCollections SMALLINT, + @UsePolicies BIT, + @UseSso BIT, + @UseGroups BIT, + @UseDirectory BIT, + @UseEvents BIT, + @UseTotp BIT, + @Use2fa BIT, + @UseApi BIT, + @UseResetPassword BIT, + @SelfHost BIT, + @UsersGetPremium BIT, + @Storage BIGINT, + @MaxStorageGb SMALLINT, + @Gateway TINYINT, + @GatewayCustomerId VARCHAR(50), + @GatewaySubscriptionId VARCHAR(50), + @ReferenceData VARCHAR(MAX), + @Enabled BIT, + @LicenseKey VARCHAR(100), + @PublicKey VARCHAR(MAX), + @PrivateKey VARCHAR(MAX), + @TwoFactorProviders NVARCHAR(MAX), + @ExpirationDate DATETIME2(7), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @OwnersNotifiedOfAutoscaling DATETIME2(7), + @MaxAutoscaleSeats INT, + @UseKeyConnector BIT = 0, + @UseScim BIT = 0, + @UseCustomPermissions BIT = 0, + @UseSecretsManager BIT = 0, + @Status TINYINT = 0, + @UsePasswordManager BIT = 1, + @SmSeats INT = null, + @SmServiceAccounts INT = null, + @MaxAutoscaleSmSeats INT = null, + @MaxAutoscaleSmServiceAccounts INT = null, + @SecretsManagerBeta BIT = 0 +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[Organization] + SET + [Identifier] = @Identifier, + [Name] = @Name, + [BusinessName] = @BusinessName, + [BusinessAddress1] = @BusinessAddress1, + [BusinessAddress2] = @BusinessAddress2, + [BusinessAddress3] = @BusinessAddress3, + [BusinessCountry] = @BusinessCountry, + [BusinessTaxNumber] = @BusinessTaxNumber, + [BillingEmail] = @BillingEmail, + [Plan] = @Plan, + [PlanType] = @PlanType, + [Seats] = @Seats, + [MaxCollections] = @MaxCollections, + [UsePolicies] = @UsePolicies, + [UseSso] = @UseSso, + [UseGroups] = @UseGroups, + [UseDirectory] = @UseDirectory, + [UseEvents] = @UseEvents, + [UseTotp] = @UseTotp, + [Use2fa] = @Use2fa, + [UseApi] = @UseApi, + [UseResetPassword] = @UseResetPassword, + [SelfHost] = @SelfHost, + [UsersGetPremium] = @UsersGetPremium, + [Storage] = @Storage, + [MaxStorageGb] = @MaxStorageGb, + [Gateway] = @Gateway, + [GatewayCustomerId] = @GatewayCustomerId, + [GatewaySubscriptionId] = @GatewaySubscriptionId, + [ReferenceData] = @ReferenceData, + [Enabled] = @Enabled, + [LicenseKey] = @LicenseKey, + [PublicKey] = @PublicKey, + [PrivateKey] = @PrivateKey, + [TwoFactorProviders] = @TwoFactorProviders, + [ExpirationDate] = @ExpirationDate, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate, + [OwnersNotifiedOfAutoscaling] = @OwnersNotifiedOfAutoscaling, + [MaxAutoscaleSeats] = @MaxAutoscaleSeats, + [UseKeyConnector] = @UseKeyConnector, + [UseScim] = @UseScim, + [UseCustomPermissions] = @UseCustomPermissions, + [UseSecretsManager] = @UseSecretsManager, + [Status] = @Status, + [UsePasswordManager] = @UsePasswordManager, + [SmSeats] = @SmSeats, + [SmServiceAccounts] = @SmServiceAccounts, + [MaxAutoscaleSmSeats] = @MaxAutoscaleSmSeats, + [MaxAutoscaleSmServiceAccounts] = @MaxAutoscaleSmServiceAccounts, + [SecretsManagerBeta] = @SecretsManagerBeta + WHERE + [Id] = @Id +END +GO + +-- Add column `SecretsManagerBeta` in OrganizationUserOrganizationDetailsView +CREATE OR ALTER VIEW [dbo].[OrganizationUserOrganizationDetailsView] +AS +SELECT + OU.[UserId], + OU.[OrganizationId], + O.[Name], + O.[Enabled], + O.[PlanType], + O.[UsePolicies], + O.[UseSso], + O.[UseKeyConnector], + O.[UseScim], + O.[UseGroups], + O.[UseDirectory], + O.[UseEvents], + O.[UseTotp], + O.[Use2fa], + O.[UseApi], + O.[UseResetPassword], + O.[SelfHost], + O.[UsersGetPremium], + O.[UseCustomPermissions], + O.[UseSecretsManager], + O.[Seats], + O.[MaxCollections], + O.[MaxStorageGb], + O.[Identifier], + OU.[Key], + OU.[ResetPasswordKey], + O.[PublicKey], + O.[PrivateKey], + OU.[Status], + OU.[Type], + SU.[ExternalId] SsoExternalId, + OU.[Permissions], + PO.[ProviderId], + P.[Name] ProviderName, + P.[Type] ProviderType, + SS.[Data] SsoConfig, + OS.[FriendlyName] FamilySponsorshipFriendlyName, + OS.[LastSyncDate] FamilySponsorshipLastSyncDate, + OS.[ToDelete] FamilySponsorshipToDelete, + OS.[ValidUntil] FamilySponsorshipValidUntil, + OU.[AccessSecretsManager], + O.[UsePasswordManager], + O.[SmSeats], + O.[SmServiceAccounts], + O.[SecretsManagerBeta] +FROM + [dbo].[OrganizationUser] OU +LEFT JOIN + [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] +LEFT JOIN + [dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId] +LEFT JOIN + [dbo].[ProviderOrganization] PO ON PO.[OrganizationId] = O.[Id] +LEFT JOIN + [dbo].[Provider] P ON P.[Id] = PO.[ProviderId] +LEFT JOIN + [dbo].[SsoConfig] SS ON SS.[OrganizationId] = OU.[OrganizationId] +LEFT JOIN + [dbo].[OrganizationSponsorship] OS ON OS.[SponsoringOrganizationUserID] = OU.[Id] +GO + +-- Update the metadata for the following stored procedures and view so that the SecretsManagerBeta column is included +IF OBJECT_ID('[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus]'; +END +GO + +IF OBJECT_ID('[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]'; +END +GO + +IF OBJECT_ID('[dbo].[OrganizationView]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationView]'; +END +GO + +-- Set SecretsManagerBeta to 1 for Organizations where UseSecretsManager is 1. +-- At this time (at GA release) these are the Organizations using SecretsManager in beta. +-- We want to mark they are using SecretsManager beta for the sunset period, before +-- eventually removing the SecretsManagerBeta column and turning off beta access. +UPDATE [dbo].[Organization] +SET [SecretsManagerBeta] = 1 +WHERE [UseSecretsManager] = 1 +GO From 66185f0f19662f2dfd356045f59e87fb0269edf2 Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Mon, 3 Jul 2023 14:54:16 -0400 Subject: [PATCH 05/41] SM-802: Run EF Migrations for SecretsManagerBeta --- src/Core/Entities/Organization.cs | 1 + ...0_SecretsManagerBetaColumn.sql.Designer.cs | 2228 ++++++++++++++++ ...3-07-03_00_SecretsManagerBetaColumn.sql.cs | 26 + .../DatabaseContextModelSnapshot.cs | 3 + ...0_SecretsManagerBetaColumn.sql.Designer.cs | 2239 +++++++++++++++++ ...3-07-03_00_SecretsManagerBetaColumn.sql.cs | 26 + .../DatabaseContextModelSnapshot.cs | 3 + ...0_SecretsManagerBetaColumn.sql.Designer.cs | 2226 ++++++++++++++++ ...3-07-03_00_SecretsManagerBetaColumn.sql.cs | 26 + .../DatabaseContextModelSnapshot.cs | 3 + 10 files changed, 6781 insertions(+) create mode 100644 util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs create mode 100644 util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs create mode 100644 util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs create mode 100644 util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs create mode 100644 util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs create mode 100644 util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs diff --git a/src/Core/Entities/Organization.cs b/src/Core/Entities/Organization.cs index 5d8ddf5855b9..5545fdb4c0f8 100644 --- a/src/Core/Entities/Organization.cs +++ b/src/Core/Entities/Organization.cs @@ -77,6 +77,7 @@ public class Organization : ITableObject, ISubscriber, IStorable, IStorabl public int? SmServiceAccounts { get; set; } public int? MaxAutoscaleSmSeats { get; set; } public int? MaxAutoscaleSmServiceAccounts { get; set; } + public bool SecretsManagerBeta { get; set; } public void SetNewId() { diff --git a/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs b/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs new file mode 100644 index 000000000000..b393b8b43f32 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs @@ -0,0 +1,2228 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql")] + partial class _20230703_00_SecretsManagerBetaColumnsql + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("varchar(25)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("AuthenticationDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MasterPasswordHash") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ResponseDate") + .HasColumnType("datetime(6)"); + + b.Property("ResponseDeviceId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("GranteeId") + .HasColumnType("char(36)"); + + b.Property("GrantorId") + .HasColumnType("char(36)"); + + b.Property("KeyEncrypted") + .HasColumnType("longtext"); + + b.Property("LastNotificationDate") + .HasColumnType("datetime(6)"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("WaitTimeDays") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ConsumedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("longtext"); + + b.Property("EncryptedPublicKey") + .HasColumnType("longtext"); + + b.Property("EncryptedUserKey") + .HasColumnType("longtext"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ActingUserId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("DeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("DomainName") + .HasColumnType("longtext"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("PolicyId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("ProviderOrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderUserId") + .HasColumnType("char(36)"); + + b.Property("SecretId") + .HasColumnType("char(36)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + + b.Property("SystemUser") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("int"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("int"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("SecretsManagerBeta") + .HasColumnType("tinyint(1)"); + + b.Property("SelfHost") + .HasColumnType("tinyint(1)"); + + b.Property("SmSeats") + .HasColumnType("int"); + + b.Property("SmServiceAccounts") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("Use2fa") + .HasColumnType("tinyint(1)"); + + b.Property("UseApi") + .HasColumnType("tinyint(1)"); + + b.Property("UseCustomPermissions") + .HasColumnType("tinyint(1)"); + + b.Property("UseDirectory") + .HasColumnType("tinyint(1)"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UsePasswordManager") + .HasColumnType("tinyint(1)"); + + b.Property("UsePolicies") + .HasColumnType("tinyint(1)"); + + b.Property("UseResetPassword") + .HasColumnType("tinyint(1)"); + + b.Property("UseScim") + .HasColumnType("tinyint(1)"); + + b.Property("UseSecretsManager") + .HasColumnType("tinyint(1)"); + + b.Property("UseSso") + .HasColumnType("tinyint(1)"); + + b.Property("UseTotp") + .HasColumnType("tinyint(1)"); + + b.Property("UsersGetPremium") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Config") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("JobRunCount") + .HasColumnType("int"); + + b.Property("LastCheckedDate") + .HasColumnType("datetime(6)"); + + b.Property("NextRunDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Txt") + .HasColumnType("longtext"); + + b.Property("VerifiedDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("LastSyncDate") + .HasColumnType("datetime(6)"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("tinyint unsigned"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("ToDelete") + .HasColumnType("tinyint(1)"); + + b.Property("ValidUntil") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("AccessSecretsManager") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BillingPhone") + .HasColumnType("longtext"); + + b.Property("BusinessAddress1") + .HasColumnType("longtext"); + + b.Property("BusinessAddress2") + .HasColumnType("longtext"); + + b.Property("BusinessAddress3") + .HasColumnType("longtext"); + + b.Property("BusinessCountry") + .HasColumnType("longtext"); + + b.Property("BusinessName") + .HasColumnType("longtext"); + + b.Property("BusinessTaxNumber") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Settings") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MaxAccessCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Rate") + .HasColumnType("decimal(65,30)"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PaymentMethodType") + .HasColumnType("tinyint unsigned"); + + b.Property("Refunded") + .HasColumnType("tinyint(1)"); + + b.Property("RefundedAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccountRevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailVerified") + .HasColumnType("tinyint(1)"); + + b.Property("EquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("longtext"); + + b.Property("FailedLoginCount") + .HasColumnType("int"); + + b.Property("ForcePasswordReset") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Kdf") + .HasColumnType("tinyint unsigned"); + + b.Property("KdfIterations") + .HasColumnType("int"); + + b.Property("KdfMemory") + .HasColumnType("int"); + + b.Property("KdfParallelism") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LastEmailChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LastFailedLoginDate") + .HasColumnType("datetime(6)"); + + b.Property("LastKdfChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LastKeyRotationDate") + .HasColumnType("datetime(6)"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Premium") + .HasColumnType("tinyint(1)"); + + b.Property("PremiumExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RenewalReminderDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Read") + .HasColumnType("tinyint(1)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Write") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Note") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Attachments") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Favorites") + .HasColumnType("longtext"); + + b.Property("Folders") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Reprompt") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("char(36)"); + + b.Property("SecretsId") + .HasColumnType("char(36)"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs b/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs new file mode 100644 index 000000000000..3507b1caad50 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations +{ + public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SecretsManagerBeta", + table: "Organization", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretsManagerBeta", + table: "Organization"); + } + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index 95a769229f7e..f64675e25668 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -608,6 +608,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Seats") .HasColumnType("int"); + b.Property("SecretsManagerBeta") + .HasColumnType("tinyint(1)"); + b.Property("SelfHost") .HasColumnType("tinyint(1)"); diff --git a/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs b/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs new file mode 100644 index 000000000000..ef5e0a548138 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs @@ -0,0 +1,2239 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql")] + partial class _20230703_00_SecretsManagerBetaColumnsql + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") + .HasAnnotation("ProductVersion", "6.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("AuthenticationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MasterPasswordHash") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ResponseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ResponseDeviceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GranteeId") + .HasColumnType("uuid"); + + b.Property("GrantorId") + .HasColumnType("uuid"); + + b.Property("KeyEncrypted") + .HasColumnType("text"); + + b.Property("LastNotificationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitTimeDays") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("text"); + + b.Property("EncryptedPublicKey") + .HasColumnType("text"); + + b.Property("EncryptedUserKey") + .HasColumnType("text"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ActingUserId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceType") + .HasColumnType("smallint"); + + b.Property("DomainName") + .HasColumnType("text"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("PolicyId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("ProviderOrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderUserId") + .HasColumnType("uuid"); + + b.Property("SecretId") + .HasColumnType("uuid"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + + b.Property("SystemUser") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("integer"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("integer"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp with time zone"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("SecretsManagerBeta") + .HasColumnType("boolean"); + + b.Property("SelfHost") + .HasColumnType("boolean"); + + b.Property("SmSeats") + .HasColumnType("integer"); + + b.Property("SmServiceAccounts") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("Use2fa") + .HasColumnType("boolean"); + + b.Property("UseApi") + .HasColumnType("boolean"); + + b.Property("UseCustomPermissions") + .HasColumnType("boolean"); + + b.Property("UseDirectory") + .HasColumnType("boolean"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UsePasswordManager") + .HasColumnType("boolean"); + + b.Property("UsePolicies") + .HasColumnType("boolean"); + + b.Property("UseResetPassword") + .HasColumnType("boolean"); + + b.Property("UseScim") + .HasColumnType("boolean"); + + b.Property("UseSecretsManager") + .HasColumnType("boolean"); + + b.Property("UseSso") + .HasColumnType("boolean"); + + b.Property("UseTotp") + .HasColumnType("boolean"); + + b.Property("UsersGetPremium") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Config") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("JobRunCount") + .HasColumnType("integer"); + + b.Property("LastCheckedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("NextRunDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Txt") + .HasColumnType("text"); + + b.Property("VerifiedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LastSyncDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("smallint"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("ToDelete") + .HasColumnType("boolean"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("AccessSecretsManager") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BillingPhone") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MaxAccessCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PaymentMethodType") + .HasColumnType("smallint"); + + b.Property("Refunded") + .HasColumnType("boolean"); + + b.Property("RefundedAmount") + .HasColumnType("numeric"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountRevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("character varying(7)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("EquivalentDomains") + .HasColumnType("text"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("text"); + + b.Property("FailedLoginCount") + .HasColumnType("integer"); + + b.Property("ForcePasswordReset") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Kdf") + .HasColumnType("smallint"); + + b.Property("KdfIterations") + .HasColumnType("integer"); + + b.Property("KdfMemory") + .HasColumnType("integer"); + + b.Property("KdfParallelism") + .HasColumnType("integer"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LastEmailChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastFailedLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastKdfChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastKeyRotationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Premium") + .HasColumnType("boolean"); + + b.Property("PremiumExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RenewalReminderDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("text"); + + b.Property("Read") + .HasColumnType("boolean"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Write") + .HasColumnType("boolean"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Favorites") + .HasColumnType("text"); + + b.Property("Folders") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Reprompt") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("uuid"); + + b.Property("SecretsId") + .HasColumnType("uuid"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs b/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs new file mode 100644 index 000000000000..316e73f9a550 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations +{ + public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SecretsManagerBeta", + table: "Organization", + type: "boolean", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretsManagerBeta", + table: "Organization"); + } + } +} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index c070e191063c..56cd8c67e09d 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -618,6 +618,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Seats") .HasColumnType("integer"); + b.Property("SecretsManagerBeta") + .HasColumnType("boolean"); + b.Property("SelfHost") .HasColumnType("boolean"); diff --git a/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs b/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs new file mode 100644 index 000000000000..37752c6f7527 --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs @@ -0,0 +1,2226 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql")] + partial class _20230703_00_SecretsManagerBetaColumnsql + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.12"); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("TEXT"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("AuthenticationDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHash") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("RequestDeviceType") + .HasColumnType("INTEGER"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ResponseDate") + .HasColumnType("TEXT"); + + b.Property("ResponseDeviceId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("GranteeId") + .HasColumnType("TEXT"); + + b.Property("GrantorId") + .HasColumnType("TEXT"); + + b.Property("KeyEncrypted") + .HasColumnType("TEXT"); + + b.Property("LastNotificationDate") + .HasColumnType("TEXT"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("WaitTimeDays") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("TEXT"); + + b.Property("EncryptedPublicKey") + .HasColumnType("TEXT"); + + b.Property("EncryptedUserKey") + .HasColumnType("TEXT"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ActingUserId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("DeviceType") + .HasColumnType("INTEGER"); + + b.Property("DomainName") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("InstallationId") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("PolicyId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("ProviderOrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderUserId") + .HasColumnType("TEXT"); + + b.Property("SecretId") + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + + b.Property("SystemUser") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessAll") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("INTEGER"); + + b.Property("MaxCollections") + .HasColumnType("INTEGER"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("TEXT"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PlanType") + .HasColumnType("INTEGER"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Seats") + .HasColumnType("INTEGER"); + + b.Property("SecretsManagerBeta") + .HasColumnType("INTEGER"); + + b.Property("SelfHost") + .HasColumnType("INTEGER"); + + b.Property("SmSeats") + .HasColumnType("INTEGER"); + + b.Property("SmServiceAccounts") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("Use2fa") + .HasColumnType("INTEGER"); + + b.Property("UseApi") + .HasColumnType("INTEGER"); + + b.Property("UseCustomPermissions") + .HasColumnType("INTEGER"); + + b.Property("UseDirectory") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.Property("UseGroups") + .HasColumnType("INTEGER"); + + b.Property("UseKeyConnector") + .HasColumnType("INTEGER"); + + b.Property("UsePasswordManager") + .HasColumnType("INTEGER"); + + b.Property("UsePolicies") + .HasColumnType("INTEGER"); + + b.Property("UseResetPassword") + .HasColumnType("INTEGER"); + + b.Property("UseScim") + .HasColumnType("INTEGER"); + + b.Property("UseSecretsManager") + .HasColumnType("INTEGER"); + + b.Property("UseSso") + .HasColumnType("INTEGER"); + + b.Property("UseTotp") + .HasColumnType("INTEGER"); + + b.Property("UsersGetPremium") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Config") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("JobRunCount") + .HasColumnType("INTEGER"); + + b.Property("LastCheckedDate") + .HasColumnType("TEXT"); + + b.Property("NextRunDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Txt") + .HasColumnType("TEXT"); + + b.Property("VerifiedDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("LastSyncDate") + .HasColumnType("TEXT"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PlanSponsorshipType") + .HasColumnType("INTEGER"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("ToDelete") + .HasColumnType("INTEGER"); + + b.Property("ValidUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessAll") + .HasColumnType("INTEGER"); + + b.Property("AccessSecretsManager") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ResetPasswordKey") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BillingEmail") + .HasColumnType("TEXT"); + + b.Property("BillingPhone") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCount") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeletionDate") + .HasColumnType("TEXT"); + + b.Property("Disabled") + .HasColumnType("INTEGER"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("HideEmail") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MaxAccessCount") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Rate") + .HasColumnType("TEXT"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Amount") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PaymentMethodType") + .HasColumnType("INTEGER"); + + b.Property("Refunded") + .HasColumnType("INTEGER"); + + b.Property("RefundedAmount") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccountRevisionDate") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailVerified") + .HasColumnType("INTEGER"); + + b.Property("EquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("FailedLoginCount") + .HasColumnType("INTEGER"); + + b.Property("ForcePasswordReset") + .HasColumnType("INTEGER"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Kdf") + .HasColumnType("INTEGER"); + + b.Property("KdfIterations") + .HasColumnType("INTEGER"); + + b.Property("KdfMemory") + .HasColumnType("INTEGER"); + + b.Property("KdfParallelism") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("LastEmailChangeDate") + .HasColumnType("TEXT"); + + b.Property("LastFailedLoginDate") + .HasColumnType("TEXT"); + + b.Property("LastKdfChangeDate") + .HasColumnType("TEXT"); + + b.Property("LastKeyRotationDate") + .HasColumnType("TEXT"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Premium") + .HasColumnType("INTEGER"); + + b.Property("PremiumExpirationDate") + .HasColumnType("TEXT"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RenewalReminderDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("UsesKeyConnector") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Read") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Write") + .HasColumnType("INTEGER"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ExpireAt") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Attachments") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Favorites") + .HasColumnType("TEXT"); + + b.Property("Folders") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Reprompt") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("TEXT"); + + b.Property("SecretsId") + .HasColumnType("TEXT"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs b/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs new file mode 100644 index 000000000000..640534aa99df --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations +{ + public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SecretsManagerBeta", + table: "Organization", + type: "INTEGER", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretsManagerBeta", + table: "Organization"); + } + } +} diff --git a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs index 076610ae31c6..b2689cd29124 100644 --- a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -606,6 +606,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Seats") .HasColumnType("INTEGER"); + b.Property("SecretsManagerBeta") + .HasColumnType("INTEGER"); + b.Property("SelfHost") .HasColumnType("INTEGER"); From c9745cdabaa6014fa17321fed6d82e467c65ac61 Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Mon, 3 Jul 2023 15:20:35 -0400 Subject: [PATCH 06/41] SM-802: Update the two Org procs and View, and move data migration to a separate file --- .../Stored Procedures/Organization_Create.sql | 7 ++-- .../Stored Procedures/Organization_Update.sql | 6 ++-- ...rganizationUserOrganizationDetailsView.sql | 3 +- ...2023-07-03_00_SecretsManagerBetaColumn.sql | 9 ----- ..._SecretsManagerBetaColumnDataMigration.sql | 17 ++++++++++ ...3-07-03_00_SecretsManagerBetaColumn.sql.cs | 33 +++++++++---------- ...3-07-03_00_SecretsManagerBetaColumn.sql.cs | 33 +++++++++---------- ...3-07-03_00_SecretsManagerBetaColumn.sql.cs | 33 +++++++++---------- 8 files changed, 76 insertions(+), 65 deletions(-) create mode 100644 util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql diff --git a/src/Sql/dbo/Stored Procedures/Organization_Create.sql b/src/Sql/dbo/Stored Procedures/Organization_Create.sql index 570e39eb7c10..126690a51252 100644 --- a/src/Sql/dbo/Stored Procedures/Organization_Create.sql +++ b/src/Sql/dbo/Stored Procedures/Organization_Create.sql @@ -49,7 +49,8 @@ @SmSeats INT = null, @SmServiceAccounts INT = null, @MaxAutoscaleSmSeats INT= null, - @MaxAutoscaleSmServiceAccounts INT = null + @MaxAutoscaleSmServiceAccounts INT = null, + @SecretsManagerBeta BIT = 0 AS BEGIN SET NOCOUNT ON @@ -107,6 +108,7 @@ BEGIN [SmServiceAccounts], [MaxAutoscaleSmSeats], [MaxAutoscaleSmServiceAccounts] + [SecretsManagerBeta] ) VALUES ( @@ -160,6 +162,7 @@ BEGIN @SmSeats, @SmServiceAccounts, @MaxAutoscaleSmSeats, - @MaxAutoscaleSmServiceAccounts + @MaxAutoscaleSmServiceAccounts, + @SecretsManagerBeta ) END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Organization_Update.sql b/src/Sql/dbo/Stored Procedures/Organization_Update.sql index f3f17c7b4ea0..c5d38e438d23 100644 --- a/src/Sql/dbo/Stored Procedures/Organization_Update.sql +++ b/src/Sql/dbo/Stored Procedures/Organization_Update.sql @@ -49,7 +49,8 @@ @SmSeats INT = null, @SmServiceAccounts INT = null, @MaxAutoscaleSmSeats INT = null, - @MaxAutoscaleSmServiceAccounts INT = null + @MaxAutoscaleSmServiceAccounts INT = null, + @SecretsManagerBeta BIT = 0 AS BEGIN SET NOCOUNT ON @@ -106,7 +107,8 @@ BEGIN [SmSeats] = @SmSeats, [SmServiceAccounts] = @SmServiceAccounts, [MaxAutoscaleSmSeats] = @MaxAutoscaleSmSeats, - [MaxAutoscaleSmServiceAccounts] = @MaxAutoscaleSmServiceAccounts + [MaxAutoscaleSmServiceAccounts] = @MaxAutoscaleSmServiceAccounts, + [SecretsManagerBeta] = @SecretsManagerBeta WHERE [Id] = @Id END diff --git a/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql b/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql index 2e18b590628a..72b3591fde6a 100644 --- a/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql +++ b/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql @@ -44,7 +44,8 @@ SELECT OU.[AccessSecretsManager], O.[UsePasswordManager], O.[SmSeats], - O.[SmServiceAccounts] + O.[SmServiceAccounts], + O.[SecretsManagerBeta] FROM [dbo].[OrganizationUser] OU LEFT JOIN diff --git a/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql b/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql index 6ceb8e63a0ed..02d7f107c226 100644 --- a/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql +++ b/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql @@ -380,12 +380,3 @@ BEGIN EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationView]'; END GO - --- Set SecretsManagerBeta to 1 for Organizations where UseSecretsManager is 1. --- At this time (at GA release) these are the Organizations using SecretsManager in beta. --- We want to mark they are using SecretsManager beta for the sunset period, before --- eventually removing the SecretsManagerBeta column and turning off beta access. -UPDATE [dbo].[Organization] -SET [SecretsManagerBeta] = 1 -WHERE [UseSecretsManager] = 1 -GO diff --git a/util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql b/util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql new file mode 100644 index 000000000000..47dd263b06df --- /dev/null +++ b/util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql @@ -0,0 +1,17 @@ +/* +This is the data migration script for adding the SecretsManagerBeta column. +The initial migration util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql should be run prior. +There is no final migration. +*/ +IF COL_LENGTH('[dbo].[Organization]', 'SecretsManagerBeta') IS NOT NULL +BEGIN + -- Set SecretsManagerBeta to 1 for Organizations where UseSecretsManager is 1. + -- At this time (at GA release) these are the Organizations using SecretsManager in beta. + -- We want to mark they are using SecretsManager beta for the sunset period, before + -- eventually removing the SecretsManagerBeta column and turning off beta access. + UPDATE [dbo].[Organization] + SET [SecretsManagerBeta] = 1 + WHERE [UseSecretsManager] = 1 + GO +END +GO diff --git a/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs b/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs index 3507b1caad50..331d0f299247 100644 --- a/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs +++ b/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs @@ -2,25 +2,24 @@ #nullable disable -namespace Bit.MySqlMigrations.Migrations +namespace Bit.MySqlMigrations.Migrations; + +public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration { - public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "SecretsManagerBeta", - table: "Organization", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - } + migrationBuilder.AddColumn( + name: "SecretsManagerBeta", + table: "Organization", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + } - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "SecretsManagerBeta", - table: "Organization"); - } + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretsManagerBeta", + table: "Organization"); } } diff --git a/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs b/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs index 316e73f9a550..2a7906cee2ac 100644 --- a/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs +++ b/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs @@ -2,25 +2,24 @@ #nullable disable -namespace Bit.PostgresMigrations.Migrations +namespace Bit.PostgresMigrations.Migrations; + +public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration { - public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "SecretsManagerBeta", - table: "Organization", - type: "boolean", - nullable: false, - defaultValue: false); - } + migrationBuilder.AddColumn( + name: "SecretsManagerBeta", + table: "Organization", + type: "boolean", + nullable: false, + defaultValue: false); + } - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "SecretsManagerBeta", - table: "Organization"); - } + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretsManagerBeta", + table: "Organization"); } } diff --git a/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs b/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs index 640534aa99df..beb54c3389b5 100644 --- a/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs +++ b/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs @@ -2,25 +2,24 @@ #nullable disable -namespace Bit.SqliteMigrations.Migrations +namespace Bit.SqliteMigrations.Migrations; + +public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration { - public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "SecretsManagerBeta", - table: "Organization", - type: "INTEGER", - nullable: false, - defaultValue: false); - } + migrationBuilder.AddColumn( + name: "SecretsManagerBeta", + table: "Organization", + type: "INTEGER", + nullable: false, + defaultValue: false); + } - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "SecretsManagerBeta", - table: "Organization"); - } + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretsManagerBeta", + table: "Organization"); } } From f00f552799713620b20c6b5e37521bed4cda59a7 Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Mon, 3 Jul 2023 15:26:48 -0400 Subject: [PATCH 07/41] SM-802: Add missing comma to Organization_Create --- src/Sql/dbo/Stored Procedures/Organization_Create.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sql/dbo/Stored Procedures/Organization_Create.sql b/src/Sql/dbo/Stored Procedures/Organization_Create.sql index 126690a51252..2b13da44d845 100644 --- a/src/Sql/dbo/Stored Procedures/Organization_Create.sql +++ b/src/Sql/dbo/Stored Procedures/Organization_Create.sql @@ -107,7 +107,7 @@ BEGIN [SmSeats], [SmServiceAccounts], [MaxAutoscaleSmSeats], - [MaxAutoscaleSmServiceAccounts] + [MaxAutoscaleSmServiceAccounts], [SecretsManagerBeta] ) VALUES From 6c0c9c62ad9e1af679132efeb546a2c0233c609b Mon Sep 17 00:00:00 2001 From: Shane Melton Date: Mon, 3 Jul 2023 15:51:51 -0700 Subject: [PATCH 08/41] [AC-1418] Add missing SecretsManagerPlan property to OrganizationResponseModel (#3055) --- .../Models/Response/Organizations/OrganizationResponseModel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs index 4a4edf5d5e8d..6fe73d438905 100644 --- a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs +++ b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs @@ -27,6 +27,7 @@ public OrganizationResponseModel(Organization organization, string obj = "organi BusinessTaxNumber = organization.BusinessTaxNumber; BillingEmail = organization.BillingEmail; Plan = new PlanResponseModel(StaticStore.PasswordManagerPlans.FirstOrDefault(plan => plan.Type == organization.PlanType)); + SecretsManagerPlan = new PlanResponseModel(StaticStore.SecretManagerPlans.FirstOrDefault(plan => plan.Type == organization.PlanType)); PlanType = organization.PlanType; Seats = organization.Seats; MaxAutoscaleSeats = organization.MaxAutoscaleSeats; @@ -65,6 +66,7 @@ public OrganizationResponseModel(Organization organization, string obj = "organi public string BusinessTaxNumber { get; set; } public string BillingEmail { get; set; } public PlanResponseModel Plan { get; set; } + public PlanResponseModel SecretsManagerPlan { get; set; } public PlanType PlanType { get; set; } public int? Seats { get; set; } public int? MaxAutoscaleSeats { get; set; } = null; From 6a5d5a9d91aa3445999258006fbbc4b906ccbccf Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Mon, 3 Jul 2023 21:02:59 -0400 Subject: [PATCH 09/41] SM-802: Remove extra GO statement from data migration script --- .../2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql b/util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql index 47dd263b06df..49df5451c308 100644 --- a/util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql +++ b/util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql @@ -12,6 +12,5 @@ BEGIN UPDATE [dbo].[Organization] SET [SecretsManagerBeta] = 1 WHERE [UseSecretsManager] = 1 - GO END GO From 692c7ff8430867e927cabc59d9735554d523219a Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Tue, 4 Jul 2023 14:36:58 +0100 Subject: [PATCH 10/41] [AC 1460] Update Stripe Configuration (#3070) * change the stripeseat id * change service accountId to align with new product * make all the Id name for consistent --- src/Core/Utilities/SecretsManagerPlanStore.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Core/Utilities/SecretsManagerPlanStore.cs b/src/Core/Utilities/SecretsManagerPlanStore.cs index 981acc3ab31b..d6d0d87acaf6 100644 --- a/src/Core/Utilities/SecretsManagerPlanStore.cs +++ b/src/Core/Utilities/SecretsManagerPlanStore.cs @@ -39,8 +39,8 @@ public static IEnumerable CreatePlan() HasCustomPermissions = true, UpgradeSortOrder = 3, DisplaySortOrder = 3, - StripeSeatPlanId = "sm-enterprise-seat-monthly", - StripeServiceAccountPlanId = "service-account-monthly", + StripeSeatPlanId = "secrets-manager-enterprise-seat-monthly", + StripeServiceAccountPlanId = "secrets-manager-service-account-monthly", BasePrice = 0, SeatPrice = 13, AdditionalPricePerServiceAccount = 0.5M, @@ -77,8 +77,8 @@ public static IEnumerable CreatePlan() HasCustomPermissions = true, UpgradeSortOrder = 3, DisplaySortOrder = 3, - StripeSeatPlanId = "sm-enterprise-seat-annually", - StripeServiceAccountPlanId = "service-account-annually", + StripeSeatPlanId = "secrets-manager-enterprise-seat-annually", + StripeServiceAccountPlanId = "secrets-manager-service-account-annually", BasePrice = 0, SeatPrice = 144, AdditionalPricePerServiceAccount = 6, @@ -107,8 +107,8 @@ public static IEnumerable CreatePlan() UsersGetPremium = true, UpgradeSortOrder = 2, DisplaySortOrder = 2, - StripeSeatPlanId = "sm-teams-seat-monthly", - StripeServiceAccountPlanId = "service-account-monthly", + StripeSeatPlanId = "secrets-manager-teams-seat-monthly", + StripeServiceAccountPlanId = "secrets-manager-service-account-monthly", BasePrice = 0, SeatPrice = 7, AdditionalPricePerServiceAccount = 0.5M, @@ -139,8 +139,8 @@ public static IEnumerable CreatePlan() UpgradeSortOrder = 2, DisplaySortOrder = 2, - StripeSeatPlanId = "sm-teams-seat-annually", - StripeServiceAccountPlanId = "service-account-annually", + StripeSeatPlanId = "secrets-manager-teams-seat-annually", + StripeServiceAccountPlanId = "secrets-manager-service-account-annually", BasePrice = 0, SeatPrice = 72, AdditionalPricePerServiceAccount = 6, From 45b6954fb3dee2dbb07df720dc332b60e44ab6b8 Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Wed, 5 Jul 2023 11:38:22 -0400 Subject: [PATCH 11/41] SM-802: Add SecretsManagerBeta to OrganizationResponseModel --- .../Models/Response/Organizations/OrganizationResponseModel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs index 4a4edf5d5e8d..63b2c10179ad 100644 --- a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs +++ b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs @@ -53,6 +53,7 @@ public OrganizationResponseModel(Organization organization, string obj = "organi SmServiceAccounts = organization.SmServiceAccounts; MaxAutoscaleSmSeats = organization.MaxAutoscaleSmSeats; MaxAutoscaleSmServiceAccounts = organization.MaxAutoscaleSmServiceAccounts; + SecretsManagerBeta = organization.SecretsManagerBeta; } public string Id { get; set; } @@ -91,6 +92,7 @@ public OrganizationResponseModel(Organization organization, string obj = "organi public int? SmServiceAccounts { get; set; } public int? MaxAutoscaleSmSeats { get; set; } public int? MaxAutoscaleSmServiceAccounts { get; set; } + public bool SecretsManagerBeta { get; set; } } public class OrganizationSubscriptionResponseModel : OrganizationResponseModel From 9cc177437a47d75fe10e75ba485009976f08e75d Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Fri, 7 Jul 2023 15:45:19 -0400 Subject: [PATCH 12/41] SM-802: Move SecretsManagerBeta from OrganizationResponseModel to OrganizationSubscriptionResponseModel. Use sp_refreshview instead of sp_refreshsqlmodule in the migration script. --- .../Response/Organizations/OrganizationResponseModel.cs | 9 +++++++-- .../DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs index 63b2c10179ad..da61db184984 100644 --- a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs +++ b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs @@ -53,7 +53,6 @@ public OrganizationResponseModel(Organization organization, string obj = "organi SmServiceAccounts = organization.SmServiceAccounts; MaxAutoscaleSmSeats = organization.MaxAutoscaleSmSeats; MaxAutoscaleSmServiceAccounts = organization.MaxAutoscaleSmServiceAccounts; - SecretsManagerBeta = organization.SecretsManagerBeta; } public string Id { get; set; } @@ -92,7 +91,6 @@ public OrganizationResponseModel(Organization organization, string obj = "organi public int? SmServiceAccounts { get; set; } public int? MaxAutoscaleSmSeats { get; set; } public int? MaxAutoscaleSmServiceAccounts { get; set; } - public bool SecretsManagerBeta { get; set; } } public class OrganizationSubscriptionResponseModel : OrganizationResponseModel @@ -104,6 +102,7 @@ public OrganizationSubscriptionResponseModel(Organization organization) : base(o CoreHelpers.ReadableBytesSize(organization.Storage.Value) : null; StorageGb = organization.Storage.HasValue ? Math.Round(organization.Storage.Value / 1073741824D, 2) : 0; // 1 GB + SecretsManagerBeta = organization.SecretsManagerBeta; } public OrganizationSubscriptionResponseModel(Organization organization, SubscriptionInfo subscription, bool hideSensitiveData) @@ -119,6 +118,8 @@ public OrganizationSubscriptionResponseModel(Organization organization, Subscrip Subscription.Items = null; UpcomingInvoice.Amount = null; } + + SecretsManagerBeta = organization.SecretsManagerBeta; } public OrganizationSubscriptionResponseModel(Organization organization, OrganizationLicense license) : @@ -133,6 +134,8 @@ public OrganizationSubscriptionResponseModel(Organization organization, Organiza license.Expires?.AddDays(-Constants .OrganizationSelfHostSubscriptionGracePeriodDays); } + + SecretsManagerBeta = organization.SecretsManagerBeta; } public string StorageName { get; set; } @@ -149,4 +152,6 @@ public OrganizationSubscriptionResponseModel(Organization organization, Organiza /// Date when a self-hosted organization expires (includes grace period). /// public DateTime? Expiration { get; set; } + + public bool SecretsManagerBeta { get; set; } } diff --git a/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql b/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql index 02d7f107c226..5689a362f858 100644 --- a/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql +++ b/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql @@ -377,6 +377,6 @@ GO IF OBJECT_ID('[dbo].[OrganizationView]') IS NOT NULL BEGIN - EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationView]'; + EXECUTE sp_refreshview N'[dbo].[OrganizationView]'; END GO From 2a441c129fabacefd2016f0d71924007942bdff3 Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Fri, 7 Jul 2023 17:10:50 -0400 Subject: [PATCH 13/41] SM-802: Remove OrganizationUserOrganizationDetailsView.sql changes --- .../OrganizationUserOrganizationDetailsView.sql | 3 +-- .../2023-07-03_00_SecretsManagerBetaColumn.sql | 14 +------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql b/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql index 72b3591fde6a..2e18b590628a 100644 --- a/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql +++ b/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql @@ -44,8 +44,7 @@ SELECT OU.[AccessSecretsManager], O.[UsePasswordManager], O.[SmSeats], - O.[SmServiceAccounts], - O.[SecretsManagerBeta] + O.[SmServiceAccounts] FROM [dbo].[OrganizationUser] OU LEFT JOIN diff --git a/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql b/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql index 5689a362f858..caf669a4f55a 100644 --- a/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql +++ b/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql @@ -362,19 +362,7 @@ LEFT JOIN [dbo].[OrganizationSponsorship] OS ON OS.[SponsoringOrganizationUserID] = OU.[Id] GO --- Update the metadata for the following stored procedures and view so that the SecretsManagerBeta column is included -IF OBJECT_ID('[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus]') IS NOT NULL -BEGIN - EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus]'; -END -GO - -IF OBJECT_ID('[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]') IS NOT NULL -BEGIN - EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]'; -END -GO - +-- Update the metadata for the following view so that the SecretsManagerBeta column is included IF OBJECT_ID('[dbo].[OrganizationView]') IS NOT NULL BEGIN EXECUTE sp_refreshview N'[dbo].[OrganizationView]'; From a1f8ca85cbcb0ef9571d87732635c8bc8613ad92 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Mon, 10 Jul 2023 08:46:32 +0100 Subject: [PATCH 14/41] [AC 1410] Secrets Manager subscription adjustment back-end changes (#3036) * Create UpgradeSecretsManagerSubscription command --------- Co-authored-by: Thomas Rittson --- src/Admin/Startup.cs | 3 + .../Controllers/OrganizationsController.cs | 32 +- ...tsManagerSubscriptionUpdateRequestModel.cs | 48 ++ src/Api/Models/Response/PlanResponseModel.cs | 9 +- .../OrganizationSmSeatsMaxReached.html.hbs | 34 + .../OrganizationSmSeatsMaxReached.text.hbs | 5 + ...zationSmServiceAccountsMaxReached.html.hbs | 34 + ...zationSmServiceAccountsMaxReached.text.hbs | 5 + .../SecretsManagerSubscriptionUpdate.cs | 56 ++ .../Models/Business/SubscriptionUpdate.cs | 57 +- ...ationServiceAccountsMaxReachedViewModel.cs | 7 + src/Core/Models/StaticStore/Plan.cs | 5 +- ...OrganizationServiceCollectionExtensions.cs | 8 + ...UpdateSecretsManagerSubscriptionCommand.cs | 8 + ...UpdateSecretsManagerSubscriptionCommand.cs | 344 ++++++++ src/Core/Services/IMailService.cs | 2 + src/Core/Services/IPaymentService.cs | 3 + .../Implementations/HandlebarsMailService.cs | 30 + .../Implementations/OrganizationService.cs | 6 +- .../Implementations/StripePaymentService.cs | 5 + .../NoopImplementations/NoopMailService.cs | 13 + src/Core/Tools/Enums/ReferenceEventType.cs | 4 + .../Tools/Models/Business/ReferenceEvent.cs | 1 + src/Core/Utilities/SecretsManagerPlanStore.cs | 6 +- src/Identity/Startup.cs | 4 +- .../OrganizationsControllerTests.cs | 6 +- ...eSecretsManagerSubscriptionCommandTests.cs | 745 ++++++++++++++++++ 27 files changed, 1467 insertions(+), 13 deletions(-) create mode 100644 src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs create mode 100644 src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs create mode 100644 src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.text.hbs create mode 100644 src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs create mode 100644 src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.text.hbs create mode 100644 src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs create mode 100644 src/Core/Models/Mail/OrganizationServiceAccountsMaxReachedViewModel.cs create mode 100644 src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/Interface/IUpdateSecretsManagerSubscriptionCommand.cs create mode 100644 src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommand.cs create mode 100644 test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs diff --git a/src/Admin/Startup.cs b/src/Admin/Startup.cs index 9482be011a99..81c789c9c05c 100644 --- a/src/Admin/Startup.cs +++ b/src/Admin/Startup.cs @@ -9,6 +9,8 @@ using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.Extensions.DependencyInjection.Extensions; using Bit.Admin.Services; +using Bit.Core.Repositories.Noop; +using Bit.Core.SecretsManager.Repositories; #if !OSS using Bit.Commercial.Core.Utilities; @@ -86,6 +88,7 @@ public void ConfigureServices(IServiceCollection services) services.AddBaseServices(globalSettings); services.AddDefaultServices(globalSettings); services.AddScoped(); + services.AddScoped(); #if OSS services.AddOosServices(); diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index 5a1569c98ed1..d0fe7eaf70e8 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -18,6 +18,7 @@ using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate.Interface; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; @@ -50,6 +51,7 @@ public class OrganizationsController : Controller private readonly IFeatureService _featureService; private readonly GlobalSettings _globalSettings; private readonly ILicensingService _licensingService; + private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; public OrganizationsController( IOrganizationRepository organizationRepository, @@ -70,7 +72,8 @@ public OrganizationsController( ICloudGetOrganizationLicenseQuery cloudGetOrganizationLicenseQuery, IFeatureService featureService, GlobalSettings globalSettings, - ILicensingService licensingService) + ILicensingService licensingService, + IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand) { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; @@ -91,6 +94,7 @@ public OrganizationsController( _featureService = featureService; _globalSettings = globalSettings; _licensingService = licensingService; + _updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand; } [HttpGet("{id}")] @@ -319,10 +323,34 @@ public async Task PostSubscription(string id, [FromBody] OrganizationSubscriptio { throw new NotFoundException(); } - await _organizationService.UpdateSubscription(orgIdGuid, model.SeatAdjustment, model.MaxAutoscaleSeats); } + [HttpPost("{id}/sm-subscription")] + [SelfHosted(NotSelfHostedOnly = true)] + public async Task PostSmSubscription(Guid id, [FromBody] SecretsManagerSubscriptionUpdateRequestModel model) + { + var organization = await _organizationRepository.GetByIdAsync(id); + if (organization == null) + { + throw new NotFoundException(); + } + + if (!await _currentContext.EditSubscription(id)) + { + throw new NotFoundException(); + } + + var secretsManagerPlan = StaticStore.GetSecretsManagerPlan(organization.PlanType); + if (secretsManagerPlan == null) + { + throw new NotFoundException("Invalid Secrets Manager plan."); + } + + var organizationUpdate = model.ToSecretsManagerSubscriptionUpdate(organization, secretsManagerPlan); + await _updateSecretsManagerSubscriptionCommand.UpdateSecretsManagerSubscription(organizationUpdate); + } + [HttpPost("{id}/seat")] [SelfHosted(NotSelfHostedOnly = true)] public async Task PostSeat(string id, [FromBody] OrganizationSeatRequestModel model) diff --git a/src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs b/src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs new file mode 100644 index 000000000000..1bd3cde33d8a --- /dev/null +++ b/src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs @@ -0,0 +1,48 @@ +using System.ComponentModel.DataAnnotations; +using Bit.Core.Entities; +using Bit.Core.Models.Business; +using Bit.Core.Models.StaticStore; + +namespace Bit.Api.Models.Request.Organizations; + +public class SecretsManagerSubscriptionUpdateRequestModel +{ + [Required] + public int SeatAdjustment { get; set; } + public int? MaxAutoscaleSeats { get; set; } + public int ServiceAccountAdjustment { get; set; } + public int? MaxAutoscaleServiceAccounts { get; set; } + + public virtual SecretsManagerSubscriptionUpdate ToSecretsManagerSubscriptionUpdate(Organization organization, Plan plan) + { + var newTotalSeats = organization.SmSeats.GetValueOrDefault() + SeatAdjustment; + var newTotalServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + ServiceAccountAdjustment; + var autoscaleSeats = MaxAutoscaleSeats.HasValue && MaxAutoscaleSeats != + organization.MaxAutoscaleSmSeats.GetValueOrDefault(); + var autoscaleServiceAccounts = MaxAutoscaleServiceAccounts.HasValue && + MaxAutoscaleServiceAccounts != + organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault(); + + var orgUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organization.Id, + + SmSeatsAdjustment = SeatAdjustment, + SmSeats = newTotalSeats, + SmSeatsExcludingBase = newTotalSeats - plan.BaseSeats, + + MaxAutoscaleSmSeats = MaxAutoscaleSeats, + + SmServiceAccountsAdjustment = ServiceAccountAdjustment, + SmServiceAccounts = newTotalServiceAccounts, + SmServiceAccountsExcludingBase = newTotalServiceAccounts - plan.BaseServiceAccount.GetValueOrDefault(), + + MaxAutoscaleSmServiceAccounts = MaxAutoscaleServiceAccounts, + + MaxAutoscaleSmSeatsChanged = autoscaleSeats, + MaxAutoscaleSmServiceAccountsChanged = autoscaleServiceAccounts + }; + + return orgUpdate; + } +} diff --git a/src/Api/Models/Response/PlanResponseModel.cs b/src/Api/Models/Response/PlanResponseModel.cs index ee86dde59e50..f3b39f113373 100644 --- a/src/Api/Models/Response/PlanResponseModel.cs +++ b/src/Api/Models/Response/PlanResponseModel.cs @@ -55,10 +55,12 @@ public PlanResponseModel(Plan plan, string obj = "plan") AdditionalPricePerServiceAccount = plan.AdditionalPricePerServiceAccount; BaseServiceAccount = plan.BaseServiceAccount; - MaxServiceAccount = plan.MaxServiceAccount; + MaxServiceAccounts = plan.MaxServiceAccounts; + MaxAdditionalServiceAccounts = plan.MaxAdditionalServiceAccount; HasAdditionalServiceAccountOption = plan.HasAdditionalServiceAccountOption; MaxProjects = plan.MaxProjects; BitwardenProduct = plan.BitwardenProduct; + StripeServiceAccountPlanId = plan.StripeServiceAccountPlanId; } public PlanType Type { get; set; } @@ -105,10 +107,11 @@ public PlanResponseModel(Plan plan, string obj = "plan") public decimal SeatPrice { get; set; } public decimal AdditionalStoragePricePerGb { get; set; } public decimal PremiumAccessOptionPrice { get; set; } - + public string StripeServiceAccountPlanId { get; set; } public decimal? AdditionalPricePerServiceAccount { get; set; } public short? BaseServiceAccount { get; set; } - public short? MaxServiceAccount { get; set; } + public short? MaxServiceAccounts { get; set; } + public short? MaxAdditionalServiceAccounts { get; set; } public bool HasAdditionalServiceAccountOption { get; set; } public short? MaxProjects { get; set; } public BitwardenProductType BitwardenProduct { get; set; } diff --git a/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs b/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs new file mode 100644 index 000000000000..8eee60b2d10e --- /dev/null +++ b/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs @@ -0,0 +1,34 @@ +{{#>FullHtmlLayout}} + + + + + + + + + + +
+ Your organization has reached the Secrets Manager seat limit of {{MaxSeatCount}} and new members cannot be invited. +
+ For more information, please refer to the following help article: + + Member management + +
+
+
+ + Manage subscription + +
+
+{{/FullHtmlLayout}} diff --git a/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.text.hbs b/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.text.hbs new file mode 100644 index 000000000000..325d4c256195 --- /dev/null +++ b/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.text.hbs @@ -0,0 +1,5 @@ +{{#>BasicTextLayout}} +Your organization has reached the Secrets Manager seat limit of {{MaxSeatCount}} and new members cannot be invited. + +For more information, please refer to the following help article: https://bitwarden.com/help/managing-users +{{/BasicTextLayout}} diff --git a/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs b/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs new file mode 100644 index 000000000000..43712bc3e3bc --- /dev/null +++ b/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs @@ -0,0 +1,34 @@ +{{#>FullHtmlLayout}} + + + + + + + + + + +
+ Your organization has reached the Secrets Manager service accounts limit of {{MaxServiceAccountsCount}}. New service accounts cannot be created +
+ For more information, please refer to the following help article: + + Member management + +
+
+
+ + Manage subscription + +
+
+{{/FullHtmlLayout}} diff --git a/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.text.hbs b/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.text.hbs new file mode 100644 index 000000000000..377c8699bff3 --- /dev/null +++ b/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.text.hbs @@ -0,0 +1,5 @@ +{{#>BasicTextLayout}} +Your organization has reached the Secrets Manager service accounts limit of {{MaxServiceAccountsCount}}. New service accounts cannot be created + +For more information, please refer to the following help article: https://bitwarden.com/help/managing-users +{{/BasicTextLayout}} diff --git a/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs b/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs new file mode 100644 index 000000000000..051a7560dfac --- /dev/null +++ b/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs @@ -0,0 +1,56 @@ +namespace Bit.Core.Models.Business; + +public class SecretsManagerSubscriptionUpdate +{ + public Guid OrganizationId { get; set; } + + /// + /// The seats to be added or removed from the organization + /// + public int SmSeatsAdjustment { get; set; } + + /// + /// The total seats the organization will have after the update, including any base seats included in the plan + /// + public int SmSeats { get; set; } + + /// + /// The seats the organization will have after the update, excluding the base seats included in the plan + /// Usually this is what the organization is billed for + /// + public int SmSeatsExcludingBase { get; set; } + + /// + /// The new autoscale limit for seats, expressed as a total (not an adjustment). + /// This may or may not be the same as the current autoscale limit. + /// + public int? MaxAutoscaleSmSeats { get; set; } + + /// + /// The service accounts to be added or removed from the organization + /// + public int SmServiceAccountsAdjustment { get; set; } + + /// + /// The total service accounts the organization will have after the update, including the base service accounts + /// included in the plan + /// + public int SmServiceAccounts { get; set; } + + /// + /// The seats the organization will have after the update, excluding the base seats included in the plan + /// Usually this is what the organization is billed for + /// + public int SmServiceAccountsExcludingBase { get; set; } + + /// + /// The new autoscale limit for service accounts, expressed as a total (not an adjustment). + /// This may or may not be the same as the current autoscale limit. + /// + public int? MaxAutoscaleSmServiceAccounts { get; set; } + + public bool SmSeatsChanged => SmSeatsAdjustment != 0; + public bool SmServiceAccountsChanged => SmServiceAccountsAdjustment != 0; + public bool MaxAutoscaleSmSeatsChanged { get; set; } + public bool MaxAutoscaleSmServiceAccountsChanged { get; set; } +} diff --git a/src/Core/Models/Business/SubscriptionUpdate.cs b/src/Core/Models/Business/SubscriptionUpdate.cs index 64b43a8de299..1a93bbfa3cbf 100644 --- a/src/Core/Models/Business/SubscriptionUpdate.cs +++ b/src/Core/Models/Business/SubscriptionUpdate.cs @@ -1,4 +1,5 @@ using Bit.Core.Entities; +using Bit.Core.Enums; using Stripe; namespace Bit.Core.Models.Business; @@ -42,7 +43,15 @@ public SeatSubscriptionUpdate(Organization organization, StaticStore.Plan plan, { _plan = plan; _additionalSeats = additionalSeats; - _previousSeats = organization.Seats ?? 0; + switch (plan.BitwardenProduct) + { + case BitwardenProductType.PasswordManager: + _previousSeats = organization.Seats.GetValueOrDefault(); + break; + case BitwardenProductType.SecretsManager: + _previousSeats = organization.SmSeats.GetValueOrDefault(); + break; + } } public override List UpgradeItemsOptions(Subscription subscription) @@ -77,6 +86,52 @@ public override List RevertItemsOptions(Subscription su } } +public class ServiceAccountSubscriptionUpdate : SubscriptionUpdate +{ + private long? _prevServiceAccounts; + private readonly StaticStore.Plan _plan; + private readonly long? _additionalServiceAccounts; + protected override List PlanIds => new() { _plan.StripeServiceAccountPlanId }; + + public ServiceAccountSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalServiceAccounts) + { + _plan = plan; + _additionalServiceAccounts = additionalServiceAccounts; + _prevServiceAccounts = organization.SmServiceAccounts ?? 0; + } + + public override List UpgradeItemsOptions(Subscription subscription) + { + var item = SubscriptionItem(subscription, PlanIds.Single()); + _prevServiceAccounts = item?.Quantity ?? 0; + return new() + { + new SubscriptionItemOptions + { + Id = item?.Id, + Plan = PlanIds.Single(), + Quantity = _additionalServiceAccounts, + Deleted = (item?.Id != null && _additionalServiceAccounts == 0) ? true : (bool?)null, + } + }; + } + + public override List RevertItemsOptions(Subscription subscription) + { + var item = SubscriptionItem(subscription, PlanIds.Single()); + return new() + { + new SubscriptionItemOptions + { + Id = item?.Id, + Plan = PlanIds.Single(), + Quantity = _prevServiceAccounts, + Deleted = _prevServiceAccounts == 0 ? true : (bool?)null, + } + }; + } +} + public class StorageSubscriptionUpdate : SubscriptionUpdate { private long? _prevStorage; diff --git a/src/Core/Models/Mail/OrganizationServiceAccountsMaxReachedViewModel.cs b/src/Core/Models/Mail/OrganizationServiceAccountsMaxReachedViewModel.cs new file mode 100644 index 000000000000..1b9c9257207a --- /dev/null +++ b/src/Core/Models/Mail/OrganizationServiceAccountsMaxReachedViewModel.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.Models.Mail; + +public class OrganizationServiceAccountsMaxReachedViewModel +{ + public Guid OrganizationId { get; set; } + public int MaxServiceAccountsCount { get; set; } +} diff --git a/src/Core/Models/StaticStore/Plan.cs b/src/Core/Models/StaticStore/Plan.cs index cc781a411cb1..f542e5e21480 100644 --- a/src/Core/Models/StaticStore/Plan.cs +++ b/src/Core/Models/StaticStore/Plan.cs @@ -15,8 +15,11 @@ public class Plan public short? BaseStorageGb { get; set; } public short? MaxCollections { get; set; } public short? MaxUsers { get; set; } + public short? MaxServiceAccounts { get; set; } public bool AllowSeatAutoscale { get; set; } + public bool AllowServiceAccountsAutoscale { get; set; } + public bool HasAdditionalSeatsOption { get; set; } public int? MaxAdditionalSeats { get; set; } public bool HasAdditionalStorageOption { get; set; } @@ -55,7 +58,7 @@ public class Plan public decimal PremiumAccessOptionPrice { get; set; } public decimal? AdditionalPricePerServiceAccount { get; set; } public short? BaseServiceAccount { get; set; } - public short? MaxServiceAccount { get; set; } + public short? MaxAdditionalServiceAccount { get; set; } public bool HasAdditionalServiceAccountOption { get; set; } public short? MaxProjects { get; set; } public BitwardenProductType BitwardenProduct { get; set; } diff --git a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs index 9e365377976b..ba2488c7ae9f 100644 --- a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs @@ -15,6 +15,8 @@ using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate.Interface; using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager; using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces; using Bit.Core.Services; @@ -41,6 +43,7 @@ public static void AddOrganizationServices(this IServiceCollection services, IGl services.AddOrganizationGroupCommands(); services.AddOrganizationLicenseCommandsQueries(); services.AddOrganizationDomainCommandsQueries(); + services.AddOrganizationSubscriptionUpdateCommandsQueries(); } private static void AddOrganizationConnectionCommands(this IServiceCollection services) @@ -110,6 +113,11 @@ private static void AddOrganizationDomainCommandsQueries(this IServiceCollection services.AddScoped(); } + private static void AddOrganizationSubscriptionUpdateCommandsQueries(this IServiceCollection services) + { + services.AddScoped(); + } + private static void AddTokenizers(this IServiceCollection services) { services.AddSingleton>(serviceProvider => diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/Interface/IUpdateSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/Interface/IUpdateSecretsManagerSubscriptionCommand.cs new file mode 100644 index 000000000000..de5271639e10 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/Interface/IUpdateSecretsManagerSubscriptionCommand.cs @@ -0,0 +1,8 @@ +using Bit.Core.Models.Business; + +namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate.Interface; + +public interface IUpdateSecretsManagerSubscriptionCommand +{ + Task UpdateSecretsManagerSubscription(SecretsManagerSubscriptionUpdate update); +} diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommand.cs new file mode 100644 index 000000000000..16d0af84e5a3 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommand.cs @@ -0,0 +1,344 @@ +#nullable enable +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.OrganizationFeatures.OrganizationSubscriptionUpdate.Interface; +using Bit.Core.Repositories; +using Bit.Core.SecretsManager.Repositories; +using Bit.Core.Services; +using Bit.Core.Utilities; +using Microsoft.Extensions.Logging; + +namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate; + +public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubscriptionCommand +{ + private readonly IOrganizationRepository _organizationRepository; + private readonly IOrganizationUserRepository _organizationUserRepository; + private readonly IPaymentService _paymentService; + private readonly IOrganizationService _organizationService; + private readonly IMailService _mailService; + private readonly ILogger _logger; + private readonly IServiceAccountRepository _serviceAccountRepository; + + public UpdateSecretsManagerSubscriptionCommand( + IOrganizationRepository organizationRepository, + IOrganizationService organizationService, + IOrganizationUserRepository organizationUserRepository, + IPaymentService paymentService, + IMailService mailService, + ILogger logger, + IServiceAccountRepository serviceAccountRepository) + { + _organizationRepository = organizationRepository; + _organizationUserRepository = organizationUserRepository; + _paymentService = paymentService; + _organizationService = organizationService; + _mailService = mailService; + _logger = logger; + _serviceAccountRepository = serviceAccountRepository; + } + + public async Task UpdateSecretsManagerSubscription(SecretsManagerSubscriptionUpdate update) + { + var organization = await _organizationRepository.GetByIdAsync(update.OrganizationId); + + ValidateOrganization(organization); + + var plan = GetPlanForOrganization(organization); + + if (update.SmSeatsChanged) + { + await ValidateSmSeatsUpdateAsync(organization, update, plan); + } + + if (update.SmServiceAccountsChanged) + { + await ValidateSmServiceAccountsUpdateAsync(organization, update, plan); + } + + if (update.MaxAutoscaleSmSeatsChanged) + { + ValidateMaxAutoscaleSmSeatsUpdateAsync(organization, update.MaxAutoscaleSmSeats.GetValueOrDefault(), plan); + } + + if (update.MaxAutoscaleSmServiceAccountsChanged) + { + ValidateMaxAutoscaleSmServiceAccountUpdate(organization, update.MaxAutoscaleSmServiceAccounts.GetValueOrDefault(), plan); + } + + await FinalizeSubscriptionAdjustmentAsync(organization, plan, update); + + await SendEmailIfAutoscaleLimitReached(organization); + } + + private Plan GetPlanForOrganization(Organization organization) + { + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); + if (plan == null) + { + throw new BadRequestException("Existing plan not found."); + } + return plan; + } + + private static void ValidateOrganization(Organization organization) + { + if (organization == null) + { + throw new NotFoundException("Organization is not found"); + } + + if (!organization.UseSecretsManager) + { + throw new BadRequestException("Organization has no access to Secrets Manager."); + } + } + + private async Task FinalizeSubscriptionAdjustmentAsync(Organization organization, + Plan plan, SecretsManagerSubscriptionUpdate update) + { + if (update.SmSeatsChanged) + { + await ProcessChargesAndRaiseEventsForAdjustSeatsAsync(organization, plan, update); + organization.SmSeats = update.SmSeats; + } + + if (update.SmServiceAccountsChanged) + { + await ProcessChargesAndRaiseEventsForAdjustServiceAccountsAsync(organization, plan, update); + organization.SmServiceAccounts = update.SmServiceAccounts; + } + + if (update.MaxAutoscaleSmSeatsChanged) + { + organization.MaxAutoscaleSmSeats = update.MaxAutoscaleSmSeats; + } + + if (update.MaxAutoscaleSmServiceAccountsChanged) + { + organization.MaxAutoscaleSmServiceAccounts = update.MaxAutoscaleSmServiceAccounts; + } + + await _organizationService.ReplaceAndUpdateCacheAsync(organization); + } + + private async Task ProcessChargesAndRaiseEventsForAdjustSeatsAsync(Organization organization, Plan plan, + SecretsManagerSubscriptionUpdate update) + { + await _paymentService.AdjustSeatsAsync(organization, plan, update.SmSeatsExcludingBase); + + // TODO: call ReferenceEventService - see AC-1481 + } + + private async Task ProcessChargesAndRaiseEventsForAdjustServiceAccountsAsync(Organization organization, Plan plan, + SecretsManagerSubscriptionUpdate update) + { + await _paymentService.AdjustServiceAccountsAsync(organization, plan, + update.SmServiceAccountsExcludingBase); + + // TODO: call ReferenceEventService - see AC-1481 + } + + private async Task SendEmailIfAutoscaleLimitReached(Organization organization) + { + if (organization.SmSeats.HasValue && organization.MaxAutoscaleSmSeats.HasValue && organization.SmSeats == organization.MaxAutoscaleSmSeats) + { + await SendSeatLimitEmailAsync(organization, organization.MaxAutoscaleSmSeats.Value); + } + + if (organization.SmServiceAccounts.HasValue && organization.MaxAutoscaleSmServiceAccounts.HasValue && organization.SmServiceAccounts == organization.MaxAutoscaleSmServiceAccounts) + { + await SendServiceAccountLimitEmailAsync(organization, organization.MaxAutoscaleSmServiceAccounts.Value); + } + } + + private async Task SendSeatLimitEmailAsync(Organization organization, int MaxAutoscaleValue) + { + try + { + var ownerEmails = (await _organizationUserRepository.GetManyByMinimumRoleAsync(organization.Id, + OrganizationUserType.Owner)) + .Select(u => u.Email).Distinct(); + + await _mailService.SendSecretsManagerMaxSeatLimitReachedEmailAsync(organization, MaxAutoscaleValue, ownerEmails); + + } + catch (Exception e) + { + _logger.LogError(e, $"Error encountered notifying organization owners of Seats limit reached."); + } + + } + + private async Task SendServiceAccountLimitEmailAsync(Organization organization, int MaxAutoscaleValue) + { + try + { + var ownerEmails = (await _organizationUserRepository.GetManyByMinimumRoleAsync(organization.Id, + OrganizationUserType.Owner)) + .Select(u => u.Email).Distinct(); + + await _mailService.SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(organization, MaxAutoscaleValue, ownerEmails); + + } + catch (Exception e) + { + _logger.LogError(e, $"Error encountered notifying organization owners of Service Accounts limit reached."); + } + + } + + private async Task ValidateSmSeatsUpdateAsync(Organization organization, SecretsManagerSubscriptionUpdate update, Plan plan) + { + if (organization.SmSeats == null) + { + throw new BadRequestException("Organization has no Secrets Manager seat limit, no need to adjust seats"); + } + + if (update.MaxAutoscaleSmSeats.HasValue && update.SmSeats > update.MaxAutoscaleSmSeats.Value) + { + throw new BadRequestException("Cannot set max seat autoscaling below seat count."); + } + + if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId)) + { + throw new BadRequestException("No payment method found."); + } + + if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId)) + { + throw new BadRequestException("No subscription found."); + } + + if (!plan.HasAdditionalSeatsOption) + { + throw new BadRequestException("Plan does not allow additional Secrets Manager seats."); + } + + if (plan.BaseSeats > update.SmSeats) + { + throw new BadRequestException($"Plan has a minimum of {plan.BaseSeats} Secrets Manager seats."); + } + + if (update.SmSeats <= 0) + { + throw new BadRequestException("You must have at least 1 Secrets Manager seat."); + } + + if (plan.MaxAdditionalSeats.HasValue && update.SmSeatsExcludingBase > plan.MaxAdditionalSeats.Value) + { + throw new BadRequestException($"Organization plan allows a maximum of " + + $"{plan.MaxAdditionalSeats.Value} additional Secrets Manager seats."); + } + + if (organization.SmSeats.Value > update.SmSeats) + { + var currentSeats = await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id); + if (currentSeats > update.SmSeats) + { + throw new BadRequestException($"Your organization currently has {currentSeats} Secrets Manager seats. " + + $"Your plan only allows ({update.SmSeats}) Secrets Manager seats. Remove some Secrets Manager users."); + } + } + } + + private async Task ValidateSmServiceAccountsUpdateAsync(Organization organization, SecretsManagerSubscriptionUpdate update, Plan plan) + { + if (organization.SmServiceAccounts == null) + { + throw new BadRequestException("Organization has no Service Accounts limit, no need to adjust Service Accounts"); + } + + if (update.MaxAutoscaleSmServiceAccounts.HasValue && update.SmServiceAccounts > update.MaxAutoscaleSmServiceAccounts.Value) + { + throw new BadRequestException("Cannot set max Service Accounts autoscaling below Service Accounts count."); + } + + if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId)) + { + throw new BadRequestException("No payment method found."); + } + + if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId)) + { + throw new BadRequestException("No subscription found."); + } + + if (!plan.HasAdditionalServiceAccountOption) + { + throw new BadRequestException("Plan does not allow additional Service Accounts."); + } + + if (plan.BaseServiceAccount > update.SmServiceAccounts) + { + throw new BadRequestException($"Plan has a minimum of {plan.BaseServiceAccount} Service Accounts."); + } + + if (update.SmServiceAccounts <= 0) + { + throw new BadRequestException("You must have at least 1 Service Account."); + } + + if (plan.MaxAdditionalServiceAccount.HasValue && update.SmServiceAccountsExcludingBase > plan.MaxAdditionalServiceAccount.Value) + { + throw new BadRequestException($"Organization plan allows a maximum of " + + $"{plan.MaxAdditionalServiceAccount.Value} additional Service Accounts."); + } + + if (!organization.SmServiceAccounts.HasValue || organization.SmServiceAccounts.Value > update.SmServiceAccounts) + { + var currentServiceAccounts = await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(organization.Id); + if (currentServiceAccounts > update.SmServiceAccounts) + { + throw new BadRequestException($"Your organization currently has {currentServiceAccounts} Service Accounts. " + + $"Your plan only allows ({update.SmServiceAccounts}) Service Accounts. Remove some Service Accounts."); + } + } + } + + private void ValidateMaxAutoscaleSmSeatsUpdateAsync(Organization organization, int maxAutoscaleSeats, Plan plan) + { + if (organization.SmSeats.HasValue && maxAutoscaleSeats < organization.SmSeats.Value) + { + throw new BadRequestException($"Cannot set max Secrets Manager seat autoscaling below current Secrets Manager seat count."); + } + + if (plan.MaxUsers.HasValue && maxAutoscaleSeats > plan.MaxUsers) + { + throw new BadRequestException(string.Concat( + $"Your plan has a Secrets Manager seat limit of {plan.MaxUsers}, ", + $"but you have specified a max autoscale count of {maxAutoscaleSeats}.", + "Reduce your max autoscale count.")); + } + + if (!plan.AllowSeatAutoscale) + { + throw new BadRequestException("Your plan does not allow Secrets Manager seat autoscaling."); + } + } + + private void ValidateMaxAutoscaleSmServiceAccountUpdate(Organization organization, int maxAutoscaleServiceAccounts, Plan plan) + { + if (organization.SmServiceAccounts.HasValue && maxAutoscaleServiceAccounts < organization.SmServiceAccounts.Value) + { + throw new BadRequestException( + $"Cannot set max Service Accounts autoscaling below current Service Accounts count."); + } + + if (!plan.AllowServiceAccountsAutoscale) + { + throw new BadRequestException("Your plan does not allow Service Accounts autoscaling."); + } + + if (plan.MaxServiceAccounts.HasValue && maxAutoscaleServiceAccounts > plan.MaxServiceAccounts) + { + throw new BadRequestException(string.Concat( + $"Your plan has a Service Accounts limit of {plan.MaxServiceAccounts}, ", + $"but you have specified a max autoscale count of {maxAutoscaleServiceAccounts}.", + "Reduce your max autoscale count.")); + } + } +} diff --git a/src/Core/Services/IMailService.cs b/src/Core/Services/IMailService.cs index 933127367c6d..4647234b78cc 100644 --- a/src/Core/Services/IMailService.cs +++ b/src/Core/Services/IMailService.cs @@ -55,4 +55,6 @@ Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate, Li Task SendFailedLoginAttemptsEmailAsync(string email, DateTime utcNow, string ip); Task SendFailedTwoFactorAttemptsEmailAsync(string email, DateTime utcNow, string ip); Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable adminEmails, string organizationId, string domainName); + Task SendSecretsManagerMaxSeatLimitReachedEmailAsync(Organization organization, int maxSeatCount, IEnumerable ownerEmails); + Task SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(Organization organization, int maxSeatCount, IEnumerable ownerEmails); } diff --git a/src/Core/Services/IPaymentService.cs b/src/Core/Services/IPaymentService.cs index ba482e0d59c8..21140a58c878 100644 --- a/src/Core/Services/IPaymentService.cs +++ b/src/Core/Services/IPaymentService.cs @@ -21,6 +21,9 @@ Task PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType short additionalStorageGb, TaxInfo taxInfo); Task AdjustSeatsAsync(Organization organization, Plan plan, int additionalSeats, DateTime? prorationDate = null); Task AdjustStorageAsync(IStorableSubscriber storableSubscriber, int additionalStorage, string storagePlanId, DateTime? prorationDate = null); + + Task AdjustServiceAccountsAsync(Organization organization, Plan plan, int additionalServiceAccounts, + DateTime? prorationDate = null); Task CancelSubscriptionAsync(ISubscriber subscriber, bool endOfPeriod = false, bool skipInAppPurchaseCheck = false); Task ReinstateSubscriptionAsync(ISubscriber subscriber); diff --git a/src/Core/Services/Implementations/HandlebarsMailService.cs b/src/Core/Services/Implementations/HandlebarsMailService.cs index 66fe70a0181b..28552a1a982b 100644 --- a/src/Core/Services/Implementations/HandlebarsMailService.cs +++ b/src/Core/Services/Implementations/HandlebarsMailService.cs @@ -896,6 +896,36 @@ public async Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable await _mailDeliveryService.SendEmailAsync(message); } + public async Task SendSecretsManagerMaxSeatLimitReachedEmailAsync(Organization organization, int maxSeatCount, + IEnumerable ownerEmails) + { + var message = CreateDefaultMessage($"{organization.Name} Secrets Manager Seat Limit Reached", ownerEmails); + var model = new OrganizationSeatsMaxReachedViewModel + { + OrganizationId = organization.Id, + MaxSeatCount = maxSeatCount, + }; + + await AddMessageContentAsync(message, "OrganizationSmSeatsMaxReached", model); + message.Category = "OrganizationSmSeatsMaxReached"; + await _mailDeliveryService.SendEmailAsync(message); + } + + public async Task SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(Organization organization, int maxSeatCount, + IEnumerable ownerEmails) + { + var message = CreateDefaultMessage($"{organization.Name} Secrets Manager Service Accounts Limit Reached", ownerEmails); + var model = new OrganizationServiceAccountsMaxReachedViewModel + { + OrganizationId = organization.Id, + MaxServiceAccountsCount = maxSeatCount, + }; + + await AddMessageContentAsync(message, "OrganizationSmServiceAccountsMaxReached", model); + message.Category = "OrganizationSmServiceAccountsMaxReached"; + await _mailDeliveryService.SendEmailAsync(message); + } + private static string GetUserIdentifier(string email, string userName) { return string.IsNullOrEmpty(userName) ? email : CoreHelpers.SanitizeForEmail(userName, false); diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index abf420392bf3..c00d9f983571 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -427,15 +427,15 @@ private async Task ValidateSecretsManagerSeatsAndServiceAccountAsync(Organizatio if (newSecretsManagerPlan.BaseServiceAccount != null) { if (!organization.SmServiceAccounts.HasValue || - organization.SmServiceAccounts.Value > newSecretsManagerPlan.MaxServiceAccount) + organization.SmServiceAccounts.Value > newSecretsManagerPlan.MaxServiceAccounts) { var currentServiceAccounts = await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(organization.Id); - if (currentServiceAccounts > newSecretsManagerPlan.MaxServiceAccount) + if (currentServiceAccounts > newSecretsManagerPlan.MaxServiceAccounts) { throw new BadRequestException( $"Your organization currently has {currentServiceAccounts} service account seats filled. " + - $"Your new plan only has ({newSecretsManagerPlan.MaxServiceAccount}) service accounts. Remove some service accounts."); + $"Your new plan only has ({newSecretsManagerPlan.MaxServiceAccounts}) service accounts. Remove some service accounts."); } } } diff --git a/src/Core/Services/Implementations/StripePaymentService.cs b/src/Core/Services/Implementations/StripePaymentService.cs index bc0507de2f16..9bd45ff0cf06 100644 --- a/src/Core/Services/Implementations/StripePaymentService.cs +++ b/src/Core/Services/Implementations/StripePaymentService.cs @@ -853,6 +853,11 @@ public Task AdjustSeatsAsync(Organization organization, StaticStore.Plan return FinalizeSubscriptionChangeAsync(organization, new SeatSubscriptionUpdate(organization, plan, additionalSeats), prorationDate); } + public Task AdjustServiceAccountsAsync(Organization organization, StaticStore.Plan plan, int additionalServiceAccounts, DateTime? prorationDate = null) + { + return FinalizeSubscriptionChangeAsync(organization, new ServiceAccountSubscriptionUpdate(organization, plan, additionalServiceAccounts), prorationDate); + } + public Task AdjustStorageAsync(IStorableSubscriber storableSubscriber, int additionalStorage, string storagePlanId, DateTime? prorationDate = null) { diff --git a/src/Core/Services/NoopImplementations/NoopMailService.cs b/src/Core/Services/NoopImplementations/NoopMailService.cs index 808489efbe01..31cc2a4ad56c 100644 --- a/src/Core/Services/NoopImplementations/NoopMailService.cs +++ b/src/Core/Services/NoopImplementations/NoopMailService.cs @@ -238,4 +238,17 @@ public Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable admin { return Task.FromResult(0); } + + public Task SendSecretsManagerMaxSeatLimitReachedEmailAsync(Organization organization, int maxSeatCount, + IEnumerable ownerEmails) + { + return Task.FromResult(0); + } + + public Task SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(Organization organization, + int maxSeatCount, + IEnumerable ownerEmails) + { + return Task.FromResult(0); + } } diff --git a/src/Core/Tools/Enums/ReferenceEventType.cs b/src/Core/Tools/Enums/ReferenceEventType.cs index a8a52e63179f..b91e83b6f12f 100644 --- a/src/Core/Tools/Enums/ReferenceEventType.cs +++ b/src/Core/Tools/Enums/ReferenceEventType.cs @@ -44,4 +44,8 @@ public enum ReferenceEventType OrganizationCreatedByAdmin, [EnumMember(Value = "sm-service-account-accessed-secret")] SmServiceAccountAccessedSecret, + [EnumMember(Value = "adjust-service-accounts")] + AdjustServiceAccounts, + [EnumMember(Value = "adjust-sm-seats")] + AdjustSmSeats, } diff --git a/src/Core/Tools/Models/Business/ReferenceEvent.cs b/src/Core/Tools/Models/Business/ReferenceEvent.cs index 382d9cf1907b..b50609752fe8 100644 --- a/src/Core/Tools/Models/Business/ReferenceEvent.cs +++ b/src/Core/Tools/Models/Business/ReferenceEvent.cs @@ -52,6 +52,7 @@ public ReferenceEvent(ReferenceEventType type, IReferenceable source, ICurrentCo public int? Seats { get; set; } public int? PreviousSeats { get; set; } + public int? PreviousServiceAccounts { get; set; } public short? Storage { get; set; } diff --git a/src/Core/Utilities/SecretsManagerPlanStore.cs b/src/Core/Utilities/SecretsManagerPlanStore.cs index d6d0d87acaf6..c37c4244623d 100644 --- a/src/Core/Utilities/SecretsManagerPlanStore.cs +++ b/src/Core/Utilities/SecretsManagerPlanStore.cs @@ -45,6 +45,7 @@ public static IEnumerable CreatePlan() SeatPrice = 13, AdditionalPricePerServiceAccount = 0.5M, AllowSeatAutoscale = true, + AllowServiceAccountsAutoscale = true }, new Plan { @@ -83,6 +84,7 @@ public static IEnumerable CreatePlan() SeatPrice = 144, AdditionalPricePerServiceAccount = 6, AllowSeatAutoscale = true, + AllowServiceAccountsAutoscale = true }, new Plan { @@ -113,6 +115,7 @@ public static IEnumerable CreatePlan() SeatPrice = 7, AdditionalPricePerServiceAccount = 0.5M, AllowSeatAutoscale = true, + AllowServiceAccountsAutoscale = true }, new Plan { @@ -145,6 +148,7 @@ public static IEnumerable CreatePlan() SeatPrice = 72, AdditionalPricePerServiceAccount = 6, AllowSeatAutoscale = true, + AllowServiceAccountsAutoscale = true }, new Plan { @@ -158,7 +162,7 @@ public static IEnumerable CreatePlan() BaseServiceAccount = 3, MaxProjects = 3, MaxUsers = 2, - MaxServiceAccount = 3, + MaxServiceAccounts = 3, UpgradeSortOrder = -1, // Always the lowest plan, cannot be upgraded to DisplaySortOrder = -1, AllowSeatAutoscale = false, diff --git a/src/Identity/Startup.cs b/src/Identity/Startup.cs index 5ba20d905bb9..5029212e6d7b 100644 --- a/src/Identity/Startup.cs +++ b/src/Identity/Startup.cs @@ -4,6 +4,8 @@ using Bit.Core; using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Context; +using Bit.Core.Repositories.Noop; +using Bit.Core.SecretsManager.Repositories; using Bit.Core.Settings; using Bit.Core.Utilities; using Bit.Identity.Utilities; @@ -46,7 +48,7 @@ public void ConfigureServices(IServiceCollection services) // Repositories services.AddDatabaseRepositories(globalSettings); - services.AddOosServices(); + services.AddScoped(); // Context services.AddScoped(); diff --git a/test/Api.Test/Controllers/OrganizationsControllerTests.cs b/test/Api.Test/Controllers/OrganizationsControllerTests.cs index 1d21e9b735a7..aa34ba5e447f 100644 --- a/test/Api.Test/Controllers/OrganizationsControllerTests.cs +++ b/test/Api.Test/Controllers/OrganizationsControllerTests.cs @@ -11,6 +11,7 @@ using Bit.Core.Exceptions; using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate.Interface; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; @@ -40,6 +41,7 @@ public class OrganizationsControllerTests : IDisposable private readonly IUpdateOrganizationLicenseCommand _updateOrganizationLicenseCommand; private readonly IFeatureService _featureService; private readonly ILicensingService _licensingService; + private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; private readonly OrganizationsController _sut; @@ -64,12 +66,14 @@ public OrganizationsControllerTests() _updateOrganizationLicenseCommand = Substitute.For(); _featureService = Substitute.For(); _licensingService = Substitute.For(); + _updateSecretsManagerSubscriptionCommand = Substitute.For(); _sut = new OrganizationsController(_organizationRepository, _organizationUserRepository, _policyRepository, _providerRepository, _organizationService, _userService, _paymentService, _currentContext, _ssoConfigRepository, _ssoConfigService, _getOrganizationApiKeyQuery, _rotateOrganizationApiKeyCommand, _createOrganizationApiKeyCommand, _organizationApiKeyRepository, _updateOrganizationLicenseCommand, - _cloudGetOrganizationLicenseQuery, _featureService, _globalSettings, _licensingService); + _cloudGetOrganizationLicenseQuery, _featureService, _globalSettings, _licensingService, + _updateSecretsManagerSubscriptionCommand); } public void Dispose() diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs new file mode 100644 index 000000000000..1a3ffdfc0841 --- /dev/null +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs @@ -0,0 +1,745 @@ +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.OrganizationFeatures.OrganizationSubscriptionUpdate; +using Bit.Core.Repositories; +using Bit.Core.SecretsManager.Repositories; +using Bit.Core.Services; +using Bit.Core.Utilities; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.OrganizationFeatures.OrganizationSubscriptionUpdate; + +[SutProviderCustomize] +public class UpdateSecretsManagerSubscriptionCommandTests +{ + [Theory] + [BitAutoData] + public async Task UpdateSecretsManagerSubscription_NoOrganization_Throws( + Guid organizationId, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetByIdAsync(organizationId) + .Returns((Organization)null); + + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = null, + SmSeatsAdjustment = 0 + }; + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("Organization is not found", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData] + public async Task UpdateSecretsManagerSubscription_NoSecretsManagerAccess_ThrowsException( + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + SmSeats = 10, + SmServiceAccounts = 5, + UseSecretsManager = false, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 10 + }; + + sutProvider.GetDependency() + .GetByIdAsync(organizationId) + .Returns(organization); + + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + SmSeatsAdjustment = 1, + MaxAutoscaleSmSeats = 1 + }; + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("Organization has no access to Secrets Manager.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData] + public async Task UpdateSecretsManagerSubscription_SeatsAdustmentGreaterThanMaxAutoscaleSeats_ThrowsException( + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + SmSeats = 10, + UseSecretsManager = true, + SmServiceAccounts = 5, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 10, + PlanType = PlanType.EnterpriseAnnually, + }; + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 10, + SmSeatsAdjustment = 15, + SmSeats = organization.SmSeats.GetValueOrDefault() + 10, + SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 10) - plan.BaseSeats, + SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 5, + SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 5) - (int)plan.BaseServiceAccount + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("Cannot set max seat autoscaling below seat count.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData] + public async Task UpdateSecretsManagerSubscription_ServiceAccountsGreaterThanMaxAutoscaleSeats_ThrowsException( + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + SmSeats = 10, + UseSecretsManager = true, + SmServiceAccounts = 5, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 10, + PlanType = PlanType.EnterpriseAnnually, + GatewayCustomerId = "1", + GatewaySubscriptionId = "9" + }; + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 15, + SmSeatsAdjustment = 1, + MaxAutoscaleSmServiceAccounts = 10, + SmServiceAccountsAdjustment = 11, + SmSeats = organization.SmSeats.GetValueOrDefault() + 1, + SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 1) - plan.BaseSeats, + SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 11, + SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 11) - (int)plan.BaseServiceAccount + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("Cannot set max Service Accounts autoscaling below Service Accounts count", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData] + public async Task UpdateSecretsManagerSubscription_NullGatewayCustomerId_ThrowsException( + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + SmSeats = 10, + SmServiceAccounts = 5, + UseSecretsManager = true, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 15, + PlanType = PlanType.EnterpriseAnnually + }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 15, + SmSeatsAdjustment = 1, + MaxAutoscaleSmServiceAccounts = 15, + SmServiceAccountsAdjustment = 1 + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("No payment method found.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData] + public async Task UpdateSecretsManagerSubscription_NullGatewaySubscriptionId_ThrowsException( + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + SmSeats = 10, + UseSecretsManager = true, + SmServiceAccounts = 5, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 15, + PlanType = PlanType.EnterpriseAnnually, + GatewayCustomerId = "1" + }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 15, + SmSeatsAdjustment = 1, + MaxAutoscaleSmServiceAccounts = 15, + SmServiceAccountsAdjustment = 1 + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("No subscription found.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData] + public async Task UpdateSecretsManagerSubscription_OrgWithNullSmSeatOnSeatsAdjustment_ThrowsException( + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + SmSeats = null, + UseSecretsManager = true, + SmServiceAccounts = 5, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 15, + PlanType = PlanType.EnterpriseAnnually, + GatewayCustomerId = "1" + }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 15, + SmSeatsAdjustment = 1, + MaxAutoscaleSmServiceAccounts = 15, + SmServiceAccountsAdjustment = 1 + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + + Assert.Contains("Organization has no Secrets Manager seat limit, no need to adjust seats", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData(PlanType.Custom)] + [BitAutoData(PlanType.FamiliesAnnually)] + [BitAutoData(PlanType.FamiliesAnnually2019)] + [BitAutoData(PlanType.EnterpriseMonthly2019)] + [BitAutoData(PlanType.EnterpriseAnnually2019)] + [BitAutoData(PlanType.TeamsMonthly2019)] + [BitAutoData(PlanType.TeamsAnnually2019)] + public async Task UpdateSecretsManagerSubscription_WithNonSecretsManagerPlanType_ThrowsBadRequestException( + PlanType planType, + Guid organizationId, + SutProvider sutProvider) + { + + var organization = new Organization + { + Id = organizationId, + SmSeats = 10, + UseSecretsManager = true, + SmServiceAccounts = 200, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 300, + PlanType = planType, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2" + }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organization.Id, + MaxAutoscaleSmSeats = 15, + SmSeatsAdjustment = 1, + MaxAutoscaleSmServiceAccounts = 300, + SmServiceAccountsAdjustment = 1 + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("Existing plan not found", exception.Message, StringComparison.InvariantCultureIgnoreCase); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData(PlanType.Free)] + public async Task UpdateSecretsManagerSubscription_WithHasAdditionalSeatsOptionfalse_ThrowsBadRequestException( + PlanType planType, + Guid organizationId, + SutProvider sutProvider) + { + + var organization = new Organization + { + Id = organizationId, + UseSecretsManager = true, + SmSeats = 10, + SmServiceAccounts = 200, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 300, + PlanType = planType, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2" + }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organization.Id, + MaxAutoscaleSmSeats = 15, + SmSeatsAdjustment = 1, + MaxAutoscaleSmServiceAccounts = 300, + SmServiceAccountsAdjustment = 1 + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("Plan does not allow additional Secrets Manager seats.", exception.Message, StringComparison.InvariantCultureIgnoreCase); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData(PlanType.Free)] + public async Task UpdateSecretsManagerSubscription_WithHasAdditionalServiceAccountOptionFalse_ThrowsBadRequestException( + PlanType planType, + Guid organizationId, + SutProvider sutProvider) + { + + var organization = new Organization + { + Id = organizationId, + UseSecretsManager = true, + SmSeats = 10, + SmServiceAccounts = 200, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 300, + PlanType = planType, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2" + }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organization.Id, + MaxAutoscaleSmSeats = 15, + SmSeatsAdjustment = 0, + MaxAutoscaleSmServiceAccounts = 300, + SmServiceAccountsAdjustment = 1 + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("Plan does not allow additional Service Accounts", exception.Message, StringComparison.InvariantCultureIgnoreCase); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.EnterpriseMonthly)] + [BitAutoData(PlanType.TeamsMonthly)] + [BitAutoData(PlanType.TeamsAnnually)] + public async Task UpdateSecretsManagerSubscription_ValidInput_Passes( + PlanType planType, + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + UseSecretsManager = true, + SmSeats = 10, + SmServiceAccounts = 200, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 350, + PlanType = planType, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2" + }; + + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 15, + SmSeatsAdjustment = 5, + MaxAutoscaleSmServiceAccounts = 300, + SmServiceAccountsAdjustment = 100, + SmSeats = organization.SmSeats.GetValueOrDefault() + 5, + SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 5) - plan.BaseSeats, + SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 100, + SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 100) - (int)plan.BaseServiceAccount, + MaxAutoscaleSmSeatsChanged = 15 != organization.MaxAutoscaleSeats.GetValueOrDefault(), + MaxAutoscaleSmServiceAccountsChanged = 200 != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + await sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate); + if (organizationUpdate.SmServiceAccountsAdjustment != 0) + { + await sutProvider.GetDependency().Received(1) + .AdjustSeatsAsync(organization, plan, organizationUpdate.SmSeatsExcludingBase); + + // TODO: call ReferenceEventService - see AC-1481 + } + + if (organizationUpdate.SmSeatsAdjustment != 0) + { + await sutProvider.GetDependency().Received(1) + .AdjustServiceAccountsAsync(organization, plan, organizationUpdate.SmServiceAccountsExcludingBase); + + // TODO: call ReferenceEventService - see AC-1481 + } + + if (organizationUpdate.SmSeatsAdjustment != 0) + { + await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( + Arg.Is(org => org.SmSeats == organizationUpdate.SmSeats)); + } + + if (organizationUpdate.SmServiceAccountsAdjustment != 0) + { + await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( + Arg.Is(org => + org.SmServiceAccounts == organizationUpdate.SmServiceAccounts)); + } + + if (organizationUpdate.MaxAutoscaleSmSeats != organization.MaxAutoscaleSmSeats) + { + await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( + Arg.Is(org => + org.MaxAutoscaleSmSeats == organizationUpdate.MaxAutoscaleSmServiceAccounts)); + } + + if (organizationUpdate.MaxAutoscaleSmServiceAccounts != organization.MaxAutoscaleSmServiceAccounts) + { + await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( + Arg.Is(org => + org.MaxAutoscaleSmServiceAccounts == organizationUpdate.MaxAutoscaleSmServiceAccounts)); + } + + await sutProvider.GetDependency().Received(1).SendSecretsManagerMaxSeatLimitReachedEmailAsync(organization, organization.MaxAutoscaleSmSeats.Value, Arg.Any>()); + await sutProvider.GetDependency().Received(1).SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(organization, organization.MaxAutoscaleSmServiceAccounts.Value, Arg.Any>()); + } + + [Theory] + [BitAutoData] + public async Task UpdateSecretsManagerSubscription_ThrowsBadRequestException_WhenMaxAutoscaleSeatsBelowSeatCount( + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + UseSecretsManager = true, + SmSeats = 5, + SmServiceAccounts = 200, + MaxAutoscaleSmSeats = 4, + MaxAutoscaleSmServiceAccounts = 300, + PlanType = PlanType.EnterpriseAnnually, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2" + }; + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); + var update = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 4, + SmSeatsAdjustment = 1, + MaxAutoscaleSmServiceAccounts = 300, + SmServiceAccountsAdjustment = 5, + SmSeats = organization.SmSeats.GetValueOrDefault() + 1, + SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 1) - plan.BaseSeats, + SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 5, + SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 5) - (int)plan.BaseServiceAccount + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(update)); + Assert.Contains("Cannot set max seat autoscaling below seat count.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData] + public async Task UpdateSecretsManagerSubscription_ThrowsBadRequestException_WhenOccupiedSeatsExceedNewSeatTotal( + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + UseSecretsManager = true, + SmSeats = 10, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2", + PlanType = PlanType.EnterpriseAnnually + }; + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); + var update = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 7, + SmSeatsAdjustment = -3, + MaxAutoscaleSmServiceAccounts = 300, + SmServiceAccountsAdjustment = 5, + SmSeats = organization.SmSeats.GetValueOrDefault() - 3, + SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() - 3) - plan.BaseSeats, + SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 5, + SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 5) - (int)plan.BaseServiceAccount + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + sutProvider.GetDependency().GetOccupiedSmSeatCountByOrganizationIdAsync(organizationId).Returns(8); + + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(update)); + Assert.Contains("Your organization currently has 8 Secrets Manager seats. Your plan only allows (7) Secrets Manager seats. Remove some Secrets Manager users", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData] + public async Task AdjustServiceAccountsAsync_ThrowsBadRequestException_WhenSmServiceAccountsIsNull( + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + SmSeats = 10, + UseSecretsManager = true, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2", + SmServiceAccounts = null, + PlanType = PlanType.EnterpriseAnnually + }; + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); + var update = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 21, + SmSeatsAdjustment = 10, + MaxAutoscaleSmServiceAccounts = 250, + SmServiceAccountsAdjustment = 1, + SmSeats = organization.SmSeats.GetValueOrDefault() + 10, + SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 10) - plan.BaseSeats, + SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 1, + SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 1) - (int)plan.BaseServiceAccount + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(update)); + Assert.Contains("Organization has no Service Accounts limit, no need to adjust Service Accounts", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData] + public async Task AutoscaleSeatsAsync_ThrowsBadRequestException_WhenMaxAutoscaleSeatsExceedPlanMaxUsers( + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + SmSeats = 3, + UseSecretsManager = true, + SmServiceAccounts = 100, + PlanType = PlanType.Free, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2", + }; + + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 15, + SmSeatsAdjustment = 0, + MaxAutoscaleSmServiceAccounts = 200, + SmServiceAccountsAdjustment = 0, + MaxAutoscaleSmSeatsChanged = 15 != organization.MaxAutoscaleSeats.GetValueOrDefault(), + MaxAutoscaleSmServiceAccountsChanged = 200 != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("Your plan has a Secrets Manager seat limit of 2, but you have specified a max autoscale count of 15.Reduce your max autoscale count.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData(PlanType.Free)] + public async Task AutoscaleSeatsAsync_ThrowsBadRequestException_WhenPlanDoesNotAllowSeatAutoscale( + PlanType planType, + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + UseSecretsManager = true, + SmSeats = 1, + SmServiceAccounts = 200, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 350, + PlanType = planType, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2" + }; + + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 1, + SmSeatsAdjustment = 0, + MaxAutoscaleSmServiceAccounts = 300, + SmServiceAccountsAdjustment = 0, + MaxAutoscaleSmSeatsChanged = 1 != organization.MaxAutoscaleSeats.GetValueOrDefault(), + MaxAutoscaleSmServiceAccountsChanged = 200 != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("Your plan does not allow Secrets Manager seat autoscaling", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + + } + + [Theory] + [BitAutoData(PlanType.Free)] + public async Task UpdateServiceAccountAutoscaling_ThrowsBadRequestException_WhenPlanDoesNotAllowServiceAccountAutoscale( + PlanType planType, + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + UseSecretsManager = true, + SmSeats = 10, + SmServiceAccounts = 200, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 350, + PlanType = planType, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2" + }; + + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = null, + SmSeatsAdjustment = 0, + MaxAutoscaleSmServiceAccounts = 300, + SmServiceAccountsAdjustment = 0, + MaxAutoscaleSmSeatsChanged = false, + MaxAutoscaleSmServiceAccountsChanged = 300 != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() + }; + + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("Your plan does not allow Service Accounts autoscaling.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + + } + + [Theory] + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.EnterpriseMonthly)] + [BitAutoData(PlanType.TeamsMonthly)] + [BitAutoData(PlanType.TeamsAnnually)] + public async Task UpdateServiceAccountAutoscaling_WhenCurrentServiceAccountsIsGreaterThanNew_ThrowsBadRequestException( + PlanType planType, + Guid organizationId, + SutProvider sutProvider) + { + var organization = new Organization + { + Id = organizationId, + UseSecretsManager = true, + SmSeats = 10, + SmServiceAccounts = 301, + MaxAutoscaleSmSeats = 20, + MaxAutoscaleSmServiceAccounts = 350, + PlanType = planType, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2" + }; + + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); + var organizationUpdate = new SecretsManagerSubscriptionUpdate + { + OrganizationId = organizationId, + MaxAutoscaleSmSeats = 15, + SmSeatsAdjustment = 5, + MaxAutoscaleSmServiceAccounts = 300, + SmServiceAccountsAdjustment = 100, + SmSeats = organization.SmSeats.GetValueOrDefault() + 5, + SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 5) - plan.BaseSeats, + SmServiceAccounts = 300, + SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 100) - (int)plan.BaseServiceAccount, + MaxAutoscaleSmSeatsChanged = 15 != organization.MaxAutoscaleSeats.GetValueOrDefault(), + MaxAutoscaleSmServiceAccountsChanged = 200 != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() + }; + var currentServiceAccounts = 301; + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + + sutProvider.GetDependency() + .GetServiceAccountCountByOrganizationIdAsync(organization.Id) + .Returns(currentServiceAccounts); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + Assert.Contains("Your organization currently has 301 Service Accounts. Your plan only allows (300) Service Accounts. Remove some Service Accounts", exception.Message); + await sutProvider.GetDependency().Received(1).GetServiceAccountCountByOrganizationIdAsync(organization.Id); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + private static async Task VerifyDependencyNotCalledAsync(SutProvider sutProvider) + { + await sutProvider.GetDependency().DidNotReceive() + .AdjustSeatsAsync(Arg.Any(), Arg.Any(), Arg.Any()); + await sutProvider.GetDependency().DidNotReceive() + .AdjustServiceAccountsAsync(Arg.Any(), Arg.Any(), Arg.Any()); + // TODO: call ReferenceEventService - see AC-1481 + await sutProvider.GetDependency().DidNotReceive() + .ReplaceAndUpdateCacheAsync(Arg.Any()); + await sutProvider.GetDependency().DidNotReceive() + .SendOrganizationMaxSeatLimitReachedEmailAsync(Arg.Any(), Arg.Any(), + Arg.Any>()); + } +} From 2cc3fcd2758fedb52424d3ccf63445fc68f8129a Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Mon, 10 Jul 2023 07:28:17 -0400 Subject: [PATCH 15/41] SM-802: Remove SecretsManagerBetaColumn migration --- ...0_SecretsManagerBetaColumn.sql.Designer.cs | 2228 ---------------- ...3-07-03_00_SecretsManagerBetaColumn.sql.cs | 25 - .../DatabaseContextModelSnapshot.cs | 3 - ...0_SecretsManagerBetaColumn.sql.Designer.cs | 2239 ----------------- ...3-07-03_00_SecretsManagerBetaColumn.sql.cs | 25 - .../DatabaseContextModelSnapshot.cs | 3 - ...0_SecretsManagerBetaColumn.sql.Designer.cs | 2226 ---------------- ...3-07-03_00_SecretsManagerBetaColumn.sql.cs | 25 - .../DatabaseContextModelSnapshot.cs | 3 - 9 files changed, 6777 deletions(-) delete mode 100644 util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs delete mode 100644 util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs delete mode 100644 util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs delete mode 100644 util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs delete mode 100644 util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs delete mode 100644 util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs diff --git a/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs b/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs deleted file mode 100644 index b393b8b43f32..000000000000 --- a/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs +++ /dev/null @@ -1,2228 +0,0 @@ -// -using System; -using Bit.Infrastructure.EntityFramework.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Bit.MySqlMigrations.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql")] - partial class _20230703_00_SecretsManagerBetaColumnsql - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "6.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("AccessCode") - .HasMaxLength(25) - .HasColumnType("varchar(25)"); - - b.Property("Approved") - .HasColumnType("tinyint(1)"); - - b.Property("AuthenticationDate") - .HasColumnType("datetime(6)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("MasterPasswordHash") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("PublicKey") - .HasColumnType("longtext"); - - b.Property("RequestDeviceIdentifier") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("RequestDeviceType") - .HasColumnType("tinyint unsigned"); - - b.Property("RequestIpAddress") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("ResponseDate") - .HasColumnType("datetime(6)"); - - b.Property("ResponseDeviceId") - .HasColumnType("char(36)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ResponseDeviceId"); - - b.HasIndex("UserId"); - - b.ToTable("AuthRequest", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("GranteeId") - .HasColumnType("char(36)"); - - b.Property("GrantorId") - .HasColumnType("char(36)"); - - b.Property("KeyEncrypted") - .HasColumnType("longtext"); - - b.Property("LastNotificationDate") - .HasColumnType("datetime(6)"); - - b.Property("RecoveryInitiatedDate") - .HasColumnType("datetime(6)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("tinyint unsigned"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("WaitTimeDays") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("GranteeId"); - - b.HasIndex("GrantorId"); - - b.ToTable("EmergencyAccess", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => - { - b.Property("Key") - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("ClientId") - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("ConsumedDate") - .HasColumnType("datetime(6)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Data") - .HasColumnType("longtext"); - - b.Property("Description") - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("ExpirationDate") - .HasColumnType("datetime(6)"); - - b.Property("SessionId") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("SubjectId") - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.HasKey("Key"); - - b.ToTable("Grant", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Data") - .HasColumnType("longtext"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("SsoConfig", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("ExternalId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("SsoUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("varchar(300)"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Collection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.Property("CollectionId") - .HasColumnType("char(36)"); - - b.Property("CipherId") - .HasColumnType("char(36)"); - - b.HasKey("CollectionId", "CipherId"); - - b.HasIndex("CipherId"); - - b.ToTable("CollectionCipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.Property("CollectionId") - .HasColumnType("char(36)"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("HidePasswords") - .HasColumnType("tinyint(1)"); - - b.Property("ReadOnly") - .HasColumnType("tinyint(1)"); - - b.HasKey("CollectionId", "GroupId"); - - b.HasIndex("GroupId"); - - b.ToTable("CollectionGroups"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.Property("CollectionId") - .HasColumnType("char(36)"); - - b.Property("OrganizationUserId") - .HasColumnType("char(36)"); - - b.Property("HidePasswords") - .HasColumnType("tinyint(1)"); - - b.Property("ReadOnly") - .HasColumnType("tinyint(1)"); - - b.HasKey("CollectionId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("EncryptedPrivateKey") - .HasColumnType("longtext"); - - b.Property("EncryptedPublicKey") - .HasColumnType("longtext"); - - b.Property("EncryptedUserKey") - .HasColumnType("longtext"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("PushToken") - .HasMaxLength(255) - .HasColumnType("varchar(255)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Device", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("ActingUserId") - .HasColumnType("char(36)"); - - b.Property("CipherId") - .HasColumnType("char(36)"); - - b.Property("CollectionId") - .HasColumnType("char(36)"); - - b.Property("Date") - .HasColumnType("datetime(6)"); - - b.Property("DeviceType") - .HasColumnType("tinyint unsigned"); - - b.Property("DomainName") - .HasColumnType("longtext"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("InstallationId") - .HasColumnType("char(36)"); - - b.Property("IpAddress") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("OrganizationUserId") - .HasColumnType("char(36)"); - - b.Property("PolicyId") - .HasColumnType("char(36)"); - - b.Property("ProviderId") - .HasColumnType("char(36)"); - - b.Property("ProviderOrganizationId") - .HasColumnType("char(36)"); - - b.Property("ProviderUserId") - .HasColumnType("char(36)"); - - b.Property("SecretId") - .HasColumnType("char(36)"); - - b.Property("ServiceAccountId") - .HasColumnType("char(36)"); - - b.Property("SystemUser") - .HasColumnType("tinyint unsigned"); - - b.Property("Type") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.ToTable("Event", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("AccessAll") - .HasColumnType("tinyint(1)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("varchar(300)"); - - b.Property("Name") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Group", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("OrganizationUserId") - .HasColumnType("char(36)"); - - b.HasKey("GroupId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("GroupUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("Key") - .HasMaxLength(150) - .HasColumnType("varchar(150)"); - - b.HasKey("Id"); - - b.ToTable("Installation", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("BillingEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("BusinessAddress1") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("BusinessAddress2") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("BusinessAddress3") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("BusinessCountry") - .HasMaxLength(2) - .HasColumnType("varchar(2)"); - - b.Property("BusinessName") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("BusinessTaxNumber") - .HasMaxLength(30) - .HasColumnType("varchar(30)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("ExpirationDate") - .HasColumnType("datetime(6)"); - - b.Property("Gateway") - .HasColumnType("tinyint unsigned"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("MaxAutoscaleSeats") - .HasColumnType("int"); - - b.Property("MaxAutoscaleSmSeats") - .HasColumnType("int"); - - b.Property("MaxAutoscaleSmServiceAccounts") - .HasColumnType("int"); - - b.Property("MaxCollections") - .HasColumnType("smallint"); - - b.Property("MaxStorageGb") - .HasColumnType("smallint"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("OwnersNotifiedOfAutoscaling") - .HasColumnType("datetime(6)"); - - b.Property("Plan") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("PlanType") - .HasColumnType("tinyint unsigned"); - - b.Property("PrivateKey") - .HasColumnType("longtext"); - - b.Property("PublicKey") - .HasColumnType("longtext"); - - b.Property("ReferenceData") - .HasColumnType("longtext"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Seats") - .HasColumnType("int"); - - b.Property("SecretsManagerBeta") - .HasColumnType("tinyint(1)"); - - b.Property("SelfHost") - .HasColumnType("tinyint(1)"); - - b.Property("SmSeats") - .HasColumnType("int"); - - b.Property("SmServiceAccounts") - .HasColumnType("int"); - - b.Property("Status") - .HasColumnType("tinyint unsigned"); - - b.Property("Storage") - .HasColumnType("bigint"); - - b.Property("TwoFactorProviders") - .HasColumnType("longtext"); - - b.Property("Use2fa") - .HasColumnType("tinyint(1)"); - - b.Property("UseApi") - .HasColumnType("tinyint(1)"); - - b.Property("UseCustomPermissions") - .HasColumnType("tinyint(1)"); - - b.Property("UseDirectory") - .HasColumnType("tinyint(1)"); - - b.Property("UseEvents") - .HasColumnType("tinyint(1)"); - - b.Property("UseGroups") - .HasColumnType("tinyint(1)"); - - b.Property("UseKeyConnector") - .HasColumnType("tinyint(1)"); - - b.Property("UsePasswordManager") - .HasColumnType("tinyint(1)"); - - b.Property("UsePolicies") - .HasColumnType("tinyint(1)"); - - b.Property("UseResetPassword") - .HasColumnType("tinyint(1)"); - - b.Property("UseScim") - .HasColumnType("tinyint(1)"); - - b.Property("UseSecretsManager") - .HasColumnType("tinyint(1)"); - - b.Property("UseSso") - .HasColumnType("tinyint(1)"); - - b.Property("UseTotp") - .HasColumnType("tinyint(1)"); - - b.Property("UsersGetPremium") - .HasColumnType("tinyint(1)"); - - b.HasKey("Id"); - - b.ToTable("Organization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("ApiKey") - .HasMaxLength(30) - .HasColumnType("varchar(30)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("Config") - .HasColumnType("longtext"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationConnection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("DomainName") - .HasMaxLength(255) - .HasColumnType("varchar(255)"); - - b.Property("JobRunCount") - .HasColumnType("int"); - - b.Property("LastCheckedDate") - .HasColumnType("datetime(6)"); - - b.Property("NextRunDate") - .HasColumnType("datetime(6)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("Txt") - .HasColumnType("longtext"); - - b.Property("VerifiedDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationDomain", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("FriendlyName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("LastSyncDate") - .HasColumnType("datetime(6)"); - - b.Property("OfferedToEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PlanSponsorshipType") - .HasColumnType("tinyint unsigned"); - - b.Property("SponsoredOrganizationId") - .HasColumnType("char(36)"); - - b.Property("SponsoringOrganizationId") - .HasColumnType("char(36)"); - - b.Property("SponsoringOrganizationUserId") - .HasColumnType("char(36)"); - - b.Property("ToDelete") - .HasColumnType("tinyint(1)"); - - b.Property("ValidUntil") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("SponsoredOrganizationId"); - - b.HasIndex("SponsoringOrganizationId"); - - b.ToTable("OrganizationSponsorship", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("AccessAll") - .HasColumnType("tinyint(1)"); - - b.Property("AccessSecretsManager") - .HasColumnType("tinyint(1)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("varchar(300)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("Permissions") - .HasColumnType("longtext"); - - b.Property("ResetPasswordKey") - .HasColumnType("longtext"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("OrganizationUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Data") - .HasColumnType("longtext"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Policy", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("BillingEmail") - .HasColumnType("longtext"); - - b.Property("BillingPhone") - .HasColumnType("longtext"); - - b.Property("BusinessAddress1") - .HasColumnType("longtext"); - - b.Property("BusinessAddress2") - .HasColumnType("longtext"); - - b.Property("BusinessAddress3") - .HasColumnType("longtext"); - - b.Property("BusinessCountry") - .HasColumnType("longtext"); - - b.Property("BusinessName") - .HasColumnType("longtext"); - - b.Property("BusinessTaxNumber") - .HasColumnType("longtext"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("tinyint unsigned"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UseEvents") - .HasColumnType("tinyint(1)"); - - b.HasKey("Id"); - - b.ToTable("Provider", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("ProviderId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Settings") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ProviderId"); - - b.ToTable("ProviderOrganization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Email") - .HasColumnType("longtext"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("Permissions") - .HasColumnType("longtext"); - - b.Property("ProviderId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("tinyint unsigned"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("ProviderId"); - - b.HasIndex("UserId"); - - b.ToTable("ProviderUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("AccessCount") - .HasColumnType("int"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Data") - .HasColumnType("longtext"); - - b.Property("DeletionDate") - .HasColumnType("datetime(6)"); - - b.Property("Disabled") - .HasColumnType("tinyint(1)"); - - b.Property("ExpirationDate") - .HasColumnType("datetime(6)"); - - b.Property("HideEmail") - .HasColumnType("tinyint(1)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("MaxAccessCount") - .HasColumnType("int"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("Password") - .HasMaxLength(300) - .HasColumnType("varchar(300)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Send", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => - { - b.Property("Id") - .HasMaxLength(40) - .HasColumnType("varchar(40)"); - - b.Property("Active") - .HasColumnType("tinyint(1)"); - - b.Property("Country") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("PostalCode") - .HasMaxLength(10) - .HasColumnType("varchar(10)"); - - b.Property("Rate") - .HasColumnType("decimal(65,30)"); - - b.Property("State") - .HasMaxLength(2) - .HasColumnType("varchar(2)"); - - b.HasKey("Id"); - - b.ToTable("TaxRate", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("Amount") - .HasColumnType("decimal(65,30)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Details") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("Gateway") - .HasColumnType("tinyint unsigned"); - - b.Property("GatewayId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("PaymentMethodType") - .HasColumnType("tinyint unsigned"); - - b.Property("Refunded") - .HasColumnType("tinyint(1)"); - - b.Property("RefundedAmount") - .HasColumnType("decimal(65,30)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Transaction", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("AccountRevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("ApiKey") - .IsRequired() - .HasMaxLength(30) - .HasColumnType("varchar(30)"); - - b.Property("AvatarColor") - .HasMaxLength(7) - .HasColumnType("varchar(7)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Culture") - .HasMaxLength(10) - .HasColumnType("varchar(10)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailVerified") - .HasColumnType("tinyint(1)"); - - b.Property("EquivalentDomains") - .HasColumnType("longtext"); - - b.Property("ExcludedGlobalEquivalentDomains") - .HasColumnType("longtext"); - - b.Property("FailedLoginCount") - .HasColumnType("int"); - - b.Property("ForcePasswordReset") - .HasColumnType("tinyint(1)"); - - b.Property("Gateway") - .HasColumnType("tinyint unsigned"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Kdf") - .HasColumnType("tinyint unsigned"); - - b.Property("KdfIterations") - .HasColumnType("int"); - - b.Property("KdfMemory") - .HasColumnType("int"); - - b.Property("KdfParallelism") - .HasColumnType("int"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("LastEmailChangeDate") - .HasColumnType("datetime(6)"); - - b.Property("LastFailedLoginDate") - .HasColumnType("datetime(6)"); - - b.Property("LastKdfChangeDate") - .HasColumnType("datetime(6)"); - - b.Property("LastKeyRotationDate") - .HasColumnType("datetime(6)"); - - b.Property("LastPasswordChangeDate") - .HasColumnType("datetime(6)"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("MasterPassword") - .HasMaxLength(300) - .HasColumnType("varchar(300)"); - - b.Property("MasterPasswordHint") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("MaxStorageGb") - .HasColumnType("smallint"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Premium") - .HasColumnType("tinyint(1)"); - - b.Property("PremiumExpirationDate") - .HasColumnType("datetime(6)"); - - b.Property("PrivateKey") - .HasColumnType("longtext"); - - b.Property("PublicKey") - .HasColumnType("longtext"); - - b.Property("ReferenceData") - .HasColumnType("longtext"); - - b.Property("RenewalReminderDate") - .HasColumnType("datetime(6)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("SecurityStamp") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Storage") - .HasColumnType("bigint"); - - b.Property("TwoFactorProviders") - .HasColumnType("longtext"); - - b.Property("TwoFactorRecoveryCode") - .HasMaxLength(32) - .HasColumnType("varchar(32)"); - - b.Property("UsesKeyConnector") - .HasColumnType("tinyint(1)"); - - b.HasKey("Id"); - - b.ToTable("User", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Read") - .HasColumnType("tinyint(1)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Write") - .HasColumnType("tinyint(1)"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.ToTable("AccessPolicy", (string)null); - - b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("ClientSecretHash") - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("EncryptedPayload") - .HasMaxLength(4000) - .HasColumnType("varchar(4000)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Scope") - .HasMaxLength(4000) - .HasColumnType("varchar(4000)"); - - b.Property("ServiceAccountId") - .HasColumnType("char(36)"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("ServiceAccountId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("DeletedDate") - .HasColumnType("datetime(6)"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Project", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("DeletedDate") - .HasColumnType("datetime(6)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("Note") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Secret", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ServiceAccount", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("Attachments") - .HasColumnType("longtext"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Data") - .HasColumnType("longtext"); - - b.Property("DeletedDate") - .HasColumnType("datetime(6)"); - - b.Property("Favorites") - .HasColumnType("longtext"); - - b.Property("Folders") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("Reprompt") - .HasColumnType("tinyint unsigned"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Cipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Folder", (string)null); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.Property("ProjectsId") - .HasColumnType("char(36)"); - - b.Property("SecretsId") - .HasColumnType("char(36)"); - - b.HasKey("ProjectsId", "SecretsId"); - - b.HasIndex("SecretsId"); - - b.ToTable("ProjectSecret"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GrantedProjectId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GrantedProjectId"); - - b.Property("ServiceAccountId") - .HasColumnType("char(36)") - .HasColumnName("ServiceAccountId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("ServiceAccountId"); - - b.HasDiscriminator().HasValue("service_account_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GrantedProjectId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") - .WithMany() - .HasForeignKey("ResponseDeviceId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("ResponseDevice"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") - .WithMany() - .HasForeignKey("GranteeId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") - .WithMany() - .HasForeignKey("GrantorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Grantee"); - - b.Navigation("Grantor"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoConfigs") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoUsers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("SsoUsers") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Collections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") - .WithMany("CollectionCiphers") - .HasForeignKey("CipherId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionCiphers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Cipher"); - - b.Navigation("Collection"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionGroups") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionUsers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("CollectionUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Groups") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany("GroupUsers") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("GroupUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Group"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("ApiKeys") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Connections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Domains") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") - .WithMany() - .HasForeignKey("SponsoredOrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") - .WithMany() - .HasForeignKey("SponsoringOrganizationId"); - - b.Navigation("SponsoredOrganization"); - - b.Navigation("SponsoringOrganization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("OrganizationUsers") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("OrganizationUsers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Policies") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Provider"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Transactions") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Transactions") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Ciphers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Ciphers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Folders") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) - .WithMany() - .HasForeignKey("ProjectsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) - .WithMany() - .HasForeignKey("SecretsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedProject"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("ServiceAccountAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("GrantedProject"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedProject"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Navigation("CollectionCiphers"); - - b.Navigation("CollectionGroups"); - - b.Navigation("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Navigation("ApiKeys"); - - b.Navigation("Ciphers"); - - b.Navigation("Collections"); - - b.Navigation("Connections"); - - b.Navigation("Domains"); - - b.Navigation("Groups"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("Policies"); - - b.Navigation("SsoConfigs"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Navigation("CollectionUsers"); - - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Navigation("Ciphers"); - - b.Navigation("Folders"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("ServiceAccountAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Navigation("CollectionCiphers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs b/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs deleted file mode 100644 index 331d0f299247..000000000000 --- a/util/MySqlMigrations/Migrations/20230703185325_2023-07-03_00_SecretsManagerBetaColumn.sql.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.MySqlMigrations.Migrations; - -public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration -{ - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "SecretsManagerBeta", - table: "Organization", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "SecretsManagerBeta", - table: "Organization"); - } -} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index f64675e25668..95a769229f7e 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -608,9 +608,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Seats") .HasColumnType("int"); - b.Property("SecretsManagerBeta") - .HasColumnType("tinyint(1)"); - b.Property("SelfHost") .HasColumnType("tinyint(1)"); diff --git a/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs b/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs deleted file mode 100644 index ef5e0a548138..000000000000 --- a/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs +++ /dev/null @@ -1,2239 +0,0 @@ -// -using System; -using Bit.Infrastructure.EntityFramework.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Bit.PostgresMigrations.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql")] - partial class _20230703_00_SecretsManagerBetaColumnsql - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") - .HasAnnotation("ProductVersion", "6.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("AccessCode") - .HasMaxLength(25) - .HasColumnType("character varying(25)"); - - b.Property("Approved") - .HasColumnType("boolean"); - - b.Property("AuthenticationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("MasterPasswordHash") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("PublicKey") - .HasColumnType("text"); - - b.Property("RequestDeviceIdentifier") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("RequestDeviceType") - .HasColumnType("smallint"); - - b.Property("RequestIpAddress") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("ResponseDate") - .HasColumnType("timestamp with time zone"); - - b.Property("ResponseDeviceId") - .HasColumnType("uuid"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ResponseDeviceId"); - - b.HasIndex("UserId"); - - b.ToTable("AuthRequest", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("GranteeId") - .HasColumnType("uuid"); - - b.Property("GrantorId") - .HasColumnType("uuid"); - - b.Property("KeyEncrypted") - .HasColumnType("text"); - - b.Property("LastNotificationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("RecoveryInitiatedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("WaitTimeDays") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("GranteeId"); - - b.HasIndex("GrantorId"); - - b.ToTable("EmergencyAccess", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => - { - b.Property("Key") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ClientId") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ConsumedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Description") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ExpirationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("SessionId") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("SubjectId") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.HasKey("Key"); - - b.ToTable("Grant", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("SsoConfig", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("ExternalId") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .UseCollation("postgresIndetermanisticCollation"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("SsoUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("character varying(300)"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Collection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.Property("CollectionId") - .HasColumnType("uuid"); - - b.Property("CipherId") - .HasColumnType("uuid"); - - b.HasKey("CollectionId", "CipherId"); - - b.HasIndex("CipherId"); - - b.ToTable("CollectionCipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.Property("CollectionId") - .HasColumnType("uuid"); - - b.Property("GroupId") - .HasColumnType("uuid"); - - b.Property("HidePasswords") - .HasColumnType("boolean"); - - b.Property("ReadOnly") - .HasColumnType("boolean"); - - b.HasKey("CollectionId", "GroupId"); - - b.HasIndex("GroupId"); - - b.ToTable("CollectionGroups"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.Property("CollectionId") - .HasColumnType("uuid"); - - b.Property("OrganizationUserId") - .HasColumnType("uuid"); - - b.Property("HidePasswords") - .HasColumnType("boolean"); - - b.Property("ReadOnly") - .HasColumnType("boolean"); - - b.HasKey("CollectionId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("EncryptedPrivateKey") - .HasColumnType("text"); - - b.Property("EncryptedPublicKey") - .HasColumnType("text"); - - b.Property("EncryptedUserKey") - .HasColumnType("text"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("PushToken") - .HasMaxLength(255) - .HasColumnType("character varying(255)"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Device", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("ActingUserId") - .HasColumnType("uuid"); - - b.Property("CipherId") - .HasColumnType("uuid"); - - b.Property("CollectionId") - .HasColumnType("uuid"); - - b.Property("Date") - .HasColumnType("timestamp with time zone"); - - b.Property("DeviceType") - .HasColumnType("smallint"); - - b.Property("DomainName") - .HasColumnType("text"); - - b.Property("GroupId") - .HasColumnType("uuid"); - - b.Property("InstallationId") - .HasColumnType("uuid"); - - b.Property("IpAddress") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("OrganizationUserId") - .HasColumnType("uuid"); - - b.Property("PolicyId") - .HasColumnType("uuid"); - - b.Property("ProviderId") - .HasColumnType("uuid"); - - b.Property("ProviderOrganizationId") - .HasColumnType("uuid"); - - b.Property("ProviderUserId") - .HasColumnType("uuid"); - - b.Property("SecretId") - .HasColumnType("uuid"); - - b.Property("ServiceAccountId") - .HasColumnType("uuid"); - - b.Property("SystemUser") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("integer"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.ToTable("Event", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("AccessAll") - .HasColumnType("boolean"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("character varying(300)"); - - b.Property("Name") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Group", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.Property("GroupId") - .HasColumnType("uuid"); - - b.Property("OrganizationUserId") - .HasColumnType("uuid"); - - b.HasKey("GroupId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("GroupUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Key") - .HasMaxLength(150) - .HasColumnType("character varying(150)"); - - b.HasKey("Id"); - - b.ToTable("Installation", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("BillingEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("BusinessAddress1") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("BusinessAddress2") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("BusinessAddress3") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("BusinessCountry") - .HasMaxLength(2) - .HasColumnType("character varying(2)"); - - b.Property("BusinessName") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("BusinessTaxNumber") - .HasMaxLength(30) - .HasColumnType("character varying(30)"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("ExpirationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Gateway") - .HasColumnType("smallint"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .UseCollation("postgresIndetermanisticCollation"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("MaxAutoscaleSeats") - .HasColumnType("integer"); - - b.Property("MaxAutoscaleSmSeats") - .HasColumnType("integer"); - - b.Property("MaxAutoscaleSmServiceAccounts") - .HasColumnType("integer"); - - b.Property("MaxCollections") - .HasColumnType("smallint"); - - b.Property("MaxStorageGb") - .HasColumnType("smallint"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("OwnersNotifiedOfAutoscaling") - .HasColumnType("timestamp with time zone"); - - b.Property("Plan") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("PlanType") - .HasColumnType("smallint"); - - b.Property("PrivateKey") - .HasColumnType("text"); - - b.Property("PublicKey") - .HasColumnType("text"); - - b.Property("ReferenceData") - .HasColumnType("text"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Seats") - .HasColumnType("integer"); - - b.Property("SecretsManagerBeta") - .HasColumnType("boolean"); - - b.Property("SelfHost") - .HasColumnType("boolean"); - - b.Property("SmSeats") - .HasColumnType("integer"); - - b.Property("SmServiceAccounts") - .HasColumnType("integer"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Storage") - .HasColumnType("bigint"); - - b.Property("TwoFactorProviders") - .HasColumnType("text"); - - b.Property("Use2fa") - .HasColumnType("boolean"); - - b.Property("UseApi") - .HasColumnType("boolean"); - - b.Property("UseCustomPermissions") - .HasColumnType("boolean"); - - b.Property("UseDirectory") - .HasColumnType("boolean"); - - b.Property("UseEvents") - .HasColumnType("boolean"); - - b.Property("UseGroups") - .HasColumnType("boolean"); - - b.Property("UseKeyConnector") - .HasColumnType("boolean"); - - b.Property("UsePasswordManager") - .HasColumnType("boolean"); - - b.Property("UsePolicies") - .HasColumnType("boolean"); - - b.Property("UseResetPassword") - .HasColumnType("boolean"); - - b.Property("UseScim") - .HasColumnType("boolean"); - - b.Property("UseSecretsManager") - .HasColumnType("boolean"); - - b.Property("UseSso") - .HasColumnType("boolean"); - - b.Property("UseTotp") - .HasColumnType("boolean"); - - b.Property("UsersGetPremium") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.ToTable("Organization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("ApiKey") - .HasMaxLength(30) - .HasColumnType("character varying(30)"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("Config") - .HasColumnType("text"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationConnection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("DomainName") - .HasMaxLength(255) - .HasColumnType("character varying(255)"); - - b.Property("JobRunCount") - .HasColumnType("integer"); - - b.Property("LastCheckedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("NextRunDate") - .HasColumnType("timestamp with time zone"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("Txt") - .HasColumnType("text"); - - b.Property("VerifiedDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationDomain", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("FriendlyName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("LastSyncDate") - .HasColumnType("timestamp with time zone"); - - b.Property("OfferedToEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PlanSponsorshipType") - .HasColumnType("smallint"); - - b.Property("SponsoredOrganizationId") - .HasColumnType("uuid"); - - b.Property("SponsoringOrganizationId") - .HasColumnType("uuid"); - - b.Property("SponsoringOrganizationUserId") - .HasColumnType("uuid"); - - b.Property("ToDelete") - .HasColumnType("boolean"); - - b.Property("ValidUntil") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("SponsoredOrganizationId"); - - b.HasIndex("SponsoringOrganizationId"); - - b.ToTable("OrganizationSponsorship", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("AccessAll") - .HasColumnType("boolean"); - - b.Property("AccessSecretsManager") - .HasColumnType("boolean"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("character varying(300)"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("Permissions") - .HasColumnType("text"); - - b.Property("ResetPasswordKey") - .HasColumnType("text"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("OrganizationUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Policy", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("BillingEmail") - .HasColumnType("text"); - - b.Property("BillingPhone") - .HasColumnType("text"); - - b.Property("BusinessAddress1") - .HasColumnType("text"); - - b.Property("BusinessAddress2") - .HasColumnType("text"); - - b.Property("BusinessAddress3") - .HasColumnType("text"); - - b.Property("BusinessCountry") - .HasColumnType("text"); - - b.Property("BusinessName") - .HasColumnType("text"); - - b.Property("BusinessTaxNumber") - .HasColumnType("text"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UseEvents") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.ToTable("Provider", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("ProviderId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Settings") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ProviderId"); - - b.ToTable("ProviderOrganization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Email") - .HasColumnType("text"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Permissions") - .HasColumnType("text"); - - b.Property("ProviderId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("ProviderId"); - - b.HasIndex("UserId"); - - b.ToTable("ProviderUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("AccessCount") - .HasColumnType("integer"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("DeletionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Disabled") - .HasColumnType("boolean"); - - b.Property("ExpirationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("HideEmail") - .HasColumnType("boolean"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("MaxAccessCount") - .HasColumnType("integer"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("Password") - .HasMaxLength(300) - .HasColumnType("character varying(300)"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Send", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => - { - b.Property("Id") - .HasMaxLength(40) - .HasColumnType("character varying(40)"); - - b.Property("Active") - .HasColumnType("boolean"); - - b.Property("Country") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("PostalCode") - .HasMaxLength(10) - .HasColumnType("character varying(10)"); - - b.Property("Rate") - .HasColumnType("numeric"); - - b.Property("State") - .HasMaxLength(2) - .HasColumnType("character varying(2)"); - - b.HasKey("Id"); - - b.ToTable("TaxRate", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("Amount") - .HasColumnType("numeric"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Details") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("Gateway") - .HasColumnType("smallint"); - - b.Property("GatewayId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("PaymentMethodType") - .HasColumnType("smallint"); - - b.Property("Refunded") - .HasColumnType("boolean"); - - b.Property("RefundedAmount") - .HasColumnType("numeric"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Transaction", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("AccountRevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("ApiKey") - .IsRequired() - .HasMaxLength(30) - .HasColumnType("character varying(30)"); - - b.Property("AvatarColor") - .HasMaxLength(7) - .HasColumnType("character varying(7)"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Culture") - .HasMaxLength(10) - .HasColumnType("character varying(10)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .UseCollation("postgresIndetermanisticCollation"); - - b.Property("EmailVerified") - .HasColumnType("boolean"); - - b.Property("EquivalentDomains") - .HasColumnType("text"); - - b.Property("ExcludedGlobalEquivalentDomains") - .HasColumnType("text"); - - b.Property("FailedLoginCount") - .HasColumnType("integer"); - - b.Property("ForcePasswordReset") - .HasColumnType("boolean"); - - b.Property("Gateway") - .HasColumnType("smallint"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Kdf") - .HasColumnType("smallint"); - - b.Property("KdfIterations") - .HasColumnType("integer"); - - b.Property("KdfMemory") - .HasColumnType("integer"); - - b.Property("KdfParallelism") - .HasColumnType("integer"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("LastEmailChangeDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LastFailedLoginDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LastKdfChangeDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LastKeyRotationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LastPasswordChangeDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("MasterPassword") - .HasMaxLength(300) - .HasColumnType("character varying(300)"); - - b.Property("MasterPasswordHint") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("MaxStorageGb") - .HasColumnType("smallint"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Premium") - .HasColumnType("boolean"); - - b.Property("PremiumExpirationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("PrivateKey") - .HasColumnType("text"); - - b.Property("PublicKey") - .HasColumnType("text"); - - b.Property("ReferenceData") - .HasColumnType("text"); - - b.Property("RenewalReminderDate") - .HasColumnType("timestamp with time zone"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("SecurityStamp") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Storage") - .HasColumnType("bigint"); - - b.Property("TwoFactorProviders") - .HasColumnType("text"); - - b.Property("TwoFactorRecoveryCode") - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b.Property("UsesKeyConnector") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.ToTable("User", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("text"); - - b.Property("Read") - .HasColumnType("boolean"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Write") - .HasColumnType("boolean"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.ToTable("AccessPolicy", (string)null); - - b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("ClientSecretHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("EncryptedPayload") - .HasMaxLength(4000) - .HasColumnType("character varying(4000)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Scope") - .HasMaxLength(4000) - .HasColumnType("character varying(4000)"); - - b.Property("ServiceAccountId") - .HasColumnType("uuid"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("ServiceAccountId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Project", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Note") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Secret", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ServiceAccount", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("Attachments") - .HasColumnType("text"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("DeletedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Favorites") - .HasColumnType("text"); - - b.Property("Folders") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("Reprompt") - .HasColumnType("smallint"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Cipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Folder", (string)null); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.Property("ProjectsId") - .HasColumnType("uuid"); - - b.Property("SecretsId") - .HasColumnType("uuid"); - - b.HasKey("ProjectsId", "SecretsId"); - - b.HasIndex("SecretsId"); - - b.ToTable("ProjectSecret"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GrantedProjectId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GrantedProjectId"); - - b.Property("ServiceAccountId") - .HasColumnType("uuid") - .HasColumnName("ServiceAccountId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("ServiceAccountId"); - - b.HasDiscriminator().HasValue("service_account_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GrantedProjectId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") - .WithMany() - .HasForeignKey("ResponseDeviceId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("ResponseDevice"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") - .WithMany() - .HasForeignKey("GranteeId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") - .WithMany() - .HasForeignKey("GrantorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Grantee"); - - b.Navigation("Grantor"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoConfigs") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoUsers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("SsoUsers") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Collections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") - .WithMany("CollectionCiphers") - .HasForeignKey("CipherId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionCiphers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Cipher"); - - b.Navigation("Collection"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionGroups") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionUsers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("CollectionUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Groups") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany("GroupUsers") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("GroupUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Group"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("ApiKeys") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Connections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Domains") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") - .WithMany() - .HasForeignKey("SponsoredOrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") - .WithMany() - .HasForeignKey("SponsoringOrganizationId"); - - b.Navigation("SponsoredOrganization"); - - b.Navigation("SponsoringOrganization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("OrganizationUsers") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("OrganizationUsers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Policies") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Provider"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Transactions") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Transactions") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Ciphers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Ciphers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Folders") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) - .WithMany() - .HasForeignKey("ProjectsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) - .WithMany() - .HasForeignKey("SecretsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedProject"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("ServiceAccountAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("GrantedProject"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedProject"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Navigation("CollectionCiphers"); - - b.Navigation("CollectionGroups"); - - b.Navigation("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Navigation("ApiKeys"); - - b.Navigation("Ciphers"); - - b.Navigation("Collections"); - - b.Navigation("Connections"); - - b.Navigation("Domains"); - - b.Navigation("Groups"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("Policies"); - - b.Navigation("SsoConfigs"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Navigation("CollectionUsers"); - - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Navigation("Ciphers"); - - b.Navigation("Folders"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("ServiceAccountAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Navigation("CollectionCiphers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs b/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs deleted file mode 100644 index 2a7906cee2ac..000000000000 --- a/util/PostgresMigrations/Migrations/20230703185317_2023-07-03_00_SecretsManagerBetaColumn.sql.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.PostgresMigrations.Migrations; - -public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration -{ - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "SecretsManagerBeta", - table: "Organization", - type: "boolean", - nullable: false, - defaultValue: false); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "SecretsManagerBeta", - table: "Organization"); - } -} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index 56cd8c67e09d..c070e191063c 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -618,9 +618,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Seats") .HasColumnType("integer"); - b.Property("SecretsManagerBeta") - .HasColumnType("boolean"); - b.Property("SelfHost") .HasColumnType("boolean"); diff --git a/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs b/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs deleted file mode 100644 index 37752c6f7527..000000000000 --- a/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.Designer.cs +++ /dev/null @@ -1,2226 +0,0 @@ -// -using System; -using Bit.Infrastructure.EntityFramework.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Bit.SqliteMigrations.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql")] - partial class _20230703_00_SecretsManagerBetaColumnsql - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "6.0.12"); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AccessCode") - .HasMaxLength(25) - .HasColumnType("TEXT"); - - b.Property("Approved") - .HasColumnType("INTEGER"); - - b.Property("AuthenticationDate") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("MasterPasswordHash") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("PublicKey") - .HasColumnType("TEXT"); - - b.Property("RequestDeviceIdentifier") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("RequestDeviceType") - .HasColumnType("INTEGER"); - - b.Property("RequestIpAddress") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("ResponseDate") - .HasColumnType("TEXT"); - - b.Property("ResponseDeviceId") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ResponseDeviceId"); - - b.HasIndex("UserId"); - - b.ToTable("AuthRequest", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("GranteeId") - .HasColumnType("TEXT"); - - b.Property("GrantorId") - .HasColumnType("TEXT"); - - b.Property("KeyEncrypted") - .HasColumnType("TEXT"); - - b.Property("LastNotificationDate") - .HasColumnType("TEXT"); - - b.Property("RecoveryInitiatedDate") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("WaitTimeDays") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("GranteeId"); - - b.HasIndex("GrantorId"); - - b.ToTable("EmergencyAccess", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => - { - b.Property("Key") - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("ClientId") - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("ConsumedDate") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Data") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("ExpirationDate") - .HasColumnType("TEXT"); - - b.Property("SessionId") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("SubjectId") - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.HasKey("Key"); - - b.ToTable("Grant", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Data") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("SsoConfig", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("ExternalId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("SsoUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Collection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.Property("CollectionId") - .HasColumnType("TEXT"); - - b.Property("CipherId") - .HasColumnType("TEXT"); - - b.HasKey("CollectionId", "CipherId"); - - b.HasIndex("CipherId"); - - b.ToTable("CollectionCipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.Property("CollectionId") - .HasColumnType("TEXT"); - - b.Property("GroupId") - .HasColumnType("TEXT"); - - b.Property("HidePasswords") - .HasColumnType("INTEGER"); - - b.Property("ReadOnly") - .HasColumnType("INTEGER"); - - b.HasKey("CollectionId", "GroupId"); - - b.HasIndex("GroupId"); - - b.ToTable("CollectionGroups"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.Property("CollectionId") - .HasColumnType("TEXT"); - - b.Property("OrganizationUserId") - .HasColumnType("TEXT"); - - b.Property("HidePasswords") - .HasColumnType("INTEGER"); - - b.Property("ReadOnly") - .HasColumnType("INTEGER"); - - b.HasKey("CollectionId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("EncryptedPrivateKey") - .HasColumnType("TEXT"); - - b.Property("EncryptedPublicKey") - .HasColumnType("TEXT"); - - b.Property("EncryptedUserKey") - .HasColumnType("TEXT"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("PushToken") - .HasMaxLength(255) - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Device", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("ActingUserId") - .HasColumnType("TEXT"); - - b.Property("CipherId") - .HasColumnType("TEXT"); - - b.Property("CollectionId") - .HasColumnType("TEXT"); - - b.Property("Date") - .HasColumnType("TEXT"); - - b.Property("DeviceType") - .HasColumnType("INTEGER"); - - b.Property("DomainName") - .HasColumnType("TEXT"); - - b.Property("GroupId") - .HasColumnType("TEXT"); - - b.Property("InstallationId") - .HasColumnType("TEXT"); - - b.Property("IpAddress") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("OrganizationUserId") - .HasColumnType("TEXT"); - - b.Property("PolicyId") - .HasColumnType("TEXT"); - - b.Property("ProviderId") - .HasColumnType("TEXT"); - - b.Property("ProviderOrganizationId") - .HasColumnType("TEXT"); - - b.Property("ProviderUserId") - .HasColumnType("TEXT"); - - b.Property("SecretId") - .HasColumnType("TEXT"); - - b.Property("ServiceAccountId") - .HasColumnType("TEXT"); - - b.Property("SystemUser") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Event", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AccessAll") - .HasColumnType("INTEGER"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("TEXT"); - - b.Property("Name") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Group", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.Property("GroupId") - .HasColumnType("TEXT"); - - b.Property("OrganizationUserId") - .HasColumnType("TEXT"); - - b.HasKey("GroupId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("GroupUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("Key") - .HasMaxLength(150) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Installation", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("BillingEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("BusinessAddress1") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("BusinessAddress2") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("BusinessAddress3") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("BusinessCountry") - .HasMaxLength(2) - .HasColumnType("TEXT"); - - b.Property("BusinessName") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("BusinessTaxNumber") - .HasMaxLength(30) - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("ExpirationDate") - .HasColumnType("TEXT"); - - b.Property("Gateway") - .HasColumnType("INTEGER"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("MaxAutoscaleSeats") - .HasColumnType("INTEGER"); - - b.Property("MaxAutoscaleSmSeats") - .HasColumnType("INTEGER"); - - b.Property("MaxAutoscaleSmServiceAccounts") - .HasColumnType("INTEGER"); - - b.Property("MaxCollections") - .HasColumnType("INTEGER"); - - b.Property("MaxStorageGb") - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("OwnersNotifiedOfAutoscaling") - .HasColumnType("TEXT"); - - b.Property("Plan") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("PlanType") - .HasColumnType("INTEGER"); - - b.Property("PrivateKey") - .HasColumnType("TEXT"); - - b.Property("PublicKey") - .HasColumnType("TEXT"); - - b.Property("ReferenceData") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Seats") - .HasColumnType("INTEGER"); - - b.Property("SecretsManagerBeta") - .HasColumnType("INTEGER"); - - b.Property("SelfHost") - .HasColumnType("INTEGER"); - - b.Property("SmSeats") - .HasColumnType("INTEGER"); - - b.Property("SmServiceAccounts") - .HasColumnType("INTEGER"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Storage") - .HasColumnType("INTEGER"); - - b.Property("TwoFactorProviders") - .HasColumnType("TEXT"); - - b.Property("Use2fa") - .HasColumnType("INTEGER"); - - b.Property("UseApi") - .HasColumnType("INTEGER"); - - b.Property("UseCustomPermissions") - .HasColumnType("INTEGER"); - - b.Property("UseDirectory") - .HasColumnType("INTEGER"); - - b.Property("UseEvents") - .HasColumnType("INTEGER"); - - b.Property("UseGroups") - .HasColumnType("INTEGER"); - - b.Property("UseKeyConnector") - .HasColumnType("INTEGER"); - - b.Property("UsePasswordManager") - .HasColumnType("INTEGER"); - - b.Property("UsePolicies") - .HasColumnType("INTEGER"); - - b.Property("UseResetPassword") - .HasColumnType("INTEGER"); - - b.Property("UseScim") - .HasColumnType("INTEGER"); - - b.Property("UseSecretsManager") - .HasColumnType("INTEGER"); - - b.Property("UseSso") - .HasColumnType("INTEGER"); - - b.Property("UseTotp") - .HasColumnType("INTEGER"); - - b.Property("UsersGetPremium") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Organization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("ApiKey") - .HasMaxLength(30) - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("Config") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationConnection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("DomainName") - .HasMaxLength(255) - .HasColumnType("TEXT"); - - b.Property("JobRunCount") - .HasColumnType("INTEGER"); - - b.Property("LastCheckedDate") - .HasColumnType("TEXT"); - - b.Property("NextRunDate") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("Txt") - .HasColumnType("TEXT"); - - b.Property("VerifiedDate") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationDomain", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("FriendlyName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("LastSyncDate") - .HasColumnType("TEXT"); - - b.Property("OfferedToEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("PlanSponsorshipType") - .HasColumnType("INTEGER"); - - b.Property("SponsoredOrganizationId") - .HasColumnType("TEXT"); - - b.Property("SponsoringOrganizationId") - .HasColumnType("TEXT"); - - b.Property("SponsoringOrganizationUserId") - .HasColumnType("TEXT"); - - b.Property("ToDelete") - .HasColumnType("INTEGER"); - - b.Property("ValidUntil") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("SponsoredOrganizationId"); - - b.HasIndex("SponsoringOrganizationId"); - - b.ToTable("OrganizationSponsorship", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AccessAll") - .HasColumnType("INTEGER"); - - b.Property("AccessSecretsManager") - .HasColumnType("INTEGER"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("Permissions") - .HasColumnType("TEXT"); - - b.Property("ResetPasswordKey") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("OrganizationUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Data") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Policy", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("BillingEmail") - .HasColumnType("TEXT"); - - b.Property("BillingPhone") - .HasColumnType("TEXT"); - - b.Property("BusinessAddress1") - .HasColumnType("TEXT"); - - b.Property("BusinessAddress2") - .HasColumnType("TEXT"); - - b.Property("BusinessAddress3") - .HasColumnType("TEXT"); - - b.Property("BusinessCountry") - .HasColumnType("TEXT"); - - b.Property("BusinessName") - .HasColumnType("TEXT"); - - b.Property("BusinessTaxNumber") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UseEvents") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Provider", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("ProviderId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Settings") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ProviderId"); - - b.ToTable("ProviderOrganization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Email") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("Permissions") - .HasColumnType("TEXT"); - - b.Property("ProviderId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ProviderId"); - - b.HasIndex("UserId"); - - b.ToTable("ProviderUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AccessCount") - .HasColumnType("INTEGER"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Data") - .HasColumnType("TEXT"); - - b.Property("DeletionDate") - .HasColumnType("TEXT"); - - b.Property("Disabled") - .HasColumnType("INTEGER"); - - b.Property("ExpirationDate") - .HasColumnType("TEXT"); - - b.Property("HideEmail") - .HasColumnType("INTEGER"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("MaxAccessCount") - .HasColumnType("INTEGER"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("Password") - .HasMaxLength(300) - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Send", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => - { - b.Property("Id") - .HasMaxLength(40) - .HasColumnType("TEXT"); - - b.Property("Active") - .HasColumnType("INTEGER"); - - b.Property("Country") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("PostalCode") - .HasMaxLength(10) - .HasColumnType("TEXT"); - - b.Property("Rate") - .HasColumnType("TEXT"); - - b.Property("State") - .HasMaxLength(2) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("TaxRate", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("Amount") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Details") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("Gateway") - .HasColumnType("INTEGER"); - - b.Property("GatewayId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("PaymentMethodType") - .HasColumnType("INTEGER"); - - b.Property("Refunded") - .HasColumnType("INTEGER"); - - b.Property("RefundedAmount") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Transaction", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AccountRevisionDate") - .HasColumnType("TEXT"); - - b.Property("ApiKey") - .IsRequired() - .HasMaxLength(30) - .HasColumnType("TEXT"); - - b.Property("AvatarColor") - .HasMaxLength(7) - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Culture") - .HasMaxLength(10) - .HasColumnType("TEXT"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EmailVerified") - .HasColumnType("INTEGER"); - - b.Property("EquivalentDomains") - .HasColumnType("TEXT"); - - b.Property("ExcludedGlobalEquivalentDomains") - .HasColumnType("TEXT"); - - b.Property("FailedLoginCount") - .HasColumnType("INTEGER"); - - b.Property("ForcePasswordReset") - .HasColumnType("INTEGER"); - - b.Property("Gateway") - .HasColumnType("INTEGER"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("Kdf") - .HasColumnType("INTEGER"); - - b.Property("KdfIterations") - .HasColumnType("INTEGER"); - - b.Property("KdfMemory") - .HasColumnType("INTEGER"); - - b.Property("KdfParallelism") - .HasColumnType("INTEGER"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("LastEmailChangeDate") - .HasColumnType("TEXT"); - - b.Property("LastFailedLoginDate") - .HasColumnType("TEXT"); - - b.Property("LastKdfChangeDate") - .HasColumnType("TEXT"); - - b.Property("LastKeyRotationDate") - .HasColumnType("TEXT"); - - b.Property("LastPasswordChangeDate") - .HasColumnType("TEXT"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("MasterPassword") - .HasMaxLength(300) - .HasColumnType("TEXT"); - - b.Property("MasterPasswordHint") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("MaxStorageGb") - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("Premium") - .HasColumnType("INTEGER"); - - b.Property("PremiumExpirationDate") - .HasColumnType("TEXT"); - - b.Property("PrivateKey") - .HasColumnType("TEXT"); - - b.Property("PublicKey") - .HasColumnType("TEXT"); - - b.Property("ReferenceData") - .HasColumnType("TEXT"); - - b.Property("RenewalReminderDate") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("SecurityStamp") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("Storage") - .HasColumnType("INTEGER"); - - b.Property("TwoFactorProviders") - .HasColumnType("TEXT"); - - b.Property("TwoFactorRecoveryCode") - .HasMaxLength(32) - .HasColumnType("TEXT"); - - b.Property("UsesKeyConnector") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("User", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Read") - .HasColumnType("INTEGER"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Write") - .HasColumnType("INTEGER"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.ToTable("AccessPolicy", (string)null); - - b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("ClientSecretHash") - .HasMaxLength(128) - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("EncryptedPayload") - .HasMaxLength(4000) - .HasColumnType("TEXT"); - - b.Property("ExpireAt") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Scope") - .HasMaxLength(4000) - .HasColumnType("TEXT"); - - b.Property("ServiceAccountId") - .HasColumnType("TEXT"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("ServiceAccountId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("DeletedDate") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Project", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("DeletedDate") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("Note") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Value") - .HasColumnType("TEXT"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Secret", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ServiceAccount", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("Attachments") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Data") - .HasColumnType("TEXT"); - - b.Property("DeletedDate") - .HasColumnType("TEXT"); - - b.Property("Favorites") - .HasColumnType("TEXT"); - - b.Property("Folders") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("Reprompt") - .HasColumnType("INTEGER"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Cipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Folder", (string)null); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.Property("ProjectsId") - .HasColumnType("TEXT"); - - b.Property("SecretsId") - .HasColumnType("TEXT"); - - b.HasKey("ProjectsId", "SecretsId"); - - b.HasIndex("SecretsId"); - - b.ToTable("ProjectSecret"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GrantedProjectId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GrantedProjectId"); - - b.Property("ServiceAccountId") - .HasColumnType("TEXT") - .HasColumnName("ServiceAccountId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("ServiceAccountId"); - - b.HasDiscriminator().HasValue("service_account_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GrantedProjectId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") - .WithMany() - .HasForeignKey("ResponseDeviceId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("ResponseDevice"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") - .WithMany() - .HasForeignKey("GranteeId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") - .WithMany() - .HasForeignKey("GrantorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Grantee"); - - b.Navigation("Grantor"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoConfigs") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoUsers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("SsoUsers") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Collections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") - .WithMany("CollectionCiphers") - .HasForeignKey("CipherId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionCiphers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Cipher"); - - b.Navigation("Collection"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionGroups") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionUsers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("CollectionUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Groups") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany("GroupUsers") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("GroupUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Group"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("ApiKeys") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Connections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Domains") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") - .WithMany() - .HasForeignKey("SponsoredOrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") - .WithMany() - .HasForeignKey("SponsoringOrganizationId"); - - b.Navigation("SponsoredOrganization"); - - b.Navigation("SponsoringOrganization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("OrganizationUsers") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("OrganizationUsers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Policies") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Provider"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Transactions") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Transactions") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Ciphers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Ciphers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Folders") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) - .WithMany() - .HasForeignKey("ProjectsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) - .WithMany() - .HasForeignKey("SecretsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedProject"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("ServiceAccountAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("GrantedProject"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedProject"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Navigation("CollectionCiphers"); - - b.Navigation("CollectionGroups"); - - b.Navigation("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Navigation("ApiKeys"); - - b.Navigation("Ciphers"); - - b.Navigation("Collections"); - - b.Navigation("Connections"); - - b.Navigation("Domains"); - - b.Navigation("Groups"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("Policies"); - - b.Navigation("SsoConfigs"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Navigation("CollectionUsers"); - - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Navigation("Ciphers"); - - b.Navigation("Folders"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("ServiceAccountAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Navigation("CollectionCiphers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs b/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs deleted file mode 100644 index beb54c3389b5..000000000000 --- a/util/SqliteMigrations/Migrations/20230703185321_2023-07-03_00_SecretsManagerBetaColumn.sql.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.SqliteMigrations.Migrations; - -public partial class _20230703_00_SecretsManagerBetaColumnsql : Migration -{ - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "SecretsManagerBeta", - table: "Organization", - type: "INTEGER", - nullable: false, - defaultValue: false); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "SecretsManagerBeta", - table: "Organization"); - } -} diff --git a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs index b2689cd29124..076610ae31c6 100644 --- a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -606,9 +606,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Seats") .HasColumnType("INTEGER"); - b.Property("SecretsManagerBeta") - .HasColumnType("INTEGER"); - b.Property("SelfHost") .HasColumnType("INTEGER"); From 88338a94def20c7544f68c0c839f56689d09b34e Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Mon, 10 Jul 2023 07:47:44 -0400 Subject: [PATCH 16/41] SM-802: Add SecretsManagerBetaColumn migration --- ...14621_SecretsManagerBetaColumn.Designer.cs | 2228 ++++++++++++++++ ...20230710114621_SecretsManagerBetaColumn.cs | 25 + .../DatabaseContextModelSnapshot.cs | 3 + ...14625_SecretsManagerBetaColumn.Designer.cs | 2239 +++++++++++++++++ ...20230710114625_SecretsManagerBetaColumn.cs | 25 + .../DatabaseContextModelSnapshot.cs | 3 + ...14629_SecretsManagerBetaColumn.Designer.cs | 2226 ++++++++++++++++ ...20230710114629_SecretsManagerBetaColumn.cs | 25 + .../DatabaseContextModelSnapshot.cs | 3 + 9 files changed, 6777 insertions(+) create mode 100644 util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.Designer.cs create mode 100644 util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.cs create mode 100644 util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.Designer.cs create mode 100644 util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.cs create mode 100644 util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.Designer.cs create mode 100644 util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.cs diff --git a/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.Designer.cs b/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.Designer.cs new file mode 100644 index 000000000000..d4fd963ba304 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.Designer.cs @@ -0,0 +1,2228 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230710114621_SecretsManagerBetaColumn")] + partial class SecretsManagerBetaColumn + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("varchar(25)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("AuthenticationDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MasterPasswordHash") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ResponseDate") + .HasColumnType("datetime(6)"); + + b.Property("ResponseDeviceId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("GranteeId") + .HasColumnType("char(36)"); + + b.Property("GrantorId") + .HasColumnType("char(36)"); + + b.Property("KeyEncrypted") + .HasColumnType("longtext"); + + b.Property("LastNotificationDate") + .HasColumnType("datetime(6)"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("WaitTimeDays") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ConsumedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("longtext"); + + b.Property("EncryptedPublicKey") + .HasColumnType("longtext"); + + b.Property("EncryptedUserKey") + .HasColumnType("longtext"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ActingUserId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("DeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("DomainName") + .HasColumnType("longtext"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("PolicyId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("ProviderOrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderUserId") + .HasColumnType("char(36)"); + + b.Property("SecretId") + .HasColumnType("char(36)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + + b.Property("SystemUser") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("int"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("int"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("SecretsManagerBeta") + .HasColumnType("tinyint(1)"); + + b.Property("SelfHost") + .HasColumnType("tinyint(1)"); + + b.Property("SmSeats") + .HasColumnType("int"); + + b.Property("SmServiceAccounts") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("Use2fa") + .HasColumnType("tinyint(1)"); + + b.Property("UseApi") + .HasColumnType("tinyint(1)"); + + b.Property("UseCustomPermissions") + .HasColumnType("tinyint(1)"); + + b.Property("UseDirectory") + .HasColumnType("tinyint(1)"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UsePasswordManager") + .HasColumnType("tinyint(1)"); + + b.Property("UsePolicies") + .HasColumnType("tinyint(1)"); + + b.Property("UseResetPassword") + .HasColumnType("tinyint(1)"); + + b.Property("UseScim") + .HasColumnType("tinyint(1)"); + + b.Property("UseSecretsManager") + .HasColumnType("tinyint(1)"); + + b.Property("UseSso") + .HasColumnType("tinyint(1)"); + + b.Property("UseTotp") + .HasColumnType("tinyint(1)"); + + b.Property("UsersGetPremium") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Config") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("JobRunCount") + .HasColumnType("int"); + + b.Property("LastCheckedDate") + .HasColumnType("datetime(6)"); + + b.Property("NextRunDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Txt") + .HasColumnType("longtext"); + + b.Property("VerifiedDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("LastSyncDate") + .HasColumnType("datetime(6)"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("tinyint unsigned"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("ToDelete") + .HasColumnType("tinyint(1)"); + + b.Property("ValidUntil") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("AccessSecretsManager") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BillingPhone") + .HasColumnType("longtext"); + + b.Property("BusinessAddress1") + .HasColumnType("longtext"); + + b.Property("BusinessAddress2") + .HasColumnType("longtext"); + + b.Property("BusinessAddress3") + .HasColumnType("longtext"); + + b.Property("BusinessCountry") + .HasColumnType("longtext"); + + b.Property("BusinessName") + .HasColumnType("longtext"); + + b.Property("BusinessTaxNumber") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Settings") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MaxAccessCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Rate") + .HasColumnType("decimal(65,30)"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PaymentMethodType") + .HasColumnType("tinyint unsigned"); + + b.Property("Refunded") + .HasColumnType("tinyint(1)"); + + b.Property("RefundedAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccountRevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailVerified") + .HasColumnType("tinyint(1)"); + + b.Property("EquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("longtext"); + + b.Property("FailedLoginCount") + .HasColumnType("int"); + + b.Property("ForcePasswordReset") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Kdf") + .HasColumnType("tinyint unsigned"); + + b.Property("KdfIterations") + .HasColumnType("int"); + + b.Property("KdfMemory") + .HasColumnType("int"); + + b.Property("KdfParallelism") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LastEmailChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LastFailedLoginDate") + .HasColumnType("datetime(6)"); + + b.Property("LastKdfChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LastKeyRotationDate") + .HasColumnType("datetime(6)"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Premium") + .HasColumnType("tinyint(1)"); + + b.Property("PremiumExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RenewalReminderDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Read") + .HasColumnType("tinyint(1)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Write") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Note") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Attachments") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Favorites") + .HasColumnType("longtext"); + + b.Property("Folders") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Reprompt") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("char(36)"); + + b.Property("SecretsId") + .HasColumnType("char(36)"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.cs b/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.cs new file mode 100644 index 000000000000..d5556b1ded38 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +public partial class SecretsManagerBetaColumn : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SecretsManagerBeta", + table: "Organization", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretsManagerBeta", + table: "Organization"); + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index 95a769229f7e..f64675e25668 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -608,6 +608,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Seats") .HasColumnType("int"); + b.Property("SecretsManagerBeta") + .HasColumnType("tinyint(1)"); + b.Property("SelfHost") .HasColumnType("tinyint(1)"); diff --git a/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.Designer.cs b/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.Designer.cs new file mode 100644 index 000000000000..823c1276b47e --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.Designer.cs @@ -0,0 +1,2239 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230710114625_SecretsManagerBetaColumn")] + partial class SecretsManagerBetaColumn + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") + .HasAnnotation("ProductVersion", "6.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("AuthenticationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MasterPasswordHash") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ResponseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ResponseDeviceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GranteeId") + .HasColumnType("uuid"); + + b.Property("GrantorId") + .HasColumnType("uuid"); + + b.Property("KeyEncrypted") + .HasColumnType("text"); + + b.Property("LastNotificationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitTimeDays") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("text"); + + b.Property("EncryptedPublicKey") + .HasColumnType("text"); + + b.Property("EncryptedUserKey") + .HasColumnType("text"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ActingUserId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceType") + .HasColumnType("smallint"); + + b.Property("DomainName") + .HasColumnType("text"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("PolicyId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("ProviderOrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderUserId") + .HasColumnType("uuid"); + + b.Property("SecretId") + .HasColumnType("uuid"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + + b.Property("SystemUser") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("integer"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("integer"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp with time zone"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("SecretsManagerBeta") + .HasColumnType("boolean"); + + b.Property("SelfHost") + .HasColumnType("boolean"); + + b.Property("SmSeats") + .HasColumnType("integer"); + + b.Property("SmServiceAccounts") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("Use2fa") + .HasColumnType("boolean"); + + b.Property("UseApi") + .HasColumnType("boolean"); + + b.Property("UseCustomPermissions") + .HasColumnType("boolean"); + + b.Property("UseDirectory") + .HasColumnType("boolean"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UsePasswordManager") + .HasColumnType("boolean"); + + b.Property("UsePolicies") + .HasColumnType("boolean"); + + b.Property("UseResetPassword") + .HasColumnType("boolean"); + + b.Property("UseScim") + .HasColumnType("boolean"); + + b.Property("UseSecretsManager") + .HasColumnType("boolean"); + + b.Property("UseSso") + .HasColumnType("boolean"); + + b.Property("UseTotp") + .HasColumnType("boolean"); + + b.Property("UsersGetPremium") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Config") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("JobRunCount") + .HasColumnType("integer"); + + b.Property("LastCheckedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("NextRunDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Txt") + .HasColumnType("text"); + + b.Property("VerifiedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LastSyncDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("smallint"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("ToDelete") + .HasColumnType("boolean"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("AccessSecretsManager") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BillingPhone") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MaxAccessCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PaymentMethodType") + .HasColumnType("smallint"); + + b.Property("Refunded") + .HasColumnType("boolean"); + + b.Property("RefundedAmount") + .HasColumnType("numeric"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountRevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("character varying(7)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("EquivalentDomains") + .HasColumnType("text"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("text"); + + b.Property("FailedLoginCount") + .HasColumnType("integer"); + + b.Property("ForcePasswordReset") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Kdf") + .HasColumnType("smallint"); + + b.Property("KdfIterations") + .HasColumnType("integer"); + + b.Property("KdfMemory") + .HasColumnType("integer"); + + b.Property("KdfParallelism") + .HasColumnType("integer"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LastEmailChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastFailedLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastKdfChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastKeyRotationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Premium") + .HasColumnType("boolean"); + + b.Property("PremiumExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RenewalReminderDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("text"); + + b.Property("Read") + .HasColumnType("boolean"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Write") + .HasColumnType("boolean"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Favorites") + .HasColumnType("text"); + + b.Property("Folders") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Reprompt") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("uuid"); + + b.Property("SecretsId") + .HasColumnType("uuid"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.cs b/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.cs new file mode 100644 index 000000000000..487d9edc3b2a --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +public partial class SecretsManagerBetaColumn : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SecretsManagerBeta", + table: "Organization", + type: "boolean", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretsManagerBeta", + table: "Organization"); + } +} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index c070e191063c..56cd8c67e09d 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -618,6 +618,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Seats") .HasColumnType("integer"); + b.Property("SecretsManagerBeta") + .HasColumnType("boolean"); + b.Property("SelfHost") .HasColumnType("boolean"); diff --git a/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.Designer.cs b/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.Designer.cs new file mode 100644 index 000000000000..404ebe3f91ac --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.Designer.cs @@ -0,0 +1,2226 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230710114629_SecretsManagerBetaColumn")] + partial class SecretsManagerBetaColumn + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.12"); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("TEXT"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("AuthenticationDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHash") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("RequestDeviceType") + .HasColumnType("INTEGER"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ResponseDate") + .HasColumnType("TEXT"); + + b.Property("ResponseDeviceId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("GranteeId") + .HasColumnType("TEXT"); + + b.Property("GrantorId") + .HasColumnType("TEXT"); + + b.Property("KeyEncrypted") + .HasColumnType("TEXT"); + + b.Property("LastNotificationDate") + .HasColumnType("TEXT"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("WaitTimeDays") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("TEXT"); + + b.Property("EncryptedPublicKey") + .HasColumnType("TEXT"); + + b.Property("EncryptedUserKey") + .HasColumnType("TEXT"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ActingUserId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("DeviceType") + .HasColumnType("INTEGER"); + + b.Property("DomainName") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("InstallationId") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("PolicyId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("ProviderOrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderUserId") + .HasColumnType("TEXT"); + + b.Property("SecretId") + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + + b.Property("SystemUser") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessAll") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("INTEGER"); + + b.Property("MaxCollections") + .HasColumnType("INTEGER"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("TEXT"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PlanType") + .HasColumnType("INTEGER"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Seats") + .HasColumnType("INTEGER"); + + b.Property("SecretsManagerBeta") + .HasColumnType("INTEGER"); + + b.Property("SelfHost") + .HasColumnType("INTEGER"); + + b.Property("SmSeats") + .HasColumnType("INTEGER"); + + b.Property("SmServiceAccounts") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("Use2fa") + .HasColumnType("INTEGER"); + + b.Property("UseApi") + .HasColumnType("INTEGER"); + + b.Property("UseCustomPermissions") + .HasColumnType("INTEGER"); + + b.Property("UseDirectory") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.Property("UseGroups") + .HasColumnType("INTEGER"); + + b.Property("UseKeyConnector") + .HasColumnType("INTEGER"); + + b.Property("UsePasswordManager") + .HasColumnType("INTEGER"); + + b.Property("UsePolicies") + .HasColumnType("INTEGER"); + + b.Property("UseResetPassword") + .HasColumnType("INTEGER"); + + b.Property("UseScim") + .HasColumnType("INTEGER"); + + b.Property("UseSecretsManager") + .HasColumnType("INTEGER"); + + b.Property("UseSso") + .HasColumnType("INTEGER"); + + b.Property("UseTotp") + .HasColumnType("INTEGER"); + + b.Property("UsersGetPremium") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Config") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("JobRunCount") + .HasColumnType("INTEGER"); + + b.Property("LastCheckedDate") + .HasColumnType("TEXT"); + + b.Property("NextRunDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Txt") + .HasColumnType("TEXT"); + + b.Property("VerifiedDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("LastSyncDate") + .HasColumnType("TEXT"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PlanSponsorshipType") + .HasColumnType("INTEGER"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("ToDelete") + .HasColumnType("INTEGER"); + + b.Property("ValidUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessAll") + .HasColumnType("INTEGER"); + + b.Property("AccessSecretsManager") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ResetPasswordKey") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BillingEmail") + .HasColumnType("TEXT"); + + b.Property("BillingPhone") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCount") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeletionDate") + .HasColumnType("TEXT"); + + b.Property("Disabled") + .HasColumnType("INTEGER"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("HideEmail") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MaxAccessCount") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Rate") + .HasColumnType("TEXT"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Amount") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PaymentMethodType") + .HasColumnType("INTEGER"); + + b.Property("Refunded") + .HasColumnType("INTEGER"); + + b.Property("RefundedAmount") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccountRevisionDate") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailVerified") + .HasColumnType("INTEGER"); + + b.Property("EquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("FailedLoginCount") + .HasColumnType("INTEGER"); + + b.Property("ForcePasswordReset") + .HasColumnType("INTEGER"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Kdf") + .HasColumnType("INTEGER"); + + b.Property("KdfIterations") + .HasColumnType("INTEGER"); + + b.Property("KdfMemory") + .HasColumnType("INTEGER"); + + b.Property("KdfParallelism") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("LastEmailChangeDate") + .HasColumnType("TEXT"); + + b.Property("LastFailedLoginDate") + .HasColumnType("TEXT"); + + b.Property("LastKdfChangeDate") + .HasColumnType("TEXT"); + + b.Property("LastKeyRotationDate") + .HasColumnType("TEXT"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Premium") + .HasColumnType("INTEGER"); + + b.Property("PremiumExpirationDate") + .HasColumnType("TEXT"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RenewalReminderDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("UsesKeyConnector") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Read") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Write") + .HasColumnType("INTEGER"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ExpireAt") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Attachments") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Favorites") + .HasColumnType("TEXT"); + + b.Property("Folders") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Reprompt") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("TEXT"); + + b.Property("SecretsId") + .HasColumnType("TEXT"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.cs b/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.cs new file mode 100644 index 000000000000..0fc3071711f1 --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations; + +public partial class SecretsManagerBetaColumn : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SecretsManagerBeta", + table: "Organization", + type: "INTEGER", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretsManagerBeta", + table: "Organization"); + } +} diff --git a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs index 076610ae31c6..b2689cd29124 100644 --- a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -606,6 +606,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Seats") .HasColumnType("INTEGER"); + b.Property("SecretsManagerBeta") + .HasColumnType("INTEGER"); + b.Property("SelfHost") .HasColumnType("INTEGER"); From d5682ce328f46bde7c418ed0c2b781923be9dfa7 Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Mon, 10 Jul 2023 11:05:52 -0400 Subject: [PATCH 17/41] SM-802: Remove OrganizationUserOrganizationDetailsView update --- ...2023-07-03_00_SecretsManagerBetaColumn.sql | 65 ------------------- 1 file changed, 65 deletions(-) diff --git a/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql b/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql index caf669a4f55a..b1aee80fd14f 100644 --- a/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql +++ b/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql @@ -297,71 +297,6 @@ BEGIN END GO --- Add column `SecretsManagerBeta` in OrganizationUserOrganizationDetailsView -CREATE OR ALTER VIEW [dbo].[OrganizationUserOrganizationDetailsView] -AS -SELECT - OU.[UserId], - OU.[OrganizationId], - O.[Name], - O.[Enabled], - O.[PlanType], - O.[UsePolicies], - O.[UseSso], - O.[UseKeyConnector], - O.[UseScim], - O.[UseGroups], - O.[UseDirectory], - O.[UseEvents], - O.[UseTotp], - O.[Use2fa], - O.[UseApi], - O.[UseResetPassword], - O.[SelfHost], - O.[UsersGetPremium], - O.[UseCustomPermissions], - O.[UseSecretsManager], - O.[Seats], - O.[MaxCollections], - O.[MaxStorageGb], - O.[Identifier], - OU.[Key], - OU.[ResetPasswordKey], - O.[PublicKey], - O.[PrivateKey], - OU.[Status], - OU.[Type], - SU.[ExternalId] SsoExternalId, - OU.[Permissions], - PO.[ProviderId], - P.[Name] ProviderName, - P.[Type] ProviderType, - SS.[Data] SsoConfig, - OS.[FriendlyName] FamilySponsorshipFriendlyName, - OS.[LastSyncDate] FamilySponsorshipLastSyncDate, - OS.[ToDelete] FamilySponsorshipToDelete, - OS.[ValidUntil] FamilySponsorshipValidUntil, - OU.[AccessSecretsManager], - O.[UsePasswordManager], - O.[SmSeats], - O.[SmServiceAccounts], - O.[SecretsManagerBeta] -FROM - [dbo].[OrganizationUser] OU -LEFT JOIN - [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] -LEFT JOIN - [dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId] -LEFT JOIN - [dbo].[ProviderOrganization] PO ON PO.[OrganizationId] = O.[Id] -LEFT JOIN - [dbo].[Provider] P ON P.[Id] = PO.[ProviderId] -LEFT JOIN - [dbo].[SsoConfig] SS ON SS.[OrganizationId] = OU.[OrganizationId] -LEFT JOIN - [dbo].[OrganizationSponsorship] OS ON OS.[SponsoringOrganizationUserID] = OU.[Id] -GO - -- Update the metadata for the following view so that the SecretsManagerBeta column is included IF OBJECT_ID('[dbo].[OrganizationView]') IS NOT NULL BEGIN From a5efec301e848c1beb2f1b6b03e5e513df4d74cf Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Tue, 11 Jul 2023 19:01:20 +1000 Subject: [PATCH 18/41] [AC-1495] Extract UpgradePlanAsync into a command (#3081) * This is a pure lift & shift with no refactors * Only register subscription commands in Api --------- Co-authored-by: cyprain-okeke --- bitwarden_license/src/Scim/Startup.cs | 2 - src/Admin/Startup.cs | 3 - .../Controllers/OrganizationsController.cs | 9 +- src/Api/Startup.cs | 2 + ...OrganizationServiceCollectionExtensions.cs | 8 - ...UpdateSecretsManagerSubscriptionCommand.cs | 2 +- .../IUpgradeOrganizationPlanCommand.cs | 6 + ...SubscriptionServiceCollectionExtensions.cs | 13 + ...UpdateSecretsManagerSubscriptionCommand.cs | 4 +- .../UpgradeOrganizationPlanCommand.cs | 339 ++++++++++++++++++ src/Core/Services/IOrganizationService.cs | 4 +- .../Implementations/OrganizationService.cs | 288 +-------------- src/Identity/Startup.cs | 5 - .../OrganizationsControllerTests.cs | 6 +- ...eSecretsManagerSubscriptionCommandTests.cs | 2 +- .../UpgradeOrganizationPlanCommandTests.cs | 113 ++++++ .../Services/OrganizationServiceTests.cs | 93 ----- 17 files changed, 493 insertions(+), 406 deletions(-) rename src/Core/OrganizationFeatures/{OrganizationSubscriptionUpdate => OrganizationSubscriptions}/Interface/IUpdateSecretsManagerSubscriptionCommand.cs (93%) create mode 100644 src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpgradeOrganizationPlanCommand.cs create mode 100644 src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs rename src/Core/OrganizationFeatures/{OrganizationSubscriptionUpdate => OrganizationSubscriptions}/UpdateSecretsManagerSubscriptionCommand.cs (99%) create mode 100644 src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs create mode 100644 test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs diff --git a/bitwarden_license/src/Scim/Startup.cs b/bitwarden_license/src/Scim/Startup.cs index 0d87f48ff789..4ef46459c3bd 100644 --- a/bitwarden_license/src/Scim/Startup.cs +++ b/bitwarden_license/src/Scim/Startup.cs @@ -42,8 +42,6 @@ public void ConfigureServices(IServiceCollection services) // Repositories services.AddDatabaseRepositories(globalSettings); - services.AddOosServices(); - // Context services.AddScoped(); services.AddScoped(); diff --git a/src/Admin/Startup.cs b/src/Admin/Startup.cs index 81c789c9c05c..9482be011a99 100644 --- a/src/Admin/Startup.cs +++ b/src/Admin/Startup.cs @@ -9,8 +9,6 @@ using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.Extensions.DependencyInjection.Extensions; using Bit.Admin.Services; -using Bit.Core.Repositories.Noop; -using Bit.Core.SecretsManager.Repositories; #if !OSS using Bit.Commercial.Core.Utilities; @@ -88,7 +86,6 @@ public void ConfigureServices(IServiceCollection services) services.AddBaseServices(globalSettings); services.AddDefaultServices(globalSettings); services.AddScoped(); - services.AddScoped(); #if OSS services.AddOosServices(); diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index d0fe7eaf70e8..8ceb1f038b53 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -18,7 +18,7 @@ using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces; -using Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate.Interface; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; @@ -52,6 +52,7 @@ public class OrganizationsController : Controller private readonly GlobalSettings _globalSettings; private readonly ILicensingService _licensingService; private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; + private readonly IUpgradeOrganizationPlanCommand _upgradeOrganizationPlanCommand; public OrganizationsController( IOrganizationRepository organizationRepository, @@ -73,7 +74,8 @@ public OrganizationsController( IFeatureService featureService, GlobalSettings globalSettings, ILicensingService licensingService, - IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand) + IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand, + IUpgradeOrganizationPlanCommand upgradeOrganizationPlanCommand) { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; @@ -95,6 +97,7 @@ public OrganizationsController( _globalSettings = globalSettings; _licensingService = licensingService; _updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand; + _upgradeOrganizationPlanCommand = upgradeOrganizationPlanCommand; } [HttpGet("{id}")] @@ -310,7 +313,7 @@ public async Task PostUpgrade(string id, [FromBody] Organi throw new NotFoundException(); } - var result = await _organizationService.UpgradePlanAsync(orgIdGuid, model.ToOrganizationUpgrade()); + var result = await _upgradeOrganizationPlanCommand.UpgradePlanAsync(orgIdGuid, model.ToOrganizationUpgrade()); return new PaymentResponseModel { Success = result.Item1, PaymentIntentClientSecret = result.Item2 }; } diff --git a/src/Api/Startup.cs b/src/Api/Startup.cs index 6070a91b29f7..2580e06df9a8 100644 --- a/src/Api/Startup.cs +++ b/src/Api/Startup.cs @@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.Extensions.DependencyInjection.Extensions; using Bit.Core.Auth.Identity; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions; #if !OSS using Bit.Commercial.Core.SecretsManager; @@ -133,6 +134,7 @@ public void ConfigureServices(IServiceCollection services) // Services services.AddBaseServices(globalSettings); services.AddDefaultServices(globalSettings); + services.AddOrganizationSubscriptionServices(); services.AddCoreLocalizationServices(); //health check diff --git a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs index 7bbe1680b80a..f302aef90b7c 100644 --- a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs @@ -17,8 +17,6 @@ using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted; -using Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate; -using Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate.Interface; using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager; using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces; using Bit.Core.Services; @@ -45,7 +43,6 @@ public static void AddOrganizationServices(this IServiceCollection services, IGl services.AddOrganizationGroupCommands(); services.AddOrganizationLicenseCommandsQueries(); services.AddOrganizationDomainCommandsQueries(); - services.AddOrganizationSubscriptionUpdateCommandsQueries(); services.AddOrganizationAuthCommands(); } @@ -116,11 +113,6 @@ private static void AddOrganizationDomainCommandsQueries(this IServiceCollection services.AddScoped(); } - private static void AddOrganizationSubscriptionUpdateCommandsQueries(this IServiceCollection services) - { - services.AddScoped(); - } - private static void AddOrganizationAuthCommands(this IServiceCollection services) { services.AddScoped(); diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/Interface/IUpdateSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpdateSecretsManagerSubscriptionCommand.cs similarity index 93% rename from src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/Interface/IUpdateSecretsManagerSubscriptionCommand.cs rename to src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpdateSecretsManagerSubscriptionCommand.cs index de5271639e10..78244d8e3ddc 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/Interface/IUpdateSecretsManagerSubscriptionCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpdateSecretsManagerSubscriptionCommand.cs @@ -1,6 +1,6 @@ using Bit.Core.Models.Business; -namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate.Interface; +namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; public interface IUpdateSecretsManagerSubscriptionCommand { diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpgradeOrganizationPlanCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpgradeOrganizationPlanCommand.cs new file mode 100644 index 000000000000..59525242bb83 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpgradeOrganizationPlanCommand.cs @@ -0,0 +1,6 @@ +namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; + +public interface IUpgradeOrganizationPlanCommand +{ + Task> UpgradePlanAsync(Guid organizationId, Models.Business.OrganizationUpgrade upgrade); +} diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs new file mode 100644 index 000000000000..1b8d67211bdf --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs @@ -0,0 +1,13 @@ +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; +using Microsoft.Extensions.DependencyInjection; + +namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptions; + +public static class OrganizationSubscriptionServiceCollectionExtensions +{ + public static void AddOrganizationSubscriptionServices(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + } +} diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs similarity index 99% rename from src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommand.cs rename to src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs index 16d0af84e5a3..9237253f79b8 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs @@ -4,14 +4,14 @@ using Bit.Core.Exceptions; using Bit.Core.Models.Business; using Bit.Core.Models.StaticStore; -using Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate.Interface; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; using Bit.Core.Repositories; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; using Bit.Core.Utilities; using Microsoft.Extensions.Logging; -namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate; +namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptions; public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubscriptionCommand { diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs new file mode 100644 index 000000000000..62ee67442f1f --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs @@ -0,0 +1,339 @@ +using Bit.Core.AdminConsole.Models.OrganizationConnectionConfigs; +using Bit.Core.Auth.Enums; +using Bit.Core.Auth.Repositories; +using Bit.Core.Context; +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.Models.Business; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; +using Bit.Core.Repositories; +using Bit.Core.SecretsManager.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.OrganizationFeatures.OrganizationSubscriptions; + +public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand +{ + private readonly IOrganizationUserRepository _organizationUserRepository; + private readonly ICollectionRepository _collectionRepository; + private readonly IGroupRepository _groupRepository; + private readonly IPaymentService _paymentService; + private readonly IPolicyRepository _policyRepository; + private readonly ISsoConfigRepository _ssoConfigRepository; + private readonly IReferenceEventService _referenceEventService; + private readonly IOrganizationConnectionRepository _organizationConnectionRepository; + private readonly ICurrentContext _currentContext; + private readonly IServiceAccountRepository _serviceAccountRepository; + private readonly IOrganizationRepository _organizationRepository; + private readonly IOrganizationService _organizationService; + + public UpgradeOrganizationPlanCommand( + IOrganizationUserRepository organizationUserRepository, + ICollectionRepository collectionRepository, + IGroupRepository groupRepository, + IPaymentService paymentService, + IPolicyRepository policyRepository, + ISsoConfigRepository ssoConfigRepository, + IReferenceEventService referenceEventService, + IOrganizationConnectionRepository organizationConnectionRepository, + ICurrentContext currentContext, + IServiceAccountRepository serviceAccountRepository, + IOrganizationRepository organizationRepository, + IOrganizationService organizationService) + { + _organizationUserRepository = organizationUserRepository; + _collectionRepository = collectionRepository; + _groupRepository = groupRepository; + _paymentService = paymentService; + _policyRepository = policyRepository; + _ssoConfigRepository = ssoConfigRepository; + _referenceEventService = referenceEventService; + _organizationConnectionRepository = organizationConnectionRepository; + _currentContext = currentContext; + _serviceAccountRepository = serviceAccountRepository; + _organizationRepository = organizationRepository; + _organizationService = organizationService; + } + + public async Task> UpgradePlanAsync(Guid organizationId, OrganizationUpgrade upgrade) + { + var organization = await GetOrgById(organizationId); + if (organization == null) + { + throw new NotFoundException(); + } + + if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId)) + { + throw new BadRequestException("Your account has no payment method available."); + } + + var existingPasswordManagerPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); + if (existingPasswordManagerPlan == null) + { + throw new BadRequestException("Existing plan not found."); + } + + var newPasswordManagerPlan = + StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled); + if (newPasswordManagerPlan == null) + { + throw new BadRequestException("Plan not found."); + } + + if (existingPasswordManagerPlan.Type == newPasswordManagerPlan.Type) + { + throw new BadRequestException("Organization is already on this plan."); + } + + if (existingPasswordManagerPlan.UpgradeSortOrder >= newPasswordManagerPlan.UpgradeSortOrder) + { + throw new BadRequestException("You cannot upgrade to this plan."); + } + + if (existingPasswordManagerPlan.Type != PlanType.Free) + { + throw new BadRequestException("You can only upgrade from the free plan. Contact support."); + } + + _organizationService.ValidatePasswordManagerPlan(newPasswordManagerPlan, upgrade); + var newSecretsManagerPlan = + StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled); + if (upgrade.UseSecretsManager) + { + _organizationService.ValidateSecretsManagerPlan(newSecretsManagerPlan, upgrade); + } + + var newPasswordManagerPlanSeats = (short)(newPasswordManagerPlan.BaseSeats + + (newPasswordManagerPlan.HasAdditionalSeatsOption ? upgrade.AdditionalSeats : 0)); + if (!organization.Seats.HasValue || organization.Seats.Value > newPasswordManagerPlanSeats) + { + var occupiedSeats = + await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); + if (occupiedSeats > newPasswordManagerPlanSeats) + { + throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " + + $"Your new plan only has ({newPasswordManagerPlanSeats}) seats. Remove some users."); + } + } + + if (newPasswordManagerPlan.MaxCollections.HasValue && (!organization.MaxCollections.HasValue || + organization.MaxCollections.Value > + newPasswordManagerPlan.MaxCollections.Value)) + { + var collectionCount = await _collectionRepository.GetCountByOrganizationIdAsync(organization.Id); + if (collectionCount > newPasswordManagerPlan.MaxCollections.Value) + { + throw new BadRequestException($"Your organization currently has {collectionCount} collections. " + + $"Your new plan allows for a maximum of ({newPasswordManagerPlan.MaxCollections.Value}) collections. " + + "Remove some collections."); + } + } + + if (!newPasswordManagerPlan.HasGroups && organization.UseGroups) + { + var groups = await _groupRepository.GetManyByOrganizationIdAsync(organization.Id); + if (groups.Any()) + { + throw new BadRequestException($"Your new plan does not allow the groups feature. " + + $"Remove your groups."); + } + } + + if (!newPasswordManagerPlan.HasPolicies && organization.UsePolicies) + { + var policies = await _policyRepository.GetManyByOrganizationIdAsync(organization.Id); + if (policies.Any(p => p.Enabled)) + { + throw new BadRequestException($"Your new plan does not allow the policies feature. " + + $"Disable your policies."); + } + } + + if (!newPasswordManagerPlan.HasSso && organization.UseSso) + { + var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id); + if (ssoConfig != null && ssoConfig.Enabled) + { + throw new BadRequestException($"Your new plan does not allow the SSO feature. " + + $"Disable your SSO configuration."); + } + } + + if (!newPasswordManagerPlan.HasKeyConnector && organization.UseKeyConnector) + { + var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id); + if (ssoConfig != null && ssoConfig.GetData().MemberDecryptionType == MemberDecryptionType.KeyConnector) + { + throw new BadRequestException("Your new plan does not allow the Key Connector feature. " + + "Disable your Key Connector."); + } + } + + if (!newPasswordManagerPlan.HasResetPassword && organization.UseResetPassword) + { + var resetPasswordPolicy = + await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword); + if (resetPasswordPolicy != null && resetPasswordPolicy.Enabled) + { + throw new BadRequestException("Your new plan does not allow the Password Reset feature. " + + "Disable your Password Reset policy."); + } + } + + if (!newPasswordManagerPlan.HasScim && organization.UseScim) + { + var scimConnections = await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organization.Id, + OrganizationConnectionType.Scim); + if (scimConnections != null && scimConnections.Any(c => c.GetConfig()?.Enabled == true)) + { + throw new BadRequestException("Your new plan does not allow the SCIM feature. " + + "Disable your SCIM configuration."); + } + } + + if (!newPasswordManagerPlan.HasCustomPermissions && organization.UseCustomPermissions) + { + var organizationCustomUsers = + await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, + OrganizationUserType.Custom); + if (organizationCustomUsers.Any()) + { + throw new BadRequestException("Your new plan does not allow the Custom Permissions feature. " + + "Disable your Custom Permissions configuration."); + } + } + + if (upgrade.UseSecretsManager && newSecretsManagerPlan != null) + { + await ValidateSecretsManagerSeatsAndServiceAccountAsync(upgrade, organization, newSecretsManagerPlan); + } + + // TODO: Check storage? + string paymentIntentClientSecret = null; + var success = true; + + if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId)) + { + var organizationUpgradePlan = upgrade.UseSecretsManager + ? StaticStore.Plans.Where(p => p.Type == upgrade.Plan).ToList() + : StaticStore.Plans.Where(p => p.Type == upgrade.Plan && p.BitwardenProduct == BitwardenProductType.PasswordManager).ToList(); + + paymentIntentClientSecret = await _paymentService.UpgradeFreeOrganizationAsync(organization, + organizationUpgradePlan, + upgrade.AdditionalStorageGb, upgrade.AdditionalSeats, upgrade.PremiumAccessAddon, upgrade.TaxInfo + , upgrade.AdditionalSmSeats.GetValueOrDefault(), upgrade.AdditionalServiceAccounts.GetValueOrDefault()); + success = string.IsNullOrWhiteSpace(paymentIntentClientSecret); + } + else + { + // TODO: Update existing sub + throw new BadRequestException("You can only upgrade from the free plan. Contact support."); + } + + organization.BusinessName = upgrade.BusinessName; + organization.PlanType = newPasswordManagerPlan.Type; + organization.Seats = (short)(newPasswordManagerPlan.BaseSeats + upgrade.AdditionalSeats); + organization.MaxCollections = newPasswordManagerPlan.MaxCollections; + organization.UseGroups = newPasswordManagerPlan.HasGroups; + organization.UseDirectory = newPasswordManagerPlan.HasDirectory; + organization.UseEvents = newPasswordManagerPlan.HasEvents; + organization.UseTotp = newPasswordManagerPlan.HasTotp; + organization.Use2fa = newPasswordManagerPlan.Has2fa; + organization.UseApi = newPasswordManagerPlan.HasApi; + organization.SelfHost = newPasswordManagerPlan.HasSelfHost; + organization.UsePolicies = newPasswordManagerPlan.HasPolicies; + organization.MaxStorageGb = !newPasswordManagerPlan.BaseStorageGb.HasValue + ? (short?)null + : (short)(newPasswordManagerPlan.BaseStorageGb.Value + upgrade.AdditionalStorageGb); + organization.UseGroups = newPasswordManagerPlan.HasGroups; + organization.UseDirectory = newPasswordManagerPlan.HasDirectory; + organization.UseEvents = newPasswordManagerPlan.HasEvents; + organization.UseTotp = newPasswordManagerPlan.HasTotp; + organization.Use2fa = newPasswordManagerPlan.Has2fa; + organization.UseApi = newPasswordManagerPlan.HasApi; + organization.UseSso = newPasswordManagerPlan.HasSso; + organization.UseKeyConnector = newPasswordManagerPlan.HasKeyConnector; + organization.UseScim = newPasswordManagerPlan.HasScim; + organization.UseResetPassword = newPasswordManagerPlan.HasResetPassword; + organization.SelfHost = newPasswordManagerPlan.HasSelfHost; + organization.UsersGetPremium = newPasswordManagerPlan.UsersGetPremium || upgrade.PremiumAccessAddon; + organization.UseCustomPermissions = newPasswordManagerPlan.HasCustomPermissions; + organization.Plan = newPasswordManagerPlan.Name; + organization.Enabled = success; + organization.PublicKey = upgrade.PublicKey; + organization.PrivateKey = upgrade.PrivateKey; + organization.UsePasswordManager = true; + organization.SmSeats = (short)(newSecretsManagerPlan.BaseSeats + upgrade.AdditionalSmSeats.GetValueOrDefault()); + organization.SmServiceAccounts = upgrade.AdditionalServiceAccounts.GetValueOrDefault(); + organization.UseSecretsManager = upgrade.UseSecretsManager; + + await _organizationService.ReplaceAndUpdateCacheAsync(organization); + + if (success) + { + await _referenceEventService.RaiseEventAsync( + new ReferenceEvent(ReferenceEventType.UpgradePlan, organization, _currentContext) + { + PlanName = newPasswordManagerPlan.Name, + PlanType = newPasswordManagerPlan.Type, + OldPlanName = existingPasswordManagerPlan.Name, + OldPlanType = existingPasswordManagerPlan.Type, + Seats = organization.Seats, + Storage = organization.MaxStorageGb, + SmSeats = organization.SmSeats, + ServiceAccounts = organization.SmServiceAccounts, + UseSecretsManager = organization.UseSecretsManager + }); + } + + return new Tuple(success, paymentIntentClientSecret); + } + + private async Task ValidateSecretsManagerSeatsAndServiceAccountAsync(OrganizationUpgrade upgrade, Organization organization, + Models.StaticStore.Plan newSecretsManagerPlan) + { + var newPlanSmSeats = (short)(newSecretsManagerPlan.BaseSeats + + (newSecretsManagerPlan.HasAdditionalSeatsOption + ? upgrade.AdditionalSmSeats + : 0)); + var occupiedSmSeats = + await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id); + + if (!organization.SmSeats.HasValue || organization.SmSeats.Value > newPlanSmSeats) + { + if (occupiedSmSeats > newPlanSmSeats) + { + throw new BadRequestException( + $"Your organization currently has {occupiedSmSeats} Secrets Manager seats filled. " + + $"Your new plan only has ({newPlanSmSeats}) seats. Remove some users."); + } + } + + if (newSecretsManagerPlan.BaseServiceAccount != null) + { + if (!organization.SmServiceAccounts.HasValue || + organization.SmServiceAccounts.Value > newSecretsManagerPlan.MaxServiceAccounts) + { + var currentServiceAccounts = + await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(organization.Id); + if (currentServiceAccounts > newSecretsManagerPlan.MaxServiceAccounts) + { + throw new BadRequestException( + $"Your organization currently has {currentServiceAccounts} service account seats filled. " + + $"Your new plan only has ({newSecretsManagerPlan.MaxServiceAccounts}) service accounts. Remove some service accounts."); + } + } + } + } + + private async Task GetOrgById(Guid id) + { + return await _organizationRepository.GetByIdAsync(id); + } +} diff --git a/src/Core/Services/IOrganizationService.cs b/src/Core/Services/IOrganizationService.cs index b912bd92149a..05fa0775a5c3 100644 --- a/src/Core/Services/IOrganizationService.cs +++ b/src/Core/Services/IOrganizationService.cs @@ -13,7 +13,6 @@ Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken, Payment TaxInfo taxInfo); Task CancelSubscriptionAsync(Guid organizationId, bool? endOfPeriod = null); Task ReinstateSubscriptionAsync(Guid organizationId); - Task> UpgradePlanAsync(Guid organizationId, OrganizationUpgrade upgrade); Task AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb); Task UpdateSubscription(Guid organizationId, int seatAdjustment, int? maxAutoscaleSeats); Task AutoAddSeatsAsync(Organization organization, int seatsToAdd, DateTime? prorationDate = null); @@ -79,4 +78,7 @@ Task>> RestoreUsersAsync(Guid organizationI /// Task InitPendingOrganization(Guid userId, Guid organizationId, string publicKey, string privateKey, string collectionName); Task ReplaceAndUpdateCacheAsync(Organization org, EventType? orgEvent = null); + + void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade); + void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade); } diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index c00d9f983571..98506d43de2d 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -1,6 +1,5 @@ using System.Security.Claims; using System.Text.Json; -using Bit.Core.AdminConsole.Models.OrganizationConnectionConfigs; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models.Business; using Bit.Core.Auth.Repositories; @@ -13,7 +12,6 @@ using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.Repositories; -using Bit.Core.SecretsManager.Repositories; using Bit.Core.Settings; using Bit.Core.Tools.Enums; using Bit.Core.Tools.Models.Business; @@ -39,7 +37,6 @@ public class OrganizationService : IOrganizationService private readonly IDeviceRepository _deviceRepository; private readonly ILicensingService _licensingService; private readonly IEventService _eventService; - private readonly IInstallationRepository _installationRepository; private readonly IApplicationCacheService _applicationCacheService; private readonly IPaymentService _paymentService; private readonly IPolicyRepository _policyRepository; @@ -49,12 +46,10 @@ public class OrganizationService : IOrganizationService private readonly IReferenceEventService _referenceEventService; private readonly IGlobalSettings _globalSettings; private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository; - private readonly IOrganizationConnectionRepository _organizationConnectionRepository; private readonly ICurrentContext _currentContext; private readonly ILogger _logger; private readonly IProviderOrganizationRepository _providerOrganizationRepository; private readonly IProviderUserRepository _providerUserRepository; - private readonly IServiceAccountRepository _serviceAccountRepository; public OrganizationService( IOrganizationRepository organizationRepository, @@ -69,7 +64,6 @@ public OrganizationService( IDeviceRepository deviceRepository, ILicensingService licensingService, IEventService eventService, - IInstallationRepository installationRepository, IApplicationCacheService applicationCacheService, IPaymentService paymentService, IPolicyRepository policyRepository, @@ -79,12 +73,10 @@ public OrganizationService( IReferenceEventService referenceEventService, IGlobalSettings globalSettings, IOrganizationApiKeyRepository organizationApiKeyRepository, - IOrganizationConnectionRepository organizationConnectionRepository, ICurrentContext currentContext, ILogger logger, IProviderOrganizationRepository providerOrganizationRepository, - IProviderUserRepository providerUserRepository, - IServiceAccountRepository serviceAccountRepository) + IProviderUserRepository providerUserRepository) { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; @@ -98,7 +90,6 @@ public OrganizationService( _deviceRepository = deviceRepository; _licensingService = licensingService; _eventService = eventService; - _installationRepository = installationRepository; _applicationCacheService = applicationCacheService; _paymentService = paymentService; _policyRepository = policyRepository; @@ -108,12 +99,10 @@ public OrganizationService( _referenceEventService = referenceEventService; _globalSettings = globalSettings; _organizationApiKeyRepository = organizationApiKeyRepository; - _organizationConnectionRepository = organizationConnectionRepository; _currentContext = currentContext; _logger = logger; _providerOrganizationRepository = providerOrganizationRepository; _providerUserRepository = providerUserRepository; - _serviceAccountRepository = serviceAccountRepository; } public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken, @@ -170,277 +159,6 @@ await _referenceEventService.RaiseEventAsync( new ReferenceEvent(ReferenceEventType.ReinstateSubscription, organization, _currentContext)); } - public async Task> UpgradePlanAsync(Guid organizationId, OrganizationUpgrade upgrade) - { - var organization = await GetOrgById(organizationId); - if (organization == null) - { - throw new NotFoundException(); - } - - if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId)) - { - throw new BadRequestException("Your account has no payment method available."); - } - - var existingPasswordManagerPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); - if (existingPasswordManagerPlan == null) - { - throw new BadRequestException("Existing plan not found."); - } - - var newPasswordManagerPlan = - StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled); - if (newPasswordManagerPlan == null) - { - throw new BadRequestException("Plan not found."); - } - - if (existingPasswordManagerPlan.Type == newPasswordManagerPlan.Type) - { - throw new BadRequestException("Organization is already on this plan."); - } - - if (existingPasswordManagerPlan.UpgradeSortOrder >= newPasswordManagerPlan.UpgradeSortOrder) - { - throw new BadRequestException("You cannot upgrade to this plan."); - } - - if (existingPasswordManagerPlan.Type != PlanType.Free) - { - throw new BadRequestException("You can only upgrade from the free plan. Contact support."); - } - - ValidatePasswordManagerPlan(newPasswordManagerPlan, upgrade); - var newSecretsManagerPlan = - StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled); - if (upgrade.UseSecretsManager) - { - ValidateSecretsManagerPlan(newSecretsManagerPlan, upgrade); - } - - var newPasswordManagerPlanSeats = (short)(newPasswordManagerPlan.BaseSeats + - (newPasswordManagerPlan.HasAdditionalSeatsOption ? upgrade.AdditionalSeats : 0)); - if (!organization.Seats.HasValue || organization.Seats.Value > newPasswordManagerPlanSeats) - { - var occupiedSeats = - await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); - if (occupiedSeats > newPasswordManagerPlanSeats) - { - throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " + - $"Your new plan only has ({newPasswordManagerPlanSeats}) seats. Remove some users."); - } - } - - if (newPasswordManagerPlan.MaxCollections.HasValue && (!organization.MaxCollections.HasValue || - organization.MaxCollections.Value > - newPasswordManagerPlan.MaxCollections.Value)) - { - var collectionCount = await _collectionRepository.GetCountByOrganizationIdAsync(organization.Id); - if (collectionCount > newPasswordManagerPlan.MaxCollections.Value) - { - throw new BadRequestException($"Your organization currently has {collectionCount} collections. " + - $"Your new plan allows for a maximum of ({newPasswordManagerPlan.MaxCollections.Value}) collections. " + - "Remove some collections."); - } - } - - if (!newPasswordManagerPlan.HasGroups && organization.UseGroups) - { - var groups = await _groupRepository.GetManyByOrganizationIdAsync(organization.Id); - if (groups.Any()) - { - throw new BadRequestException($"Your new plan does not allow the groups feature. " + - $"Remove your groups."); - } - } - - if (!newPasswordManagerPlan.HasPolicies && organization.UsePolicies) - { - var policies = await _policyRepository.GetManyByOrganizationIdAsync(organization.Id); - if (policies.Any(p => p.Enabled)) - { - throw new BadRequestException($"Your new plan does not allow the policies feature. " + - $"Disable your policies."); - } - } - - if (!newPasswordManagerPlan.HasSso && organization.UseSso) - { - var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id); - if (ssoConfig != null && ssoConfig.Enabled) - { - throw new BadRequestException($"Your new plan does not allow the SSO feature. " + - $"Disable your SSO configuration."); - } - } - - if (!newPasswordManagerPlan.HasKeyConnector && organization.UseKeyConnector) - { - var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id); - if (ssoConfig != null && ssoConfig.GetData().MemberDecryptionType == MemberDecryptionType.KeyConnector) - { - throw new BadRequestException("Your new plan does not allow the Key Connector feature. " + - "Disable your Key Connector."); - } - } - - if (!newPasswordManagerPlan.HasResetPassword && organization.UseResetPassword) - { - var resetPasswordPolicy = - await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword); - if (resetPasswordPolicy != null && resetPasswordPolicy.Enabled) - { - throw new BadRequestException("Your new plan does not allow the Password Reset feature. " + - "Disable your Password Reset policy."); - } - } - - if (!newPasswordManagerPlan.HasScim && organization.UseScim) - { - var scimConnections = await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organization.Id, - OrganizationConnectionType.Scim); - if (scimConnections != null && scimConnections.Any(c => c.GetConfig()?.Enabled == true)) - { - throw new BadRequestException("Your new plan does not allow the SCIM feature. " + - "Disable your SCIM configuration."); - } - } - - if (!newPasswordManagerPlan.HasCustomPermissions && organization.UseCustomPermissions) - { - var organizationCustomUsers = - await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, - OrganizationUserType.Custom); - if (organizationCustomUsers.Any()) - { - throw new BadRequestException("Your new plan does not allow the Custom Permissions feature. " + - "Disable your Custom Permissions configuration."); - } - } - - if (upgrade.UseSecretsManager && newSecretsManagerPlan != null) - { - await ValidateSecretsManagerSeatsAndServiceAccountAsync(upgrade, organization, newSecretsManagerPlan); - } - - // TODO: Check storage? - string paymentIntentClientSecret = null; - var success = true; - - if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId)) - { - var organizationUpgradePlan = upgrade.UseSecretsManager - ? StaticStore.Plans.Where(p => p.Type == upgrade.Plan).ToList() - : StaticStore.Plans.Where(p => p.Type == upgrade.Plan && p.BitwardenProduct == BitwardenProductType.PasswordManager).ToList(); - - paymentIntentClientSecret = await _paymentService.UpgradeFreeOrganizationAsync(organization, - organizationUpgradePlan, - upgrade.AdditionalStorageGb, upgrade.AdditionalSeats, upgrade.PremiumAccessAddon, upgrade.TaxInfo - , upgrade.AdditionalSmSeats.GetValueOrDefault(), upgrade.AdditionalServiceAccounts.GetValueOrDefault()); - success = string.IsNullOrWhiteSpace(paymentIntentClientSecret); - } - else - { - // TODO: Update existing sub - throw new BadRequestException("You can only upgrade from the free plan. Contact support."); - } - - organization.BusinessName = upgrade.BusinessName; - organization.PlanType = newPasswordManagerPlan.Type; - organization.Seats = (short)(newPasswordManagerPlan.BaseSeats + upgrade.AdditionalSeats); - organization.MaxCollections = newPasswordManagerPlan.MaxCollections; - organization.UseGroups = newPasswordManagerPlan.HasGroups; - organization.UseDirectory = newPasswordManagerPlan.HasDirectory; - organization.UseEvents = newPasswordManagerPlan.HasEvents; - organization.UseTotp = newPasswordManagerPlan.HasTotp; - organization.Use2fa = newPasswordManagerPlan.Has2fa; - organization.UseApi = newPasswordManagerPlan.HasApi; - organization.SelfHost = newPasswordManagerPlan.HasSelfHost; - organization.UsePolicies = newPasswordManagerPlan.HasPolicies; - organization.MaxStorageGb = !newPasswordManagerPlan.BaseStorageGb.HasValue - ? (short?)null - : (short)(newPasswordManagerPlan.BaseStorageGb.Value + upgrade.AdditionalStorageGb); - organization.UseGroups = newPasswordManagerPlan.HasGroups; - organization.UseDirectory = newPasswordManagerPlan.HasDirectory; - organization.UseEvents = newPasswordManagerPlan.HasEvents; - organization.UseTotp = newPasswordManagerPlan.HasTotp; - organization.Use2fa = newPasswordManagerPlan.Has2fa; - organization.UseApi = newPasswordManagerPlan.HasApi; - organization.UseSso = newPasswordManagerPlan.HasSso; - organization.UseKeyConnector = newPasswordManagerPlan.HasKeyConnector; - organization.UseScim = newPasswordManagerPlan.HasScim; - organization.UseResetPassword = newPasswordManagerPlan.HasResetPassword; - organization.SelfHost = newPasswordManagerPlan.HasSelfHost; - organization.UsersGetPremium = newPasswordManagerPlan.UsersGetPremium || upgrade.PremiumAccessAddon; - organization.UseCustomPermissions = newPasswordManagerPlan.HasCustomPermissions; - organization.Plan = newPasswordManagerPlan.Name; - organization.Enabled = success; - organization.PublicKey = upgrade.PublicKey; - organization.PrivateKey = upgrade.PrivateKey; - organization.UsePasswordManager = true; - organization.SmSeats = (short)(newSecretsManagerPlan.BaseSeats + upgrade.AdditionalSmSeats.GetValueOrDefault()); - organization.SmServiceAccounts = upgrade.AdditionalServiceAccounts.GetValueOrDefault(); - organization.UseSecretsManager = upgrade.UseSecretsManager; - - await ReplaceAndUpdateCacheAsync(organization); - if (success) - { - await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.UpgradePlan, organization, _currentContext) - { - PlanName = newPasswordManagerPlan.Name, - PlanType = newPasswordManagerPlan.Type, - OldPlanName = existingPasswordManagerPlan.Name, - OldPlanType = existingPasswordManagerPlan.Type, - Seats = organization.Seats, - Storage = organization.MaxStorageGb, - SmSeats = organization.SmSeats, - ServiceAccounts = organization.SmServiceAccounts, - UseSecretsManager = organization.UseSecretsManager - }); - } - - return new Tuple(success, paymentIntentClientSecret); - } - - private async Task ValidateSecretsManagerSeatsAndServiceAccountAsync(OrganizationUpgrade upgrade, Organization organization, - Models.StaticStore.Plan newSecretsManagerPlan) - { - var newPlanSmSeats = (short)(newSecretsManagerPlan.BaseSeats + - (newSecretsManagerPlan.HasAdditionalSeatsOption - ? upgrade.AdditionalSmSeats - : 0)); - var occupiedSmSeats = - await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id); - - if (!organization.SmSeats.HasValue || organization.SmSeats.Value > newPlanSmSeats) - { - if (occupiedSmSeats > newPlanSmSeats) - { - throw new BadRequestException( - $"Your organization currently has {occupiedSmSeats} Secrets Manager seats filled. " + - $"Your new plan only has ({newPlanSmSeats}) seats. Remove some users."); - } - } - - if (newSecretsManagerPlan.BaseServiceAccount != null) - { - if (!organization.SmServiceAccounts.HasValue || - organization.SmServiceAccounts.Value > newSecretsManagerPlan.MaxServiceAccounts) - { - var currentServiceAccounts = - await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(organization.Id); - if (currentServiceAccounts > newSecretsManagerPlan.MaxServiceAccounts) - { - throw new BadRequestException( - $"Your organization currently has {currentServiceAccounts} service account seats filled. " + - $"Your new plan only has ({newSecretsManagerPlan.MaxServiceAccounts}) service accounts. Remove some service accounts."); - } - } - } - } - public async Task AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb) { var organization = await GetOrgById(organizationId); @@ -2162,7 +1880,7 @@ private static void ValidatePlan(Models.StaticStore.Plan plan, int additionalSea } } - private static void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade) + public void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade) { ValidatePlan(plan, upgrade.AdditionalSeats, "Password Manager"); @@ -2204,7 +1922,7 @@ private static void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, Or } } - private static void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade) + public void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade) { ValidatePlan(plan, upgrade.AdditionalSmSeats.GetValueOrDefault(), "Secrets Manager"); diff --git a/src/Identity/Startup.cs b/src/Identity/Startup.cs index 5029212e6d7b..6a0f274ce8bb 100644 --- a/src/Identity/Startup.cs +++ b/src/Identity/Startup.cs @@ -4,8 +4,6 @@ using Bit.Core; using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Context; -using Bit.Core.Repositories.Noop; -using Bit.Core.SecretsManager.Repositories; using Bit.Core.Settings; using Bit.Core.Utilities; using Bit.Identity.Utilities; @@ -48,8 +46,6 @@ public void ConfigureServices(IServiceCollection services) // Repositories services.AddDatabaseRepositories(globalSettings); - services.AddScoped(); - // Context services.AddScoped(); services.TryAddSingleton(); @@ -155,7 +151,6 @@ public void ConfigureServices(IServiceCollection services) }); } - public void Configure( IApplicationBuilder app, IWebHostEnvironment env, diff --git a/test/Api.Test/Controllers/OrganizationsControllerTests.cs b/test/Api.Test/Controllers/OrganizationsControllerTests.cs index aa34ba5e447f..bfed60a9a462 100644 --- a/test/Api.Test/Controllers/OrganizationsControllerTests.cs +++ b/test/Api.Test/Controllers/OrganizationsControllerTests.cs @@ -11,7 +11,7 @@ using Bit.Core.Exceptions; using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces; -using Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate.Interface; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; @@ -42,6 +42,7 @@ public class OrganizationsControllerTests : IDisposable private readonly IFeatureService _featureService; private readonly ILicensingService _licensingService; private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; + private readonly IUpgradeOrganizationPlanCommand _upgradeOrganizationPlanCommand; private readonly OrganizationsController _sut; @@ -67,13 +68,14 @@ public OrganizationsControllerTests() _featureService = Substitute.For(); _licensingService = Substitute.For(); _updateSecretsManagerSubscriptionCommand = Substitute.For(); + _upgradeOrganizationPlanCommand = Substitute.For(); _sut = new OrganizationsController(_organizationRepository, _organizationUserRepository, _policyRepository, _providerRepository, _organizationService, _userService, _paymentService, _currentContext, _ssoConfigRepository, _ssoConfigService, _getOrganizationApiKeyQuery, _rotateOrganizationApiKeyCommand, _createOrganizationApiKeyCommand, _organizationApiKeyRepository, _updateOrganizationLicenseCommand, _cloudGetOrganizationLicenseQuery, _featureService, _globalSettings, _licensingService, - _updateSecretsManagerSubscriptionCommand); + _updateSecretsManagerSubscriptionCommand, _upgradeOrganizationPlanCommand); } public void Dispose() diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs index 1a3ffdfc0841..9210140bc68f 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs @@ -3,7 +3,7 @@ using Bit.Core.Exceptions; using Bit.Core.Models.Business; using Bit.Core.Models.StaticStore; -using Bit.Core.OrganizationFeatures.OrganizationSubscriptionUpdate; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions; using Bit.Core.Repositories; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs new file mode 100644 index 000000000000..e71e3052ff61 --- /dev/null +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs @@ -0,0 +1,113 @@ +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.Models.Business; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Bit.Core.Test.AutoFixture.OrganizationFixtures; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; +using Organization = Bit.Core.Entities.Organization; + +namespace Bit.Core.Test.OrganizationFeatures.OrganizationSubscriptionUpdate; + +[SutProviderCustomize] +public class UpgradeOrganizationPlanCommandTests +{ + [Theory, BitAutoData] + public async Task UpgradePlan_OrganizationIsNull_Throws(Guid organizationId, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(Task.FromResult(null)); + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpgradePlanAsync(organizationId, upgrade)); + } + + [Theory, BitAutoData] + public async Task UpgradePlan_GatewayCustomIdIsNull_Throws(Organization organization, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + organization.GatewayCustomerId = string.Empty; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); + Assert.Contains("no payment method", exception.Message); + } + + [Theory, BitAutoData] + public async Task UpgradePlan_AlreadyInPlan_Throws(Organization organization, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + upgrade.Plan = organization.PlanType; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); + Assert.Contains("already on this plan", exception.Message); + } + + [Theory, BitAutoData] + public async Task UpgradePlan_SM_AlreadyInPlan_Throws(Organization organization, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + upgrade.Plan = organization.PlanType; + upgrade.UseSecretsManager = true; + upgrade.AdditionalSmSeats = 10; + upgrade.AdditionalServiceAccounts = 10; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); + Assert.Contains("already on this plan", exception.Message); + } + + [Theory, PaidOrganizationCustomize(CheckedPlanType = PlanType.Free), BitAutoData] + public async Task UpgradePlan_UpgradeFromPaidPlan_Throws(Organization organization, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); + Assert.Contains("can only upgrade", exception.Message); + } + + [Theory, PaidOrganizationCustomize(CheckedPlanType = PlanType.Free), BitAutoData] + public async Task UpgradePlan_SM_UpgradeFromPaidPlan_Throws(Organization organization, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + upgrade.UseSecretsManager = true; + upgrade.AdditionalSmSeats = 10; + upgrade.AdditionalServiceAccounts = 10; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); + Assert.Contains("can only upgrade", exception.Message); + } + + [Theory] + [FreeOrganizationUpgradeCustomize, BitAutoData] + public async Task UpgradePlan_Passes(Organization organization, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + upgrade.AdditionalSmSeats = 10; + upgrade.AdditionalSeats = 10; + await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade); + await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync(organization); + } + + [Theory] + [FreeOrganizationUpgradeCustomize, BitAutoData] + public async Task UpgradePlan_SM_Passes(Organization organization, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + upgrade.AdditionalSmSeats = 10; + upgrade.AdditionalSeats = 10; + var result = await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade); + await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync(organization); + Assert.True(result.Item1); + Assert.NotNull(result.Item2); + } + +} diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index b2c73c24fc9d..f3c88663e1f1 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -145,85 +145,6 @@ await sutProvider.GetDependency().Received(1) referenceEvent.Users == expectedNewUsersCount)); } - [Theory, BitAutoData] - public async Task UpgradePlan_OrganizationIsNull_Throws(Guid organizationId, OrganizationUpgrade upgrade, - SutProvider sutProvider) - { - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(Task.FromResult(null)); - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.UpgradePlanAsync(organizationId, upgrade)); - } - - [Theory, BitAutoData] - public async Task UpgradePlan_GatewayCustomIdIsNull_Throws(Organization organization, OrganizationUpgrade upgrade, - SutProvider sutProvider) - { - organization.GatewayCustomerId = string.Empty; - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); - Assert.Contains("no payment method", exception.Message); - } - - [Theory, BitAutoData] - public async Task UpgradePlan_AlreadyInPlan_Throws(Organization organization, OrganizationUpgrade upgrade, - SutProvider sutProvider) - { - upgrade.Plan = organization.PlanType; - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); - Assert.Contains("already on this plan", exception.Message); - } - - [Theory, BitAutoData] - public async Task UpgradePlan_SM_AlreadyInPlan_Throws(Organization organization, OrganizationUpgrade upgrade, - SutProvider sutProvider) - { - upgrade.Plan = organization.PlanType; - upgrade.UseSecretsManager = true; - upgrade.AdditionalSmSeats = 10; - upgrade.AdditionalServiceAccounts = 10; - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); - Assert.Contains("already on this plan", exception.Message); - } - - [Theory, PaidOrganizationCustomize(CheckedPlanType = PlanType.Free), BitAutoData] - public async Task UpgradePlan_UpgradeFromPaidPlan_Throws(Organization organization, OrganizationUpgrade upgrade, - SutProvider sutProvider) - { - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); - Assert.Contains("can only upgrade", exception.Message); - } - - [Theory, PaidOrganizationCustomize(CheckedPlanType = PlanType.Free), BitAutoData] - public async Task UpgradePlan_SM_UpgradeFromPaidPlan_Throws(Organization organization, OrganizationUpgrade upgrade, - SutProvider sutProvider) - { - upgrade.UseSecretsManager = true; - upgrade.AdditionalSmSeats = 10; - upgrade.AdditionalServiceAccounts = 10; - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); - Assert.Contains("can only upgrade", exception.Message); - } - - [Theory] - [FreeOrganizationUpgradeCustomize, BitAutoData] - public async Task UpgradePlan_Passes(Organization organization, OrganizationUpgrade upgrade, - SutProvider sutProvider) - { - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - upgrade.AdditionalSmSeats = 10; - upgrade.AdditionalSeats = 10; - await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade); - await sutProvider.GetDependency().Received(1).ReplaceAsync(organization); - } [Theory] [BitAutoData] @@ -327,20 +248,6 @@ public async Task SignUpAsync_InvalidateServiceAccount_ShouldThrowException(Orga Assert.Contains("You can't subtract Service Accounts!", exception.Message); } - [Theory] - [FreeOrganizationUpgradeCustomize, BitAutoData] - public async Task UpgradePlan_SM_Passes(Organization organization, OrganizationUpgrade upgrade, - SutProvider sutProvider) - { - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - upgrade.AdditionalSmSeats = 10; - upgrade.AdditionalSeats = 10; - var result = await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade); - await sutProvider.GetDependency().Received(1).ReplaceAsync(organization); - Assert.True(result.Item1); - Assert.NotNull(result.Item2); - } - [Theory] [OrganizationInviteCustomize(InviteeUserType = OrganizationUserType.User, InvitorUserType = OrganizationUserType.Owner), BitAutoData] From cab23cb1099e81dc866ef0237ae0f6d2a715daf5 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Tue, 11 Jul 2023 19:07:57 +1000 Subject: [PATCH 19/41] [AC-1503] Fix Stripe integration on organization upgrade (#3084) * Fix SM parameters not being passed to Stripe * Fix flaky test * Fix error message --- .../Business/SubscriptionCreateOptions.cs | 16 ++++---- ...UpdateSecretsManagerSubscriptionCommand.cs | 4 +- .../UpgradeOrganizationPlanCommand.cs | 4 +- src/Core/Services/IPaymentService.cs | 4 +- .../Implementations/StripePaymentService.cs | 6 +-- ...eSecretsManagerSubscriptionCommandTests.cs | 4 +- .../Services/OrganizationServiceTests.cs | 14 +++---- .../Services/StripePaymentServiceTests.cs | 39 ++++++++++++++++--- 8 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/Core/Models/Business/SubscriptionCreateOptions.cs b/src/Core/Models/Business/SubscriptionCreateOptions.cs index bd51f431f065..86524a55b6cb 100644 --- a/src/Core/Models/Business/SubscriptionCreateOptions.cs +++ b/src/Core/Models/Business/SubscriptionCreateOptions.cs @@ -7,7 +7,7 @@ namespace Bit.Core.Models.Business; public class OrganizationSubscriptionOptionsBase : Stripe.SubscriptionCreateOptions { public OrganizationSubscriptionOptionsBase(Organization org, List plans, TaxInfo taxInfo, int additionalSeats, - int additionalStorageGb, bool premiumAccessAddon, int additionalSmSeats = 0, int additionalServiceAccounts = 0) + int additionalStorageGb, bool premiumAccessAddon, int additionalSmSeats, int additionalServiceAccounts) { Items = new List(); Metadata = new Dictionary @@ -95,9 +95,9 @@ public class OrganizationPurchaseSubscriptionOptions : OrganizationSubscriptionO { public OrganizationPurchaseSubscriptionOptions( Organization org, List plans, - TaxInfo taxInfo, int additionalSeats = 0, - int additionalStorageGb = 0, bool premiumAccessAddon = false, - int additionalSmSeats = 0, int additionalServiceAccounts = 0) : + TaxInfo taxInfo, int additionalSeats, + int additionalStorageGb, bool premiumAccessAddon, + int additionalSmSeats, int additionalServiceAccounts) : base(org, plans, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon, additionalSmSeats, additionalServiceAccounts) { OffSession = true; @@ -109,10 +109,10 @@ public class OrganizationUpgradeSubscriptionOptions : OrganizationSubscriptionOp { public OrganizationUpgradeSubscriptionOptions( string customerId, Organization org, - List plans, TaxInfo taxInfo, - int additionalSeats = 0, int additionalStorageGb = 0, - bool premiumAccessAddon = false, int additionalSmSeats = 0, int additionalServiceAccounts = 0) : - base(org, plans, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon, additionalSmSeats, additionalServiceAccounts) + List plans, OrganizationUpgrade upgrade) : + base(org, plans, upgrade.TaxInfo, upgrade.AdditionalSeats, upgrade.AdditionalStorageGb, + upgrade.PremiumAccessAddon, upgrade.AdditionalSmSeats.GetValueOrDefault(), + upgrade.AdditionalServiceAccounts.GetValueOrDefault()) { Customer = customerId; } diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs index 9237253f79b8..a57c874da73f 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs @@ -240,7 +240,7 @@ private async Task ValidateSmSeatsUpdateAsync(Organization organization, Secrets if (currentSeats > update.SmSeats) { throw new BadRequestException($"Your organization currently has {currentSeats} Secrets Manager seats. " + - $"Your plan only allows ({update.SmSeats}) Secrets Manager seats. Remove some Secrets Manager users."); + $"Your plan only allows {update.SmSeats} Secrets Manager seats. Remove some Secrets Manager users."); } } } @@ -294,7 +294,7 @@ private async Task ValidateSmServiceAccountsUpdateAsync(Organization organizatio if (currentServiceAccounts > update.SmServiceAccounts) { throw new BadRequestException($"Your organization currently has {currentServiceAccounts} Service Accounts. " + - $"Your plan only allows ({update.SmServiceAccounts}) Service Accounts. Remove some Service Accounts."); + $"Your plan only allows {update.SmServiceAccounts} Service Accounts. Remove some Service Accounts."); } } } diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs index 62ee67442f1f..24a459ea3218 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs @@ -225,9 +225,7 @@ await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, : StaticStore.Plans.Where(p => p.Type == upgrade.Plan && p.BitwardenProduct == BitwardenProductType.PasswordManager).ToList(); paymentIntentClientSecret = await _paymentService.UpgradeFreeOrganizationAsync(organization, - organizationUpgradePlan, - upgrade.AdditionalStorageGb, upgrade.AdditionalSeats, upgrade.PremiumAccessAddon, upgrade.TaxInfo - , upgrade.AdditionalSmSeats.GetValueOrDefault(), upgrade.AdditionalServiceAccounts.GetValueOrDefault()); + organizationUpgradePlan, upgrade); success = string.IsNullOrWhiteSpace(paymentIntentClientSecret); } else diff --git a/src/Core/Services/IPaymentService.cs b/src/Core/Services/IPaymentService.cs index 21140a58c878..d636fa22653a 100644 --- a/src/Core/Services/IPaymentService.cs +++ b/src/Core/Services/IPaymentService.cs @@ -14,9 +14,7 @@ Task PurchaseOrganizationAsync(Organization org, PaymentMethodType payme int additionalServiceAccount = 0); Task SponsorOrganizationAsync(Organization org, OrganizationSponsorship sponsorship); Task RemoveOrganizationSponsorshipAsync(Organization org, OrganizationSponsorship sponsorship); - Task UpgradeFreeOrganizationAsync(Organization org, List plans, - short additionalStorageGb, int additionalSeats, bool premiumAccessAddon, TaxInfo taxInfo, - int additionalSmSeats = 0, int additionalServiceAccounts = 0); + Task UpgradeFreeOrganizationAsync(Organization org, List plans, OrganizationUpgrade upgrade); Task PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken, short additionalStorageGb, TaxInfo taxInfo); Task AdjustSeatsAsync(Organization organization, Plan plan, int additionalSeats, DateTime? prorationDate = null); diff --git a/src/Core/Services/Implementations/StripePaymentService.cs b/src/Core/Services/Implementations/StripePaymentService.cs index 9bd45ff0cf06..1d482ccc1bf1 100644 --- a/src/Core/Services/Implementations/StripePaymentService.cs +++ b/src/Core/Services/Implementations/StripePaymentService.cs @@ -224,8 +224,7 @@ public Task RemoveOrganizationSponsorshipAsync(Organization org, OrganizationSpo ChangeOrganizationSponsorship(org, sponsorship, false); public async Task UpgradeFreeOrganizationAsync(Organization org, List plans, - short additionalStorageGb, int additionalSeats, bool premiumAccessAddon, TaxInfo taxInfo, - int additionalSmSeats = 0, int additionalServiceAccounts = 0) + OrganizationUpgrade upgrade) { if (!string.IsNullOrWhiteSpace(org.GatewaySubscriptionId)) { @@ -241,6 +240,7 @@ public async Task UpgradeFreeOrganizationAsync(Organization org, List UpgradeFreeOrganizationAsync(Organization org, List(() => sutProvider.Sut.UpdateSecretsManagerSubscription(update)); - Assert.Contains("Your organization currently has 8 Secrets Manager seats. Your plan only allows (7) Secrets Manager seats. Remove some Secrets Manager users", exception.Message); + Assert.Contains("Your organization currently has 8 Secrets Manager seats. Your plan only allows 7 Secrets Manager seats. Remove some Secrets Manager users", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } @@ -724,7 +724,7 @@ public async Task UpdateServiceAccountAutoscaling_WhenCurrentServiceAccountsIsGr .Returns(currentServiceAccounts); var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); - Assert.Contains("Your organization currently has 301 Service Accounts. Your plan only allows (300) Service Accounts. Remove some Service Accounts", exception.Message); + Assert.Contains("Your organization currently has 301 Service Accounts. Your plan only allows 300 Service Accounts. Remove some Service Accounts", exception.Message); await sutProvider.GetDependency().Received(1).GetServiceAccountCountByOrganizationIdAsync(organization.Id); await VerifyDependencyNotCalledAsync(sutProvider); } diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index f3c88663e1f1..7729d748cdef 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -194,20 +194,16 @@ await sutProvider.GetDependency().Received(1).PurchaseOrganizat [Theory] [BitAutoData] - public async Task SignUpAsync_SecretManagerValidation_ShouldThrowException(OrganizationSignup signup, SutProvider sutProvider) + public async Task SignUpAsync_SecretManager_AdditionalServiceAccounts_NotAllowedByPlan_ShouldThrowException(OrganizationSignup signup, SutProvider sutProvider) { - signup.AdditionalSmSeats = 10; - signup.AdditionalSeats = 10; - signup.Plan = PlanType.EnterpriseAnnually; + signup.AdditionalSmSeats = 0; + signup.AdditionalSeats = 0; + signup.Plan = PlanType.Free; signup.UseSecretsManager = true; signup.PaymentMethodType = PaymentMethodType.Card; signup.PremiumAccessAddon = false; signup.AdditionalServiceAccounts = 10; - - var purchaseOrganizationPlan = StaticStore.Plans.Where(x => x.Type == signup.Plan && x.BitwardenProduct == BitwardenProductType.PasswordManager).ToList(); - var secretsManagerPlan = StaticStore.GetSecretsManagerPlan(PlanType.EnterpriseAnnually); - secretsManagerPlan.HasAdditionalServiceAccountOption = false; - purchaseOrganizationPlan.Add(secretsManagerPlan); + signup.AdditionalStorageGb = 0; var exception = await Assert.ThrowsAsync( () => sutProvider.Sut.SignUpAsync(signup)); diff --git a/test/Core.Test/Services/StripePaymentServiceTests.cs b/test/Core.Test/Services/StripePaymentServiceTests.cs index 96f9a6a6f315..91e383cb58bf 100644 --- a/test/Core.Test/Services/StripePaymentServiceTests.cs +++ b/test/Core.Test/Services/StripePaymentServiceTests.cs @@ -443,6 +443,8 @@ await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var passwordManagerPlan = plans.Single(p => p.BitwardenProduct == BitwardenProductType.PasswordManager); + var secretsManagerPlan = plans.Single(p => p.BitwardenProduct == BitwardenProductType.SecretsManager); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -465,8 +467,12 @@ public async void PurchaseOrganizationAsync_SM_Paypal(SutProvider(); braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult); + var additionalStorage = (short)2; + var additionalSeats = 10; + var additionalSmSeats = 5; + var additionalServiceAccounts = 20; var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, - 2, 10, false, taxInfo, false, 10, 10); + additionalStorage, additionalSeats, false, taxInfo, false, additionalSmSeats, additionalServiceAccounts); Assert.Null(result); Assert.Equal(GatewayType.Stripe, organization.Gateway); @@ -495,7 +501,11 @@ await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is 0 + s.Items.Count == 4 && + s.Items.Count(i => i.Plan == passwordManagerPlan.StripeSeatPlanId && i.Quantity == additionalSeats) == 1 && + s.Items.Count(i => i.Plan == passwordManagerPlan.StripeStoragePlanId && i.Quantity == additionalStorage) == 1 && + s.Items.Count(i => i.Plan == secretsManagerPlan.StripeSeatPlanId && i.Quantity == additionalSmSeats) == 1 && + s.Items.Count(i => i.Plan == secretsManagerPlan.StripeServiceAccountPlanId && i.Quantity == additionalServiceAccounts) == 1 )); } @@ -600,7 +610,17 @@ public async void UpgradeFreeOrganizationAsync_Success(SutProvider p.Type == PlanType.EnterpriseAnnually).ToList(); - var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plans, 0, 0, false, taxInfo); + + var upgrade = new OrganizationUpgrade() + { + AdditionalStorageGb = 0, + AdditionalSeats = 0, + PremiumAccessAddon = false, + TaxInfo = taxInfo, + AdditionalSmSeats = 0, + AdditionalServiceAccounts = 0 + }; + var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plans, upgrade); Assert.Null(result); } @@ -626,9 +646,18 @@ public async void UpgradeFreeOrganizationAsync_SM_Success(SutProvider p.Type == PlanType.EnterpriseAnnually).ToList(); - var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plans, 0, 0, - false, taxInfo); + var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plans, upgrade); Assert.Null(result); } From 3a6b17bf1904224d7dd7e156ce76b8f0109fd691 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Tue, 11 Jul 2023 19:08:57 +1000 Subject: [PATCH 20/41] [AC-1504] Allow SM max autoscale limits to be disabled (#3085) --- ...tsManagerSubscriptionUpdateRequestModel.cs | 11 +++----- ...UpdateSecretsManagerSubscriptionCommand.cs | 28 +++++++++++++------ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs b/src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs index 1bd3cde33d8a..31f071953a92 100644 --- a/src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs +++ b/src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs @@ -17,11 +17,6 @@ public virtual SecretsManagerSubscriptionUpdate ToSecretsManagerSubscriptionUpda { var newTotalSeats = organization.SmSeats.GetValueOrDefault() + SeatAdjustment; var newTotalServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + ServiceAccountAdjustment; - var autoscaleSeats = MaxAutoscaleSeats.HasValue && MaxAutoscaleSeats != - organization.MaxAutoscaleSmSeats.GetValueOrDefault(); - var autoscaleServiceAccounts = MaxAutoscaleServiceAccounts.HasValue && - MaxAutoscaleServiceAccounts != - organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault(); var orgUpdate = new SecretsManagerSubscriptionUpdate { @@ -39,8 +34,10 @@ public virtual SecretsManagerSubscriptionUpdate ToSecretsManagerSubscriptionUpda MaxAutoscaleSmServiceAccounts = MaxAutoscaleServiceAccounts, - MaxAutoscaleSmSeatsChanged = autoscaleSeats, - MaxAutoscaleSmServiceAccountsChanged = autoscaleServiceAccounts + MaxAutoscaleSmSeatsChanged = + MaxAutoscaleSeats.GetValueOrDefault() != organization.MaxAutoscaleSmSeats.GetValueOrDefault(), + MaxAutoscaleSmServiceAccountsChanged = + MaxAutoscaleServiceAccounts.GetValueOrDefault() != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() }; return orgUpdate; diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs index a57c874da73f..87db6c83d834 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs @@ -61,12 +61,12 @@ public async Task UpdateSecretsManagerSubscription(SecretsManagerSubscriptionUpd if (update.MaxAutoscaleSmSeatsChanged) { - ValidateMaxAutoscaleSmSeatsUpdateAsync(organization, update.MaxAutoscaleSmSeats.GetValueOrDefault(), plan); + ValidateMaxAutoscaleSmSeatsUpdateAsync(organization, update.MaxAutoscaleSmSeats, plan); } if (update.MaxAutoscaleSmServiceAccountsChanged) { - ValidateMaxAutoscaleSmServiceAccountUpdate(organization, update.MaxAutoscaleSmServiceAccounts.GetValueOrDefault(), plan); + ValidateMaxAutoscaleSmServiceAccountUpdate(organization, update.MaxAutoscaleSmServiceAccounts, plan); } await FinalizeSubscriptionAdjustmentAsync(organization, plan, update); @@ -299,14 +299,20 @@ private async Task ValidateSmServiceAccountsUpdateAsync(Organization organizatio } } - private void ValidateMaxAutoscaleSmSeatsUpdateAsync(Organization organization, int maxAutoscaleSeats, Plan plan) + private void ValidateMaxAutoscaleSmSeatsUpdateAsync(Organization organization, int? maxAutoscaleSeats, Plan plan) { - if (organization.SmSeats.HasValue && maxAutoscaleSeats < organization.SmSeats.Value) + if (!maxAutoscaleSeats.HasValue) + { + // autoscale limit has been turned off, no validation required + return; + } + + if (organization.SmSeats.HasValue && maxAutoscaleSeats.Value < organization.SmSeats.Value) { throw new BadRequestException($"Cannot set max Secrets Manager seat autoscaling below current Secrets Manager seat count."); } - if (plan.MaxUsers.HasValue && maxAutoscaleSeats > plan.MaxUsers) + if (plan.MaxUsers.HasValue && maxAutoscaleSeats.Value > plan.MaxUsers) { throw new BadRequestException(string.Concat( $"Your plan has a Secrets Manager seat limit of {plan.MaxUsers}, ", @@ -320,9 +326,15 @@ private void ValidateMaxAutoscaleSmSeatsUpdateAsync(Organization organization, i } } - private void ValidateMaxAutoscaleSmServiceAccountUpdate(Organization organization, int maxAutoscaleServiceAccounts, Plan plan) + private void ValidateMaxAutoscaleSmServiceAccountUpdate(Organization organization, int? maxAutoscaleServiceAccounts, Plan plan) { - if (organization.SmServiceAccounts.HasValue && maxAutoscaleServiceAccounts < organization.SmServiceAccounts.Value) + if (!maxAutoscaleServiceAccounts.HasValue) + { + // autoscale limit has been turned off, no validation required + return; + } + + if (organization.SmServiceAccounts.HasValue && maxAutoscaleServiceAccounts.Value < organization.SmServiceAccounts.Value) { throw new BadRequestException( $"Cannot set max Service Accounts autoscaling below current Service Accounts count."); @@ -333,7 +345,7 @@ private void ValidateMaxAutoscaleSmServiceAccountUpdate(Organization organizatio throw new BadRequestException("Your plan does not allow Service Accounts autoscaling."); } - if (plan.MaxServiceAccounts.HasValue && maxAutoscaleServiceAccounts > plan.MaxServiceAccounts) + if (plan.MaxServiceAccounts.HasValue && maxAutoscaleServiceAccounts.Value > plan.MaxServiceAccounts) { throw new BadRequestException(string.Concat( $"Your plan has a Service Accounts limit of {plan.MaxServiceAccounts}, ", From 3ea14a2205699e9454d86de36ef3cbe4dc1067b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Tue, 11 Jul 2023 14:34:09 +0100 Subject: [PATCH 21/41] [AC-1488] Changed SM Signup and Upgrade paths to set SmServiceAccounts to include the plan BaseServiceAccount (#3086) --- .../UpgradeOrganizationPlanCommand.cs | 2 +- .../Implementations/OrganizationService.cs | 2 +- ...eSecretsManagerSubscriptionCommandTests.cs | 45 ++++++++++--------- .../UpgradeOrganizationPlanCommandTests.cs | 28 +++++++++--- .../Services/OrganizationServiceTests.cs | 24 +++++++--- 5 files changed, 68 insertions(+), 33 deletions(-) diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs index 24a459ea3218..54ca15ba13fb 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs @@ -268,7 +268,7 @@ await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, organization.PrivateKey = upgrade.PrivateKey; organization.UsePasswordManager = true; organization.SmSeats = (short)(newSecretsManagerPlan.BaseSeats + upgrade.AdditionalSmSeats.GetValueOrDefault()); - organization.SmServiceAccounts = upgrade.AdditionalServiceAccounts.GetValueOrDefault(); + organization.SmServiceAccounts = newSecretsManagerPlan.BaseServiceAccount + upgrade.AdditionalServiceAccounts.GetValueOrDefault(); organization.UseSecretsManager = upgrade.UseSecretsManager; await _organizationService.ReplaceAndUpdateCacheAsync(organization); diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 98506d43de2d..a34a1c915cc0 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -447,7 +447,7 @@ public async Task> SignUpAsync(Organizatio Status = OrganizationStatusType.Created, UsePasswordManager = true, SmSeats = (short)(secretsManagerPlan.BaseSeats + signup.AdditionalSmSeats.GetValueOrDefault()), - SmServiceAccounts = signup.AdditionalServiceAccounts.GetValueOrDefault(), + SmServiceAccounts = secretsManagerPlan.BaseServiceAccount + signup.AdditionalServiceAccounts.GetValueOrDefault(), UseSecretsManager = signup.UseSecretsManager }; diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs index 5bd08a136c42..11990c1733b0 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs @@ -372,13 +372,19 @@ public async Task UpdateSecretsManagerSubscription_ValidInput_Passes( Guid organizationId, SutProvider sutProvider) { + const int organizationServiceAccounts = 200; + const int seatAdjustment = 5; + const int maxAutoscaleSeats = 15; + const int serviceAccountAdjustment = 100; + const int maxAutoScaleServiceAccounts = 300; + var organization = new Organization { Id = organizationId, UseSecretsManager = true, SmSeats = 10, - SmServiceAccounts = 200, MaxAutoscaleSmSeats = 20, + SmServiceAccounts = organizationServiceAccounts, MaxAutoscaleSmServiceAccounts = 350, PlanType = planType, GatewayCustomerId = "1", @@ -389,28 +395,23 @@ public async Task UpdateSecretsManagerSubscription_ValidInput_Passes( var organizationUpdate = new SecretsManagerSubscriptionUpdate { OrganizationId = organizationId, - MaxAutoscaleSmSeats = 15, - SmSeatsAdjustment = 5, - MaxAutoscaleSmServiceAccounts = 300, - SmServiceAccountsAdjustment = 100, - SmSeats = organization.SmSeats.GetValueOrDefault() + 5, - SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 5) - plan.BaseSeats, - SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 100, - SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 100) - (int)plan.BaseServiceAccount, - MaxAutoscaleSmSeatsChanged = 15 != organization.MaxAutoscaleSeats.GetValueOrDefault(), + SmSeatsAdjustment = seatAdjustment, + SmSeats = organization.SmSeats.GetValueOrDefault() + seatAdjustment, + SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + seatAdjustment) - plan.BaseSeats, + MaxAutoscaleSmSeats = maxAutoscaleSeats, + + SmServiceAccountsAdjustment = serviceAccountAdjustment, + SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + serviceAccountAdjustment, + SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + serviceAccountAdjustment) - (int)plan.BaseServiceAccount, + MaxAutoscaleSmServiceAccounts = maxAutoScaleServiceAccounts, + + MaxAutoscaleSmSeatsChanged = maxAutoscaleSeats != organization.MaxAutoscaleSeats.GetValueOrDefault(), MaxAutoscaleSmServiceAccountsChanged = 200 != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() }; sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); await sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate); - if (organizationUpdate.SmServiceAccountsAdjustment != 0) - { - await sutProvider.GetDependency().Received(1) - .AdjustSeatsAsync(organization, plan, organizationUpdate.SmSeatsExcludingBase); - - // TODO: call ReferenceEventService - see AC-1481 - } if (organizationUpdate.SmSeatsAdjustment != 0) { @@ -418,19 +419,21 @@ await sutProvider.GetDependency().Received(1) .AdjustServiceAccountsAsync(organization, plan, organizationUpdate.SmServiceAccountsExcludingBase); // TODO: call ReferenceEventService - see AC-1481 - } - if (organizationUpdate.SmSeatsAdjustment != 0) - { await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( Arg.Is(org => org.SmSeats == organizationUpdate.SmSeats)); } if (organizationUpdate.SmServiceAccountsAdjustment != 0) { + await sutProvider.GetDependency().Received(1) + .AdjustSeatsAsync(organization, plan, organizationUpdate.SmSeatsExcludingBase); + + // TODO: call ReferenceEventService - see AC-1481 + await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( Arg.Is(org => - org.SmServiceAccounts == organizationUpdate.SmServiceAccounts)); + org.SmServiceAccounts == (organizationServiceAccounts + organizationUpdate.SmServiceAccountsAdjustment))); } if (organizationUpdate.MaxAutoscaleSmSeats != organization.MaxAutoscaleSmSeats) diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs index e71e3052ff61..f9524b2d6bd8 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs @@ -5,6 +5,7 @@ using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Test.AutoFixture.OrganizationFixtures; +using Bit.Core.Utilities; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; @@ -96,16 +97,33 @@ public async Task UpgradePlan_Passes(Organization organization, OrganizationUpgr await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync(organization); } - [Theory] - [FreeOrganizationUpgradeCustomize, BitAutoData] - public async Task UpgradePlan_SM_Passes(Organization organization, OrganizationUpgrade upgrade, + [Theory, FreeOrganizationUpgradeCustomize] + [BitAutoData(PlanType.EnterpriseMonthly)] + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.TeamsMonthly)] + [BitAutoData(PlanType.TeamsAnnually)] + public async Task UpgradePlan_SM_Passes(PlanType planType, Organization organization, OrganizationUpgrade upgrade, SutProvider sutProvider) { + upgrade.Plan = planType; + + var passwordManagerPlan = StaticStore.GetPasswordManagerPlan(upgrade.Plan); + var secretsManagerPlan = StaticStore.GetSecretsManagerPlan(upgrade.Plan); + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + + upgrade.AdditionalSeats = 15; upgrade.AdditionalSmSeats = 10; - upgrade.AdditionalSeats = 10; + upgrade.AdditionalServiceAccounts = 20; + var result = await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade); - await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync(organization); + + await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( + Arg.Is(o => + o.Seats == passwordManagerPlan.BaseSeats + upgrade.AdditionalSeats + && o.SmSeats == secretsManagerPlan.BaseSeats + upgrade.AdditionalSmSeats + && o.SmServiceAccounts == secretsManagerPlan.BaseServiceAccount + upgrade.AdditionalServiceAccounts)); + Assert.True(result.Item1); Assert.NotNull(result.Item2); } diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index 7729d748cdef..699f0509d6a7 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -147,13 +147,21 @@ await sutProvider.GetDependency().Received(1) [Theory] - [BitAutoData] - public async Task SignUp_SM_Passes(OrganizationSignup signup, SutProvider sutProvider) + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.EnterpriseMonthly)] + [BitAutoData(PlanType.TeamsAnnually)] + [BitAutoData(PlanType.TeamsMonthly)] + public async Task SignUp_SM_Passes(PlanType planType, OrganizationSignup signup, SutProvider sutProvider) { - signup.AdditionalSmSeats = 10; - signup.AdditionalSeats = 10; - signup.Plan = PlanType.EnterpriseAnnually; + signup.Plan = planType; + + var passwordManagerPlan = StaticStore.GetPasswordManagerPlan(signup.Plan); + var secretsManagerPlan = StaticStore.GetSecretsManagerPlan(signup.Plan); + signup.UseSecretsManager = true; + signup.AdditionalSeats = 15; + signup.AdditionalSmSeats = 10; + signup.AdditionalServiceAccounts = 20; signup.PaymentMethodType = PaymentMethodType.Card; signup.PremiumAccessAddon = false; @@ -161,6 +169,12 @@ public async Task SignUp_SM_Passes(OrganizationSignup signup, SutProvider().Received(1).CreateAsync( + Arg.Is(o => + o.Seats == passwordManagerPlan.BaseSeats + signup.AdditionalSeats + && o.SmSeats == secretsManagerPlan.BaseSeats + signup.AdditionalSmSeats + && o.SmServiceAccounts == secretsManagerPlan.BaseServiceAccount + signup.AdditionalServiceAccounts)); + await sutProvider.GetDependency().Received(1) .RaiseEventAsync(Arg.Is(referenceEvent => referenceEvent.Type == ReferenceEventType.Signup && From 0ec3020ecc13d2a446357216172280296ec1824c Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Wed, 12 Jul 2023 00:36:30 +0100 Subject: [PATCH 22/41] [AC-1510] Enable access to Secrets Manager to Organization owner for new Subscription (#3089) --- src/Core/Services/Implementations/OrganizationService.cs | 1 + test/Core.Test/Services/OrganizationServiceTests.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index a34a1c915cc0..1e791214bdff 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -604,6 +604,7 @@ await _organizationApiKeyRepository.CreateAsync(new OrganizationApiKey OrganizationId = organization.Id, UserId = ownerId, Key = ownerKey, + AccessSecretsManager = organization.UseSecretsManager, Type = OrganizationUserType.Owner, Status = OrganizationUserStatusType.Confirmed, AccessAll = true, diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index 699f0509d6a7..aa71d210b62f 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -174,6 +174,8 @@ await sutProvider.GetDependency().Received(1).CreateAsy o.Seats == passwordManagerPlan.BaseSeats + signup.AdditionalSeats && o.SmSeats == secretsManagerPlan.BaseSeats + signup.AdditionalSmSeats && o.SmServiceAccounts == secretsManagerPlan.BaseServiceAccount + signup.AdditionalServiceAccounts)); + await sutProvider.GetDependency().Received(1).CreateAsync( + Arg.Is(o => o.AccessSecretsManager == signup.UseSecretsManager)); await sutProvider.GetDependency().Received(1) .RaiseEventAsync(Arg.Is(referenceEvent => From e1bede110851377046c52c30eaeeef5ebb1c6f03 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Thu, 13 Jul 2023 09:27:54 +1000 Subject: [PATCH 23/41] Revert changes to ReferenceEvent code (#3091) * Revert changes to ReferenceEvent code This will be done in AC-1481 --- .../UpgradeOrganizationPlanCommand.cs | 4 +--- src/Core/Services/Implementations/OrganizationService.cs | 4 +--- src/Core/Tools/Models/Business/ReferenceEvent.cs | 4 ---- test/Core.Test/Services/OrganizationServiceTests.cs | 4 +--- 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs index 54ca15ba13fb..c9fb75dc372f 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs @@ -284,9 +284,7 @@ await _referenceEventService.RaiseEventAsync( OldPlanType = existingPasswordManagerPlan.Type, Seats = organization.Seats, Storage = organization.MaxStorageGb, - SmSeats = organization.SmSeats, - ServiceAccounts = organization.SmServiceAccounts, - UseSecretsManager = organization.UseSecretsManager + // TODO: add reference events for SmSeats and Service Accounts - see AC-1481 }); } diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 1e791214bdff..3c3f5d709ca8 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -481,9 +481,7 @@ await _referenceEventService.RaiseEventAsync( PlanType = passwordManagerPlan.Type, Seats = returnValue.Item1.Seats, Storage = returnValue.Item1.MaxStorageGb, - SmSeats = returnValue.Item1.SmSeats, - ServiceAccounts = returnValue.Item1.SmServiceAccounts, - UseSecretsManager = returnValue.Item1.UseSecretsManager + // TODO: add reference events for SmSeats and Service Accounts - see AC-1481 }); return returnValue; } diff --git a/src/Core/Tools/Models/Business/ReferenceEvent.cs b/src/Core/Tools/Models/Business/ReferenceEvent.cs index b50609752fe8..393708668a96 100644 --- a/src/Core/Tools/Models/Business/ReferenceEvent.cs +++ b/src/Core/Tools/Models/Business/ReferenceEvent.cs @@ -52,7 +52,6 @@ public ReferenceEvent(ReferenceEventType type, IReferenceable source, ICurrentCo public int? Seats { get; set; } public int? PreviousSeats { get; set; } - public int? PreviousServiceAccounts { get; set; } public short? Storage { get; set; } @@ -71,7 +70,4 @@ public ReferenceEvent(ReferenceEventType type, IReferenceable source, ICurrentCo public string ClientId { get; set; } public Version ClientVersion { get; set; } - public int? SmSeats { get; set; } - public int? ServiceAccounts { get; set; } - public bool UseSecretsManager { get; set; } } diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index aa71d210b62f..b786fdb83e1f 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -183,10 +183,8 @@ await sutProvider.GetDependency().Received(1) referenceEvent.PlanName == purchaseOrganizationPlan[0].Name && referenceEvent.PlanType == purchaseOrganizationPlan[0].Type && referenceEvent.Seats == result.Item1.Seats && - referenceEvent.SmSeats == result.Item1.SmSeats && - referenceEvent.ServiceAccounts == result.Item1.SmServiceAccounts && - referenceEvent.UseSecretsManager == result.Item1.UseSecretsManager && referenceEvent.Storage == result.Item1.MaxStorageGb)); + // TODO: add reference events for SmSeats and Service Accounts - see AC-1481 Assert.NotNull(result); Assert.NotNull(result.Item1); From b14c383aa3da312804520c83a4a6241a5035dfef Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Thu, 13 Jul 2023 10:52:38 +1000 Subject: [PATCH 24/41] Revert ReferenceEventType change --- src/Core/Tools/Enums/ReferenceEventType.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Core/Tools/Enums/ReferenceEventType.cs b/src/Core/Tools/Enums/ReferenceEventType.cs index b91e83b6f12f..a8a52e63179f 100644 --- a/src/Core/Tools/Enums/ReferenceEventType.cs +++ b/src/Core/Tools/Enums/ReferenceEventType.cs @@ -44,8 +44,4 @@ public enum ReferenceEventType OrganizationCreatedByAdmin, [EnumMember(Value = "sm-service-account-accessed-secret")] SmServiceAccountAccessedSecret, - [EnumMember(Value = "adjust-service-accounts")] - AdjustServiceAccounts, - [EnumMember(Value = "adjust-sm-seats")] - AdjustSmSeats, } From aa5c2001e77a66f249cd444c9acc810ff5f4d809 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Thu, 13 Jul 2023 10:56:08 +1000 Subject: [PATCH 25/41] Move NoopServiceAccountRepository to SM and update namespace --- .../Repositories/Noop/NoopServiceAccountRepository.cs | 3 +-- src/SharedWeb/Utilities/ServiceCollectionExtensions.cs | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/Core/{ => SecretsManager}/Repositories/Noop/NoopServiceAccountRepository.cs (95%) diff --git a/src/Core/Repositories/Noop/NoopServiceAccountRepository.cs b/src/Core/SecretsManager/Repositories/Noop/NoopServiceAccountRepository.cs similarity index 95% rename from src/Core/Repositories/Noop/NoopServiceAccountRepository.cs rename to src/Core/SecretsManager/Repositories/Noop/NoopServiceAccountRepository.cs index 526ff1cb8821..eab0b069b9ea 100644 --- a/src/Core/Repositories/Noop/NoopServiceAccountRepository.cs +++ b/src/Core/SecretsManager/Repositories/Noop/NoopServiceAccountRepository.cs @@ -1,8 +1,7 @@ using Bit.Core.Enums; using Bit.Core.SecretsManager.Entities; -using Bit.Core.SecretsManager.Repositories; -namespace Bit.Core.Repositories.Noop; +namespace Bit.Core.SecretsManager.Repositories.Noop; public class NoopServiceAccountRepository : IServiceAccountRepository { diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index 355f4b46c4ca..97695d171f5a 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -19,6 +19,7 @@ using Bit.Core.Repositories; using Bit.Core.Resources; using Bit.Core.SecretsManager.Repositories; +using Bit.Core.SecretsManager.Repositories.Noop; using Bit.Core.Services; using Bit.Core.Settings; using Bit.Core.Tokens; @@ -330,7 +331,7 @@ public static void AddDefaultServices(this IServiceCollection services, GlobalSe public static void AddOosServices(this IServiceCollection services) { services.AddScoped(); - services.AddScoped(); + services.AddScoped(); } public static void AddNoopServices(this IServiceCollection services) From 0277a702e6bfc54df4ba3b366e64d8c9fbf1c4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Mon, 17 Jul 2023 08:06:56 +0100 Subject: [PATCH 26/41] [AC-1462] Add secrets manager service accounts autoscaling commands (#3059) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding the Secret manager to the Plan List * Adding the unit test for the StaticStoreTests class * Fix whitespace formatting * Fix whitespace formatting * Price update * Resolving the PR comments * Resolving PR comments * Fixing the whitespace * only password manager plans are return for now * format whitespace * Resolve the test issue * Fixing the failing test * Refactoring the Plan separation * add a unit test for SingleOrDefault * Fix the whitespace format * Separate the PM and SM plans * Fixing the whitespace * Remove unnecessary directive * Fix imports ordering * Fix imports ordering * Resolve imports ordering * Fixing imports ordering * Fix response model, add MaxProjects * Fix filename * Fix format * Fix: seat price should match annual/monthly * Fix service account annual pricing * Changes for secret manager signup and upgradeplan * Changes for secrets manager signup and upgrade * refactoring the code * Format whitespace * remove unnecessary using directive * Changes for subscription Update * Update the seatAdjustment and update * Resolve the PR comment on Subscription creation * Resolve PR comment * Add password manager to the error message * Add UseSecretsManager to the event log * Resolve PR comment on plan validation * Resolving pr comments for service account count * Resolving pr comments for service account count * Resolve the pr comments * Remove the store procedure that is no-longer needed * Add a new class for update subscription * Modify the Update subscription for sm * Add the missing property * Rename a property properly * Resolving the PR comment * Resolve PR comments * Resolving PR comments * Resolving the Pr comments * Resolving some PR comments * Resolving the PR comments * Resolving the build identity build * Add additional Validation * Resolve the Lint issues * remove unnecessary using directive * Remove the white spaces * Adding unit test for the stripe payment * Remove the incomplete test * Fixing the failing test * Fix the failing test * Fix the fail test on organization service * Fix the failing unit test * Fix the whitespace format * Fix the failing test * Fix the whitespace format * resolve pr comments * Fix the lint message * refactor the code * Fix the failing Test * adding a new endpoint * Remove the unwanted code * Changes for Command and Queries * changes for command and queries * Fix the Lint issues * Fix imports ordering * Resolve the PR comments * resolve pr comments * Resolve pr comments * Fix the failing test on adjustSeatscommandtests * Fix the failing test * Fix the whitespaces * resolve failing test * rename a property * Resolve the pr comments * refactoring the existing implementation * Resolve the whitespaces format issue * Resolve the pr comments * [AC-1462] Created IAvailableServiceAccountsQuery along its implementation and with unit tests * [AC-1462] Renamed ICountNewServiceAccountSlotsRequiredQuery * [AC-1462] Added IAutoscaleServiceAccountsCommand and implementation * Add more unit testing * fix the whitespaces issues * [AC-1462] Added unit tests for AutoscaleServiceAccountsCommand * Add more unit test * Remove unnecessary directive * Resolve some pr comments * Adding more unit test * adding more test * add more test * Resolving some pr comments * Resolving some pr comments * Resolving some pr comments * resolve some pr comments * Resolving pr comments * remove whitespaces * remove white spaces * Resolving pr comments * resolving pr comments and fixing white spaces * resolving the lint error * Run dotnet format * resolving the pr comments * Add a missing properties to plan response model * Add the email sender for sm seat and service acct * Add the email sender for sm seat and service acct * Fix the failing test after email sender changes * Add staticstorewrapper to properly test the plans * Add more test and validate the existing test * Fix the white spaces issues * Remove staticstorewrapper and fix the test * fix a null issue on autoscaling * Suggestion: do all seat calculations in update model * Resolve some pr comments * resolving some pr comments * Return value is unnecessary * Resolve the failing test * resolve pr comments * Resolve the pr comments * Resolving admin api failure and adding more test * Resolve the issue failing admin project * Fixing the failed test * Clarify naming and add comments * Clarify naming conventions * Dotnet format * Fix the failing dependency * remove similar test * [AC-1462] Rewrote AutoscaleServiceAccountsCommand to use UpdateSecretsManagerSubscriptionCommand which has the same logic * [AC-1462] Deleted IAutoscaleServiceAccountsCommand as the logic will be moved to UpdateSecretsManagerSubscriptionCommand * [AC-1462] Created method AdjustSecretsManagerServiceAccountsAsync * [AC-1462] Changed SecretsManagerSubscriptionUpdate to only be set by its constructor * [AC-1462] Added check to CountNewServiceAccountSlotsRequiredQuery and revised unit tests * [AC-1462] Revised logic for CountNewServiceAccountSlotsRequiredQuery and fixed unit tests * [AC-1462] Changed SecretsManagerSubscriptionUpdate to receive Organization as a parameter and fixed the unit tests * [AC-1462] Renamed IUpdateSecretsManagerSubscriptionCommand methods UpdateSubscriptionAsync and AdjustServiceAccountsAsync * [AC-1462] Rewrote unit test UpdateSubscriptionAsync_ValidInput_Passes * [AC-1462] Registered CountNewServiceAccountSlotsRequiredQuery for dependency injection * [AC-1462] Added parameter names to SecretsManagerSubscriptionUpdateRequestModel * [AC-1462] Updated SecretsManagerSubscriptionUpdate logic to handle null parameters. Revised the unit tests to test null values --------- Co-authored-by: cyprain-okeke Co-authored-by: Thomas Rittson Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> --- ...ountNewServiceAccountSlotsRequiredQuery.cs | 44 ++ .../SecretsManagerCollectionExtensions.cs | 3 + ...ewServiceAccountSlotsRequiredQueryTests.cs | 102 ++++ .../Controllers/OrganizationsController.cs | 10 +- ...tsManagerSubscriptionUpdateRequestModel.cs | 31 +- .../SecretsManagerSubscriptionUpdate.cs | 42 +- ...UpdateSecretsManagerSubscriptionCommand.cs | 6 +- ...UpdateSecretsManagerSubscriptionCommand.cs | 21 +- ...ountNewServiceAccountSlotsRequiredQuery.cs | 6 + ...eSecretsManagerSubscriptionCommandTests.cs | 477 +++++++----------- 10 files changed, 404 insertions(+), 338 deletions(-) create mode 100644 bitwarden_license/src/Commercial.Core/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQuery.cs create mode 100644 bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQueryTests.cs create mode 100644 src/Core/SecretsManager/Queries/ServiceAccounts/Interfaces/ICountNewServiceAccountSlotsRequiredQuery.cs diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQuery.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQuery.cs new file mode 100644 index 000000000000..f85006316bdb --- /dev/null +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQuery.cs @@ -0,0 +1,44 @@ +using Bit.Core.Exceptions; +using Bit.Core.Repositories; +using Bit.Core.SecretsManager.Queries.ServiceAccounts.Interfaces; +using Bit.Core.SecretsManager.Repositories; + +namespace Bit.Commercial.Core.SecretsManager.Queries.ServiceAccounts; + +public class CountNewServiceAccountSlotsRequiredQuery : ICountNewServiceAccountSlotsRequiredQuery +{ + private readonly IOrganizationRepository _organizationRepository; + private readonly IServiceAccountRepository _serviceAccountRepository; + + public CountNewServiceAccountSlotsRequiredQuery( + IOrganizationRepository organizationRepository, + IServiceAccountRepository serviceAccountRepository) + { + _organizationRepository = organizationRepository; + _serviceAccountRepository = serviceAccountRepository; + } + + public async Task CountNewServiceAccountSlotsRequiredAsync(Guid organizationId, int serviceAccountsToAdd) + { + var organization = await _organizationRepository.GetByIdAsync(organizationId); + if (organization == null || !organization.UseSecretsManager) + { + throw new NotFoundException(); + } + + if (!organization.SmServiceAccounts.HasValue || serviceAccountsToAdd == 0) + { + return 0; + } + + var serviceAccountCount = await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(organizationId); + var availableServiceAccountSlots = organization.SmServiceAccounts.Value - serviceAccountCount; + + if (availableServiceAccountSlots >= serviceAccountsToAdd) + { + return 0; + } + + return serviceAccountsToAdd - availableServiceAccountSlots; + } +} diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerCollectionExtensions.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerCollectionExtensions.cs index 614bd4610539..dca27b735550 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerCollectionExtensions.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerCollectionExtensions.cs @@ -10,6 +10,7 @@ using Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts; using Bit.Commercial.Core.SecretsManager.Commands.Trash; using Bit.Commercial.Core.SecretsManager.Queries; +using Bit.Commercial.Core.SecretsManager.Queries.ServiceAccounts; using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces; using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces; using Bit.Core.SecretsManager.Commands.Porting.Interfaces; @@ -18,6 +19,7 @@ using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces; using Bit.Core.SecretsManager.Commands.Trash.Interfaces; using Bit.Core.SecretsManager.Queries.Interfaces; +using Bit.Core.SecretsManager.Queries.ServiceAccounts.Interfaces; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; @@ -41,6 +43,7 @@ public static void AddSecretsManagerServices(this IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQueryTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQueryTests.cs new file mode 100644 index 000000000000..f3c64b3d07b3 --- /dev/null +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQueryTests.cs @@ -0,0 +1,102 @@ +using Bit.Commercial.Core.SecretsManager.Queries.ServiceAccounts; +using Bit.Core.Entities; +using Bit.Core.Exceptions; +using Bit.Core.Repositories; +using Bit.Core.SecretsManager.Repositories; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Commercial.Core.Test.SecretsManager.Queries.ServiceAccounts; + +[SutProviderCustomize] +public class CountNewServiceAccountSlotsRequiredQueryTests +{ + [Theory] + [BitAutoData(2, 5, 2, 0)] + [BitAutoData(0, 5, 2, 0)] + [BitAutoData(6, 5, 2, 3)] + [BitAutoData(2, 5, 10, 7)] + public async Task CountNewServiceAccountSlotsRequiredAsync_ReturnsCorrectCount( + int serviceAccountsToAdd, + int organizationSmServiceAccounts, + int currentServiceAccounts, + int expectedNewServiceAccountsRequired, + Organization organization, + SutProvider sutProvider) + { + organization.UseSecretsManager = true; + organization.SmServiceAccounts = organizationSmServiceAccounts; + + sutProvider.GetDependency() + .GetByIdAsync(organization.Id) + .Returns(organization); + + sutProvider.GetDependency() + .GetServiceAccountCountByOrganizationIdAsync(organization.Id) + .Returns(currentServiceAccounts); + + var result = await sutProvider.Sut.CountNewServiceAccountSlotsRequiredAsync(organization.Id, serviceAccountsToAdd); + + Assert.Equal(expectedNewServiceAccountsRequired, result); + + if (serviceAccountsToAdd > 0) + { + await sutProvider.GetDependency().Received(1) + .GetServiceAccountCountByOrganizationIdAsync(organization.Id); + } + } + + [Theory] + [BitAutoData(0)] + [BitAutoData(5)] + public async Task CountNewServiceAccountSlotsRequiredAsync_WithNullSmServiceAccounts_ReturnsZero( + int currentServiceAccounts, + int serviceAccountsToAdd, + Organization organization, + SutProvider sutProvider) + { + const int expectedRequiredServiceAccountsToScale = 0; + + organization.UseSecretsManager = true; + organization.SmServiceAccounts = null; + + sutProvider.GetDependency() + .GetByIdAsync(organization.Id) + .Returns(organization); + + sutProvider.GetDependency() + .GetServiceAccountCountByOrganizationIdAsync(organization.Id) + .Returns(currentServiceAccounts); + + var result = await sutProvider.Sut.CountNewServiceAccountSlotsRequiredAsync(organization.Id, serviceAccountsToAdd); + + Assert.Equal(expectedRequiredServiceAccountsToScale, result); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetServiceAccountCountByOrganizationIdAsync(default); + } + + [Theory, BitAutoData] + public async Task CountNewServiceAccountSlotsRequiredAsync_WithNonExistentOrganizationId_ThrowsNotFound( + Guid organizationId, int serviceAccountsToAdd, + SutProvider sutProvider) + { + await Assert.ThrowsAsync(async () => await sutProvider.Sut.CountNewServiceAccountSlotsRequiredAsync(organizationId, serviceAccountsToAdd)); + } + + [Theory, BitAutoData] + public async Task CountNewServiceAccountSlotsRequiredAsync_WithOrganizationUseSecretsManagerFalse_ThrowsNotFound( + Organization organization, int serviceAccountsToAdd, + SutProvider sutProvider) + { + organization.UseSecretsManager = false; + + sutProvider.GetDependency() + .GetByIdAsync(organization.Id) + .Returns(organization); + + await Assert.ThrowsAsync(async () => await sutProvider.Sut.CountNewServiceAccountSlotsRequiredAsync(organization.Id, serviceAccountsToAdd)); + } +} diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index 8ceb1f038b53..9a0a19438dca 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -344,14 +344,8 @@ public async Task PostSmSubscription(Guid id, [FromBody] SecretsManagerSubscript throw new NotFoundException(); } - var secretsManagerPlan = StaticStore.GetSecretsManagerPlan(organization.PlanType); - if (secretsManagerPlan == null) - { - throw new NotFoundException("Invalid Secrets Manager plan."); - } - - var organizationUpdate = model.ToSecretsManagerSubscriptionUpdate(organization, secretsManagerPlan); - await _updateSecretsManagerSubscriptionCommand.UpdateSecretsManagerSubscription(organizationUpdate); + var organizationUpdate = model.ToSecretsManagerSubscriptionUpdate(organization); + await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(organizationUpdate); } [HttpPost("{id}/seat")] diff --git a/src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs b/src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs index 31f071953a92..a8adbe92e5b3 100644 --- a/src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs +++ b/src/Api/Models/Request/Organizations/SecretsManagerSubscriptionUpdateRequestModel.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; using Bit.Core.Entities; using Bit.Core.Models.Business; -using Bit.Core.Models.StaticStore; namespace Bit.Api.Models.Request.Organizations; @@ -13,32 +12,12 @@ public class SecretsManagerSubscriptionUpdateRequestModel public int ServiceAccountAdjustment { get; set; } public int? MaxAutoscaleServiceAccounts { get; set; } - public virtual SecretsManagerSubscriptionUpdate ToSecretsManagerSubscriptionUpdate(Organization organization, Plan plan) + public virtual SecretsManagerSubscriptionUpdate ToSecretsManagerSubscriptionUpdate(Organization organization) { - var newTotalSeats = organization.SmSeats.GetValueOrDefault() + SeatAdjustment; - var newTotalServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + ServiceAccountAdjustment; - - var orgUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organization.Id, - - SmSeatsAdjustment = SeatAdjustment, - SmSeats = newTotalSeats, - SmSeatsExcludingBase = newTotalSeats - plan.BaseSeats, - - MaxAutoscaleSmSeats = MaxAutoscaleSeats, - - SmServiceAccountsAdjustment = ServiceAccountAdjustment, - SmServiceAccounts = newTotalServiceAccounts, - SmServiceAccountsExcludingBase = newTotalServiceAccounts - plan.BaseServiceAccount.GetValueOrDefault(), - - MaxAutoscaleSmServiceAccounts = MaxAutoscaleServiceAccounts, - - MaxAutoscaleSmSeatsChanged = - MaxAutoscaleSeats.GetValueOrDefault() != organization.MaxAutoscaleSmSeats.GetValueOrDefault(), - MaxAutoscaleSmServiceAccountsChanged = - MaxAutoscaleServiceAccounts.GetValueOrDefault() != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() - }; + var orgUpdate = new SecretsManagerSubscriptionUpdate( + organization, + seatAdjustment: SeatAdjustment, maxAutoscaleSeats: MaxAutoscaleSeats, + serviceAccountAdjustment: ServiceAccountAdjustment, maxAutoscaleServiceAccounts: MaxAutoscaleServiceAccounts); return orgUpdate; } diff --git a/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs b/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs index 051a7560dfac..f8b678d1b166 100644 --- a/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs +++ b/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs @@ -1,8 +1,11 @@ -namespace Bit.Core.Models.Business; +using Bit.Core.Entities; +using Bit.Core.Exceptions; + +namespace Bit.Core.Models.Business; public class SecretsManagerSubscriptionUpdate { - public Guid OrganizationId { get; set; } + public Organization Organization { get; set; } /// /// The seats to be added or removed from the organization @@ -53,4 +56,39 @@ public class SecretsManagerSubscriptionUpdate public bool SmServiceAccountsChanged => SmServiceAccountsAdjustment != 0; public bool MaxAutoscaleSmSeatsChanged { get; set; } public bool MaxAutoscaleSmServiceAccountsChanged { get; set; } + + public SecretsManagerSubscriptionUpdate( + Organization organization, + int seatAdjustment, int? maxAutoscaleSeats, + int serviceAccountAdjustment, int? maxAutoscaleServiceAccounts) + { + if (organization == null) + { + throw new NotFoundException("Organization is not found."); + } + + var secretsManagerPlan = Utilities.StaticStore.GetSecretsManagerPlan(organization.PlanType); + if (secretsManagerPlan == null) + { + throw new NotFoundException("Invalid Secrets Manager plan."); + } + + var newTotalSeats = organization.SmSeats.GetValueOrDefault() + seatAdjustment; + var newTotalServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + serviceAccountAdjustment; + + Organization = organization; + + SmSeats = newTotalSeats; + SmSeatsAdjustment = seatAdjustment; + SmSeatsExcludingBase = newTotalSeats - secretsManagerPlan.BaseSeats; + MaxAutoscaleSmSeats = maxAutoscaleSeats; + + SmServiceAccounts = newTotalServiceAccounts; + SmServiceAccountsAdjustment = serviceAccountAdjustment; + SmServiceAccountsExcludingBase = newTotalServiceAccounts - secretsManagerPlan.BaseServiceAccount.GetValueOrDefault(); + MaxAutoscaleSmServiceAccounts = maxAutoscaleServiceAccounts; + + MaxAutoscaleSmSeatsChanged = maxAutoscaleSeats != organization.MaxAutoscaleSmSeats; + MaxAutoscaleSmServiceAccountsChanged = maxAutoscaleServiceAccounts != organization.MaxAutoscaleSmServiceAccounts; + } } diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpdateSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpdateSecretsManagerSubscriptionCommand.cs index 78244d8e3ddc..6ed4007e5747 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpdateSecretsManagerSubscriptionCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpdateSecretsManagerSubscriptionCommand.cs @@ -1,8 +1,10 @@ -using Bit.Core.Models.Business; +using Bit.Core.Entities; +using Bit.Core.Models.Business; namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; public interface IUpdateSecretsManagerSubscriptionCommand { - Task UpdateSecretsManagerSubscription(SecretsManagerSubscriptionUpdate update); + Task UpdateSubscriptionAsync(SecretsManagerSubscriptionUpdate update); + Task AdjustServiceAccountsAsync(Organization organization, int smServiceAccountsAdjustment); } diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs index 87db6c83d834..8d6a668326a2 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs @@ -1,5 +1,4 @@ -#nullable enable -using Bit.Core.Entities; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Business; @@ -15,7 +14,6 @@ namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptions; public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubscriptionCommand { - private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IPaymentService _paymentService; private readonly IOrganizationService _organizationService; @@ -24,7 +22,6 @@ public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubs private readonly IServiceAccountRepository _serviceAccountRepository; public UpdateSecretsManagerSubscriptionCommand( - IOrganizationRepository organizationRepository, IOrganizationService organizationService, IOrganizationUserRepository organizationUserRepository, IPaymentService paymentService, @@ -32,7 +29,6 @@ public UpdateSecretsManagerSubscriptionCommand( ILogger logger, IServiceAccountRepository serviceAccountRepository) { - _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; _paymentService = paymentService; _organizationService = organizationService; @@ -41,9 +37,9 @@ public UpdateSecretsManagerSubscriptionCommand( _serviceAccountRepository = serviceAccountRepository; } - public async Task UpdateSecretsManagerSubscription(SecretsManagerSubscriptionUpdate update) + public async Task UpdateSubscriptionAsync(SecretsManagerSubscriptionUpdate update) { - var organization = await _organizationRepository.GetByIdAsync(update.OrganizationId); + var organization = update.Organization; ValidateOrganization(organization); @@ -74,6 +70,15 @@ public async Task UpdateSecretsManagerSubscription(SecretsManagerSubscriptionUpd await SendEmailIfAutoscaleLimitReached(organization); } + public async Task AdjustServiceAccountsAsync(Organization organization, int smServiceAccountsAdjustment) + { + var secretsManagerSubscriptionUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 0, maxAutoscaleSeats: organization?.MaxAutoscaleSmSeats, + serviceAccountAdjustment: smServiceAccountsAdjustment, maxAutoscaleServiceAccounts: organization?.MaxAutoscaleSmServiceAccounts); + + await UpdateSubscriptionAsync(secretsManagerSubscriptionUpdate); + } + private Plan GetPlanForOrganization(Organization organization) { var plan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); @@ -88,7 +93,7 @@ private static void ValidateOrganization(Organization organization) { if (organization == null) { - throw new NotFoundException("Organization is not found"); + throw new NotFoundException("Organization is not found."); } if (!organization.UseSecretsManager) diff --git a/src/Core/SecretsManager/Queries/ServiceAccounts/Interfaces/ICountNewServiceAccountSlotsRequiredQuery.cs b/src/Core/SecretsManager/Queries/ServiceAccounts/Interfaces/ICountNewServiceAccountSlotsRequiredQuery.cs new file mode 100644 index 000000000000..a19c77112d3e --- /dev/null +++ b/src/Core/SecretsManager/Queries/ServiceAccounts/Interfaces/ICountNewServiceAccountSlotsRequiredQuery.cs @@ -0,0 +1,6 @@ +namespace Bit.Core.SecretsManager.Queries.ServiceAccounts.Interfaces; + +public interface ICountNewServiceAccountSlotsRequiredQuery +{ + Task CountNewServiceAccountSlotsRequiredAsync(Guid organizationId, int serviceAccountsToAdd); +} diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs index 11990c1733b0..5c2643cc3009 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs @@ -20,36 +20,26 @@ public class UpdateSecretsManagerSubscriptionCommandTests { [Theory] [BitAutoData] - public async Task UpdateSecretsManagerSubscription_NoOrganization_Throws( - Guid organizationId, + public async Task UpdateSubscriptionAsync_NoOrganization_Throws( + SecretsManagerSubscriptionUpdate secretsManagerSubscriptionUpdate, SutProvider sutProvider) { - sutProvider.GetDependency() - .GetByIdAsync(organizationId) - .Returns((Organization)null); - - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = null, - SmSeatsAdjustment = 0 - }; + secretsManagerSubscriptionUpdate.Organization = null; var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + () => sutProvider.Sut.UpdateSubscriptionAsync(secretsManagerSubscriptionUpdate)); + Assert.Contains("Organization is not found", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } [Theory] [BitAutoData] - public async Task UpdateSecretsManagerSubscription_NoSecretsManagerAccess_ThrowsException( - Guid organizationId, + public async Task UpdateSubscriptionAsync_NoSecretsManagerAccess_ThrowsException( SutProvider sutProvider) { var organization = new Organization { - Id = organizationId, SmSeats = 10, SmServiceAccounts = 5, UseSecretsManager = false, @@ -57,26 +47,18 @@ public async Task UpdateSecretsManagerSubscription_NoSecretsManagerAccess_Throws MaxAutoscaleSmServiceAccounts = 10 }; - sutProvider.GetDependency() - .GetByIdAsync(organizationId) - .Returns(organization); - - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - SmSeatsAdjustment = 1, - MaxAutoscaleSmSeats = 1 - }; + var secretsManagerSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(organization, seatAdjustment: 0, maxAutoscaleSeats: null, serviceAccountAdjustment: 0, maxAutoscaleServiceAccounts: null); var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + () => sutProvider.Sut.UpdateSubscriptionAsync(secretsManagerSubscriptionUpdate)); + Assert.Contains("Organization has no access to Secrets Manager.", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } [Theory] [BitAutoData] - public async Task UpdateSecretsManagerSubscription_SeatsAdustmentGreaterThanMaxAutoscaleSeats_ThrowsException( + public async Task UpdateSubscriptionAsync_SeatsAdustmentGreaterThanMaxAutoscaleSeats_ThrowsException( Guid organizationId, SutProvider sutProvider) { @@ -90,28 +72,17 @@ public async Task UpdateSecretsManagerSubscription_SeatsAdustmentGreaterThanMaxA MaxAutoscaleSmServiceAccounts = 10, PlanType = PlanType.EnterpriseAnnually, }; - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = 10, - SmSeatsAdjustment = 15, - SmSeats = organization.SmSeats.GetValueOrDefault() + 10, - SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 10) - plan.BaseSeats, - SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 5, - SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 5) - (int)plan.BaseServiceAccount - }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 15, maxAutoscaleSeats: 10, serviceAccountAdjustment: 0, maxAutoscaleServiceAccounts: null); - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); - - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); Assert.Contains("Cannot set max seat autoscaling below seat count.", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } [Theory] [BitAutoData] - public async Task UpdateSecretsManagerSubscription_ServiceAccountsGreaterThanMaxAutoscaleSeats_ThrowsException( + public async Task UpdateSubscriptionAsync_ServiceAccountsGreaterThanMaxAutoscaleSeats_ThrowsException( Guid organizationId, SutProvider sutProvider) { @@ -127,30 +98,17 @@ public async Task UpdateSecretsManagerSubscription_ServiceAccountsGreaterThanMax GatewayCustomerId = "1", GatewaySubscriptionId = "9" }; - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = 15, - SmSeatsAdjustment = 1, - MaxAutoscaleSmServiceAccounts = 10, - SmServiceAccountsAdjustment = 11, - SmSeats = organization.SmSeats.GetValueOrDefault() + 1, - SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 1) - plan.BaseSeats, - SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 11, - SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 11) - (int)plan.BaseServiceAccount - }; - - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 1, maxAutoscaleSeats: 15, serviceAccountAdjustment: 11, maxAutoscaleServiceAccounts: 10); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); Assert.Contains("Cannot set max Service Accounts autoscaling below Service Accounts count", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } [Theory] [BitAutoData] - public async Task UpdateSecretsManagerSubscription_NullGatewayCustomerId_ThrowsException( + public async Task UpdateSubscriptionAsync_NullGatewayCustomerId_ThrowsException( Guid organizationId, SutProvider sutProvider) { @@ -164,25 +122,17 @@ public async Task UpdateSecretsManagerSubscription_NullGatewayCustomerId_ThrowsE MaxAutoscaleSmServiceAccounts = 15, PlanType = PlanType.EnterpriseAnnually }; - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = 15, - SmSeatsAdjustment = 1, - MaxAutoscaleSmServiceAccounts = 15, - SmServiceAccountsAdjustment = 1 - }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 1, maxAutoscaleSeats: 15, serviceAccountAdjustment: 1, maxAutoscaleServiceAccounts: 15); - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); - - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); Assert.Contains("No payment method found.", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } [Theory] [BitAutoData] - public async Task UpdateSecretsManagerSubscription_NullGatewaySubscriptionId_ThrowsException( + public async Task UpdateSubscriptionAsync_NullGatewaySubscriptionId_ThrowsException( Guid organizationId, SutProvider sutProvider) { @@ -197,25 +147,17 @@ public async Task UpdateSecretsManagerSubscription_NullGatewaySubscriptionId_Thr PlanType = PlanType.EnterpriseAnnually, GatewayCustomerId = "1" }; - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = 15, - SmSeatsAdjustment = 1, - MaxAutoscaleSmServiceAccounts = 15, - SmServiceAccountsAdjustment = 1 - }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 1, maxAutoscaleSeats: 15, serviceAccountAdjustment: 1, maxAutoscaleServiceAccounts: 15); - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); - - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); Assert.Contains("No subscription found.", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } [Theory] [BitAutoData] - public async Task UpdateSecretsManagerSubscription_OrgWithNullSmSeatOnSeatsAdjustment_ThrowsException( + public async Task UpdateSubscriptionAsync_OrgWithNullSmSeatOnSeatsAdjustment_ThrowsException( Guid organizationId, SutProvider sutProvider) { @@ -230,19 +172,11 @@ public async Task UpdateSecretsManagerSubscription_OrgWithNullSmSeatOnSeatsAdjus PlanType = PlanType.EnterpriseAnnually, GatewayCustomerId = "1" }; - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = 15, - SmSeatsAdjustment = 1, - MaxAutoscaleSmServiceAccounts = 15, - SmServiceAccountsAdjustment = 1 - }; - - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 1, maxAutoscaleSeats: 15, serviceAccountAdjustment: 1, maxAutoscaleServiceAccounts: 15); var exception = await Assert.ThrowsAsync( - () => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + () => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); Assert.Contains("Organization has no Secrets Manager seat limit, no need to adjust seats", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); @@ -256,12 +190,11 @@ public async Task UpdateSecretsManagerSubscription_OrgWithNullSmSeatOnSeatsAdjus [BitAutoData(PlanType.EnterpriseAnnually2019)] [BitAutoData(PlanType.TeamsMonthly2019)] [BitAutoData(PlanType.TeamsAnnually2019)] - public async Task UpdateSecretsManagerSubscription_WithNonSecretsManagerPlanType_ThrowsBadRequestException( + public async Task UpdateSubscriptionAsync_WithNonSecretsManagerPlanType_ThrowsBadRequestException( PlanType planType, Guid organizationId, SutProvider sutProvider) { - var organization = new Organization { Id = organizationId, @@ -270,34 +203,26 @@ public async Task UpdateSecretsManagerSubscription_WithNonSecretsManagerPlanType SmServiceAccounts = 200, MaxAutoscaleSmSeats = 20, MaxAutoscaleSmServiceAccounts = 300, - PlanType = planType, + PlanType = PlanType.EnterpriseAnnually, GatewayCustomerId = "1", GatewaySubscriptionId = "2" }; - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organization.Id, - MaxAutoscaleSmSeats = 15, - SmSeatsAdjustment = 1, - MaxAutoscaleSmServiceAccounts = 300, - SmServiceAccountsAdjustment = 1 - }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 1, maxAutoscaleSeats: 15, serviceAccountAdjustment: 1, maxAutoscaleServiceAccounts: 300); + organization.PlanType = planType; - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); - - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); Assert.Contains("Existing plan not found", exception.Message, StringComparison.InvariantCultureIgnoreCase); await VerifyDependencyNotCalledAsync(sutProvider); } [Theory] [BitAutoData(PlanType.Free)] - public async Task UpdateSecretsManagerSubscription_WithHasAdditionalSeatsOptionfalse_ThrowsBadRequestException( + public async Task UpdateSubscriptionAsync_WithHasAdditionalSeatsOptionfalse_ThrowsBadRequestException( PlanType planType, Guid organizationId, SutProvider sutProvider) { - var organization = new Organization { Id = organizationId, @@ -310,30 +235,21 @@ public async Task UpdateSecretsManagerSubscription_WithHasAdditionalSeatsOptionf GatewayCustomerId = "1", GatewaySubscriptionId = "2" }; - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organization.Id, - MaxAutoscaleSmSeats = 15, - SmSeatsAdjustment = 1, - MaxAutoscaleSmServiceAccounts = 300, - SmServiceAccountsAdjustment = 1 - }; - - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 1, maxAutoscaleSeats: 15, serviceAccountAdjustment: 1, maxAutoscaleServiceAccounts: 300); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); Assert.Contains("Plan does not allow additional Secrets Manager seats.", exception.Message, StringComparison.InvariantCultureIgnoreCase); await VerifyDependencyNotCalledAsync(sutProvider); } [Theory] [BitAutoData(PlanType.Free)] - public async Task UpdateSecretsManagerSubscription_WithHasAdditionalServiceAccountOptionFalse_ThrowsBadRequestException( + public async Task UpdateSubscriptionAsync_WithHasAdditionalServiceAccountOptionFalse_ThrowsBadRequestException( PlanType planType, Guid organizationId, SutProvider sutProvider) { - var organization = new Organization { Id = organizationId, @@ -346,18 +262,10 @@ public async Task UpdateSecretsManagerSubscription_WithHasAdditionalServiceAccou GatewayCustomerId = "1", GatewaySubscriptionId = "2" }; - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organization.Id, - MaxAutoscaleSmSeats = 15, - SmSeatsAdjustment = 0, - MaxAutoscaleSmServiceAccounts = 300, - SmServiceAccountsAdjustment = 1 - }; - - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 0, maxAutoscaleSeats: 15, serviceAccountAdjustment: 1, maxAutoscaleServiceAccounts: 300); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); Assert.Contains("Plan does not allow additional Service Accounts", exception.Message, StringComparison.InvariantCultureIgnoreCase); await VerifyDependencyNotCalledAsync(sutProvider); } @@ -367,7 +275,7 @@ public async Task UpdateSecretsManagerSubscription_WithHasAdditionalServiceAccou [BitAutoData(PlanType.EnterpriseMonthly)] [BitAutoData(PlanType.TeamsMonthly)] [BitAutoData(PlanType.TeamsAnnually)] - public async Task UpdateSecretsManagerSubscription_ValidInput_Passes( + public async Task UpdateSubscriptionAsync_ValidInput_Passes( PlanType planType, Guid organizationId, SutProvider sutProvider) @@ -392,71 +300,91 @@ public async Task UpdateSecretsManagerSubscription_ValidInput_Passes( }; var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - SmSeatsAdjustment = seatAdjustment, - SmSeats = organization.SmSeats.GetValueOrDefault() + seatAdjustment, - SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + seatAdjustment) - plan.BaseSeats, - MaxAutoscaleSmSeats = maxAutoscaleSeats, - - SmServiceAccountsAdjustment = serviceAccountAdjustment, - SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + serviceAccountAdjustment, - SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + serviceAccountAdjustment) - (int)plan.BaseServiceAccount, - MaxAutoscaleSmServiceAccounts = maxAutoScaleServiceAccounts, - - MaxAutoscaleSmSeatsChanged = maxAutoscaleSeats != organization.MaxAutoscaleSeats.GetValueOrDefault(), - MaxAutoscaleSmServiceAccountsChanged = 200 != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() - }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, + seatAdjustment: seatAdjustment, maxAutoscaleSeats: maxAutoscaleSeats, + serviceAccountAdjustment: serviceAccountAdjustment, maxAutoscaleServiceAccounts: maxAutoScaleServiceAccounts); - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + await sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate); - await sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate); + await sutProvider.GetDependency().Received(1) + .AdjustSeatsAsync(organization, plan, organizationUpdate.SmSeatsExcludingBase); + await sutProvider.GetDependency().Received(1) + .AdjustServiceAccountsAsync(organization, plan, organizationUpdate.SmServiceAccountsExcludingBase); - if (organizationUpdate.SmSeatsAdjustment != 0) - { - await sutProvider.GetDependency().Received(1) - .AdjustServiceAccountsAsync(organization, plan, organizationUpdate.SmServiceAccountsExcludingBase); + // TODO: call ReferenceEventService - see AC-1481 - // TODO: call ReferenceEventService - see AC-1481 + await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( + Arg.Is(org => + org.Id == organizationId + && org.SmSeats == organizationUpdate.SmSeats + && org.MaxAutoscaleSmSeats == organizationUpdate.MaxAutoscaleSmSeats + && org.SmServiceAccounts == (organizationServiceAccounts + organizationUpdate.SmServiceAccountsAdjustment) + && org.MaxAutoscaleSmServiceAccounts == organizationUpdate.MaxAutoscaleSmServiceAccounts)); - await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( - Arg.Is(org => org.SmSeats == organizationUpdate.SmSeats)); - } + await sutProvider.GetDependency().Received(1).SendSecretsManagerMaxSeatLimitReachedEmailAsync(organization, organization.MaxAutoscaleSmSeats.Value, Arg.Any>()); + await sutProvider.GetDependency().Received(1).SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(organization, organization.MaxAutoscaleSmServiceAccounts.Value, Arg.Any>()); + } - if (organizationUpdate.SmServiceAccountsAdjustment != 0) + [Theory] + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.EnterpriseMonthly)] + [BitAutoData(PlanType.TeamsMonthly)] + [BitAutoData(PlanType.TeamsAnnually)] + public async Task UpdateSubscriptionAsync_ValidInput_WithNullMaxAutoscale_Passes( + PlanType planType, + Guid organizationId, + SutProvider sutProvider) + { + int organizationServiceAccounts = 200; + int seatAdjustment = 5; + int? maxAutoscaleSeats = null; + int serviceAccountAdjustment = 100; + int? maxAutoScaleServiceAccounts = null; + + var organization = new Organization { - await sutProvider.GetDependency().Received(1) - .AdjustSeatsAsync(organization, plan, organizationUpdate.SmSeatsExcludingBase); + Id = organizationId, + UseSecretsManager = true, + SmSeats = 10, + MaxAutoscaleSmSeats = 20, + SmServiceAccounts = organizationServiceAccounts, + MaxAutoscaleSmServiceAccounts = 350, + PlanType = planType, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2" + }; - // TODO: call ReferenceEventService - see AC-1481 + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, + seatAdjustment: seatAdjustment, maxAutoscaleSeats: maxAutoscaleSeats, + serviceAccountAdjustment: serviceAccountAdjustment, maxAutoscaleServiceAccounts: maxAutoScaleServiceAccounts); - await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( - Arg.Is(org => - org.SmServiceAccounts == (organizationServiceAccounts + organizationUpdate.SmServiceAccountsAdjustment))); - } + await sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate); - if (organizationUpdate.MaxAutoscaleSmSeats != organization.MaxAutoscaleSmSeats) - { - await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( - Arg.Is(org => - org.MaxAutoscaleSmSeats == organizationUpdate.MaxAutoscaleSmServiceAccounts)); - } + await sutProvider.GetDependency().Received(1) + .AdjustSeatsAsync(organization, plan, organizationUpdate.SmSeatsExcludingBase); + await sutProvider.GetDependency().Received(1) + .AdjustServiceAccountsAsync(organization, plan, organizationUpdate.SmServiceAccountsExcludingBase); - if (organizationUpdate.MaxAutoscaleSmServiceAccounts != organization.MaxAutoscaleSmServiceAccounts) - { - await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( - Arg.Is(org => - org.MaxAutoscaleSmServiceAccounts == organizationUpdate.MaxAutoscaleSmServiceAccounts)); - } + // TODO: call ReferenceEventService - see AC-1481 - await sutProvider.GetDependency().Received(1).SendSecretsManagerMaxSeatLimitReachedEmailAsync(organization, organization.MaxAutoscaleSmSeats.Value, Arg.Any>()); - await sutProvider.GetDependency().Received(1).SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(organization, organization.MaxAutoscaleSmServiceAccounts.Value, Arg.Any>()); + await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( + Arg.Is(org => + org.Id == organizationId + && org.SmSeats == organizationUpdate.SmSeats + && org.MaxAutoscaleSmSeats == organizationUpdate.MaxAutoscaleSmSeats + && org.SmServiceAccounts == (organizationServiceAccounts + organizationUpdate.SmServiceAccountsAdjustment) + && org.MaxAutoscaleSmServiceAccounts == organizationUpdate.MaxAutoscaleSmServiceAccounts)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().SendSecretsManagerMaxSeatLimitReachedEmailAsync(default, default, default); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(default, default, default); } [Theory] [BitAutoData] - public async Task UpdateSecretsManagerSubscription_ThrowsBadRequestException_WhenMaxAutoscaleSeatsBelowSeatCount( + public async Task UpdateSubscriptionAsync_ThrowsBadRequestException_WhenMaxAutoscaleSeatsBelowSeatCount( Guid organizationId, SutProvider sutProvider) { @@ -472,30 +400,17 @@ public async Task UpdateSecretsManagerSubscription_ThrowsBadRequestException_Whe GatewayCustomerId = "1", GatewaySubscriptionId = "2" }; - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); - var update = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = 4, - SmSeatsAdjustment = 1, - MaxAutoscaleSmServiceAccounts = 300, - SmServiceAccountsAdjustment = 5, - SmSeats = organization.SmSeats.GetValueOrDefault() + 1, - SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 1) - plan.BaseSeats, - SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 5, - SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 5) - (int)plan.BaseServiceAccount - }; - - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + var update = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 1, maxAutoscaleSeats: 4, serviceAccountAdjustment: 5, maxAutoscaleServiceAccounts: 300); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(update)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); Assert.Contains("Cannot set max seat autoscaling below seat count.", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } [Theory] [BitAutoData] - public async Task UpdateSecretsManagerSubscription_ThrowsBadRequestException_WhenOccupiedSeatsExceedNewSeatTotal( + public async Task UpdateSubscriptionAsync_ThrowsBadRequestException_WhenOccupiedSeatsExceedNewSeatTotal( Guid organizationId, SutProvider sutProvider) { @@ -508,25 +423,12 @@ public async Task UpdateSecretsManagerSubscription_ThrowsBadRequestException_Whe GatewaySubscriptionId = "2", PlanType = PlanType.EnterpriseAnnually }; - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); - var update = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = 7, - SmSeatsAdjustment = -3, - MaxAutoscaleSmServiceAccounts = 300, - SmServiceAccountsAdjustment = 5, - SmSeats = organization.SmSeats.GetValueOrDefault() - 3, - SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() - 3) - plan.BaseSeats, - SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 5, - SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 5) - (int)plan.BaseServiceAccount - }; + var update = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: -3, maxAutoscaleSeats: 7, serviceAccountAdjustment: 5, maxAutoscaleServiceAccounts: 300); - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); sutProvider.GetDependency().GetOccupiedSmSeatCountByOrganizationIdAsync(organizationId).Returns(8); - - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(update)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); Assert.Contains("Your organization currently has 8 Secrets Manager seats. Your plan only allows 7 Secrets Manager seats. Remove some Secrets Manager users", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } @@ -547,23 +449,10 @@ public async Task AdjustServiceAccountsAsync_ThrowsBadRequestException_WhenSmSer SmServiceAccounts = null, PlanType = PlanType.EnterpriseAnnually }; - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); - var update = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = 21, - SmSeatsAdjustment = 10, - MaxAutoscaleSmServiceAccounts = 250, - SmServiceAccountsAdjustment = 1, - SmSeats = organization.SmSeats.GetValueOrDefault() + 10, - SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 10) - plan.BaseSeats, - SmServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + 1, - SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 1) - (int)plan.BaseServiceAccount - }; - - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + var update = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 10, maxAutoscaleSeats: 21, serviceAccountAdjustment: 1, maxAutoscaleServiceAccounts: 250); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(update)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); Assert.Contains("Organization has no Service Accounts limit, no need to adjust Service Accounts", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } @@ -585,20 +474,10 @@ public async Task AutoscaleSeatsAsync_ThrowsBadRequestException_WhenMaxAutoscale GatewaySubscriptionId = "2", }; - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = 15, - SmSeatsAdjustment = 0, - MaxAutoscaleSmServiceAccounts = 200, - SmServiceAccountsAdjustment = 0, - MaxAutoscaleSmSeatsChanged = 15 != organization.MaxAutoscaleSeats.GetValueOrDefault(), - MaxAutoscaleSmServiceAccountsChanged = 200 != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() - }; - - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 0, maxAutoscaleSeats: 15, serviceAccountAdjustment: 0, maxAutoscaleServiceAccounts: 200); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); Assert.Contains("Your plan has a Secrets Manager seat limit of 2, but you have specified a max autoscale count of 15.Reduce your max autoscale count.", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } @@ -623,20 +502,10 @@ public async Task AutoscaleSeatsAsync_ThrowsBadRequestException_WhenPlanDoesNotA GatewaySubscriptionId = "2" }; - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = 1, - SmSeatsAdjustment = 0, - MaxAutoscaleSmServiceAccounts = 300, - SmServiceAccountsAdjustment = 0, - MaxAutoscaleSmSeatsChanged = 1 != organization.MaxAutoscaleSeats.GetValueOrDefault(), - MaxAutoscaleSmServiceAccountsChanged = 200 != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() - }; - - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 0, maxAutoscaleSeats: 1, serviceAccountAdjustment: 0, maxAutoscaleServiceAccounts: 300); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); Assert.Contains("Your plan does not allow Secrets Manager seat autoscaling", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); @@ -662,20 +531,10 @@ public async Task UpdateServiceAccountAutoscaling_ThrowsBadRequestException_When GatewaySubscriptionId = "2" }; - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = null, - SmSeatsAdjustment = 0, - MaxAutoscaleSmServiceAccounts = 300, - SmServiceAccountsAdjustment = 0, - MaxAutoscaleSmSeatsChanged = false, - MaxAutoscaleSmServiceAccountsChanged = 300 != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() - }; - - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 0, maxAutoscaleSeats: null, serviceAccountAdjustment: 0, maxAutoscaleServiceAccounts: 300); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); Assert.Contains("Your plan does not allow Service Accounts autoscaling.", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); @@ -696,42 +555,76 @@ public async Task UpdateServiceAccountAutoscaling_WhenCurrentServiceAccountsIsGr Id = organizationId, UseSecretsManager = true, SmSeats = 10, - SmServiceAccounts = 301, MaxAutoscaleSmSeats = 20, + SmServiceAccounts = 301, MaxAutoscaleSmServiceAccounts = 350, PlanType = planType, GatewayCustomerId = "1", GatewaySubscriptionId = "2" }; - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); - var organizationUpdate = new SecretsManagerSubscriptionUpdate - { - OrganizationId = organizationId, - MaxAutoscaleSmSeats = 15, - SmSeatsAdjustment = 5, - MaxAutoscaleSmServiceAccounts = 300, - SmServiceAccountsAdjustment = 100, - SmSeats = organization.SmSeats.GetValueOrDefault() + 5, - SmSeatsExcludingBase = (organization.SmSeats.GetValueOrDefault() + 5) - plan.BaseSeats, - SmServiceAccounts = 300, - SmServiceAccountsExcludingBase = (organization.SmServiceAccounts.GetValueOrDefault() + 100) - (int)plan.BaseServiceAccount, - MaxAutoscaleSmSeatsChanged = 15 != organization.MaxAutoscaleSeats.GetValueOrDefault(), - MaxAutoscaleSmServiceAccountsChanged = 200 != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault() - }; + var organizationUpdate = new SecretsManagerSubscriptionUpdate( + organization, seatAdjustment: 5, maxAutoscaleSeats: 15, serviceAccountAdjustment: -100, maxAutoscaleServiceAccounts: 300); var currentServiceAccounts = 301; - sutProvider.GetDependency().GetByIdAsync(organizationId).Returns(organization); sutProvider.GetDependency() .GetServiceAccountCountByOrganizationIdAsync(organization.Id) .Returns(currentServiceAccounts); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSecretsManagerSubscription(organizationUpdate)); - Assert.Contains("Your organization currently has 301 Service Accounts. Your plan only allows 300 Service Accounts. Remove some Service Accounts", exception.Message); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); + Assert.Contains("Your organization currently has 301 Service Accounts. Your plan only allows 201 Service Accounts. Remove some Service Accounts", exception.Message); await sutProvider.GetDependency().Received(1).GetServiceAccountCountByOrganizationIdAsync(organization.Id); await VerifyDependencyNotCalledAsync(sutProvider); } + [Theory] + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.EnterpriseMonthly)] + [BitAutoData(PlanType.TeamsMonthly)] + [BitAutoData(PlanType.TeamsAnnually)] + public async Task AdjustServiceAccountsAsync_WithEnterpriseOrTeamsPlans_Success(PlanType planType, Guid organizationId, + SutProvider sutProvider) + { + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == planType); + + var organizationSeats = plan.BaseSeats + 10; + var organizationMaxAutoscaleSeats = 20; + var organizationServiceAccounts = plan.BaseServiceAccount.GetValueOrDefault() + 10; + var organizationMaxAutoscaleServiceAccounts = 300; + + var organization = new Organization + { + Id = organizationId, + PlanType = planType, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2", + UseSecretsManager = true, + SmSeats = organizationSeats, + MaxAutoscaleSmSeats = organizationMaxAutoscaleSeats, + SmServiceAccounts = organizationServiceAccounts, + MaxAutoscaleSmServiceAccounts = organizationMaxAutoscaleServiceAccounts + }; + + var smServiceAccountsAdjustment = 10; + var expectedSmServiceAccounts = organizationServiceAccounts + smServiceAccountsAdjustment; + var expectedSmServiceAccountsExcludingBase = expectedSmServiceAccounts - plan.BaseServiceAccount.GetValueOrDefault(); + + await sutProvider.Sut.AdjustServiceAccountsAsync(organization, smServiceAccountsAdjustment); + + await sutProvider.GetDependency().Received(1).AdjustServiceAccountsAsync( + Arg.Is(o => o.Id == organizationId), + plan, + expectedSmServiceAccountsExcludingBase); + // TODO: call ReferenceEventService - see AC-1481 + await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( + Arg.Is(o => + o.Id == organizationId + && o.SmSeats == organizationSeats + && o.MaxAutoscaleSmSeats == organizationMaxAutoscaleSeats + && o.SmServiceAccounts == expectedSmServiceAccounts + && o.MaxAutoscaleSmServiceAccounts == organizationMaxAutoscaleServiceAccounts)); + } + private static async Task VerifyDependencyNotCalledAsync(SutProvider sutProvider) { await sutProvider.GetDependency().DidNotReceive() From e3387c2e2b58f698e5647e72506c8f25ec8add3b Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Tue, 18 Jul 2023 18:53:11 +1000 Subject: [PATCH 27/41] Add UsePasswordManager to sync data (#3114) --- src/Api/Models/Response/ProfileOrganizationResponseModel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Api/Models/Response/ProfileOrganizationResponseModel.cs b/src/Api/Models/Response/ProfileOrganizationResponseModel.cs index 4cfe368397b3..a0ededa0b6b1 100644 --- a/src/Api/Models/Response/ProfileOrganizationResponseModel.cs +++ b/src/Api/Models/Response/ProfileOrganizationResponseModel.cs @@ -29,6 +29,7 @@ public ProfileOrganizationResponseModel(OrganizationUserOrganizationDetails orga UseApi = organization.UseApi; UseResetPassword = organization.UseResetPassword; UseSecretsManager = organization.UseSecretsManager; + UsePasswordManager = organization.UsePasswordManager; UsersGetPremium = organization.UsersGetPremium; UseCustomPermissions = organization.UseCustomPermissions; UseActivateAutofillPolicy = organization.PlanType == PlanType.EnterpriseAnnually || @@ -82,6 +83,7 @@ public ProfileOrganizationResponseModel(OrganizationUserOrganizationDetails orga public bool UseApi { get; set; } public bool UseResetPassword { get; set; } public bool UseSecretsManager { get; set; } + public bool UsePasswordManager { get; set; } public bool UsersGetPremium { get; set; } public bool UseCustomPermissions { get; set; } public bool UseActivateAutofillPolicy { get; set; } From 1b6898e1880de20ff95a051fb370ae085237121c Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Wed, 19 Jul 2023 07:36:39 +1000 Subject: [PATCH 28/41] [AC-1522] Fix service account check on upgrading (#3111) --- .../UpgradeOrganizationPlanCommand.cs | 26 ++++----- .../UpgradeOrganizationPlanCommandTests.cs | 54 +++++++++++++++++++ 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs index c9fb75dc372f..ee0d0bbc2adf 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs @@ -307,23 +307,25 @@ private async Task ValidateSecretsManagerSeatsAndServiceAccountAsync(Organizatio { throw new BadRequestException( $"Your organization currently has {occupiedSmSeats} Secrets Manager seats filled. " + - $"Your new plan only has ({newPlanSmSeats}) seats. Remove some users."); + $"Your new plan only has {newPlanSmSeats} seats. Remove some users or increase your subscription."); } } - if (newSecretsManagerPlan.BaseServiceAccount != null) + var additionalServiceAccounts = newSecretsManagerPlan.HasAdditionalServiceAccountOption + ? upgrade.AdditionalServiceAccounts + : 0; + var newPlanServiceAccounts = newSecretsManagerPlan.BaseServiceAccount + additionalServiceAccounts; + + if (!organization.SmServiceAccounts.HasValue || organization.SmServiceAccounts.Value > newPlanServiceAccounts) { - if (!organization.SmServiceAccounts.HasValue || - organization.SmServiceAccounts.Value > newSecretsManagerPlan.MaxServiceAccounts) + var currentServiceAccounts = + await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(organization.Id); + if (currentServiceAccounts > newPlanServiceAccounts) { - var currentServiceAccounts = - await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(organization.Id); - if (currentServiceAccounts > newSecretsManagerPlan.MaxServiceAccounts) - { - throw new BadRequestException( - $"Your organization currently has {currentServiceAccounts} service account seats filled. " + - $"Your new plan only has ({newSecretsManagerPlan.MaxServiceAccounts}) service accounts. Remove some service accounts."); - } + throw new BadRequestException( + $"Your organization currently has {currentServiceAccounts} service accounts. " + + $"Your new plan only allows {newSecretsManagerPlan.MaxServiceAccounts} service accounts. " + + "Remove some service accounts or increase your subscription."); } } } diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs index f9524b2d6bd8..f18a8b5de95c 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs @@ -3,6 +3,7 @@ using Bit.Core.Models.Business; using Bit.Core.OrganizationFeatures.OrganizationSubscriptions; using Bit.Core.Repositories; +using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; using Bit.Core.Test.AutoFixture.OrganizationFixtures; using Bit.Core.Utilities; @@ -128,4 +129,57 @@ await sutProvider.GetDependency().Received(1).ReplaceAndUp Assert.NotNull(result.Item2); } + + [Theory, FreeOrganizationUpgradeCustomize] + [BitAutoData(PlanType.EnterpriseMonthly)] + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.TeamsMonthly)] + [BitAutoData(PlanType.TeamsAnnually)] + public async Task UpgradePlan_SM_NotEnoughSmSeats_Throws(PlanType planType, Organization organization, OrganizationUpgrade upgrade, + SutProvider sutProvider) + { + upgrade.Plan = planType; + upgrade.AdditionalSeats = 15; + upgrade.AdditionalSmSeats = 1; + upgrade.AdditionalServiceAccounts = 0; + + organization.SmSeats = 2; + + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + sutProvider.GetDependency() + .GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id).Returns(2); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); + Assert.Contains("Your organization currently has 2 Secrets Manager seats filled. Your new plan only has", exception.Message); + + sutProvider.GetDependency().DidNotReceiveWithAnyArgs().ReplaceAndUpdateCacheAsync(default); + } + + [Theory, FreeOrganizationUpgradeCustomize] + [BitAutoData(PlanType.EnterpriseMonthly, 201)] + [BitAutoData(PlanType.EnterpriseAnnually, 201)] + [BitAutoData(PlanType.TeamsMonthly, 51)] + [BitAutoData(PlanType.TeamsAnnually, 51)] + public async Task UpgradePlan_SM_NotEnoughServiceAccounts_Throws(PlanType planType, int currentServiceAccounts, + Organization organization, OrganizationUpgrade upgrade, SutProvider sutProvider) + { + upgrade.Plan = planType; + upgrade.AdditionalSeats = 15; + upgrade.AdditionalSmSeats = 1; + upgrade.AdditionalServiceAccounts = 0; + + organization.SmSeats = 1; + organization.SmServiceAccounts = currentServiceAccounts; + + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + sutProvider.GetDependency() + .GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id).Returns(1); + sutProvider.GetDependency() + .GetServiceAccountCountByOrganizationIdAsync(organization.Id).Returns(currentServiceAccounts); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade)); + Assert.Contains($"Your organization currently has {currentServiceAccounts} service accounts. Your new plan only allows", exception.Message); + + sutProvider.GetDependency().DidNotReceiveWithAnyArgs().ReplaceAndUpdateCacheAsync(default); + } } From 1087f66197318f785df0548de9b5b0d6cbf5f246 Mon Sep 17 00:00:00 2001 From: cyprain-okeke Date: Wed, 19 Jul 2023 16:58:30 +0100 Subject: [PATCH 29/41] Resolved the checkmarx issues --- .../Handlebars/OrganizationSmSeatsMaxReached.html.hbs | 2 +- .../Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs b/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs index 8eee60b2d10e..4c81bb1a0df3 100644 --- a/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs +++ b/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs @@ -24,7 +24,7 @@ - + Manage subscription
diff --git a/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs b/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs index 43712bc3e3bc..caf3fb0a6eae 100644 --- a/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs +++ b/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs @@ -24,7 +24,7 @@ - + Manage subscription
From c72d29b8b516ce5604d449c508e6db8fea3cad40 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Thu, 20 Jul 2023 08:40:29 +1000 Subject: [PATCH 30/41] [AC-1521] Address checkmarx security feedback (#3124) * Reinstate target attribute but add noopener noreferrer --- .../Handlebars/OrganizationSmSeatsMaxReached.html.hbs | 2 +- .../Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs b/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs index 4c81bb1a0df3..a6db21effc93 100644 --- a/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs +++ b/src/Core/MailTemplates/Handlebars/OrganizationSmSeatsMaxReached.html.hbs @@ -24,7 +24,7 @@ - + Manage subscription
diff --git a/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs b/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs index caf3fb0a6eae..6376d72826a3 100644 --- a/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs +++ b/src/Core/MailTemplates/Handlebars/OrganizationSmServiceAccountsMaxReached.html.hbs @@ -24,7 +24,7 @@ - + Manage subscription
From 2c0ff0960797df9229168ed35a29b880108cc3ad Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Mon, 24 Jul 2023 14:24:14 +1000 Subject: [PATCH 31/41] Update date on migration script --- ...ql => 2023-07-24_00_OrgUserReadOccupiedSmSeatCountByOrgId.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename util/Migrator/DbScripts/{2023-06-08_00_OrgUserReadOccupiedSmSeatCountByOrgId.sql => 2023-07-24_00_OrgUserReadOccupiedSmSeatCountByOrgId.sql} (100%) diff --git a/util/Migrator/DbScripts/2023-06-08_00_OrgUserReadOccupiedSmSeatCountByOrgId.sql b/util/Migrator/DbScripts/2023-07-24_00_OrgUserReadOccupiedSmSeatCountByOrgId.sql similarity index 100% rename from util/Migrator/DbScripts/2023-06-08_00_OrgUserReadOccupiedSmSeatCountByOrgId.sql rename to util/Migrator/DbScripts/2023-07-24_00_OrgUserReadOccupiedSmSeatCountByOrgId.sql From 4fcb9da4d62af815c01579ab265d0ce11b47a9bb Mon Sep 17 00:00:00 2001 From: cyprain-okeke Date: Mon, 24 Jul 2023 17:13:58 +0100 Subject: [PATCH 32/41] Remove unused constant --- src/Core/Constants.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 5cd0349291b4..8ff378d00cb7 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -36,7 +36,6 @@ public static class FeatureFlagKeys public const string DisplayEuEnvironment = "display-eu-environment"; public const string DisplayLowKdfIterationWarning = "display-kdf-iteration-warning"; public const string TrustedDeviceEncryption = "trusted-device-encryption"; - public const string SecretsManagerBilling = "sm-ga-billing"; public static List GetAllKeys() { From fd4e3065a25f58b6c58eb051667fd1c15192be1c Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Tue, 25 Jul 2023 06:58:45 +1000 Subject: [PATCH 33/41] Revert "Remove unused constant" This reverts commit 4fcb9da4d62af815c01579ab265d0ce11b47a9bb. This is required to make feature flags work on the client --- src/Core/Constants.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 8ff378d00cb7..5cd0349291b4 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -36,6 +36,7 @@ public static class FeatureFlagKeys public const string DisplayEuEnvironment = "display-eu-environment"; public const string DisplayLowKdfIterationWarning = "display-kdf-iteration-warning"; public const string TrustedDeviceEncryption = "trusted-device-encryption"; + public const string SecretsManagerBilling = "sm-ga-billing"; public static List GetAllKeys() { From 7858d59eee36018db41b999785c893df7a833d1a Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Tue, 25 Jul 2023 23:34:50 +0100 Subject: [PATCH 34/41] [AC-1458] Add Endpoint And Service Logic for secrets manager to existing subscription (#3087) --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Co-authored-by: Thomas Rittson --- .../Controllers/OrganizationsController.cs | 30 +++- .../SecretsManagerSubscribeRequestModel.cs | 14 ++ .../Models/Business/SubscriptionUpdate.cs | 75 ++++++++- .../AddSecretsManagerSubscriptionCommand.cs | 76 +++++++++ .../IAddSecretsManagerSubscriptionCommand.cs | 11 ++ ...SubscriptionServiceCollectionExtensions.cs | 1 + src/Core/Services/IPaymentService.cs | 2 + .../Implementations/StripePaymentService.cs | 6 + .../OrganizationsControllerTests.cs | 4 +- ...dSecretsManagerSubscriptionCommandTests.cs | 107 +++++++++++++ .../Services/OrganizationServiceTests.cs | 150 ++++++++++++++++++ 11 files changed, 473 insertions(+), 3 deletions(-) create mode 100644 src/Api/Models/Request/Organizations/SecretsManagerSubscribeRequestModel.cs create mode 100644 src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs create mode 100644 src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IAddSecretsManagerSubscriptionCommand.cs create mode 100644 test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index 9a0a19438dca..5a075c1ed9f3 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -53,6 +53,7 @@ public class OrganizationsController : Controller private readonly ILicensingService _licensingService; private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; private readonly IUpgradeOrganizationPlanCommand _upgradeOrganizationPlanCommand; + private readonly IAddSecretsManagerSubscriptionCommand _addSecretsManagerSubscriptionCommand; public OrganizationsController( IOrganizationRepository organizationRepository, @@ -75,7 +76,8 @@ public OrganizationsController( GlobalSettings globalSettings, ILicensingService licensingService, IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand, - IUpgradeOrganizationPlanCommand upgradeOrganizationPlanCommand) + IUpgradeOrganizationPlanCommand upgradeOrganizationPlanCommand, + IAddSecretsManagerSubscriptionCommand addSecretsManagerSubscriptionCommand) { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; @@ -98,6 +100,7 @@ public OrganizationsController( _licensingService = licensingService; _updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand; _upgradeOrganizationPlanCommand = upgradeOrganizationPlanCommand; + _addSecretsManagerSubscriptionCommand = addSecretsManagerSubscriptionCommand; } [HttpGet("{id}")] @@ -348,6 +351,31 @@ public async Task PostSmSubscription(Guid id, [FromBody] SecretsManagerSubscript await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(organizationUpdate); } + [HttpPost("{id}/subscribe-secrets-manager")] + [SelfHosted(NotSelfHostedOnly = true)] + public async Task PostSubscribeSecretsManagerAsync(Guid id, [FromBody] SecretsManagerSubscribeRequestModel model) + { + var organization = await _organizationRepository.GetByIdAsync(id); + if (organization == null) + { + throw new NotFoundException(); + } + + if (!await _currentContext.EditSubscription(id)) + { + throw new NotFoundException(); + } + + await _addSecretsManagerSubscriptionCommand.SignUpAsync(organization, model.AdditionalSmSeats, + model.AdditionalServiceAccounts); + + var userId = _userService.GetProperUserId(User).Value; + var organizationDetails = await _organizationUserRepository.GetDetailsByUserAsync(userId, organization.Id, + OrganizationUserStatusType.Confirmed); + + return new ProfileOrganizationResponseModel(organizationDetails); + } + [HttpPost("{id}/seat")] [SelfHosted(NotSelfHostedOnly = true)] public async Task PostSeat(string id, [FromBody] OrganizationSeatRequestModel model) diff --git a/src/Api/Models/Request/Organizations/SecretsManagerSubscribeRequestModel.cs b/src/Api/Models/Request/Organizations/SecretsManagerSubscribeRequestModel.cs new file mode 100644 index 000000000000..5297e11c161f --- /dev/null +++ b/src/Api/Models/Request/Organizations/SecretsManagerSubscribeRequestModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace Bit.Api.Models.Request.Organizations; + +public class SecretsManagerSubscribeRequestModel +{ + [Required] + [Range(0, int.MaxValue)] + public int AdditionalSmSeats { get; set; } + + [Required] + [Range(0, int.MaxValue)] + public int AdditionalServiceAccounts { get; set; } +} diff --git a/src/Core/Models/Business/SubscriptionUpdate.cs b/src/Core/Models/Business/SubscriptionUpdate.cs index 1a93bbfa3cbf..93122b3cf201 100644 --- a/src/Core/Models/Business/SubscriptionUpdate.cs +++ b/src/Core/Models/Business/SubscriptionUpdate.cs @@ -30,7 +30,6 @@ protected static SubscriptionItem SubscriptionItem(Subscription subscription, st planId == null ? null : subscription.Items?.Data?.FirstOrDefault(i => i.Plan.Id == planId); } - public class SeatSubscriptionUpdate : SubscriptionUpdate { private readonly int _previousSeats; @@ -262,3 +261,77 @@ private Stripe.SubscriptionItem AddStripeItem(Subscription subscription) => SubscriptionItem(subscription, _existingPlanStripeId); } + +public class SecretsManagerSubscribeUpdate : SubscriptionUpdate +{ + private readonly StaticStore.Plan _plan; + private readonly long? _additionalSeats; + private readonly long? _additionalServiceAccounts; + private readonly int _previousSeats; + private readonly int _previousServiceAccounts; + protected override List PlanIds => new() { _plan.StripeSeatPlanId, _plan.StripeServiceAccountPlanId }; + public SecretsManagerSubscribeUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats, long? additionalServiceAccounts) + { + _plan = plan; + _additionalSeats = additionalSeats; + _additionalServiceAccounts = additionalServiceAccounts; + _previousSeats = organization.SmSeats.GetValueOrDefault(); + _previousServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault(); + } + + public override List RevertItemsOptions(Subscription subscription) + { + var updatedItems = new List(); + + RemovePreviousSecretsManagerItems(updatedItems); + + return updatedItems; + } + + public override List UpgradeItemsOptions(Subscription subscription) + { + var updatedItems = new List(); + + AddNewSecretsManagerItems(updatedItems); + + return updatedItems; + } + + private void AddNewSecretsManagerItems(List updatedItems) + { + if (_additionalSeats > 0) + { + updatedItems.Add(new SubscriptionItemOptions + { + Price = _plan.StripeSeatPlanId, + Quantity = _additionalSeats + }); + } + + if (_additionalServiceAccounts > 0) + { + updatedItems.Add(new SubscriptionItemOptions + { + Price = _plan.StripeServiceAccountPlanId, + Quantity = _additionalServiceAccounts + }); + } + } + + private void RemovePreviousSecretsManagerItems(List updatedItems) + { + updatedItems.Add(new SubscriptionItemOptions + { + Price = _plan.StripeSeatPlanId, + Quantity = _previousSeats, + Deleted = _previousSeats == 0 ? true : (bool?)null, + }); + + updatedItems.Add(new SubscriptionItemOptions + { + Price = _plan.StripeServiceAccountPlanId, + Quantity = _previousServiceAccounts, + Deleted = _previousServiceAccounts == 0 ? true : (bool?)null, + }); + } +} diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs new file mode 100644 index 000000000000..6b514089e14c --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs @@ -0,0 +1,76 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.Models.Business; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; +using Bit.Core.Services; +using Bit.Core.Utilities; + +namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptions; + +public class AddSecretsManagerSubscriptionCommand : IAddSecretsManagerSubscriptionCommand +{ + private readonly IPaymentService _paymentService; + private readonly IOrganizationService _organizationService; + public AddSecretsManagerSubscriptionCommand( + IPaymentService paymentService, + IOrganizationService organizationService) + { + _paymentService = paymentService; + _organizationService = organizationService; + } + public async Task SignUpAsync(Organization organization, int additionalSmSeats, + int additionalServiceAccounts) + { + ValidateOrganization(organization); + + var plan = StaticStore.GetSecretsManagerPlan(organization.PlanType); + var signup = SetOrganizationUpgrade(organization, additionalSmSeats, additionalServiceAccounts); + _organizationService.ValidateSecretsManagerPlan(plan, signup); + + if (plan.Product != ProductType.Free) + { + await _paymentService.AddSecretsManagerToSubscription(organization, plan, additionalSmSeats, additionalServiceAccounts); + } + + organization.SmSeats = plan.BaseSeats + additionalSmSeats; + organization.SmServiceAccounts = plan.BaseServiceAccount.GetValueOrDefault() + additionalServiceAccounts; + organization.UseSecretsManager = true; + + await _organizationService.ReplaceAndUpdateCacheAsync(organization); + + // TODO: call ReferenceEventService - see AC-1481 + } + + private static OrganizationUpgrade SetOrganizationUpgrade(Organization organization, int additionalSeats, + int additionalServiceAccounts) + { + var signup = new OrganizationUpgrade + { + UseSecretsManager = true, + AdditionalSmSeats = additionalSeats, + AdditionalServiceAccounts = additionalServiceAccounts, + AdditionalSeats = organization.Seats.GetValueOrDefault() + }; + return signup; + } + + private static void ValidateOrganization(Organization organization) + { + if (organization == null) + { + throw new NotFoundException(); + } + + var plan = StaticStore.GetSecretsManagerPlan(organization.PlanType); + if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId) && plan.Product != ProductType.Free) + { + throw new BadRequestException("No payment method found."); + } + + if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId) && plan.Product != ProductType.Free) + { + throw new BadRequestException("No subscription found."); + } + } +} diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IAddSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IAddSecretsManagerSubscriptionCommand.cs new file mode 100644 index 000000000000..79adc740a5aa --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IAddSecretsManagerSubscriptionCommand.cs @@ -0,0 +1,11 @@ +using Bit.Core.Entities; + +namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; + +/// +/// This is only for adding SM to an existing organization +/// +public interface IAddSecretsManagerSubscriptionCommand +{ + Task SignUpAsync(Organization organization, int additionalSmSeats, int additionalServiceAccounts); +} diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs index 1b8d67211bdf..39029c34984a 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs @@ -9,5 +9,6 @@ public static void AddOrganizationSubscriptionServices(this IServiceCollection s { services.AddScoped(); services.AddScoped(); + services.AddScoped(); } } diff --git a/src/Core/Services/IPaymentService.cs b/src/Core/Services/IPaymentService.cs index d636fa22653a..642a423dc47c 100644 --- a/src/Core/Services/IPaymentService.cs +++ b/src/Core/Services/IPaymentService.cs @@ -37,4 +37,6 @@ Task UpdatePaymentMethodAsync(ISubscriber subscriber, PaymentMethodType pa Task CreateTaxRateAsync(TaxRate taxRate); Task UpdateTaxRateAsync(TaxRate taxRate); Task ArchiveTaxRateAsync(TaxRate taxRate); + Task AddSecretsManagerToSubscription(Organization org, Plan plan, int additionalSmSeats, + int additionalServiceAccount, DateTime? prorationDate = null); } diff --git a/src/Core/Services/Implementations/StripePaymentService.cs b/src/Core/Services/Implementations/StripePaymentService.cs index 190666b1ea8c..4d2eb4ef855b 100644 --- a/src/Core/Services/Implementations/StripePaymentService.cs +++ b/src/Core/Services/Implementations/StripePaymentService.cs @@ -1710,6 +1710,12 @@ public async Task ArchiveTaxRateAsync(TaxRate taxRate) } } + public async Task AddSecretsManagerToSubscription(Organization org, StaticStore.Plan plan, int additionalSmSeats, + int additionalServiceAccount, DateTime? prorationDate = null) + { + return await FinalizeSubscriptionChangeAsync(org, new SecretsManagerSubscribeUpdate(org, plan, additionalSmSeats, additionalServiceAccount), prorationDate); + } + private Stripe.PaymentMethod GetLatestCardPaymentMethod(string customerId) { var cardPaymentMethods = _stripeAdapter.PaymentMethodListAutoPaging( diff --git a/test/Api.Test/Controllers/OrganizationsControllerTests.cs b/test/Api.Test/Controllers/OrganizationsControllerTests.cs index bfed60a9a462..8f8f9b20d935 100644 --- a/test/Api.Test/Controllers/OrganizationsControllerTests.cs +++ b/test/Api.Test/Controllers/OrganizationsControllerTests.cs @@ -43,6 +43,7 @@ public class OrganizationsControllerTests : IDisposable private readonly ILicensingService _licensingService; private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; private readonly IUpgradeOrganizationPlanCommand _upgradeOrganizationPlanCommand; + private readonly IAddSecretsManagerSubscriptionCommand _addSecretsManagerSubscriptionCommand; private readonly OrganizationsController _sut; @@ -69,13 +70,14 @@ public OrganizationsControllerTests() _licensingService = Substitute.For(); _updateSecretsManagerSubscriptionCommand = Substitute.For(); _upgradeOrganizationPlanCommand = Substitute.For(); + _addSecretsManagerSubscriptionCommand = Substitute.For(); _sut = new OrganizationsController(_organizationRepository, _organizationUserRepository, _policyRepository, _providerRepository, _organizationService, _userService, _paymentService, _currentContext, _ssoConfigRepository, _ssoConfigService, _getOrganizationApiKeyQuery, _rotateOrganizationApiKeyCommand, _createOrganizationApiKeyCommand, _organizationApiKeyRepository, _updateOrganizationLicenseCommand, _cloudGetOrganizationLicenseQuery, _featureService, _globalSettings, _licensingService, - _updateSecretsManagerSubscriptionCommand, _upgradeOrganizationPlanCommand); + _updateSecretsManagerSubscriptionCommand, _upgradeOrganizationPlanCommand, _addSecretsManagerSubscriptionCommand); } public void Dispose() diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs new file mode 100644 index 000000000000..9d635a5afc1e --- /dev/null +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs @@ -0,0 +1,107 @@ +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.OrganizationFeatures.OrganizationSubscriptions; +using Bit.Core.Services; +using Bit.Core.Utilities; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.OrganizationFeatures.OrganizationSubscriptionUpdate; +[SutProviderCustomize] +public class AddSecretsManagerSubscriptionCommandTests +{ + [Theory] + [BitAutoData(PlanType.TeamsAnnually)] + [BitAutoData(PlanType.TeamsMonthly)] + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.EnterpriseMonthly)] + public async Task SignUpAsync_ReturnsSuccessAndClientSecret_WhenOrganizationAndPlanExist(PlanType planType, + SutProvider sutProvider, + int additionalServiceAccounts, + int additionalSmSeats, + Organization organization, + bool useSecretsManager) + { + organization.PlanType = planType; + + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); + + await sutProvider.Sut.SignUpAsync(organization, additionalSmSeats, additionalServiceAccounts); + + sutProvider.GetDependency().Received(1) + .ValidateSecretsManagerPlan(plan, Arg.Is(c => + c.UseSecretsManager == useSecretsManager && + c.AdditionalSmSeats == additionalSmSeats && + c.AdditionalServiceAccounts == additionalServiceAccounts && + c.AdditionalSeats == organization.Seats.GetValueOrDefault())); + + await sutProvider.GetDependency().Received() + .AddSecretsManagerToSubscription(organization, plan, additionalSmSeats, additionalServiceAccounts); + + // TODO: call ReferenceEventService - see AC-1481 + + sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync(Arg.Is(c => + c.SmSeats == plan.BaseSeats + additionalSmSeats && + c.SmServiceAccounts == plan.BaseServiceAccount.GetValueOrDefault() + additionalServiceAccounts && + c.UseSecretsManager == true)); + } + + [Theory] + [BitAutoData] + public async Task SignUpAsync_ThrowsNotFoundException_WhenOrganizationIsNull( + SutProvider sutProvider, + int additionalServiceAccounts, + int additionalSmSeats) + { + await Assert.ThrowsAsync(() => + sutProvider.Sut.SignUpAsync(null, additionalSmSeats, additionalServiceAccounts)); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData] + public async Task SignUpAsync_ThrowsGatewayException_WhenGatewayCustomerIdIsNullOrWhitespace( + SutProvider sutProvider, + Organization organization, + int additionalServiceAccounts, + int additionalSmSeats) + { + organization.GatewayCustomerId = null; + organization.PlanType = PlanType.EnterpriseAnnually; + var exception = await Assert.ThrowsAsync(() => + sutProvider.Sut.SignUpAsync(organization, additionalSmSeats, additionalServiceAccounts)); + Assert.Contains("No payment method found.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData] + public async Task SignUpAsync_ThrowsGatewayException_WhenGatewaySubscriptionIdIsNullOrWhitespace( + SutProvider sutProvider, + Organization organization, + int additionalServiceAccounts, + int additionalSmSeats) + { + organization.GatewaySubscriptionId = null; + organization.PlanType = PlanType.EnterpriseAnnually; + var exception = await Assert.ThrowsAsync(() => + sutProvider.Sut.SignUpAsync(organization, additionalSmSeats, additionalServiceAccounts)); + Assert.Contains("No subscription found.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + private static async Task VerifyDependencyNotCalledAsync(SutProvider sutProvider) + { + await sutProvider.GetDependency().DidNotReceive() + .AddSecretsManagerToSubscription(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); + + // TODO: call ReferenceEventService - see AC-1481 + + await sutProvider.GetDependency().DidNotReceive().ReplaceAndUpdateCacheAsync(Arg.Any()); + } +} diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index b786fdb83e1f..4be161924789 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -1534,4 +1534,154 @@ public async Task HasConfirmedOwnersExcept_WithConfirmedProviderUser_IncludeProv Assert.Equal(includeProvider, result); } + [Theory] + [BitAutoData(PlanType.EnterpriseAnnually2019)] + public void ValidateSecretsManagerPlan_ThrowsException_WhenInvalidPlanSelected( + PlanType planType, SutProvider sutProvider) + { + var plan = StaticStore.Plans.FirstOrDefault(x => x.Type == planType); + + var signup = new OrganizationUpgrade + { + UseSecretsManager = true, + AdditionalSmSeats = 1, + AdditionalServiceAccounts = 10, + AdditionalSeats = 1 + }; + + var exception = Assert.Throws(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup)); + Assert.Contains("Invalid Secrets Manager plan selected.", exception.Message); + } + + [Theory] + [BitAutoData(PlanType.TeamsAnnually)] + [BitAutoData(PlanType.TeamsMonthly)] + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.EnterpriseMonthly)] + public void ValidateSecretsManagerPlan_ThrowsException_WhenNoSecretsManagerSeats(PlanType planType, SutProvider sutProvider) + { + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var signup = new OrganizationUpgrade + { + UseSecretsManager = true, + AdditionalSmSeats = 0, + AdditionalServiceAccounts = 5, + AdditionalSeats = 2 + }; + + var exception = Assert.Throws(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup)); + Assert.Contains("You do not have any Secrets Manager seats!", exception.Message); + } + + [Theory] + [BitAutoData(PlanType.Free)] + public void ValidateSecretsManagerPlan_ThrowsException_WhenSubtractingSeats(PlanType planType, SutProvider sutProvider) + { + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var signup = new OrganizationUpgrade + { + UseSecretsManager = true, + AdditionalSmSeats = -1, + AdditionalServiceAccounts = 5 + }; + var exception = Assert.Throws(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup)); + Assert.Contains("You can't subtract Secrets Manager seats!", exception.Message); + } + + [Theory] + [BitAutoData(PlanType.Free)] + public void ValidateSecretsManagerPlan_ThrowsException_WhenPlanDoesNotAllowAdditionalServiceAccounts( + PlanType planType, + SutProvider sutProvider) + { + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var signup = new OrganizationUpgrade + { + UseSecretsManager = true, + AdditionalSmSeats = 2, + AdditionalServiceAccounts = 5, + AdditionalSeats = 3 + }; + var exception = Assert.Throws(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup)); + Assert.Contains("Plan does not allow additional Service Accounts.", exception.Message); + } + + [Theory] + [BitAutoData(PlanType.TeamsAnnually)] + [BitAutoData(PlanType.TeamsMonthly)] + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.EnterpriseMonthly)] + public void ValidateSecretsManagerPlan_ThrowsException_WhenMoreSeatsThanPasswordManagerSeats(PlanType planType, SutProvider sutProvider) + { + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var signup = new OrganizationUpgrade + { + UseSecretsManager = true, + AdditionalSmSeats = 4, + AdditionalServiceAccounts = 5, + AdditionalSeats = 3 + }; + var exception = Assert.Throws(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup)); + Assert.Contains("You cannot have more Secrets Manager seats than Password Manager seats.", exception.Message); + } + + [Theory] + [BitAutoData(PlanType.TeamsAnnually)] + [BitAutoData(PlanType.TeamsMonthly)] + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.EnterpriseMonthly)] + public void ValidateSecretsManagerPlan_ThrowsException_WhenSubtractingServiceAccounts( + PlanType planType, + SutProvider sutProvider) + { + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var signup = new OrganizationUpgrade + { + UseSecretsManager = true, + AdditionalSmSeats = 4, + AdditionalServiceAccounts = -5, + AdditionalSeats = 5 + }; + var exception = Assert.Throws(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup)); + Assert.Contains("You can't subtract Service Accounts!", exception.Message); + } + + [Theory] + [BitAutoData(PlanType.Free)] + public void ValidateSecretsManagerPlan_ThrowsException_WhenPlanDoesNotAllowAdditionalUsers( + PlanType planType, + SutProvider sutProvider) + { + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var signup = new OrganizationUpgrade + { + UseSecretsManager = true, + AdditionalSmSeats = 2, + AdditionalServiceAccounts = 0, + AdditionalSeats = 5 + }; + var exception = Assert.Throws(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup)); + Assert.Contains("Plan does not allow additional users.", exception.Message); + } + + [Theory] + [BitAutoData(PlanType.TeamsAnnually)] + [BitAutoData(PlanType.TeamsMonthly)] + [BitAutoData(PlanType.EnterpriseAnnually)] + [BitAutoData(PlanType.EnterpriseMonthly)] + public void ValidateSecretsManagerPlan_ValidPlan_NoExceptionThrown( + PlanType planType, + SutProvider sutProvider) + { + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var signup = new OrganizationUpgrade + { + UseSecretsManager = true, + AdditionalSmSeats = 2, + AdditionalServiceAccounts = 0, + AdditionalSeats = 4 + }; + + sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup); + } } From 2dd00139198a4789c5d7dfdc7a0c6408cb259bfb Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Thu, 27 Jul 2023 10:54:00 +1000 Subject: [PATCH 35/41] Remove duplicate migrations from incorrectly resolved merge --- ...2023-07-03_00_SecretsManagerBetaColumn.sql | 305 --- ..._SecretsManagerBetaColumnDataMigration.sql | 16 - ...14621_SecretsManagerBetaColumn.Designer.cs | 2228 ---------------- ...20230710114621_SecretsManagerBetaColumn.cs | 25 - ...14625_SecretsManagerBetaColumn.Designer.cs | 2239 ----------------- ...20230710114625_SecretsManagerBetaColumn.cs | 25 - ...14629_SecretsManagerBetaColumn.Designer.cs | 2226 ---------------- ...20230710114629_SecretsManagerBetaColumn.cs | 25 - 8 files changed, 7089 deletions(-) delete mode 100644 util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql delete mode 100644 util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql delete mode 100644 util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.Designer.cs delete mode 100644 util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.cs delete mode 100644 util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.Designer.cs delete mode 100644 util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.cs delete mode 100644 util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.Designer.cs delete mode 100644 util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.cs diff --git a/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql b/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql deleted file mode 100644 index b1aee80fd14f..000000000000 --- a/util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql +++ /dev/null @@ -1,305 +0,0 @@ --- Add SecretsManagerBeta Column -IF COL_LENGTH('[dbo].[Organization]', 'SecretsManagerBeta') IS NULL -BEGIN -ALTER TABLE - [dbo].[Organization] - ADD [SecretsManagerBeta] BIT NOT NULL - CONSTRAINT [DF_Organization_SecretsManagerBeta] - DEFAULT (0); -END -GO - --- Insert value in column 'SecretsManagerBeta' in Organization_Create -CREATE or ALTER PROCEDURE [dbo].[Organization_Create] - @Id UNIQUEIDENTIFIER OUTPUT, - @Identifier NVARCHAR(50), - @Name NVARCHAR(50), - @BusinessName NVARCHAR(50), - @BusinessAddress1 NVARCHAR(50), - @BusinessAddress2 NVARCHAR(50), - @BusinessAddress3 NVARCHAR(50), - @BusinessCountry VARCHAR(2), - @BusinessTaxNumber NVARCHAR(30), - @BillingEmail NVARCHAR(256), - @Plan NVARCHAR(50), - @PlanType TINYINT, - @Seats INT, - @MaxCollections SMALLINT, - @UsePolicies BIT, - @UseSso BIT, - @UseGroups BIT, - @UseDirectory BIT, - @UseEvents BIT, - @UseTotp BIT, - @Use2fa BIT, - @UseApi BIT, - @UseResetPassword BIT, - @SelfHost BIT, - @UsersGetPremium BIT, - @Storage BIGINT, - @MaxStorageGb SMALLINT, - @Gateway TINYINT, - @GatewayCustomerId VARCHAR(50), - @GatewaySubscriptionId VARCHAR(50), - @ReferenceData VARCHAR(MAX), - @Enabled BIT, - @LicenseKey VARCHAR(100), - @PublicKey VARCHAR(MAX), - @PrivateKey VARCHAR(MAX), - @TwoFactorProviders NVARCHAR(MAX), - @ExpirationDate DATETIME2(7), - @CreationDate DATETIME2(7), - @RevisionDate DATETIME2(7), - @OwnersNotifiedOfAutoscaling DATETIME2(7), - @MaxAutoscaleSeats INT, - @UseKeyConnector BIT = 0, - @UseScim BIT = 0, - @UseCustomPermissions BIT = 0, - @UseSecretsManager BIT = 0, - @Status TINYINT = 0, - @UsePasswordManager BIT = 1, - @SmSeats INT = null, - @SmServiceAccounts INT = null, - @MaxAutoscaleSmSeats INT= null, - @MaxAutoscaleSmServiceAccounts INT = null, - @SecretsManagerBeta BIT = 0 -AS -BEGIN - SET NOCOUNT ON - - INSERT INTO [dbo].[Organization] - ( - [Id], - [Identifier], - [Name], - [BusinessName], - [BusinessAddress1], - [BusinessAddress2], - [BusinessAddress3], - [BusinessCountry], - [BusinessTaxNumber], - [BillingEmail], - [Plan], - [PlanType], - [Seats], - [MaxCollections], - [UsePolicies], - [UseSso], - [UseGroups], - [UseDirectory], - [UseEvents], - [UseTotp], - [Use2fa], - [UseApi], - [UseResetPassword], - [SelfHost], - [UsersGetPremium], - [Storage], - [MaxStorageGb], - [Gateway], - [GatewayCustomerId], - [GatewaySubscriptionId], - [ReferenceData], - [Enabled], - [LicenseKey], - [PublicKey], - [PrivateKey], - [TwoFactorProviders], - [ExpirationDate], - [CreationDate], - [RevisionDate], - [OwnersNotifiedOfAutoscaling], - [MaxAutoscaleSeats], - [UseKeyConnector], - [UseScim], - [UseCustomPermissions], - [UseSecretsManager], - [Status], - [UsePasswordManager], - [SmSeats], - [SmServiceAccounts], - [MaxAutoscaleSmSeats], - [MaxAutoscaleSmServiceAccounts], - [SecretsManagerBeta] - ) - VALUES - ( - @Id, - @Identifier, - @Name, - @BusinessName, - @BusinessAddress1, - @BusinessAddress2, - @BusinessAddress3, - @BusinessCountry, - @BusinessTaxNumber, - @BillingEmail, - @Plan, - @PlanType, - @Seats, - @MaxCollections, - @UsePolicies, - @UseSso, - @UseGroups, - @UseDirectory, - @UseEvents, - @UseTotp, - @Use2fa, - @UseApi, - @UseResetPassword, - @SelfHost, - @UsersGetPremium, - @Storage, - @MaxStorageGb, - @Gateway, - @GatewayCustomerId, - @GatewaySubscriptionId, - @ReferenceData, - @Enabled, - @LicenseKey, - @PublicKey, - @PrivateKey, - @TwoFactorProviders, - @ExpirationDate, - @CreationDate, - @RevisionDate, - @OwnersNotifiedOfAutoscaling, - @MaxAutoscaleSeats, - @UseKeyConnector, - @UseScim, - @UseCustomPermissions, - @UseSecretsManager, - @Status, - @UsePasswordManager, - @SmSeats, - @SmServiceAccounts, - @MaxAutoscaleSmSeats, - @MaxAutoscaleSmServiceAccounts, - @SecretsManagerBeta - ) -END -GO - --- Insert value in column `SecretsManagerBeta` in Organization_Update -CREATE OR ALTER PROCEDURE [dbo].[Organization_Update] - @Id UNIQUEIDENTIFIER, - @Identifier NVARCHAR(50), - @Name NVARCHAR(50), - @BusinessName NVARCHAR(50), - @BusinessAddress1 NVARCHAR(50), - @BusinessAddress2 NVARCHAR(50), - @BusinessAddress3 NVARCHAR(50), - @BusinessCountry VARCHAR(2), - @BusinessTaxNumber NVARCHAR(30), - @BillingEmail NVARCHAR(256), - @Plan NVARCHAR(50), - @PlanType TINYINT, - @Seats INT, - @MaxCollections SMALLINT, - @UsePolicies BIT, - @UseSso BIT, - @UseGroups BIT, - @UseDirectory BIT, - @UseEvents BIT, - @UseTotp BIT, - @Use2fa BIT, - @UseApi BIT, - @UseResetPassword BIT, - @SelfHost BIT, - @UsersGetPremium BIT, - @Storage BIGINT, - @MaxStorageGb SMALLINT, - @Gateway TINYINT, - @GatewayCustomerId VARCHAR(50), - @GatewaySubscriptionId VARCHAR(50), - @ReferenceData VARCHAR(MAX), - @Enabled BIT, - @LicenseKey VARCHAR(100), - @PublicKey VARCHAR(MAX), - @PrivateKey VARCHAR(MAX), - @TwoFactorProviders NVARCHAR(MAX), - @ExpirationDate DATETIME2(7), - @CreationDate DATETIME2(7), - @RevisionDate DATETIME2(7), - @OwnersNotifiedOfAutoscaling DATETIME2(7), - @MaxAutoscaleSeats INT, - @UseKeyConnector BIT = 0, - @UseScim BIT = 0, - @UseCustomPermissions BIT = 0, - @UseSecretsManager BIT = 0, - @Status TINYINT = 0, - @UsePasswordManager BIT = 1, - @SmSeats INT = null, - @SmServiceAccounts INT = null, - @MaxAutoscaleSmSeats INT = null, - @MaxAutoscaleSmServiceAccounts INT = null, - @SecretsManagerBeta BIT = 0 -AS -BEGIN - SET NOCOUNT ON - - UPDATE - [dbo].[Organization] - SET - [Identifier] = @Identifier, - [Name] = @Name, - [BusinessName] = @BusinessName, - [BusinessAddress1] = @BusinessAddress1, - [BusinessAddress2] = @BusinessAddress2, - [BusinessAddress3] = @BusinessAddress3, - [BusinessCountry] = @BusinessCountry, - [BusinessTaxNumber] = @BusinessTaxNumber, - [BillingEmail] = @BillingEmail, - [Plan] = @Plan, - [PlanType] = @PlanType, - [Seats] = @Seats, - [MaxCollections] = @MaxCollections, - [UsePolicies] = @UsePolicies, - [UseSso] = @UseSso, - [UseGroups] = @UseGroups, - [UseDirectory] = @UseDirectory, - [UseEvents] = @UseEvents, - [UseTotp] = @UseTotp, - [Use2fa] = @Use2fa, - [UseApi] = @UseApi, - [UseResetPassword] = @UseResetPassword, - [SelfHost] = @SelfHost, - [UsersGetPremium] = @UsersGetPremium, - [Storage] = @Storage, - [MaxStorageGb] = @MaxStorageGb, - [Gateway] = @Gateway, - [GatewayCustomerId] = @GatewayCustomerId, - [GatewaySubscriptionId] = @GatewaySubscriptionId, - [ReferenceData] = @ReferenceData, - [Enabled] = @Enabled, - [LicenseKey] = @LicenseKey, - [PublicKey] = @PublicKey, - [PrivateKey] = @PrivateKey, - [TwoFactorProviders] = @TwoFactorProviders, - [ExpirationDate] = @ExpirationDate, - [CreationDate] = @CreationDate, - [RevisionDate] = @RevisionDate, - [OwnersNotifiedOfAutoscaling] = @OwnersNotifiedOfAutoscaling, - [MaxAutoscaleSeats] = @MaxAutoscaleSeats, - [UseKeyConnector] = @UseKeyConnector, - [UseScim] = @UseScim, - [UseCustomPermissions] = @UseCustomPermissions, - [UseSecretsManager] = @UseSecretsManager, - [Status] = @Status, - [UsePasswordManager] = @UsePasswordManager, - [SmSeats] = @SmSeats, - [SmServiceAccounts] = @SmServiceAccounts, - [MaxAutoscaleSmSeats] = @MaxAutoscaleSmSeats, - [MaxAutoscaleSmServiceAccounts] = @MaxAutoscaleSmServiceAccounts, - [SecretsManagerBeta] = @SecretsManagerBeta - WHERE - [Id] = @Id -END -GO - --- Update the metadata for the following view so that the SecretsManagerBeta column is included -IF OBJECT_ID('[dbo].[OrganizationView]') IS NOT NULL -BEGIN - EXECUTE sp_refreshview N'[dbo].[OrganizationView]'; -END -GO diff --git a/util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql b/util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql deleted file mode 100644 index 49df5451c308..000000000000 --- a/util/Migrator/DbScripts_data_migration/2023-07-03_00_SecretsManagerBetaColumnDataMigration.sql +++ /dev/null @@ -1,16 +0,0 @@ -/* -This is the data migration script for adding the SecretsManagerBeta column. -The initial migration util/Migrator/DbScripts/2023-07-03_00_SecretsManagerBetaColumn.sql should be run prior. -There is no final migration. -*/ -IF COL_LENGTH('[dbo].[Organization]', 'SecretsManagerBeta') IS NOT NULL -BEGIN - -- Set SecretsManagerBeta to 1 for Organizations where UseSecretsManager is 1. - -- At this time (at GA release) these are the Organizations using SecretsManager in beta. - -- We want to mark they are using SecretsManager beta for the sunset period, before - -- eventually removing the SecretsManagerBeta column and turning off beta access. - UPDATE [dbo].[Organization] - SET [SecretsManagerBeta] = 1 - WHERE [UseSecretsManager] = 1 -END -GO diff --git a/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.Designer.cs b/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.Designer.cs deleted file mode 100644 index d4fd963ba304..000000000000 --- a/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.Designer.cs +++ /dev/null @@ -1,2228 +0,0 @@ -// -using System; -using Bit.Infrastructure.EntityFramework.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Bit.MySqlMigrations.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20230710114621_SecretsManagerBetaColumn")] - partial class SecretsManagerBetaColumn - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "6.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("AccessCode") - .HasMaxLength(25) - .HasColumnType("varchar(25)"); - - b.Property("Approved") - .HasColumnType("tinyint(1)"); - - b.Property("AuthenticationDate") - .HasColumnType("datetime(6)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("MasterPasswordHash") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("PublicKey") - .HasColumnType("longtext"); - - b.Property("RequestDeviceIdentifier") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("RequestDeviceType") - .HasColumnType("tinyint unsigned"); - - b.Property("RequestIpAddress") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("ResponseDate") - .HasColumnType("datetime(6)"); - - b.Property("ResponseDeviceId") - .HasColumnType("char(36)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ResponseDeviceId"); - - b.HasIndex("UserId"); - - b.ToTable("AuthRequest", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("GranteeId") - .HasColumnType("char(36)"); - - b.Property("GrantorId") - .HasColumnType("char(36)"); - - b.Property("KeyEncrypted") - .HasColumnType("longtext"); - - b.Property("LastNotificationDate") - .HasColumnType("datetime(6)"); - - b.Property("RecoveryInitiatedDate") - .HasColumnType("datetime(6)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("tinyint unsigned"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("WaitTimeDays") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("GranteeId"); - - b.HasIndex("GrantorId"); - - b.ToTable("EmergencyAccess", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => - { - b.Property("Key") - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("ClientId") - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("ConsumedDate") - .HasColumnType("datetime(6)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Data") - .HasColumnType("longtext"); - - b.Property("Description") - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("ExpirationDate") - .HasColumnType("datetime(6)"); - - b.Property("SessionId") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("SubjectId") - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.HasKey("Key"); - - b.ToTable("Grant", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Data") - .HasColumnType("longtext"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("SsoConfig", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("ExternalId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("SsoUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("varchar(300)"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Collection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.Property("CollectionId") - .HasColumnType("char(36)"); - - b.Property("CipherId") - .HasColumnType("char(36)"); - - b.HasKey("CollectionId", "CipherId"); - - b.HasIndex("CipherId"); - - b.ToTable("CollectionCipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.Property("CollectionId") - .HasColumnType("char(36)"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("HidePasswords") - .HasColumnType("tinyint(1)"); - - b.Property("ReadOnly") - .HasColumnType("tinyint(1)"); - - b.HasKey("CollectionId", "GroupId"); - - b.HasIndex("GroupId"); - - b.ToTable("CollectionGroups"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.Property("CollectionId") - .HasColumnType("char(36)"); - - b.Property("OrganizationUserId") - .HasColumnType("char(36)"); - - b.Property("HidePasswords") - .HasColumnType("tinyint(1)"); - - b.Property("ReadOnly") - .HasColumnType("tinyint(1)"); - - b.HasKey("CollectionId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("EncryptedPrivateKey") - .HasColumnType("longtext"); - - b.Property("EncryptedPublicKey") - .HasColumnType("longtext"); - - b.Property("EncryptedUserKey") - .HasColumnType("longtext"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("PushToken") - .HasMaxLength(255) - .HasColumnType("varchar(255)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Device", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("ActingUserId") - .HasColumnType("char(36)"); - - b.Property("CipherId") - .HasColumnType("char(36)"); - - b.Property("CollectionId") - .HasColumnType("char(36)"); - - b.Property("Date") - .HasColumnType("datetime(6)"); - - b.Property("DeviceType") - .HasColumnType("tinyint unsigned"); - - b.Property("DomainName") - .HasColumnType("longtext"); - - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("InstallationId") - .HasColumnType("char(36)"); - - b.Property("IpAddress") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("OrganizationUserId") - .HasColumnType("char(36)"); - - b.Property("PolicyId") - .HasColumnType("char(36)"); - - b.Property("ProviderId") - .HasColumnType("char(36)"); - - b.Property("ProviderOrganizationId") - .HasColumnType("char(36)"); - - b.Property("ProviderUserId") - .HasColumnType("char(36)"); - - b.Property("SecretId") - .HasColumnType("char(36)"); - - b.Property("ServiceAccountId") - .HasColumnType("char(36)"); - - b.Property("SystemUser") - .HasColumnType("tinyint unsigned"); - - b.Property("Type") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.ToTable("Event", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("AccessAll") - .HasColumnType("tinyint(1)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("varchar(300)"); - - b.Property("Name") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Group", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.Property("GroupId") - .HasColumnType("char(36)"); - - b.Property("OrganizationUserId") - .HasColumnType("char(36)"); - - b.HasKey("GroupId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("GroupUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("Key") - .HasMaxLength(150) - .HasColumnType("varchar(150)"); - - b.HasKey("Id"); - - b.ToTable("Installation", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("BillingEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("BusinessAddress1") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("BusinessAddress2") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("BusinessAddress3") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("BusinessCountry") - .HasMaxLength(2) - .HasColumnType("varchar(2)"); - - b.Property("BusinessName") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("BusinessTaxNumber") - .HasMaxLength(30) - .HasColumnType("varchar(30)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("ExpirationDate") - .HasColumnType("datetime(6)"); - - b.Property("Gateway") - .HasColumnType("tinyint unsigned"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("MaxAutoscaleSeats") - .HasColumnType("int"); - - b.Property("MaxAutoscaleSmSeats") - .HasColumnType("int"); - - b.Property("MaxAutoscaleSmServiceAccounts") - .HasColumnType("int"); - - b.Property("MaxCollections") - .HasColumnType("smallint"); - - b.Property("MaxStorageGb") - .HasColumnType("smallint"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("OwnersNotifiedOfAutoscaling") - .HasColumnType("datetime(6)"); - - b.Property("Plan") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("PlanType") - .HasColumnType("tinyint unsigned"); - - b.Property("PrivateKey") - .HasColumnType("longtext"); - - b.Property("PublicKey") - .HasColumnType("longtext"); - - b.Property("ReferenceData") - .HasColumnType("longtext"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Seats") - .HasColumnType("int"); - - b.Property("SecretsManagerBeta") - .HasColumnType("tinyint(1)"); - - b.Property("SelfHost") - .HasColumnType("tinyint(1)"); - - b.Property("SmSeats") - .HasColumnType("int"); - - b.Property("SmServiceAccounts") - .HasColumnType("int"); - - b.Property("Status") - .HasColumnType("tinyint unsigned"); - - b.Property("Storage") - .HasColumnType("bigint"); - - b.Property("TwoFactorProviders") - .HasColumnType("longtext"); - - b.Property("Use2fa") - .HasColumnType("tinyint(1)"); - - b.Property("UseApi") - .HasColumnType("tinyint(1)"); - - b.Property("UseCustomPermissions") - .HasColumnType("tinyint(1)"); - - b.Property("UseDirectory") - .HasColumnType("tinyint(1)"); - - b.Property("UseEvents") - .HasColumnType("tinyint(1)"); - - b.Property("UseGroups") - .HasColumnType("tinyint(1)"); - - b.Property("UseKeyConnector") - .HasColumnType("tinyint(1)"); - - b.Property("UsePasswordManager") - .HasColumnType("tinyint(1)"); - - b.Property("UsePolicies") - .HasColumnType("tinyint(1)"); - - b.Property("UseResetPassword") - .HasColumnType("tinyint(1)"); - - b.Property("UseScim") - .HasColumnType("tinyint(1)"); - - b.Property("UseSecretsManager") - .HasColumnType("tinyint(1)"); - - b.Property("UseSso") - .HasColumnType("tinyint(1)"); - - b.Property("UseTotp") - .HasColumnType("tinyint(1)"); - - b.Property("UsersGetPremium") - .HasColumnType("tinyint(1)"); - - b.HasKey("Id"); - - b.ToTable("Organization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("ApiKey") - .HasMaxLength(30) - .HasColumnType("varchar(30)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("Config") - .HasColumnType("longtext"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationConnection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("DomainName") - .HasMaxLength(255) - .HasColumnType("varchar(255)"); - - b.Property("JobRunCount") - .HasColumnType("int"); - - b.Property("LastCheckedDate") - .HasColumnType("datetime(6)"); - - b.Property("NextRunDate") - .HasColumnType("datetime(6)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("Txt") - .HasColumnType("longtext"); - - b.Property("VerifiedDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationDomain", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("FriendlyName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("LastSyncDate") - .HasColumnType("datetime(6)"); - - b.Property("OfferedToEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PlanSponsorshipType") - .HasColumnType("tinyint unsigned"); - - b.Property("SponsoredOrganizationId") - .HasColumnType("char(36)"); - - b.Property("SponsoringOrganizationId") - .HasColumnType("char(36)"); - - b.Property("SponsoringOrganizationUserId") - .HasColumnType("char(36)"); - - b.Property("ToDelete") - .HasColumnType("tinyint(1)"); - - b.Property("ValidUntil") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("SponsoredOrganizationId"); - - b.HasIndex("SponsoringOrganizationId"); - - b.ToTable("OrganizationSponsorship", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("AccessAll") - .HasColumnType("tinyint(1)"); - - b.Property("AccessSecretsManager") - .HasColumnType("tinyint(1)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("varchar(300)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("Permissions") - .HasColumnType("longtext"); - - b.Property("ResetPasswordKey") - .HasColumnType("longtext"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("OrganizationUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Data") - .HasColumnType("longtext"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Policy", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("BillingEmail") - .HasColumnType("longtext"); - - b.Property("BillingPhone") - .HasColumnType("longtext"); - - b.Property("BusinessAddress1") - .HasColumnType("longtext"); - - b.Property("BusinessAddress2") - .HasColumnType("longtext"); - - b.Property("BusinessAddress3") - .HasColumnType("longtext"); - - b.Property("BusinessCountry") - .HasColumnType("longtext"); - - b.Property("BusinessName") - .HasColumnType("longtext"); - - b.Property("BusinessTaxNumber") - .HasColumnType("longtext"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Enabled") - .HasColumnType("tinyint(1)"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("tinyint unsigned"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UseEvents") - .HasColumnType("tinyint(1)"); - - b.HasKey("Id"); - - b.ToTable("Provider", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("ProviderId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Settings") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ProviderId"); - - b.ToTable("ProviderOrganization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Email") - .HasColumnType("longtext"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("Permissions") - .HasColumnType("longtext"); - - b.Property("ProviderId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("tinyint unsigned"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("ProviderId"); - - b.HasIndex("UserId"); - - b.ToTable("ProviderUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("AccessCount") - .HasColumnType("int"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Data") - .HasColumnType("longtext"); - - b.Property("DeletionDate") - .HasColumnType("datetime(6)"); - - b.Property("Disabled") - .HasColumnType("tinyint(1)"); - - b.Property("ExpirationDate") - .HasColumnType("datetime(6)"); - - b.Property("HideEmail") - .HasColumnType("tinyint(1)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("MaxAccessCount") - .HasColumnType("int"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("Password") - .HasMaxLength(300) - .HasColumnType("varchar(300)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Send", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => - { - b.Property("Id") - .HasMaxLength(40) - .HasColumnType("varchar(40)"); - - b.Property("Active") - .HasColumnType("tinyint(1)"); - - b.Property("Country") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("PostalCode") - .HasMaxLength(10) - .HasColumnType("varchar(10)"); - - b.Property("Rate") - .HasColumnType("decimal(65,30)"); - - b.Property("State") - .HasMaxLength(2) - .HasColumnType("varchar(2)"); - - b.HasKey("Id"); - - b.ToTable("TaxRate", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("Amount") - .HasColumnType("decimal(65,30)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Details") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("Gateway") - .HasColumnType("tinyint unsigned"); - - b.Property("GatewayId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("PaymentMethodType") - .HasColumnType("tinyint unsigned"); - - b.Property("Refunded") - .HasColumnType("tinyint(1)"); - - b.Property("RefundedAmount") - .HasColumnType("decimal(65,30)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Transaction", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("AccountRevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("ApiKey") - .IsRequired() - .HasMaxLength(30) - .HasColumnType("varchar(30)"); - - b.Property("AvatarColor") - .HasMaxLength(7) - .HasColumnType("varchar(7)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Culture") - .HasMaxLength(10) - .HasColumnType("varchar(10)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailVerified") - .HasColumnType("tinyint(1)"); - - b.Property("EquivalentDomains") - .HasColumnType("longtext"); - - b.Property("ExcludedGlobalEquivalentDomains") - .HasColumnType("longtext"); - - b.Property("FailedLoginCount") - .HasColumnType("int"); - - b.Property("ForcePasswordReset") - .HasColumnType("tinyint(1)"); - - b.Property("Gateway") - .HasColumnType("tinyint unsigned"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Kdf") - .HasColumnType("tinyint unsigned"); - - b.Property("KdfIterations") - .HasColumnType("int"); - - b.Property("KdfMemory") - .HasColumnType("int"); - - b.Property("KdfParallelism") - .HasColumnType("int"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("LastEmailChangeDate") - .HasColumnType("datetime(6)"); - - b.Property("LastFailedLoginDate") - .HasColumnType("datetime(6)"); - - b.Property("LastKdfChangeDate") - .HasColumnType("datetime(6)"); - - b.Property("LastKeyRotationDate") - .HasColumnType("datetime(6)"); - - b.Property("LastPasswordChangeDate") - .HasColumnType("datetime(6)"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("MasterPassword") - .HasMaxLength(300) - .HasColumnType("varchar(300)"); - - b.Property("MasterPasswordHint") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("MaxStorageGb") - .HasColumnType("smallint"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Premium") - .HasColumnType("tinyint(1)"); - - b.Property("PremiumExpirationDate") - .HasColumnType("datetime(6)"); - - b.Property("PrivateKey") - .HasColumnType("longtext"); - - b.Property("PublicKey") - .HasColumnType("longtext"); - - b.Property("ReferenceData") - .HasColumnType("longtext"); - - b.Property("RenewalReminderDate") - .HasColumnType("datetime(6)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("SecurityStamp") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Storage") - .HasColumnType("bigint"); - - b.Property("TwoFactorProviders") - .HasColumnType("longtext"); - - b.Property("TwoFactorRecoveryCode") - .HasMaxLength(32) - .HasColumnType("varchar(32)"); - - b.Property("UsesKeyConnector") - .HasColumnType("tinyint(1)"); - - b.HasKey("Id"); - - b.ToTable("User", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Read") - .HasColumnType("tinyint(1)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Write") - .HasColumnType("tinyint(1)"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.ToTable("AccessPolicy", (string)null); - - b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("ClientSecretHash") - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("EncryptedPayload") - .HasMaxLength(4000) - .HasColumnType("varchar(4000)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Scope") - .HasMaxLength(4000) - .HasColumnType("varchar(4000)"); - - b.Property("ServiceAccountId") - .HasColumnType("char(36)"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("ServiceAccountId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("DeletedDate") - .HasColumnType("datetime(6)"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Project", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("DeletedDate") - .HasColumnType("datetime(6)"); - - b.Property("Key") - .HasColumnType("longtext"); - - b.Property("Note") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Secret", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ServiceAccount", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("Attachments") - .HasColumnType("longtext"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Data") - .HasColumnType("longtext"); - - b.Property("DeletedDate") - .HasColumnType("datetime(6)"); - - b.Property("Favorites") - .HasColumnType("longtext"); - - b.Property("Folders") - .HasColumnType("longtext"); - - b.Property("OrganizationId") - .HasColumnType("char(36)"); - - b.Property("Reprompt") - .HasColumnType("tinyint unsigned"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("Type") - .HasColumnType("tinyint unsigned"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Cipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("CreationDate") - .HasColumnType("datetime(6)"); - - b.Property("Name") - .HasColumnType("longtext"); - - b.Property("RevisionDate") - .HasColumnType("datetime(6)"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Folder", (string)null); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.Property("ProjectsId") - .HasColumnType("char(36)"); - - b.Property("SecretsId") - .HasColumnType("char(36)"); - - b.HasKey("ProjectsId", "SecretsId"); - - b.HasIndex("SecretsId"); - - b.ToTable("ProjectSecret"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GrantedProjectId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GrantedProjectId"); - - b.Property("ServiceAccountId") - .HasColumnType("char(36)") - .HasColumnName("ServiceAccountId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("ServiceAccountId"); - - b.HasDiscriminator().HasValue("service_account_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GrantedProjectId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("char(36)") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") - .WithMany() - .HasForeignKey("ResponseDeviceId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("ResponseDevice"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") - .WithMany() - .HasForeignKey("GranteeId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") - .WithMany() - .HasForeignKey("GrantorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Grantee"); - - b.Navigation("Grantor"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoConfigs") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoUsers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("SsoUsers") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Collections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") - .WithMany("CollectionCiphers") - .HasForeignKey("CipherId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionCiphers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Cipher"); - - b.Navigation("Collection"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionGroups") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionUsers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("CollectionUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Groups") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany("GroupUsers") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("GroupUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Group"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("ApiKeys") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Connections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Domains") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") - .WithMany() - .HasForeignKey("SponsoredOrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") - .WithMany() - .HasForeignKey("SponsoringOrganizationId"); - - b.Navigation("SponsoredOrganization"); - - b.Navigation("SponsoringOrganization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("OrganizationUsers") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("OrganizationUsers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Policies") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Provider"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Transactions") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Transactions") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Ciphers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Ciphers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Folders") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) - .WithMany() - .HasForeignKey("ProjectsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) - .WithMany() - .HasForeignKey("SecretsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedProject"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("ServiceAccountAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("GrantedProject"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedProject"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Navigation("CollectionCiphers"); - - b.Navigation("CollectionGroups"); - - b.Navigation("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Navigation("ApiKeys"); - - b.Navigation("Ciphers"); - - b.Navigation("Collections"); - - b.Navigation("Connections"); - - b.Navigation("Domains"); - - b.Navigation("Groups"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("Policies"); - - b.Navigation("SsoConfigs"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Navigation("CollectionUsers"); - - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Navigation("Ciphers"); - - b.Navigation("Folders"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("ServiceAccountAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Navigation("CollectionCiphers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.cs b/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.cs deleted file mode 100644 index d5556b1ded38..000000000000 --- a/util/MySqlMigrations/Migrations/20230710114621_SecretsManagerBetaColumn.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.MySqlMigrations.Migrations; - -public partial class SecretsManagerBetaColumn : Migration -{ - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "SecretsManagerBeta", - table: "Organization", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "SecretsManagerBeta", - table: "Organization"); - } -} diff --git a/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.Designer.cs b/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.Designer.cs deleted file mode 100644 index 823c1276b47e..000000000000 --- a/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.Designer.cs +++ /dev/null @@ -1,2239 +0,0 @@ -// -using System; -using Bit.Infrastructure.EntityFramework.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Bit.PostgresMigrations.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20230710114625_SecretsManagerBetaColumn")] - partial class SecretsManagerBetaColumn - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") - .HasAnnotation("ProductVersion", "6.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("AccessCode") - .HasMaxLength(25) - .HasColumnType("character varying(25)"); - - b.Property("Approved") - .HasColumnType("boolean"); - - b.Property("AuthenticationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("MasterPasswordHash") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("PublicKey") - .HasColumnType("text"); - - b.Property("RequestDeviceIdentifier") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("RequestDeviceType") - .HasColumnType("smallint"); - - b.Property("RequestIpAddress") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("ResponseDate") - .HasColumnType("timestamp with time zone"); - - b.Property("ResponseDeviceId") - .HasColumnType("uuid"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ResponseDeviceId"); - - b.HasIndex("UserId"); - - b.ToTable("AuthRequest", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("GranteeId") - .HasColumnType("uuid"); - - b.Property("GrantorId") - .HasColumnType("uuid"); - - b.Property("KeyEncrypted") - .HasColumnType("text"); - - b.Property("LastNotificationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("RecoveryInitiatedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("WaitTimeDays") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("GranteeId"); - - b.HasIndex("GrantorId"); - - b.ToTable("EmergencyAccess", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => - { - b.Property("Key") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ClientId") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ConsumedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Description") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ExpirationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("SessionId") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("SubjectId") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.HasKey("Key"); - - b.ToTable("Grant", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("SsoConfig", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("ExternalId") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .UseCollation("postgresIndetermanisticCollation"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("SsoUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("character varying(300)"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Collection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.Property("CollectionId") - .HasColumnType("uuid"); - - b.Property("CipherId") - .HasColumnType("uuid"); - - b.HasKey("CollectionId", "CipherId"); - - b.HasIndex("CipherId"); - - b.ToTable("CollectionCipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.Property("CollectionId") - .HasColumnType("uuid"); - - b.Property("GroupId") - .HasColumnType("uuid"); - - b.Property("HidePasswords") - .HasColumnType("boolean"); - - b.Property("ReadOnly") - .HasColumnType("boolean"); - - b.HasKey("CollectionId", "GroupId"); - - b.HasIndex("GroupId"); - - b.ToTable("CollectionGroups"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.Property("CollectionId") - .HasColumnType("uuid"); - - b.Property("OrganizationUserId") - .HasColumnType("uuid"); - - b.Property("HidePasswords") - .HasColumnType("boolean"); - - b.Property("ReadOnly") - .HasColumnType("boolean"); - - b.HasKey("CollectionId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("EncryptedPrivateKey") - .HasColumnType("text"); - - b.Property("EncryptedPublicKey") - .HasColumnType("text"); - - b.Property("EncryptedUserKey") - .HasColumnType("text"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("PushToken") - .HasMaxLength(255) - .HasColumnType("character varying(255)"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Device", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("ActingUserId") - .HasColumnType("uuid"); - - b.Property("CipherId") - .HasColumnType("uuid"); - - b.Property("CollectionId") - .HasColumnType("uuid"); - - b.Property("Date") - .HasColumnType("timestamp with time zone"); - - b.Property("DeviceType") - .HasColumnType("smallint"); - - b.Property("DomainName") - .HasColumnType("text"); - - b.Property("GroupId") - .HasColumnType("uuid"); - - b.Property("InstallationId") - .HasColumnType("uuid"); - - b.Property("IpAddress") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("OrganizationUserId") - .HasColumnType("uuid"); - - b.Property("PolicyId") - .HasColumnType("uuid"); - - b.Property("ProviderId") - .HasColumnType("uuid"); - - b.Property("ProviderOrganizationId") - .HasColumnType("uuid"); - - b.Property("ProviderUserId") - .HasColumnType("uuid"); - - b.Property("SecretId") - .HasColumnType("uuid"); - - b.Property("ServiceAccountId") - .HasColumnType("uuid"); - - b.Property("SystemUser") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("integer"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.ToTable("Event", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("AccessAll") - .HasColumnType("boolean"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("character varying(300)"); - - b.Property("Name") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Group", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.Property("GroupId") - .HasColumnType("uuid"); - - b.Property("OrganizationUserId") - .HasColumnType("uuid"); - - b.HasKey("GroupId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("GroupUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Key") - .HasMaxLength(150) - .HasColumnType("character varying(150)"); - - b.HasKey("Id"); - - b.ToTable("Installation", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("BillingEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("BusinessAddress1") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("BusinessAddress2") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("BusinessAddress3") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("BusinessCountry") - .HasMaxLength(2) - .HasColumnType("character varying(2)"); - - b.Property("BusinessName") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("BusinessTaxNumber") - .HasMaxLength(30) - .HasColumnType("character varying(30)"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("ExpirationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Gateway") - .HasColumnType("smallint"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .UseCollation("postgresIndetermanisticCollation"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("MaxAutoscaleSeats") - .HasColumnType("integer"); - - b.Property("MaxAutoscaleSmSeats") - .HasColumnType("integer"); - - b.Property("MaxAutoscaleSmServiceAccounts") - .HasColumnType("integer"); - - b.Property("MaxCollections") - .HasColumnType("smallint"); - - b.Property("MaxStorageGb") - .HasColumnType("smallint"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("OwnersNotifiedOfAutoscaling") - .HasColumnType("timestamp with time zone"); - - b.Property("Plan") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("PlanType") - .HasColumnType("smallint"); - - b.Property("PrivateKey") - .HasColumnType("text"); - - b.Property("PublicKey") - .HasColumnType("text"); - - b.Property("ReferenceData") - .HasColumnType("text"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Seats") - .HasColumnType("integer"); - - b.Property("SecretsManagerBeta") - .HasColumnType("boolean"); - - b.Property("SelfHost") - .HasColumnType("boolean"); - - b.Property("SmSeats") - .HasColumnType("integer"); - - b.Property("SmServiceAccounts") - .HasColumnType("integer"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Storage") - .HasColumnType("bigint"); - - b.Property("TwoFactorProviders") - .HasColumnType("text"); - - b.Property("Use2fa") - .HasColumnType("boolean"); - - b.Property("UseApi") - .HasColumnType("boolean"); - - b.Property("UseCustomPermissions") - .HasColumnType("boolean"); - - b.Property("UseDirectory") - .HasColumnType("boolean"); - - b.Property("UseEvents") - .HasColumnType("boolean"); - - b.Property("UseGroups") - .HasColumnType("boolean"); - - b.Property("UseKeyConnector") - .HasColumnType("boolean"); - - b.Property("UsePasswordManager") - .HasColumnType("boolean"); - - b.Property("UsePolicies") - .HasColumnType("boolean"); - - b.Property("UseResetPassword") - .HasColumnType("boolean"); - - b.Property("UseScim") - .HasColumnType("boolean"); - - b.Property("UseSecretsManager") - .HasColumnType("boolean"); - - b.Property("UseSso") - .HasColumnType("boolean"); - - b.Property("UseTotp") - .HasColumnType("boolean"); - - b.Property("UsersGetPremium") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.ToTable("Organization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("ApiKey") - .HasMaxLength(30) - .HasColumnType("character varying(30)"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("Config") - .HasColumnType("text"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationConnection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("DomainName") - .HasMaxLength(255) - .HasColumnType("character varying(255)"); - - b.Property("JobRunCount") - .HasColumnType("integer"); - - b.Property("LastCheckedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("NextRunDate") - .HasColumnType("timestamp with time zone"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("Txt") - .HasColumnType("text"); - - b.Property("VerifiedDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationDomain", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("FriendlyName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("LastSyncDate") - .HasColumnType("timestamp with time zone"); - - b.Property("OfferedToEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PlanSponsorshipType") - .HasColumnType("smallint"); - - b.Property("SponsoredOrganizationId") - .HasColumnType("uuid"); - - b.Property("SponsoringOrganizationId") - .HasColumnType("uuid"); - - b.Property("SponsoringOrganizationUserId") - .HasColumnType("uuid"); - - b.Property("ToDelete") - .HasColumnType("boolean"); - - b.Property("ValidUntil") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("SponsoredOrganizationId"); - - b.HasIndex("SponsoringOrganizationId"); - - b.ToTable("OrganizationSponsorship", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("AccessAll") - .HasColumnType("boolean"); - - b.Property("AccessSecretsManager") - .HasColumnType("boolean"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("character varying(300)"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("Permissions") - .HasColumnType("text"); - - b.Property("ResetPasswordKey") - .HasColumnType("text"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("OrganizationUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Policy", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("BillingEmail") - .HasColumnType("text"); - - b.Property("BillingPhone") - .HasColumnType("text"); - - b.Property("BusinessAddress1") - .HasColumnType("text"); - - b.Property("BusinessAddress2") - .HasColumnType("text"); - - b.Property("BusinessAddress3") - .HasColumnType("text"); - - b.Property("BusinessCountry") - .HasColumnType("text"); - - b.Property("BusinessName") - .HasColumnType("text"); - - b.Property("BusinessTaxNumber") - .HasColumnType("text"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UseEvents") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.ToTable("Provider", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("ProviderId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Settings") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ProviderId"); - - b.ToTable("ProviderOrganization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Email") - .HasColumnType("text"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Permissions") - .HasColumnType("text"); - - b.Property("ProviderId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Status") - .HasColumnType("smallint"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("ProviderId"); - - b.HasIndex("UserId"); - - b.ToTable("ProviderUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("AccessCount") - .HasColumnType("integer"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("DeletionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Disabled") - .HasColumnType("boolean"); - - b.Property("ExpirationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("HideEmail") - .HasColumnType("boolean"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("MaxAccessCount") - .HasColumnType("integer"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("Password") - .HasMaxLength(300) - .HasColumnType("character varying(300)"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Send", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => - { - b.Property("Id") - .HasMaxLength(40) - .HasColumnType("character varying(40)"); - - b.Property("Active") - .HasColumnType("boolean"); - - b.Property("Country") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("PostalCode") - .HasMaxLength(10) - .HasColumnType("character varying(10)"); - - b.Property("Rate") - .HasColumnType("numeric"); - - b.Property("State") - .HasMaxLength(2) - .HasColumnType("character varying(2)"); - - b.HasKey("Id"); - - b.ToTable("TaxRate", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("Amount") - .HasColumnType("numeric"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Details") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("Gateway") - .HasColumnType("smallint"); - - b.Property("GatewayId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("PaymentMethodType") - .HasColumnType("smallint"); - - b.Property("Refunded") - .HasColumnType("boolean"); - - b.Property("RefundedAmount") - .HasColumnType("numeric"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Transaction", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("AccountRevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("ApiKey") - .IsRequired() - .HasMaxLength(30) - .HasColumnType("character varying(30)"); - - b.Property("AvatarColor") - .HasMaxLength(7) - .HasColumnType("character varying(7)"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Culture") - .HasMaxLength(10) - .HasColumnType("character varying(10)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .UseCollation("postgresIndetermanisticCollation"); - - b.Property("EmailVerified") - .HasColumnType("boolean"); - - b.Property("EquivalentDomains") - .HasColumnType("text"); - - b.Property("ExcludedGlobalEquivalentDomains") - .HasColumnType("text"); - - b.Property("FailedLoginCount") - .HasColumnType("integer"); - - b.Property("ForcePasswordReset") - .HasColumnType("boolean"); - - b.Property("Gateway") - .HasColumnType("smallint"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Kdf") - .HasColumnType("smallint"); - - b.Property("KdfIterations") - .HasColumnType("integer"); - - b.Property("KdfMemory") - .HasColumnType("integer"); - - b.Property("KdfParallelism") - .HasColumnType("integer"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("LastEmailChangeDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LastFailedLoginDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LastKdfChangeDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LastKeyRotationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LastPasswordChangeDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("MasterPassword") - .HasMaxLength(300) - .HasColumnType("character varying(300)"); - - b.Property("MasterPasswordHint") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("MaxStorageGb") - .HasColumnType("smallint"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Premium") - .HasColumnType("boolean"); - - b.Property("PremiumExpirationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("PrivateKey") - .HasColumnType("text"); - - b.Property("PublicKey") - .HasColumnType("text"); - - b.Property("ReferenceData") - .HasColumnType("text"); - - b.Property("RenewalReminderDate") - .HasColumnType("timestamp with time zone"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("SecurityStamp") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.Property("Storage") - .HasColumnType("bigint"); - - b.Property("TwoFactorProviders") - .HasColumnType("text"); - - b.Property("TwoFactorRecoveryCode") - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b.Property("UsesKeyConnector") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.ToTable("User", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("text"); - - b.Property("Read") - .HasColumnType("boolean"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Write") - .HasColumnType("boolean"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.ToTable("AccessPolicy", (string)null); - - b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("ClientSecretHash") - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("EncryptedPayload") - .HasMaxLength(4000) - .HasColumnType("character varying(4000)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Scope") - .HasMaxLength(4000) - .HasColumnType("character varying(4000)"); - - b.Property("ServiceAccountId") - .HasColumnType("uuid"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("ServiceAccountId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Project", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Note") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Secret", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ServiceAccount", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("Attachments") - .HasColumnType("text"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("DeletedDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Favorites") - .HasColumnType("text"); - - b.Property("Folders") - .HasColumnType("text"); - - b.Property("OrganizationId") - .HasColumnType("uuid"); - - b.Property("Reprompt") - .HasColumnType("smallint"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("smallint"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Cipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.Property("Id") - .HasColumnType("uuid"); - - b.Property("CreationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("RevisionDate") - .HasColumnType("timestamp with time zone"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Folder", (string)null); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.Property("ProjectsId") - .HasColumnType("uuid"); - - b.Property("SecretsId") - .HasColumnType("uuid"); - - b.HasKey("ProjectsId", "SecretsId"); - - b.HasIndex("SecretsId"); - - b.ToTable("ProjectSecret"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GrantedProjectId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GrantedProjectId"); - - b.Property("ServiceAccountId") - .HasColumnType("uuid") - .HasColumnName("ServiceAccountId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("ServiceAccountId"); - - b.HasDiscriminator().HasValue("service_account_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GrantedProjectId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("uuid") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") - .WithMany() - .HasForeignKey("ResponseDeviceId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("ResponseDevice"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") - .WithMany() - .HasForeignKey("GranteeId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") - .WithMany() - .HasForeignKey("GrantorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Grantee"); - - b.Navigation("Grantor"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoConfigs") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoUsers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("SsoUsers") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Collections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") - .WithMany("CollectionCiphers") - .HasForeignKey("CipherId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionCiphers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Cipher"); - - b.Navigation("Collection"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionGroups") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionUsers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("CollectionUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Groups") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany("GroupUsers") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("GroupUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Group"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("ApiKeys") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Connections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Domains") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") - .WithMany() - .HasForeignKey("SponsoredOrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") - .WithMany() - .HasForeignKey("SponsoringOrganizationId"); - - b.Navigation("SponsoredOrganization"); - - b.Navigation("SponsoringOrganization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("OrganizationUsers") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("OrganizationUsers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Policies") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Provider"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Transactions") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Transactions") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Ciphers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Ciphers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Folders") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) - .WithMany() - .HasForeignKey("ProjectsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) - .WithMany() - .HasForeignKey("SecretsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedProject"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("ServiceAccountAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("GrantedProject"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedProject"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Navigation("CollectionCiphers"); - - b.Navigation("CollectionGroups"); - - b.Navigation("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Navigation("ApiKeys"); - - b.Navigation("Ciphers"); - - b.Navigation("Collections"); - - b.Navigation("Connections"); - - b.Navigation("Domains"); - - b.Navigation("Groups"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("Policies"); - - b.Navigation("SsoConfigs"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Navigation("CollectionUsers"); - - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Navigation("Ciphers"); - - b.Navigation("Folders"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("ServiceAccountAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Navigation("CollectionCiphers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.cs b/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.cs deleted file mode 100644 index 487d9edc3b2a..000000000000 --- a/util/PostgresMigrations/Migrations/20230710114625_SecretsManagerBetaColumn.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.PostgresMigrations.Migrations; - -public partial class SecretsManagerBetaColumn : Migration -{ - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "SecretsManagerBeta", - table: "Organization", - type: "boolean", - nullable: false, - defaultValue: false); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "SecretsManagerBeta", - table: "Organization"); - } -} diff --git a/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.Designer.cs b/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.Designer.cs deleted file mode 100644 index 404ebe3f91ac..000000000000 --- a/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.Designer.cs +++ /dev/null @@ -1,2226 +0,0 @@ -// -using System; -using Bit.Infrastructure.EntityFramework.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Bit.SqliteMigrations.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20230710114629_SecretsManagerBetaColumn")] - partial class SecretsManagerBetaColumn - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "6.0.12"); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AccessCode") - .HasMaxLength(25) - .HasColumnType("TEXT"); - - b.Property("Approved") - .HasColumnType("INTEGER"); - - b.Property("AuthenticationDate") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("MasterPasswordHash") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("PublicKey") - .HasColumnType("TEXT"); - - b.Property("RequestDeviceIdentifier") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("RequestDeviceType") - .HasColumnType("INTEGER"); - - b.Property("RequestIpAddress") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("ResponseDate") - .HasColumnType("TEXT"); - - b.Property("ResponseDeviceId") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ResponseDeviceId"); - - b.HasIndex("UserId"); - - b.ToTable("AuthRequest", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("GranteeId") - .HasColumnType("TEXT"); - - b.Property("GrantorId") - .HasColumnType("TEXT"); - - b.Property("KeyEncrypted") - .HasColumnType("TEXT"); - - b.Property("LastNotificationDate") - .HasColumnType("TEXT"); - - b.Property("RecoveryInitiatedDate") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("WaitTimeDays") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("GranteeId"); - - b.HasIndex("GrantorId"); - - b.ToTable("EmergencyAccess", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => - { - b.Property("Key") - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("ClientId") - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("ConsumedDate") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Data") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("ExpirationDate") - .HasColumnType("TEXT"); - - b.Property("SessionId") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("SubjectId") - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.HasKey("Key"); - - b.ToTable("Grant", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Data") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("SsoConfig", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("ExternalId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("SsoUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Collection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.Property("CollectionId") - .HasColumnType("TEXT"); - - b.Property("CipherId") - .HasColumnType("TEXT"); - - b.HasKey("CollectionId", "CipherId"); - - b.HasIndex("CipherId"); - - b.ToTable("CollectionCipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.Property("CollectionId") - .HasColumnType("TEXT"); - - b.Property("GroupId") - .HasColumnType("TEXT"); - - b.Property("HidePasswords") - .HasColumnType("INTEGER"); - - b.Property("ReadOnly") - .HasColumnType("INTEGER"); - - b.HasKey("CollectionId", "GroupId"); - - b.HasIndex("GroupId"); - - b.ToTable("CollectionGroups"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.Property("CollectionId") - .HasColumnType("TEXT"); - - b.Property("OrganizationUserId") - .HasColumnType("TEXT"); - - b.Property("HidePasswords") - .HasColumnType("INTEGER"); - - b.Property("ReadOnly") - .HasColumnType("INTEGER"); - - b.HasKey("CollectionId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("EncryptedPrivateKey") - .HasColumnType("TEXT"); - - b.Property("EncryptedPublicKey") - .HasColumnType("TEXT"); - - b.Property("EncryptedUserKey") - .HasColumnType("TEXT"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("PushToken") - .HasMaxLength(255) - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Device", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("ActingUserId") - .HasColumnType("TEXT"); - - b.Property("CipherId") - .HasColumnType("TEXT"); - - b.Property("CollectionId") - .HasColumnType("TEXT"); - - b.Property("Date") - .HasColumnType("TEXT"); - - b.Property("DeviceType") - .HasColumnType("INTEGER"); - - b.Property("DomainName") - .HasColumnType("TEXT"); - - b.Property("GroupId") - .HasColumnType("TEXT"); - - b.Property("InstallationId") - .HasColumnType("TEXT"); - - b.Property("IpAddress") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("OrganizationUserId") - .HasColumnType("TEXT"); - - b.Property("PolicyId") - .HasColumnType("TEXT"); - - b.Property("ProviderId") - .HasColumnType("TEXT"); - - b.Property("ProviderOrganizationId") - .HasColumnType("TEXT"); - - b.Property("ProviderUserId") - .HasColumnType("TEXT"); - - b.Property("SecretId") - .HasColumnType("TEXT"); - - b.Property("ServiceAccountId") - .HasColumnType("TEXT"); - - b.Property("SystemUser") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Event", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AccessAll") - .HasColumnType("INTEGER"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("TEXT"); - - b.Property("Name") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Group", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.Property("GroupId") - .HasColumnType("TEXT"); - - b.Property("OrganizationUserId") - .HasColumnType("TEXT"); - - b.HasKey("GroupId", "OrganizationUserId"); - - b.HasIndex("OrganizationUserId"); - - b.ToTable("GroupUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("Key") - .HasMaxLength(150) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Installation", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("BillingEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("BusinessAddress1") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("BusinessAddress2") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("BusinessAddress3") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("BusinessCountry") - .HasMaxLength(2) - .HasColumnType("TEXT"); - - b.Property("BusinessName") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("BusinessTaxNumber") - .HasMaxLength(30) - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("ExpirationDate") - .HasColumnType("TEXT"); - - b.Property("Gateway") - .HasColumnType("INTEGER"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("Identifier") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("MaxAutoscaleSeats") - .HasColumnType("INTEGER"); - - b.Property("MaxAutoscaleSmSeats") - .HasColumnType("INTEGER"); - - b.Property("MaxAutoscaleSmServiceAccounts") - .HasColumnType("INTEGER"); - - b.Property("MaxCollections") - .HasColumnType("INTEGER"); - - b.Property("MaxStorageGb") - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("OwnersNotifiedOfAutoscaling") - .HasColumnType("TEXT"); - - b.Property("Plan") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("PlanType") - .HasColumnType("INTEGER"); - - b.Property("PrivateKey") - .HasColumnType("TEXT"); - - b.Property("PublicKey") - .HasColumnType("TEXT"); - - b.Property("ReferenceData") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Seats") - .HasColumnType("INTEGER"); - - b.Property("SecretsManagerBeta") - .HasColumnType("INTEGER"); - - b.Property("SelfHost") - .HasColumnType("INTEGER"); - - b.Property("SmSeats") - .HasColumnType("INTEGER"); - - b.Property("SmServiceAccounts") - .HasColumnType("INTEGER"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Storage") - .HasColumnType("INTEGER"); - - b.Property("TwoFactorProviders") - .HasColumnType("TEXT"); - - b.Property("Use2fa") - .HasColumnType("INTEGER"); - - b.Property("UseApi") - .HasColumnType("INTEGER"); - - b.Property("UseCustomPermissions") - .HasColumnType("INTEGER"); - - b.Property("UseDirectory") - .HasColumnType("INTEGER"); - - b.Property("UseEvents") - .HasColumnType("INTEGER"); - - b.Property("UseGroups") - .HasColumnType("INTEGER"); - - b.Property("UseKeyConnector") - .HasColumnType("INTEGER"); - - b.Property("UsePasswordManager") - .HasColumnType("INTEGER"); - - b.Property("UsePolicies") - .HasColumnType("INTEGER"); - - b.Property("UseResetPassword") - .HasColumnType("INTEGER"); - - b.Property("UseScim") - .HasColumnType("INTEGER"); - - b.Property("UseSecretsManager") - .HasColumnType("INTEGER"); - - b.Property("UseSso") - .HasColumnType("INTEGER"); - - b.Property("UseTotp") - .HasColumnType("INTEGER"); - - b.Property("UsersGetPremium") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Organization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("ApiKey") - .HasMaxLength(30) - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("Config") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationConnection", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("DomainName") - .HasMaxLength(255) - .HasColumnType("TEXT"); - - b.Property("JobRunCount") - .HasColumnType("INTEGER"); - - b.Property("LastCheckedDate") - .HasColumnType("TEXT"); - - b.Property("NextRunDate") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("Txt") - .HasColumnType("TEXT"); - - b.Property("VerifiedDate") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("OrganizationDomain", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("FriendlyName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("LastSyncDate") - .HasColumnType("TEXT"); - - b.Property("OfferedToEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("PlanSponsorshipType") - .HasColumnType("INTEGER"); - - b.Property("SponsoredOrganizationId") - .HasColumnType("TEXT"); - - b.Property("SponsoringOrganizationId") - .HasColumnType("TEXT"); - - b.Property("SponsoringOrganizationUserId") - .HasColumnType("TEXT"); - - b.Property("ToDelete") - .HasColumnType("INTEGER"); - - b.Property("ValidUntil") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("SponsoredOrganizationId"); - - b.HasIndex("SponsoringOrganizationId"); - - b.ToTable("OrganizationSponsorship", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AccessAll") - .HasColumnType("INTEGER"); - - b.Property("AccessSecretsManager") - .HasColumnType("INTEGER"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("ExternalId") - .HasMaxLength(300) - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("Permissions") - .HasColumnType("TEXT"); - - b.Property("ResetPasswordKey") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("OrganizationUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Data") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.ToTable("Policy", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("BillingEmail") - .HasColumnType("TEXT"); - - b.Property("BillingPhone") - .HasColumnType("TEXT"); - - b.Property("BusinessAddress1") - .HasColumnType("TEXT"); - - b.Property("BusinessAddress2") - .HasColumnType("TEXT"); - - b.Property("BusinessAddress3") - .HasColumnType("TEXT"); - - b.Property("BusinessCountry") - .HasColumnType("TEXT"); - - b.Property("BusinessName") - .HasColumnType("TEXT"); - - b.Property("BusinessTaxNumber") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UseEvents") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Provider", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("ProviderId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Settings") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("ProviderId"); - - b.ToTable("ProviderOrganization", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Email") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("Permissions") - .HasColumnType("TEXT"); - - b.Property("ProviderId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ProviderId"); - - b.HasIndex("UserId"); - - b.ToTable("ProviderUser", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AccessCount") - .HasColumnType("INTEGER"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Data") - .HasColumnType("TEXT"); - - b.Property("DeletionDate") - .HasColumnType("TEXT"); - - b.Property("Disabled") - .HasColumnType("INTEGER"); - - b.Property("ExpirationDate") - .HasColumnType("TEXT"); - - b.Property("HideEmail") - .HasColumnType("INTEGER"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("MaxAccessCount") - .HasColumnType("INTEGER"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("Password") - .HasMaxLength(300) - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Send", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => - { - b.Property("Id") - .HasMaxLength(40) - .HasColumnType("TEXT"); - - b.Property("Active") - .HasColumnType("INTEGER"); - - b.Property("Country") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("PostalCode") - .HasMaxLength(10) - .HasColumnType("TEXT"); - - b.Property("Rate") - .HasColumnType("TEXT"); - - b.Property("State") - .HasMaxLength(2) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("TaxRate", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("Amount") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Details") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("Gateway") - .HasColumnType("INTEGER"); - - b.Property("GatewayId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("PaymentMethodType") - .HasColumnType("INTEGER"); - - b.Property("Refunded") - .HasColumnType("INTEGER"); - - b.Property("RefundedAmount") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Transaction", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AccountRevisionDate") - .HasColumnType("TEXT"); - - b.Property("ApiKey") - .IsRequired() - .HasMaxLength(30) - .HasColumnType("TEXT"); - - b.Property("AvatarColor") - .HasMaxLength(7) - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Culture") - .HasMaxLength(10) - .HasColumnType("TEXT"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EmailVerified") - .HasColumnType("INTEGER"); - - b.Property("EquivalentDomains") - .HasColumnType("TEXT"); - - b.Property("ExcludedGlobalEquivalentDomains") - .HasColumnType("TEXT"); - - b.Property("FailedLoginCount") - .HasColumnType("INTEGER"); - - b.Property("ForcePasswordReset") - .HasColumnType("INTEGER"); - - b.Property("Gateway") - .HasColumnType("INTEGER"); - - b.Property("GatewayCustomerId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("GatewaySubscriptionId") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("Kdf") - .HasColumnType("INTEGER"); - - b.Property("KdfIterations") - .HasColumnType("INTEGER"); - - b.Property("KdfMemory") - .HasColumnType("INTEGER"); - - b.Property("KdfParallelism") - .HasColumnType("INTEGER"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("LastEmailChangeDate") - .HasColumnType("TEXT"); - - b.Property("LastFailedLoginDate") - .HasColumnType("TEXT"); - - b.Property("LastKdfChangeDate") - .HasColumnType("TEXT"); - - b.Property("LastKeyRotationDate") - .HasColumnType("TEXT"); - - b.Property("LastPasswordChangeDate") - .HasColumnType("TEXT"); - - b.Property("LicenseKey") - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("MasterPassword") - .HasMaxLength(300) - .HasColumnType("TEXT"); - - b.Property("MasterPasswordHint") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("MaxStorageGb") - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("Premium") - .HasColumnType("INTEGER"); - - b.Property("PremiumExpirationDate") - .HasColumnType("TEXT"); - - b.Property("PrivateKey") - .HasColumnType("TEXT"); - - b.Property("PublicKey") - .HasColumnType("TEXT"); - - b.Property("ReferenceData") - .HasColumnType("TEXT"); - - b.Property("RenewalReminderDate") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("SecurityStamp") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("TEXT"); - - b.Property("Storage") - .HasColumnType("INTEGER"); - - b.Property("TwoFactorProviders") - .HasColumnType("TEXT"); - - b.Property("TwoFactorRecoveryCode") - .HasMaxLength(32) - .HasColumnType("TEXT"); - - b.Property("UsesKeyConnector") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("User", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Read") - .HasColumnType("INTEGER"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Write") - .HasColumnType("INTEGER"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.ToTable("AccessPolicy", (string)null); - - b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("ClientSecretHash") - .HasMaxLength(128) - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("EncryptedPayload") - .HasMaxLength(4000) - .HasColumnType("TEXT"); - - b.Property("ExpireAt") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Scope") - .HasMaxLength(4000) - .HasColumnType("TEXT"); - - b.Property("ServiceAccountId") - .HasColumnType("TEXT"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("ServiceAccountId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ApiKey", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("DeletedDate") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Project", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("DeletedDate") - .HasColumnType("TEXT"); - - b.Property("Key") - .HasColumnType("TEXT"); - - b.Property("Note") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Value") - .HasColumnType("TEXT"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("DeletedDate") - .HasAnnotation("SqlServer:Clustered", false); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("Secret", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.HasKey("Id") - .HasAnnotation("SqlServer:Clustered", true); - - b.HasIndex("OrganizationId") - .HasAnnotation("SqlServer:Clustered", false); - - b.ToTable("ServiceAccount", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("Attachments") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Data") - .HasColumnType("TEXT"); - - b.Property("DeletedDate") - .HasColumnType("TEXT"); - - b.Property("Favorites") - .HasColumnType("TEXT"); - - b.Property("Folders") - .HasColumnType("TEXT"); - - b.Property("OrganizationId") - .HasColumnType("TEXT"); - - b.Property("Reprompt") - .HasColumnType("INTEGER"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("OrganizationId"); - - b.HasIndex("UserId"); - - b.ToTable("Cipher", (string)null); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("RevisionDate") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Folder", (string)null); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.Property("ProjectsId") - .HasColumnType("TEXT"); - - b.Property("SecretsId") - .HasColumnType("TEXT"); - - b.HasKey("ProjectsId", "SecretsId"); - - b.HasIndex("SecretsId"); - - b.ToTable("ProjectSecret"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GrantedProjectId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("GroupId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GroupId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("GroupId"); - - b.HasDiscriminator().HasValue("group_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GrantedProjectId"); - - b.Property("ServiceAccountId") - .HasColumnType("TEXT") - .HasColumnName("ServiceAccountId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("ServiceAccountId"); - - b.HasDiscriminator().HasValue("service_account_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedProjectId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GrantedProjectId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedProjectId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_project"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); - - b.Property("GrantedServiceAccountId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("GrantedServiceAccountId"); - - b.Property("OrganizationUserId") - .ValueGeneratedOnUpdateSometimes() - .HasColumnType("TEXT") - .HasColumnName("OrganizationUserId"); - - b.HasIndex("GrantedServiceAccountId"); - - b.HasIndex("OrganizationUserId"); - - b.HasDiscriminator().HasValue("user_service_account"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") - .WithMany() - .HasForeignKey("ResponseDeviceId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("ResponseDevice"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") - .WithMany() - .HasForeignKey("GranteeId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") - .WithMany() - .HasForeignKey("GrantorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Grantee"); - - b.Navigation("Grantor"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoConfigs") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("SsoUsers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("SsoUsers") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Collections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") - .WithMany("CollectionCiphers") - .HasForeignKey("CipherId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionCiphers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Cipher"); - - b.Navigation("Collection"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionGroups") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") - .WithMany("CollectionUsers") - .HasForeignKey("CollectionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("CollectionUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Collection"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Groups") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany("GroupUsers") - .HasForeignKey("GroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany("GroupUsers") - .HasForeignKey("OrganizationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Group"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("ApiKeys") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Connections") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Domains") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") - .WithMany() - .HasForeignKey("SponsoredOrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") - .WithMany() - .HasForeignKey("SponsoringOrganizationId"); - - b.Navigation("SponsoredOrganization"); - - b.Navigation("SponsoringOrganization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("OrganizationUsers") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("OrganizationUsers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Policies") - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Provider"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Transactions") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Transactions") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany() - .HasForeignKey("OrganizationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Organization"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") - .WithMany("Ciphers") - .HasForeignKey("OrganizationId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Ciphers") - .HasForeignKey("UserId"); - - b.Navigation("Organization"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") - .WithMany("Folders") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("ProjectSecret", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) - .WithMany() - .HasForeignKey("ProjectsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) - .WithMany() - .HasForeignKey("SecretsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedProject"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") - .WithMany() - .HasForeignKey("GroupId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("Group"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("ServiceAccountAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") - .WithMany() - .HasForeignKey("ServiceAccountId"); - - b.Navigation("GrantedProject"); - - b.Navigation("ServiceAccount"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedProjectId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedProject"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => - { - b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") - .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedServiceAccountId"); - - b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") - .WithMany() - .HasForeignKey("OrganizationUserId"); - - b.Navigation("GrantedServiceAccount"); - - b.Navigation("OrganizationUser"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => - { - b.Navigation("CollectionCiphers"); - - b.Navigation("CollectionGroups"); - - b.Navigation("CollectionUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => - { - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => - { - b.Navigation("ApiKeys"); - - b.Navigation("Ciphers"); - - b.Navigation("Collections"); - - b.Navigation("Connections"); - - b.Navigation("Domains"); - - b.Navigation("Groups"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("Policies"); - - b.Navigation("SsoConfigs"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => - { - b.Navigation("CollectionUsers"); - - b.Navigation("GroupUsers"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => - { - b.Navigation("Ciphers"); - - b.Navigation("Folders"); - - b.Navigation("OrganizationUsers"); - - b.Navigation("SsoUsers"); - - b.Navigation("Transactions"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("ServiceAccountAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => - { - b.Navigation("GroupAccessPolicies"); - - b.Navigation("UserAccessPolicies"); - }); - - modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => - { - b.Navigation("CollectionCiphers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.cs b/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.cs deleted file mode 100644 index 0fc3071711f1..000000000000 --- a/util/SqliteMigrations/Migrations/20230710114629_SecretsManagerBetaColumn.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Bit.SqliteMigrations.Migrations; - -public partial class SecretsManagerBetaColumn : Migration -{ - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "SecretsManagerBeta", - table: "Organization", - type: "INTEGER", - nullable: false, - defaultValue: false); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "SecretsManagerBeta", - table: "Organization"); - } -} From d35de15dfa444ec0af56f3027b3f4ff49f41c96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Thu, 27 Jul 2023 22:20:23 +0100 Subject: [PATCH 36/41] [AC-1468] Modified CountNewServiceAccountSlotsRequiredQuery to return zero if organization has SecretsManagerBeta == true (#3112) Co-authored-by: Thomas Rittson --- ...ountNewServiceAccountSlotsRequiredQuery.cs | 2 +- ...ewServiceAccountSlotsRequiredQueryTests.cs | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQuery.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQuery.cs index f85006316bdb..cbb8b98f8cbd 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQuery.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQuery.cs @@ -26,7 +26,7 @@ public async Task CountNewServiceAccountSlotsRequiredAsync(Guid organizatio throw new NotFoundException(); } - if (!organization.SmServiceAccounts.HasValue || serviceAccountsToAdd == 0) + if (!organization.SmServiceAccounts.HasValue || serviceAccountsToAdd == 0 || organization.SecretsManagerBeta) { return 0; } diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQueryTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQueryTests.cs index f3c64b3d07b3..21c91920a9dc 100644 --- a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQueryTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/CountNewServiceAccountSlotsRequiredQueryTests.cs @@ -28,6 +28,7 @@ public async Task CountNewServiceAccountSlotsRequiredAsync_ReturnsCorrectCount( { organization.UseSecretsManager = true; organization.SmServiceAccounts = organizationSmServiceAccounts; + organization.SecretsManagerBeta = false; sutProvider.GetDependency() .GetByIdAsync(organization.Id) @@ -61,6 +62,7 @@ public async Task CountNewServiceAccountSlotsRequiredAsync_WithNullSmServiceAcco organization.UseSecretsManager = true; organization.SmServiceAccounts = null; + organization.SecretsManagerBeta = false; sutProvider.GetDependency() .GetByIdAsync(organization.Id) @@ -78,6 +80,27 @@ await sutProvider.GetDependency().DidNotReceiveWithAn .GetServiceAccountCountByOrganizationIdAsync(default); } + [Theory, BitAutoData] + public async Task CountNewServiceAccountSlotsRequiredAsync_WithSecretsManagerBeta_ReturnsZero( + int serviceAccountsToAdd, + Organization organization, + SutProvider sutProvider) + { + organization.UseSecretsManager = true; + organization.SecretsManagerBeta = true; + + sutProvider.GetDependency() + .GetByIdAsync(organization.Id) + .Returns(organization); + + var result = await sutProvider.Sut.CountNewServiceAccountSlotsRequiredAsync(organization.Id, serviceAccountsToAdd); + + Assert.Equal(0, result); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetServiceAccountCountByOrganizationIdAsync(default); + } + [Theory, BitAutoData] public async Task CountNewServiceAccountSlotsRequiredAsync_WithNonExistentOrganizationId_ThrowsNotFound( Guid organizationId, int serviceAccountsToAdd, From fb569a907cb8e1839114a21927003d95e0966b76 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Mon, 31 Jul 2023 10:28:56 +0100 Subject: [PATCH 37/41] [Ac 1563] Unable to load billing and subscription related pages for non-enterprise organizations (#3138) * Resolve the failing family plan * resolve issues * Resolve code related pr comments * Resolve test related comments * Resolving or comments --- .../OrganizationResponseModel.cs | 6 ++- .../UpgradeOrganizationPlanCommand.cs | 9 +++- .../Implementations/OrganizationService.cs | 11 ++-- .../Services/OrganizationServiceTests.cs | 53 +++++++++++++++++++ 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs index fb196901b84c..11f5a795a48a 100644 --- a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs +++ b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs @@ -27,7 +27,11 @@ public OrganizationResponseModel(Organization organization, string obj = "organi BusinessTaxNumber = organization.BusinessTaxNumber; BillingEmail = organization.BillingEmail; Plan = new PlanResponseModel(StaticStore.PasswordManagerPlans.FirstOrDefault(plan => plan.Type == organization.PlanType)); - SecretsManagerPlan = new PlanResponseModel(StaticStore.SecretManagerPlans.FirstOrDefault(plan => plan.Type == organization.PlanType)); + var matchingPlan = StaticStore.GetSecretsManagerPlan(organization.PlanType); + if (matchingPlan != null) + { + SecretsManagerPlan = new PlanResponseModel(matchingPlan); + } PlanType = organization.PlanType; Seats = organization.Seats; MaxAutoscaleSeats = organization.MaxAutoscaleSeats; diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs index ee0d0bbc2adf..dec05ac9e936 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs @@ -267,10 +267,15 @@ await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, organization.PublicKey = upgrade.PublicKey; organization.PrivateKey = upgrade.PrivateKey; organization.UsePasswordManager = true; - organization.SmSeats = (short)(newSecretsManagerPlan.BaseSeats + upgrade.AdditionalSmSeats.GetValueOrDefault()); - organization.SmServiceAccounts = newSecretsManagerPlan.BaseServiceAccount + upgrade.AdditionalServiceAccounts.GetValueOrDefault(); organization.UseSecretsManager = upgrade.UseSecretsManager; + if (upgrade.UseSecretsManager) + { + organization.SmSeats = newSecretsManagerPlan.BaseSeats + upgrade.AdditionalSmSeats.GetValueOrDefault(); + organization.SmServiceAccounts = newSecretsManagerPlan.BaseServiceAccount.GetValueOrDefault() + + upgrade.AdditionalServiceAccounts.GetValueOrDefault(); + } + await _organizationService.ReplaceAndUpdateCacheAsync(organization); if (success) diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 3c3f5d709ca8..0fb53dbc58ec 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -446,11 +446,16 @@ public async Task> SignUpAsync(Organizatio RevisionDate = DateTime.UtcNow, Status = OrganizationStatusType.Created, UsePasswordManager = true, - SmSeats = (short)(secretsManagerPlan.BaseSeats + signup.AdditionalSmSeats.GetValueOrDefault()), - SmServiceAccounts = secretsManagerPlan.BaseServiceAccount + signup.AdditionalServiceAccounts.GetValueOrDefault(), - UseSecretsManager = signup.UseSecretsManager + UseSecretsManager = signup.UseSecretsManager, }; + if (signup.UseSecretsManager) + { + organization.SmSeats = secretsManagerPlan.BaseSeats + signup.AdditionalSmSeats.GetValueOrDefault(); + organization.SmServiceAccounts = secretsManagerPlan.BaseServiceAccount.GetValueOrDefault() + + signup.AdditionalServiceAccounts.GetValueOrDefault(); + } + if (passwordManagerPlan.Type == PlanType.Free && !provider) { var adminCount = diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index 4be161924789..06dc51b31760 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -145,6 +145,59 @@ await sutProvider.GetDependency().Received(1) referenceEvent.Users == expectedNewUsersCount)); } + [Theory] + [BitAutoData(PlanType.FamiliesAnnually)] + public async Task SignUp_PM_Family_Passes(PlanType planType, OrganizationSignup signup, SutProvider sutProvider) + { + signup.Plan = planType; + + var passwordManagerPlan = StaticStore.GetPasswordManagerPlan(signup.Plan); + + signup.AdditionalSeats = 0; + signup.PaymentMethodType = PaymentMethodType.Card; + signup.PremiumAccessAddon = false; + signup.UseSecretsManager = false; + + var purchaseOrganizationPlan = StaticStore.Plans.Where(x => x.Type == signup.Plan).ToList(); + + var result = await sutProvider.Sut.SignUpAsync(signup); + + await sutProvider.GetDependency().Received(1).CreateAsync( + Arg.Is(o => + o.Seats == passwordManagerPlan.BaseSeats + signup.AdditionalSeats + && o.SmSeats == null + && o.SmServiceAccounts == null)); + await sutProvider.GetDependency().Received(1).CreateAsync( + Arg.Is(o => o.AccessSecretsManager == signup.UseSecretsManager)); + + await sutProvider.GetDependency().Received(1) + .RaiseEventAsync(Arg.Is(referenceEvent => + referenceEvent.Type == ReferenceEventType.Signup && + referenceEvent.PlanName == passwordManagerPlan.Name && + referenceEvent.PlanType == passwordManagerPlan.Type && + referenceEvent.Seats == result.Item1.Seats && + referenceEvent.Storage == result.Item1.MaxStorageGb)); + // TODO: add reference events for SmSeats and Service Accounts - see AC-1481 + + Assert.NotNull(result); + Assert.NotNull(result.Item1); + Assert.NotNull(result.Item2); + Assert.IsType>(result); + + await sutProvider.GetDependency().Received(1).PurchaseOrganizationAsync( + Arg.Any(), + signup.PaymentMethodType.Value, + signup.PaymentToken, + Arg.Is>(plan => plan.Single() == passwordManagerPlan), + signup.AdditionalStorageGb, + signup.AdditionalSeats, + signup.PremiumAccessAddon, + signup.TaxInfo, + false, + signup.AdditionalSmSeats.GetValueOrDefault(), + signup.AdditionalServiceAccounts.GetValueOrDefault() + ); + } [Theory] [BitAutoData(PlanType.EnterpriseAnnually)] From f69bf1adb4359bccb2c3b777cd671c4353f28941 Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Mon, 31 Jul 2023 09:49:04 -0500 Subject: [PATCH 38/41] [SM-809] Add service account slot limit check (#3093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add service account slot limit check * Add query to DI * [AC-1462] Registered CountNewServiceAccountSlotsRequiredQuery for dependency injection * remove duplicate DI entry * Update unit tests * Remove comment * Code review updates --------- Co-authored-by: cyprain-okeke Co-authored-by: Thomas Rittson Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Co-authored-by: Rui Tome --- .../Controllers/ServiceAccountsController.cs | 21 +++++++ .../ServiceAccountsControllerTests.cs | 59 ++++++++++++++++++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs b/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs index 1b7307b8a283..b72923e61d16 100644 --- a/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs +++ b/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs @@ -4,10 +4,13 @@ using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Exceptions; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; +using Bit.Core.Repositories; using Bit.Core.SecretsManager.AuthorizationRequirements; using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces; using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces; using Bit.Core.SecretsManager.Entities; +using Bit.Core.SecretsManager.Queries.ServiceAccounts.Interfaces; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; using Bit.Core.Utilities; @@ -26,6 +29,9 @@ public class ServiceAccountsController : Controller private readonly IAuthorizationService _authorizationService; private readonly IServiceAccountRepository _serviceAccountRepository; private readonly IApiKeyRepository _apiKeyRepository; + private readonly IOrganizationRepository _organizationRepository; + private readonly ICountNewServiceAccountSlotsRequiredQuery _countNewServiceAccountSlotsRequiredQuery; + private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; private readonly ICreateAccessTokenCommand _createAccessTokenCommand; private readonly ICreateServiceAccountCommand _createServiceAccountCommand; private readonly IUpdateServiceAccountCommand _updateServiceAccountCommand; @@ -38,6 +44,9 @@ public ServiceAccountsController( IAuthorizationService authorizationService, IServiceAccountRepository serviceAccountRepository, IApiKeyRepository apiKeyRepository, + IOrganizationRepository organizationRepository, + ICountNewServiceAccountSlotsRequiredQuery countNewServiceAccountSlotsRequiredQuery, + IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand, ICreateAccessTokenCommand createAccessTokenCommand, ICreateServiceAccountCommand createServiceAccountCommand, IUpdateServiceAccountCommand updateServiceAccountCommand, @@ -49,11 +58,14 @@ public ServiceAccountsController( _authorizationService = authorizationService; _serviceAccountRepository = serviceAccountRepository; _apiKeyRepository = apiKeyRepository; + _organizationRepository = organizationRepository; + _countNewServiceAccountSlotsRequiredQuery = countNewServiceAccountSlotsRequiredQuery; _createServiceAccountCommand = createServiceAccountCommand; _updateServiceAccountCommand = updateServiceAccountCommand; _deleteServiceAccountsCommand = deleteServiceAccountsCommand; _revokeAccessTokensCommand = revokeAccessTokensCommand; _createAccessTokenCommand = createAccessTokenCommand; + _updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand; } [HttpGet("/organizations/{organizationId}/service-accounts")] @@ -105,6 +117,15 @@ public async Task CreateAsync([FromRoute] Guid orga throw new NotFoundException(); } + var newServiceAccountSlotsRequired = await _countNewServiceAccountSlotsRequiredQuery + .CountNewServiceAccountSlotsRequiredAsync(organizationId, 1); + if (newServiceAccountSlotsRequired > 0) + { + var org = await _organizationRepository.GetByIdAsync(organizationId); + await _updateSecretsManagerSubscriptionCommand.AdjustServiceAccountsAsync(org, + newServiceAccountSlotsRequired); + } + var userId = _userService.GetProperUserId(User).Value; var result = await _createServiceAccountCommand.CreateAsync(createRequest.ToServiceAccount(organizationId), userId); diff --git a/test/Api.Test/SecretsManager/Controllers/ServiceAccountsControllerTests.cs b/test/Api.Test/SecretsManager/Controllers/ServiceAccountsControllerTests.cs index f075d0ec31ae..6ad8673aabe2 100644 --- a/test/Api.Test/SecretsManager/Controllers/ServiceAccountsControllerTests.cs +++ b/test/Api.Test/SecretsManager/Controllers/ServiceAccountsControllerTests.cs @@ -2,12 +2,16 @@ using Bit.Api.SecretsManager.Controllers; using Bit.Api.SecretsManager.Models.Request; using Bit.Core.Context; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; +using Bit.Core.Repositories; using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces; using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces; using Bit.Core.SecretsManager.Entities; using Bit.Core.SecretsManager.Models.Data; +using Bit.Core.SecretsManager.Queries.ServiceAccounts.Interfaces; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; using Bit.Test.Common.AutoFixture; @@ -88,14 +92,51 @@ await sutProvider.GetDependency().DidNotReceiveWit .CreateAsync(Arg.Any(), Arg.Any()); } + [Theory] + [BitAutoData(0)] + public async void CreateServiceAccount_WhenAutoscalingNotRequired_DoesNotCallUpdateSubscription( + int newSlotsRequired, SutProvider sutProvider, + ServiceAccountCreateRequestModel data, Organization organization) + { + ArrangeCreateServiceAccountAutoScalingTest(newSlotsRequired, sutProvider, data, organization); + + await sutProvider.Sut.CreateAsync(organization.Id, data); + + await sutProvider.GetDependency().Received(1) + .CreateAsync(Arg.Is(sa => sa.Name == data.Name), Arg.Any()); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .AdjustServiceAccountsAsync(Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData(1)] + [BitAutoData(2)] + public async void CreateServiceAccount_WhenAutoscalingRequired_CallsUpdateSubscription(int newSlotsRequired, + SutProvider sutProvider, + ServiceAccountCreateRequestModel data, Organization organization) + { + ArrangeCreateServiceAccountAutoScalingTest(newSlotsRequired, sutProvider, data, organization); + + await sutProvider.Sut.CreateAsync(organization.Id, data); + + await sutProvider.GetDependency().Received(1) + .CreateAsync(Arg.Is(sa => sa.Name == data.Name), Arg.Any()); + + await sutProvider.GetDependency().Received(1) + .AdjustServiceAccountsAsync(Arg.Is(organization), Arg.Is(newSlotsRequired)); + } + [Theory] [BitAutoData] public async void CreateServiceAccount_Success(SutProvider sutProvider, - ServiceAccountCreateRequestModel data, Guid organizationId) + ServiceAccountCreateRequestModel data, Guid organizationId, Organization mockOrg) { + mockOrg.Id = organizationId; sutProvider.GetDependency() .AuthorizeAsync(Arg.Any(), data.ToServiceAccount(organizationId), Arg.Any>()).ReturnsForAnyArgs(AuthorizationResult.Success()); + sutProvider.GetDependency().GetByIdAsync(Arg.Is(organizationId)).Returns(mockOrg); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); var resultServiceAccount = data.ToServiceAccount(organizationId); sutProvider.GetDependency().CreateAsync(default, default) @@ -364,4 +405,20 @@ await sutProvider.GetDependency().Received(1) Assert.Null(result.Error); } } + + private static void ArrangeCreateServiceAccountAutoScalingTest(int newSlotsRequired, SutProvider sutProvider, + ServiceAccountCreateRequestModel data, Organization organization) + { + sutProvider.GetDependency() + .AuthorizeAsync(Arg.Any(), data.ToServiceAccount(organization.Id), + Arg.Any>()).ReturnsForAnyArgs(AuthorizationResult.Success()); + sutProvider.GetDependency().GetByIdAsync(Arg.Is(organization.Id)).Returns(organization); + sutProvider.GetDependency() + .CountNewServiceAccountSlotsRequiredAsync(organization.Id, 1) + .ReturnsForAnyArgs(newSlotsRequired); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + var resultServiceAccount = data.ToServiceAccount(organization.Id); + sutProvider.GetDependency().CreateAsync(default, default) + .ReturnsForAnyArgs(resultServiceAccount); + } } From 88b81e34cdfd8735c8a699a0337a70855a02a515 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Tue, 1 Aug 2023 09:22:15 +1000 Subject: [PATCH 39/41] [AC-1461] Secrets manager seat autoscaling (#3121) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add autoscaling code to invite user, save user, and bulk enable SM flows * Add tests * Delete command for BulkEnableSecretsManager * circular dependency between OrganizationService and UpdateSecretsManagerSubscriptionCommand - fixed by temporarily duplicating ReplaceAndUpdateCache * Unresolvable dependencies in other services - fixed by temporarily registering noop services and moving around some DI code All should be resolved in PM-1880 * Refactor: improve the update object and use it to adjust values, remove excess interfaces on the command * Handle autoscaling-specific errors --------- Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com> --- bitwarden_license/src/Scim/Startup.cs | 6 + bitwarden_license/src/Sso/Startup.cs | 6 + src/Admin/Admin.csproj | 1 + src/Admin/Startup.cs | 2 + src/Admin/packages.lock.json | 34 ++- .../OrganizationUsersController.cs | 36 ++- src/Billing/Startup.cs | 6 + .../SecretsManagerSubscriptionUpdate.cs | 93 ++++--- ...OrganizationServiceCollectionExtensions.cs | 21 +- ...UpdateSecretsManagerSubscriptionCommand.cs | 1 + ...SubscriptionServiceCollectionExtensions.cs | 1 - ...UpdateSecretsManagerSubscriptionCommand.cs | 248 +++++++++++------- .../CountNewSmSeatsRequiredQuery.cs | 54 ++++ .../ICountNewSmSeatsRequiredQuery.cs | 6 + .../EnableAccessSecretsManagerCommand.cs | 43 --- .../IEnableAccessSecretsManagerCommand.cs | 9 - .../Implementations/OrganizationService.cs | 68 ++++- src/Identity/Startup.cs | 6 + ...eSecretsManagerSubscriptionCommandTests.cs | 162 ++++++++++-- .../CountNewSmSeatsRequiredQueryTests.cs | 110 ++++++++ .../EnableAccessSecretsManagerCommandTests.cs | 81 ------ .../Services/OrganizationServiceTests.cs | 94 +++++++ 22 files changed, 756 insertions(+), 332 deletions(-) create mode 100644 src/Core/OrganizationFeatures/OrganizationUsers/CountNewSmSeatsRequiredQuery.cs create mode 100644 src/Core/OrganizationFeatures/OrganizationUsers/Interfaces/ICountNewSmSeatsRequiredQuery.cs delete mode 100644 src/Core/SecretsManager/Commands/EnableAccessSecretsManager/EnableAccessSecretsManagerCommand.cs delete mode 100644 src/Core/SecretsManager/Commands/EnableAccessSecretsManager/Interfaces/IEnableAccessSecretsManagerCommand.cs create mode 100644 test/Core.Test/OrganizationFeatures/OrganizationUsers/CountNewSmSeatsRequiredQueryTests.cs delete mode 100644 test/Core.Test/SecretsManager/Commands/EnableAccessSecretsManager/EnableAccessSecretsManagerCommandTests.cs diff --git a/bitwarden_license/src/Scim/Startup.cs b/bitwarden_license/src/Scim/Startup.cs index 4ef46459c3bd..e70b6a2883a8 100644 --- a/bitwarden_license/src/Scim/Startup.cs +++ b/bitwarden_license/src/Scim/Startup.cs @@ -1,5 +1,7 @@ using System.Globalization; using Bit.Core.Context; +using Bit.Core.SecretsManager.Repositories; +using Bit.Core.SecretsManager.Repositories.Noop; using Bit.Core.Settings; using Bit.Core.Utilities; using Bit.Scim.Context; @@ -69,6 +71,10 @@ public void ConfigureServices(IServiceCollection services) services.TryAddSingleton(); + // TODO: Remove when OrganizationUser methods are moved out of OrganizationService, this noop dependency should + // TODO: no longer be required - see PM-1880 + services.AddScoped(); + // Mvc services.AddMvc(config => { diff --git a/bitwarden_license/src/Sso/Startup.cs b/bitwarden_license/src/Sso/Startup.cs index aeeaa7e6fd31..635c91f44188 100644 --- a/bitwarden_license/src/Sso/Startup.cs +++ b/bitwarden_license/src/Sso/Startup.cs @@ -1,5 +1,7 @@ using Bit.Core; using Bit.Core.Context; +using Bit.Core.SecretsManager.Repositories; +using Bit.Core.SecretsManager.Repositories.Noop; using Bit.Core.Settings; using Bit.Core.Utilities; using Bit.SharedWeb.Utilities; @@ -78,6 +80,10 @@ public void ConfigureServices(IServiceCollection services) services.AddBaseServices(globalSettings); services.AddDefaultServices(globalSettings); services.AddCoreLocalizationServices(); + + // TODO: Remove when OrganizationUser methods are moved out of OrganizationService, this noop dependency should + // TODO: no longer be required - see PM-1880 + services.AddScoped(); } public void Configure( diff --git a/src/Admin/Admin.csproj b/src/Admin/Admin.csproj index 334f0e3d749d..ac7efce42bc6 100644 --- a/src/Admin/Admin.csproj +++ b/src/Admin/Admin.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Admin/Startup.cs b/src/Admin/Startup.cs index 9482be011a99..a10cd4d2de56 100644 --- a/src/Admin/Startup.cs +++ b/src/Admin/Startup.cs @@ -12,6 +12,7 @@ #if !OSS using Bit.Commercial.Core.Utilities; +using Bit.Commercial.Infrastructure.EntityFramework.SecretsManager; #endif namespace Bit.Admin; @@ -91,6 +92,7 @@ public void ConfigureServices(IServiceCollection services) services.AddOosServices(); #else services.AddCommercialCoreServices(); + services.AddSecretsManagerEfRepositories(); #endif // Mvc diff --git a/src/Admin/packages.lock.json b/src/Admin/packages.lock.json index 45b0a4eeae42..f32f6cafd3de 100644 --- a/src/Admin/packages.lock.json +++ b/src/Admin/packages.lock.json @@ -2821,7 +2821,15 @@ "commercial.core": { "type": "Project", "dependencies": { - "Core": "[2023.5.1, )" + "Core": "[2023.7.0, )" + } + }, + "commercial.infrastructure.entityframework": { + "type": "Project", + "dependencies": { + "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", + "Core": "[2023.7.0, )", + "Infrastructure.EntityFramework": "[2023.7.0, )" } }, "core": { @@ -2869,7 +2877,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.5.1, )", + "Core": "[2023.7.0, )", "Dapper": "[2.0.123, )" } }, @@ -2877,7 +2885,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.5.1, )", + "Core": "[2023.7.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2889,7 +2897,7 @@ "migrator": { "type": "Project", "dependencies": { - "Core": "[2023.5.1, )", + "Core": "[2023.7.0, )", "Microsoft.Extensions.Logging": "[6.0.0, )", "dbup-sqlserver": "[5.0.8, )" } @@ -2897,30 +2905,30 @@ "mysqlmigrations": { "type": "Project", "dependencies": { - "Core": "[2023.5.1, )", - "Infrastructure.EntityFramework": "[2023.5.1, )" + "Core": "[2023.7.0, )", + "Infrastructure.EntityFramework": "[2023.7.0, )" } }, "postgresmigrations": { "type": "Project", "dependencies": { - "Core": "[2023.5.1, )", - "Infrastructure.EntityFramework": "[2023.5.1, )" + "Core": "[2023.7.0, )", + "Infrastructure.EntityFramework": "[2023.7.0, )" } }, "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.5.1, )", - "Infrastructure.Dapper": "[2023.5.1, )", - "Infrastructure.EntityFramework": "[2023.5.1, )" + "Core": "[2023.7.0, )", + "Infrastructure.Dapper": "[2023.7.0, )", + "Infrastructure.EntityFramework": "[2023.7.0, )" } }, "sqlitemigrations": { "type": "Project", "dependencies": { - "Core": "[2023.5.1, )", - "Infrastructure.EntityFramework": "[2023.5.1, )" + "Core": "[2023.7.0, )", + "Infrastructure.EntityFramework": "[2023.7.0, )" } } } diff --git a/src/Api/Controllers/OrganizationUsersController.cs b/src/Api/Controllers/OrganizationUsersController.cs index baaf9de6d5e9..7e09cb8df7e1 100644 --- a/src/Api/Controllers/OrganizationUsersController.cs +++ b/src/Api/Controllers/OrganizationUsersController.cs @@ -8,8 +8,9 @@ using Bit.Core.Models.Business; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Models.Data.Organizations.Policies; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; +using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.Repositories; -using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces; using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -20,7 +21,6 @@ namespace Bit.Api.Controllers; [Authorize("Application")] public class OrganizationUsersController : Controller { - private readonly IEnableAccessSecretsManagerCommand _enableAccessSecretsManagerCommand; private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationService _organizationService; @@ -29,9 +29,10 @@ public class OrganizationUsersController : Controller private readonly IUserService _userService; private readonly IPolicyRepository _policyRepository; private readonly ICurrentContext _currentContext; + private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery; + private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; public OrganizationUsersController( - IEnableAccessSecretsManagerCommand enableAccessSecretsManagerCommand, IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, IOrganizationService organizationService, @@ -39,9 +40,10 @@ public OrganizationUsersController( IGroupRepository groupRepository, IUserService userService, IPolicyRepository policyRepository, - ICurrentContext currentContext) + ICurrentContext currentContext, + ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery, + IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand) { - _enableAccessSecretsManagerCommand = enableAccessSecretsManagerCommand; _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; _organizationService = organizationService; @@ -50,6 +52,8 @@ public OrganizationUsersController( _userService = userService; _policyRepository = policyRepository; _currentContext = currentContext; + _countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery; + _updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand; } [HttpGet("{id}")] @@ -426,7 +430,7 @@ public async Task> BulkRest [HttpPatch("enable-secrets-manager")] [HttpPut("enable-secrets-manager")] - public async Task> BulkEnableSecretsManagerAsync(Guid orgId, + public async Task BulkEnableSecretsManagerAsync(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model) { if (!await _currentContext.ManageUsers(orgId)) @@ -435,16 +439,28 @@ public async Task> BulkEnab } var orgUsers = (await _organizationUserRepository.GetManyAsync(model.Ids)) - .Where(ou => ou.OrganizationId == orgId).ToList(); + .Where(ou => ou.OrganizationId == orgId && !ou.AccessSecretsManager).ToList(); if (orgUsers.Count == 0) { throw new BadRequestException("Users invalid."); } - var results = await _enableAccessSecretsManagerCommand.EnableUsersAsync(orgUsers); + var additionalSmSeatsRequired = await _countNewSmSeatsRequiredQuery.CountNewSmSeatsRequiredAsync(orgId, + orgUsers.Count); + if (additionalSmSeatsRequired > 0) + { + var organization = await _organizationRepository.GetByIdAsync(orgId); + var update = new SecretsManagerSubscriptionUpdate(organization, true); + update.AdjustSeats(additionalSmSeatsRequired); + await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(update); + } - return new ListResponseModel(results.Select(r => - new OrganizationUserBulkResponseModel(r.organizationUser.Id, r.error))); + foreach (var orgUser in orgUsers) + { + orgUser.AccessSecretsManager = true; + } + + await _organizationUserRepository.ReplaceManyAsync(orgUsers); } private async Task RestoreOrRevokeUserAsync( diff --git a/src/Billing/Startup.cs b/src/Billing/Startup.cs index caf2235698da..d665677d62ab 100644 --- a/src/Billing/Startup.cs +++ b/src/Billing/Startup.cs @@ -1,5 +1,7 @@ using System.Globalization; using Bit.Core.Context; +using Bit.Core.SecretsManager.Repositories; +using Bit.Core.SecretsManager.Repositories.Noop; using Bit.Core.Settings; using Bit.Core.Utilities; using Bit.SharedWeb.Utilities; @@ -58,6 +60,10 @@ public void ConfigureServices(IServiceCollection services) services.TryAddSingleton(); + // TODO: Remove when OrganizationUser methods are moved out of OrganizationService, this noop dependency should + // TODO: no longer be required - see PM-1880 + services.AddScoped(); + // Mvc services.AddMvc(config => { diff --git a/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs b/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs index f8b678d1b166..a2e687a92edf 100644 --- a/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs +++ b/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs @@ -1,5 +1,6 @@ using Bit.Core.Entities; using Bit.Core.Exceptions; +using Bit.Core.Models.StaticStore; namespace Bit.Core.Models.Business; @@ -8,87 +9,95 @@ public class SecretsManagerSubscriptionUpdate public Organization Organization { get; set; } /// - /// The seats to be added or removed from the organization + /// The total seats the organization will have after the update, including any base seats included in the plan /// - public int SmSeatsAdjustment { get; set; } + public int? SmSeats { get; set; } /// - /// The total seats the organization will have after the update, including any base seats included in the plan + /// The new autoscale limit for seats, expressed as a total (not an adjustment). + /// This may or may not be the same as the current autoscale limit. /// - public int SmSeats { get; set; } + public int? MaxAutoscaleSmSeats { get; set; } /// - /// The seats the organization will have after the update, excluding the base seats included in the plan - /// Usually this is what the organization is billed for + /// The total service accounts the organization will have after the update, including the base service accounts + /// included in the plan /// - public int SmSeatsExcludingBase { get; set; } + public int? SmServiceAccounts { get; set; } /// - /// The new autoscale limit for seats, expressed as a total (not an adjustment). + /// The new autoscale limit for service accounts, expressed as a total (not an adjustment). /// This may or may not be the same as the current autoscale limit. /// - public int? MaxAutoscaleSmSeats { get; set; } + public int? MaxAutoscaleSmServiceAccounts { get; set; } /// - /// The service accounts to be added or removed from the organization + /// The proration date for the subscription update (optional) /// - public int SmServiceAccountsAdjustment { get; set; } + public DateTime? ProrationDate { get; set; } /// - /// The total service accounts the organization will have after the update, including the base service accounts - /// included in the plan + /// Whether the subscription update is a result of autoscaling /// - public int SmServiceAccounts { get; set; } + public bool Autoscaling { get; init; } /// /// The seats the organization will have after the update, excluding the base seats included in the plan /// Usually this is what the organization is billed for /// - public int SmServiceAccountsExcludingBase { get; set; } - + public int SmSeatsExcludingBase => SmSeats.HasValue ? SmSeats.Value - Plan.BaseSeats : 0; /// - /// The new autoscale limit for service accounts, expressed as a total (not an adjustment). - /// This may or may not be the same as the current autoscale limit. + /// The seats the organization will have after the update, excluding the base seats included in the plan + /// Usually this is what the organization is billed for /// - public int? MaxAutoscaleSmServiceAccounts { get; set; } - - public bool SmSeatsChanged => SmSeatsAdjustment != 0; - public bool SmServiceAccountsChanged => SmServiceAccountsAdjustment != 0; - public bool MaxAutoscaleSmSeatsChanged { get; set; } - public bool MaxAutoscaleSmServiceAccountsChanged { get; set; } + public int SmServiceAccountsExcludingBase => SmServiceAccounts.HasValue ? SmServiceAccounts.Value - Plan.BaseServiceAccount.GetValueOrDefault() : 0; + public bool SmSeatsChanged => SmSeats != Organization.SmSeats; + public bool SmServiceAccountsChanged => SmServiceAccounts != Organization.SmServiceAccounts; + public bool MaxAutoscaleSmSeatsChanged => MaxAutoscaleSmSeats != Organization.MaxAutoscaleSmSeats; + public bool MaxAutoscaleSmServiceAccountsChanged => + MaxAutoscaleSmServiceAccounts != Organization.MaxAutoscaleSmServiceAccounts; + public Plan Plan => Utilities.StaticStore.GetSecretsManagerPlan(Organization.PlanType); public SecretsManagerSubscriptionUpdate( Organization organization, int seatAdjustment, int? maxAutoscaleSeats, - int serviceAccountAdjustment, int? maxAutoscaleServiceAccounts) + int serviceAccountAdjustment, int? maxAutoscaleServiceAccounts) : this(organization, false) + { + AdjustSeats(seatAdjustment); + AdjustServiceAccounts(serviceAccountAdjustment); + + MaxAutoscaleSmSeats = maxAutoscaleSeats; + MaxAutoscaleSmServiceAccounts = maxAutoscaleServiceAccounts; + } + + public SecretsManagerSubscriptionUpdate(Organization organization, bool autoscaling) { if (organization == null) { throw new NotFoundException("Organization is not found."); } - var secretsManagerPlan = Utilities.StaticStore.GetSecretsManagerPlan(organization.PlanType); - if (secretsManagerPlan == null) + Organization = organization; + + if (Plan == null) { throw new NotFoundException("Invalid Secrets Manager plan."); } - var newTotalSeats = organization.SmSeats.GetValueOrDefault() + seatAdjustment; - var newTotalServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + serviceAccountAdjustment; - - Organization = organization; - - SmSeats = newTotalSeats; - SmSeatsAdjustment = seatAdjustment; - SmSeatsExcludingBase = newTotalSeats - secretsManagerPlan.BaseSeats; - MaxAutoscaleSmSeats = maxAutoscaleSeats; + SmSeats = organization.SmSeats; + MaxAutoscaleSmSeats = organization.MaxAutoscaleSmSeats; + SmServiceAccounts = organization.SmServiceAccounts; + MaxAutoscaleSmServiceAccounts = organization.MaxAutoscaleSmServiceAccounts; + Autoscaling = autoscaling; + } - SmServiceAccounts = newTotalServiceAccounts; - SmServiceAccountsAdjustment = serviceAccountAdjustment; - SmServiceAccountsExcludingBase = newTotalServiceAccounts - secretsManagerPlan.BaseServiceAccount.GetValueOrDefault(); - MaxAutoscaleSmServiceAccounts = maxAutoscaleServiceAccounts; + public void AdjustSeats(int adjustment) + { + SmSeats = SmSeats.GetValueOrDefault() + adjustment; + } - MaxAutoscaleSmSeatsChanged = maxAutoscaleSeats != organization.MaxAutoscaleSmSeats; - MaxAutoscaleSmServiceAccountsChanged = maxAutoscaleServiceAccounts != organization.MaxAutoscaleSmServiceAccounts; + public void AdjustServiceAccounts(int adjustment) + { + SmServiceAccounts = SmServiceAccounts.GetValueOrDefault() + adjustment; } } diff --git a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs index f302aef90b7c..a534ed5bea2d 100644 --- a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs @@ -17,8 +17,10 @@ using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted; -using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager; -using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; +using Bit.Core.OrganizationFeatures.OrganizationUsers; +using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.Services; using Bit.Core.Settings; using Bit.Core.Tokens; @@ -33,7 +35,6 @@ public static class OrganizationServiceCollectionExtensions public static void AddOrganizationServices(this IServiceCollection services, IGlobalSettings globalSettings) { services.AddScoped(); - services.AddScoped(); services.AddTokenizers(); services.AddOrganizationGroupCommands(); services.AddOrganizationConnectionCommands(); @@ -44,6 +45,8 @@ public static void AddOrganizationServices(this IServiceCollection services, IGl services.AddOrganizationLicenseCommandsQueries(); services.AddOrganizationDomainCommandsQueries(); services.AddOrganizationAuthCommands(); + services.AddOrganizationUserCommandsQueries(); + services.AddBaseOrganizationSubscriptionCommandsQueries(); } private static void AddOrganizationConnectionCommands(this IServiceCollection services) @@ -118,6 +121,18 @@ private static void AddOrganizationAuthCommands(this IServiceCollection services services.AddScoped(); } + private static void AddOrganizationUserCommandsQueries(this IServiceCollection services) + { + services.AddScoped(); + } + + // TODO: move to OrganizationSubscriptionServiceCollectionExtensions when OrganizationUser methods are moved out of + // TODO: OrganizationService - see PM-1880 + private static void AddBaseOrganizationSubscriptionCommandsQueries(this IServiceCollection services) + { + services.AddScoped(); + } + private static void AddTokenizers(this IServiceCollection services) { services.AddSingleton>(serviceProvider => diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpdateSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpdateSecretsManagerSubscriptionCommand.cs index 6ed4007e5747..38126d17c650 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpdateSecretsManagerSubscriptionCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/Interface/IUpdateSecretsManagerSubscriptionCommand.cs @@ -7,4 +7,5 @@ public interface IUpdateSecretsManagerSubscriptionCommand { Task UpdateSubscriptionAsync(SecretsManagerSubscriptionUpdate update); Task AdjustServiceAccountsAsync(Organization organization, int smServiceAccountsAdjustment); + Task ValidateUpdate(SecretsManagerSubscriptionUpdate update); } diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs index 39029c34984a..2e65fd05632a 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/OrganizationSubscriptionServiceCollectionExtensions.cs @@ -7,7 +7,6 @@ public static class OrganizationSubscriptionServiceCollectionExtensions { public static void AddOrganizationSubscriptionServices(this IServiceCollection services) { - services.AddScoped(); services.AddScoped(); services.AddScoped(); } diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs index 8d6a668326a2..ec4b482e4b01 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs @@ -7,6 +7,7 @@ using Bit.Core.Repositories; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; +using Bit.Core.Settings; using Bit.Core.Utilities; using Microsoft.Extensions.Logging; @@ -16,90 +17,55 @@ public class UpdateSecretsManagerSubscriptionCommand : IUpdateSecretsManagerSubs { private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IPaymentService _paymentService; - private readonly IOrganizationService _organizationService; private readonly IMailService _mailService; private readonly ILogger _logger; private readonly IServiceAccountRepository _serviceAccountRepository; + private readonly IGlobalSettings _globalSettings; + private readonly IOrganizationRepository _organizationRepository; + private readonly IApplicationCacheService _applicationCacheService; + private readonly IEventService _eventService; public UpdateSecretsManagerSubscriptionCommand( - IOrganizationService organizationService, IOrganizationUserRepository organizationUserRepository, IPaymentService paymentService, IMailService mailService, ILogger logger, - IServiceAccountRepository serviceAccountRepository) + IServiceAccountRepository serviceAccountRepository, + IGlobalSettings globalSettings, + IOrganizationRepository organizationRepository, + IApplicationCacheService applicationCacheService, + IEventService eventService) { _organizationUserRepository = organizationUserRepository; _paymentService = paymentService; - _organizationService = organizationService; _mailService = mailService; _logger = logger; _serviceAccountRepository = serviceAccountRepository; + _globalSettings = globalSettings; + _organizationRepository = organizationRepository; + _applicationCacheService = applicationCacheService; + _eventService = eventService; } public async Task UpdateSubscriptionAsync(SecretsManagerSubscriptionUpdate update) { - var organization = update.Organization; - - ValidateOrganization(organization); - - var plan = GetPlanForOrganization(organization); + await ValidateUpdate(update); - if (update.SmSeatsChanged) - { - await ValidateSmSeatsUpdateAsync(organization, update, plan); - } + await FinalizeSubscriptionAdjustmentAsync(update.Organization, update.Plan, update); - if (update.SmServiceAccountsChanged) - { - await ValidateSmServiceAccountsUpdateAsync(organization, update, plan); - } - - if (update.MaxAutoscaleSmSeatsChanged) - { - ValidateMaxAutoscaleSmSeatsUpdateAsync(organization, update.MaxAutoscaleSmSeats, plan); - } - - if (update.MaxAutoscaleSmServiceAccountsChanged) - { - ValidateMaxAutoscaleSmServiceAccountUpdate(organization, update.MaxAutoscaleSmServiceAccounts, plan); - } - - await FinalizeSubscriptionAdjustmentAsync(organization, plan, update); - - await SendEmailIfAutoscaleLimitReached(organization); + await SendEmailIfAutoscaleLimitReached(update.Organization); } public async Task AdjustServiceAccountsAsync(Organization organization, int smServiceAccountsAdjustment) { - var secretsManagerSubscriptionUpdate = new SecretsManagerSubscriptionUpdate( + var update = new SecretsManagerSubscriptionUpdate( organization, seatAdjustment: 0, maxAutoscaleSeats: organization?.MaxAutoscaleSmSeats, - serviceAccountAdjustment: smServiceAccountsAdjustment, maxAutoscaleServiceAccounts: organization?.MaxAutoscaleSmServiceAccounts); - - await UpdateSubscriptionAsync(secretsManagerSubscriptionUpdate); - } - - private Plan GetPlanForOrganization(Organization organization) - { - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); - if (plan == null) + serviceAccountAdjustment: smServiceAccountsAdjustment, maxAutoscaleServiceAccounts: organization?.MaxAutoscaleSmServiceAccounts) { - throw new BadRequestException("Existing plan not found."); - } - return plan; - } - - private static void ValidateOrganization(Organization organization) - { - if (organization == null) - { - throw new NotFoundException("Organization is not found."); - } + Autoscaling = true + }; - if (!organization.UseSecretsManager) - { - throw new BadRequestException("Organization has no access to Secrets Manager."); - } + await UpdateSubscriptionAsync(update); } private async Task FinalizeSubscriptionAdjustmentAsync(Organization organization, @@ -127,13 +93,13 @@ private async Task FinalizeSubscriptionAdjustmentAsync(Organization organization organization.MaxAutoscaleSmServiceAccounts = update.MaxAutoscaleSmServiceAccounts; } - await _organizationService.ReplaceAndUpdateCacheAsync(organization); + await ReplaceAndUpdateCacheAsync(organization); } private async Task ProcessChargesAndRaiseEventsForAdjustSeatsAsync(Organization organization, Plan plan, SecretsManagerSubscriptionUpdate update) { - await _paymentService.AdjustSeatsAsync(organization, plan, update.SmSeatsExcludingBase); + await _paymentService.AdjustSeatsAsync(organization, plan, update.SmSeatsExcludingBase, update.ProrationDate); // TODO: call ReferenceEventService - see AC-1481 } @@ -142,7 +108,7 @@ private async Task ProcessChargesAndRaiseEventsForAdjustServiceAccountsAsync(Org SecretsManagerSubscriptionUpdate update) { await _paymentService.AdjustServiceAccountsAsync(organization, plan, - update.SmServiceAccountsExcludingBase); + update.SmServiceAccountsExcludingBase, update.ProrationDate); // TODO: call ReferenceEventService - see AC-1481 } @@ -175,7 +141,6 @@ private async Task SendSeatLimitEmailAsync(Organization organization, int MaxAut { _logger.LogError(e, $"Error encountered notifying organization owners of Seats limit reached."); } - } private async Task SendServiceAccountLimitEmailAsync(Organization organization, int MaxAutoscaleValue) @@ -196,16 +161,59 @@ private async Task SendServiceAccountLimitEmailAsync(Organization organization, } - private async Task ValidateSmSeatsUpdateAsync(Organization organization, SecretsManagerSubscriptionUpdate update, Plan plan) + public async Task ValidateUpdate(SecretsManagerSubscriptionUpdate update) { - if (organization.SmSeats == null) + if (_globalSettings.SelfHosted) { - throw new BadRequestException("Organization has no Secrets Manager seat limit, no need to adjust seats"); + var message = update.Autoscaling + ? "Cannot autoscale on a self-hosted instance." + : "Cannot update subscription on a self-hosted instance."; + throw new BadRequestException(message); + } + + var organization = update.Organization; + ValidateOrganization(organization); + + var plan = GetPlanForOrganization(organization); + + if (update.SmSeatsChanged) + { + await ValidateSmSeatsUpdateAsync(organization, update, plan); + } + + if (update.SmServiceAccountsChanged) + { + await ValidateSmServiceAccountsUpdateAsync(organization, update, plan); } - if (update.MaxAutoscaleSmSeats.HasValue && update.SmSeats > update.MaxAutoscaleSmSeats.Value) + if (update.MaxAutoscaleSmSeatsChanged) { - throw new BadRequestException("Cannot set max seat autoscaling below seat count."); + ValidateMaxAutoscaleSmSeatsUpdateAsync(organization, update.MaxAutoscaleSmSeats, plan); + } + + if (update.MaxAutoscaleSmServiceAccountsChanged) + { + ValidateMaxAutoscaleSmServiceAccountUpdate(organization, update.MaxAutoscaleSmServiceAccounts, plan); + } + } + + private void ValidateOrganization(Organization organization) + { + if (organization == null) + { + throw new NotFoundException("Organization is not found."); + } + + if (!organization.UseSecretsManager) + { + throw new BadRequestException("Organization has no access to Secrets Manager."); + } + + var plan = GetPlanForOrganization(organization); + if (plan.Product == ProductType.Free) + { + // No need to check the organization is set up with Stripe + return; } if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId)) @@ -217,32 +225,65 @@ private async Task ValidateSmSeatsUpdateAsync(Organization organization, Secrets { throw new BadRequestException("No subscription found."); } + } - if (!plan.HasAdditionalSeatsOption) + private Plan GetPlanForOrganization(Organization organization) + { + var plan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); + if (plan == null) { - throw new BadRequestException("Plan does not allow additional Secrets Manager seats."); + throw new BadRequestException("Existing plan not found."); } + return plan; + } - if (plan.BaseSeats > update.SmSeats) + private async Task ValidateSmSeatsUpdateAsync(Organization organization, SecretsManagerSubscriptionUpdate update, Plan plan) + { + // Check if the organization has unlimited seats + if (organization.SmSeats == null) { - throw new BadRequestException($"Plan has a minimum of {plan.BaseSeats} Secrets Manager seats."); + throw new BadRequestException("Organization has no Secrets Manager seat limit, no need to adjust seats"); } - if (update.SmSeats <= 0) + if (update.Autoscaling && update.SmSeats.Value < organization.SmSeats.Value) { - throw new BadRequestException("You must have at least 1 Secrets Manager seat."); + throw new BadRequestException("Cannot use autoscaling to subtract seats."); + } + + // Check plan maximum seats + if (!plan.HasAdditionalSeatsOption || + (plan.MaxAdditionalSeats.HasValue && update.SmSeatsExcludingBase > plan.MaxAdditionalSeats.Value)) + { + var planMaxSeats = plan.BaseSeats + plan.MaxAdditionalSeats.GetValueOrDefault(); + throw new BadRequestException($"You have reached the maximum number of Secrets Manager seats ({planMaxSeats}) for this plan."); } - if (plan.MaxAdditionalSeats.HasValue && update.SmSeatsExcludingBase > plan.MaxAdditionalSeats.Value) + // Check autoscale maximum seats + if (update.MaxAutoscaleSmSeats.HasValue && update.SmSeats.Value > update.MaxAutoscaleSmSeats.Value) { - throw new BadRequestException($"Organization plan allows a maximum of " + - $"{plan.MaxAdditionalSeats.Value} additional Secrets Manager seats."); + var message = update.Autoscaling + ? "Secrets Manager seat limit has been reached." + : "Cannot set max seat autoscaling below seat count."; + throw new BadRequestException(message); + } + + // Check minimum seats included with plan + if (plan.BaseSeats > update.SmSeats.Value) + { + throw new BadRequestException($"Plan has a minimum of {plan.BaseSeats} Secrets Manager seats."); + } + + // Check minimum seats required by business logic + if (update.SmSeats.Value <= 0) + { + throw new BadRequestException("You must have at least 1 Secrets Manager seat."); } - if (organization.SmSeats.Value > update.SmSeats) + // Check minimum seats currently in use by the organization + if (organization.SmSeats.Value > update.SmSeats.Value) { var currentSeats = await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id); - if (currentSeats > update.SmSeats) + if (currentSeats > update.SmSeats.Value) { throw new BadRequestException($"Your organization currently has {currentSeats} Secrets Manager seats. " + $"Your plan only allows {update.SmSeats} Secrets Manager seats. Remove some Secrets Manager users."); @@ -252,48 +293,50 @@ private async Task ValidateSmSeatsUpdateAsync(Organization organization, Secrets private async Task ValidateSmServiceAccountsUpdateAsync(Organization organization, SecretsManagerSubscriptionUpdate update, Plan plan) { + // Check if the organization has unlimited service accounts if (organization.SmServiceAccounts == null) { throw new BadRequestException("Organization has no Service Accounts limit, no need to adjust Service Accounts"); } - if (update.MaxAutoscaleSmServiceAccounts.HasValue && update.SmServiceAccounts > update.MaxAutoscaleSmServiceAccounts.Value) + if (update.Autoscaling && update.SmServiceAccounts.Value < organization.SmServiceAccounts.Value) { - throw new BadRequestException("Cannot set max Service Accounts autoscaling below Service Accounts count."); + throw new BadRequestException("Cannot use autoscaling to subtract service accounts."); } - if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId)) - { - throw new BadRequestException("No payment method found."); - } - - if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId)) + // Check plan maximum service accounts + if (!plan.HasAdditionalServiceAccountOption || + (plan.MaxAdditionalServiceAccount.HasValue && update.SmServiceAccountsExcludingBase > plan.MaxAdditionalServiceAccount.Value)) { - throw new BadRequestException("No subscription found."); + var planMaxServiceAccounts = plan.BaseServiceAccount.GetValueOrDefault() + + plan.MaxAdditionalServiceAccount.GetValueOrDefault(); + throw new BadRequestException($"You have reached the maximum number of service accounts ({planMaxServiceAccounts}) for this plan."); } - if (!plan.HasAdditionalServiceAccountOption) + // Check autoscale maximum service accounts + if (update.MaxAutoscaleSmServiceAccounts.HasValue && + update.SmServiceAccounts.Value > update.MaxAutoscaleSmServiceAccounts.Value) { - throw new BadRequestException("Plan does not allow additional Service Accounts."); + var message = update.Autoscaling + ? "Secrets Manager service account limit has been reached." + : "Cannot set max service accounts autoscaling below service account amount."; + throw new BadRequestException(message); } - if (plan.BaseServiceAccount > update.SmServiceAccounts) + // Check minimum service accounts included with plan + if (plan.BaseServiceAccount.HasValue && plan.BaseServiceAccount.Value > update.SmServiceAccounts.Value) { throw new BadRequestException($"Plan has a minimum of {plan.BaseServiceAccount} Service Accounts."); } - if (update.SmServiceAccounts <= 0) + // Check minimum service accounts required by business logic + if (update.SmServiceAccounts.Value <= 0) { throw new BadRequestException("You must have at least 1 Service Account."); } - if (plan.MaxAdditionalServiceAccount.HasValue && update.SmServiceAccountsExcludingBase > plan.MaxAdditionalServiceAccount.Value) - { - throw new BadRequestException($"Organization plan allows a maximum of " + - $"{plan.MaxAdditionalServiceAccount.Value} additional Service Accounts."); - } - - if (!organization.SmServiceAccounts.HasValue || organization.SmServiceAccounts.Value > update.SmServiceAccounts) + // Check minimum service accounts currently in use by the organization + if (!organization.SmServiceAccounts.HasValue || organization.SmServiceAccounts.Value > update.SmServiceAccounts.Value) { var currentServiceAccounts = await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(organization.Id); if (currentServiceAccounts > update.SmServiceAccounts) @@ -358,4 +401,17 @@ private void ValidateMaxAutoscaleSmServiceAccountUpdate(Organization organizatio "Reduce your max autoscale count.")); } } + + // TODO: This is a temporary duplication of OrganizationService.ReplaceAndUpdateCache to avoid a circular dependency. + // TODO: This should no longer be necessary when user-related methods are extracted from OrganizationService: see PM-1880 + private async Task ReplaceAndUpdateCacheAsync(Organization org, EventType? orgEvent = null) + { + await _organizationRepository.ReplaceAsync(org); + await _applicationCacheService.UpsertOrganizationAbilityAsync(org); + + if (orgEvent.HasValue) + { + await _eventService.LogOrganizationEventAsync(org, orgEvent.Value); + } + } } diff --git a/src/Core/OrganizationFeatures/OrganizationUsers/CountNewSmSeatsRequiredQuery.cs b/src/Core/OrganizationFeatures/OrganizationUsers/CountNewSmSeatsRequiredQuery.cs new file mode 100644 index 000000000000..24bef3dc7ef1 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationUsers/CountNewSmSeatsRequiredQuery.cs @@ -0,0 +1,54 @@ +using Bit.Core.Exceptions; +using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; +using Bit.Core.Repositories; + +namespace Bit.Core.OrganizationFeatures.OrganizationUsers; + +public class CountNewSmSeatsRequiredQuery : ICountNewSmSeatsRequiredQuery +{ + private readonly IOrganizationUserRepository _organizationUserRepository; + private readonly IOrganizationRepository _organizationRepository; + + public CountNewSmSeatsRequiredQuery(IOrganizationUserRepository organizationUserRepository, + IOrganizationRepository organizationRepository) + { + _organizationUserRepository = organizationUserRepository; + _organizationRepository = organizationRepository; + } + + public async Task CountNewSmSeatsRequiredAsync(Guid organizationId, int usersToAdd) + { + if (usersToAdd == 0) + { + return 0; + } + + var organization = await _organizationRepository.GetByIdAsync(organizationId); + if (organization == null) + { + throw new NotFoundException(); + } + + if (!organization.UseSecretsManager) + { + throw new BadRequestException("Organization does not use Secrets Manager"); + } + + if (!organization.SmSeats.HasValue || organization.SecretsManagerBeta) + { + return 0; + } + + var occupiedSmSeats = + await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id); + + var availableSmSeats = organization.SmSeats.Value - occupiedSmSeats; + + if (availableSmSeats >= usersToAdd) + { + return 0; + } + + return usersToAdd - availableSmSeats; + } +} diff --git a/src/Core/OrganizationFeatures/OrganizationUsers/Interfaces/ICountNewSmSeatsRequiredQuery.cs b/src/Core/OrganizationFeatures/OrganizationUsers/Interfaces/ICountNewSmSeatsRequiredQuery.cs new file mode 100644 index 000000000000..851aef73a2ab --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationUsers/Interfaces/ICountNewSmSeatsRequiredQuery.cs @@ -0,0 +1,6 @@ +namespace Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; + +public interface ICountNewSmSeatsRequiredQuery +{ + public Task CountNewSmSeatsRequiredAsync(Guid organizationId, int usersToAdd); +} diff --git a/src/Core/SecretsManager/Commands/EnableAccessSecretsManager/EnableAccessSecretsManagerCommand.cs b/src/Core/SecretsManager/Commands/EnableAccessSecretsManager/EnableAccessSecretsManagerCommand.cs deleted file mode 100644 index fb172fbd1691..000000000000 --- a/src/Core/SecretsManager/Commands/EnableAccessSecretsManager/EnableAccessSecretsManagerCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Bit.Core.Entities; -using Bit.Core.Repositories; -using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces; - -namespace Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager; - -public class EnableAccessSecretsManagerCommand : IEnableAccessSecretsManagerCommand -{ - private readonly IOrganizationUserRepository _organizationUserRepository; - - public EnableAccessSecretsManagerCommand(IOrganizationUserRepository organizationUserRepository) - { - _organizationUserRepository = organizationUserRepository; - } - - public async Task> EnableUsersAsync( - IEnumerable organizationUsers) - { - var results = new List<(OrganizationUser organizationUser, string error)>(); - var usersToEnable = new List(); - - foreach (var orgUser in organizationUsers) - { - if (orgUser.AccessSecretsManager) - { - results.Add((orgUser, "User already has access to Secrets Manager")); - } - else - { - orgUser.AccessSecretsManager = true; - usersToEnable.Add(orgUser); - results.Add((orgUser, "")); - } - } - - if (usersToEnable.Any()) - { - await _organizationUserRepository.ReplaceManyAsync(usersToEnable); - } - - return results; - } -} diff --git a/src/Core/SecretsManager/Commands/EnableAccessSecretsManager/Interfaces/IEnableAccessSecretsManagerCommand.cs b/src/Core/SecretsManager/Commands/EnableAccessSecretsManager/Interfaces/IEnableAccessSecretsManagerCommand.cs deleted file mode 100644 index b7fa2150ce0c..000000000000 --- a/src/Core/SecretsManager/Commands/EnableAccessSecretsManager/Interfaces/IEnableAccessSecretsManagerCommand.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Bit.Core.Entities; - -namespace Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces; - -public interface IEnableAccessSecretsManagerCommand -{ - Task> EnableUsersAsync( - IEnumerable organizationUsers); -} diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 0fb53dbc58ec..c0aa2fe1004d 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -11,6 +11,8 @@ using Bit.Core.Models.Business; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.Policies; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; +using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.Repositories; using Bit.Core.Settings; using Bit.Core.Tools.Enums; @@ -50,6 +52,8 @@ public class OrganizationService : IOrganizationService private readonly ILogger _logger; private readonly IProviderOrganizationRepository _providerOrganizationRepository; private readonly IProviderUserRepository _providerUserRepository; + private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery; + private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand; public OrganizationService( IOrganizationRepository organizationRepository, @@ -76,7 +80,9 @@ public OrganizationService( ICurrentContext currentContext, ILogger logger, IProviderOrganizationRepository providerOrganizationRepository, - IProviderUserRepository providerUserRepository) + IProviderUserRepository providerUserRepository, + ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery, + IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand) { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; @@ -103,6 +109,8 @@ public OrganizationService( _logger = logger; _providerOrganizationRepository = providerOrganizationRepository; _providerUserRepository = providerUserRepository; + _countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery; + _updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand; } public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken, @@ -821,9 +829,12 @@ public async Task> InviteUsersAsync(Guid organizationId, throw new NotFoundException(); } - var newSeatsRequired = 0; var existingEmails = new HashSet(await _organizationUserRepository.SelectKnownEmailsAsync( organizationId, invites.SelectMany(i => i.invite.Emails), false), StringComparer.InvariantCultureIgnoreCase); + + // Seat autoscaling + var initialSmSeatCount = organization.SmSeats; + var newSeatsRequired = 0; if (organization.Seats.HasValue) { var occupiedSeats = await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); @@ -840,6 +851,21 @@ public async Task> InviteUsersAsync(Guid organizationId, } } + // Secrets Manager seat autoscaling + SecretsManagerSubscriptionUpdate smSubscriptionUpdate = null; + var inviteWithSmAccessCount = invites + .Where(i => i.invite.AccessSecretsManager) + .SelectMany(i => i.invite.Emails) + .Count(email => !existingEmails.Contains(email)); + + var additionalSmSeatsRequired = await _countNewSmSeatsRequiredQuery.CountNewSmSeatsRequiredAsync(organization.Id, inviteWithSmAccessCount); + if (additionalSmSeatsRequired > 0) + { + smSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(organization, true); + smSubscriptionUpdate.AdjustSeats(additionalSmSeatsRequired); + await _updateSecretsManagerSubscriptionCommand.ValidateUpdate(smSubscriptionUpdate); + } + var invitedAreAllOwners = invites.All(i => i.invite.Type == OrganizationUserType.Owner); if (!invitedAreAllOwners && !await HasConfirmedOwnersExceptAsync(organizationId, new Guid[] { }, includeProvider: true)) { @@ -933,6 +959,11 @@ public async Task> InviteUsersAsync(Guid organizationId, throw new BadRequestException("Cannot add seats. Cannot manage organization users."); } + if (additionalSmSeatsRequired > 0) + { + smSubscriptionUpdate.ProrationDate = prorationDate; + await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(smSubscriptionUpdate); + } await AutoAddSeatsAsync(organization, newSeatsRequired, prorationDate); await SendInvitesAsync(orgUsers.Concat(limitedCollectionOrgUsers.Select(u => u.Item1)), organization); @@ -947,11 +978,24 @@ await _referenceEventService.RaiseEventAsync( // Revert any added users. var invitedOrgUserIds = orgUsers.Select(u => u.Id).Concat(limitedCollectionOrgUsers.Select(u => u.Item1.Id)); await _organizationUserRepository.DeleteManyAsync(invitedOrgUserIds); - var currentSeatCount = (await _organizationRepository.GetByIdAsync(organization.Id)).Seats; + var currentOrganization = await _organizationRepository.GetByIdAsync(organization.Id); - if (initialSeatCount.HasValue && currentSeatCount.HasValue && currentSeatCount.Value != initialSeatCount.Value) + // Revert autoscaling + if (initialSeatCount.HasValue && currentOrganization.Seats.HasValue && currentOrganization.Seats.Value != initialSeatCount.Value) { - await AdjustSeatsAsync(organization, initialSeatCount.Value - currentSeatCount.Value, prorationDate); + await AdjustSeatsAsync(organization, initialSeatCount.Value - currentOrganization.Seats.Value, prorationDate); + } + + // Revert SmSeat autoscaling + if (initialSmSeatCount.HasValue && currentOrganization.SmSeats.HasValue && + currentOrganization.SmSeats.Value != initialSmSeatCount.Value) + { + var smSubscriptionUpdateRevert = new SecretsManagerSubscriptionUpdate(currentOrganization, false) + { + SmSeats = initialSmSeatCount.Value, + ProrationDate = prorationDate + }; + await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(smSubscriptionUpdateRevert); } exceptions.Add(e); @@ -1348,6 +1392,20 @@ public async Task SaveUserAsync(OrganizationUser user, Guid? savingUserId, throw new BadRequestException("Organization must have at least one confirmed owner."); } + // Only autoscale (if required) after all validation has passed so that we know it's a valid request before + // updating Stripe + if (!originalUser.AccessSecretsManager && user.AccessSecretsManager) + { + var additionalSmSeatsRequired = await _countNewSmSeatsRequiredQuery.CountNewSmSeatsRequiredAsync(user.OrganizationId, 1); + if (additionalSmSeatsRequired > 0) + { + var organization = await _organizationRepository.GetByIdAsync(user.OrganizationId); + var update = new SecretsManagerSubscriptionUpdate(organization, true); + update.AdjustSeats(additionalSmSeatsRequired); + await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(update); + } + } + if (user.AccessAll) { // We don't need any collections if we're flagged to have all access. diff --git a/src/Identity/Startup.cs b/src/Identity/Startup.cs index 6a0f274ce8bb..472dad809f85 100644 --- a/src/Identity/Startup.cs +++ b/src/Identity/Startup.cs @@ -4,6 +4,8 @@ using Bit.Core; using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Context; +using Bit.Core.SecretsManager.Repositories; +using Bit.Core.SecretsManager.Repositories.Noop; using Bit.Core.Settings; using Bit.Core.Utilities; using Bit.Identity.Utilities; @@ -138,6 +140,10 @@ public void ConfigureServices(IServiceCollection services) services.AddDefaultServices(globalSettings); services.AddCoreLocalizationServices(); + // TODO: Remove when OrganizationUser methods are moved out of OrganizationService, this noop dependency should + // TODO: no longer be required - see PM-1880 + services.AddScoped(); + if (CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ConnectionString) && CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ApplicationCacheTopicName)) { diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs index 5c2643cc3009..329ad60af8f6 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs @@ -7,6 +7,7 @@ using Bit.Core.Repositories; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; +using Bit.Core.Settings; using Bit.Core.Utilities; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -58,7 +59,7 @@ public async Task UpdateSubscriptionAsync_NoSecretsManagerAccess_ThrowsException [Theory] [BitAutoData] - public async Task UpdateSubscriptionAsync_SeatsAdustmentGreaterThanMaxAutoscaleSeats_ThrowsException( + public async Task UpdateSubscriptionAsync_SeatsAdjustmentGreaterThanMaxAutoscaleSeats_ThrowsException( Guid organizationId, SutProvider sutProvider) { @@ -71,6 +72,8 @@ public async Task UpdateSubscriptionAsync_SeatsAdustmentGreaterThanMaxAutoscaleS MaxAutoscaleSmSeats = 20, MaxAutoscaleSmServiceAccounts = 10, PlanType = PlanType.EnterpriseAnnually, + GatewayCustomerId = "1", + GatewaySubscriptionId = "2" }; var organizationUpdate = new SecretsManagerSubscriptionUpdate( organization, seatAdjustment: 15, maxAutoscaleSeats: 10, serviceAccountAdjustment: 0, maxAutoscaleServiceAccounts: null); @@ -102,7 +105,7 @@ public async Task UpdateSubscriptionAsync_ServiceAccountsGreaterThanMaxAutoscale organization, seatAdjustment: 1, maxAutoscaleSeats: 15, serviceAccountAdjustment: 11, maxAutoscaleServiceAccounts: 10); var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); - Assert.Contains("Cannot set max Service Accounts autoscaling below Service Accounts count", exception.Message); + Assert.Contains("Cannot set max service accounts autoscaling below service account amount", exception.Message); await VerifyDependencyNotCalledAsync(sutProvider); } @@ -170,7 +173,8 @@ public async Task UpdateSubscriptionAsync_OrgWithNullSmSeatOnSeatsAdjustment_Thr MaxAutoscaleSmSeats = 20, MaxAutoscaleSmServiceAccounts = 15, PlanType = PlanType.EnterpriseAnnually, - GatewayCustomerId = "1" + GatewayCustomerId = "1", + GatewaySubscriptionId = "2" }; var organizationUpdate = new SecretsManagerSubscriptionUpdate( organization, seatAdjustment: 1, maxAutoscaleSeats: 15, serviceAccountAdjustment: 1, maxAutoscaleServiceAccounts: 15); @@ -218,7 +222,7 @@ public async Task UpdateSubscriptionAsync_WithNonSecretsManagerPlanType_ThrowsBa [Theory] [BitAutoData(PlanType.Free)] - public async Task UpdateSubscriptionAsync_WithHasAdditionalSeatsOptionfalse_ThrowsBadRequestException( + public async Task UpdateSubscriptionAsync_WithHasAdditionalSeatsOptionFalse_ThrowsBadRequestException( PlanType planType, Guid organizationId, SutProvider sutProvider) @@ -227,19 +231,18 @@ public async Task UpdateSubscriptionAsync_WithHasAdditionalSeatsOptionfalse_Thro { Id = organizationId, UseSecretsManager = true, - SmSeats = 10, - SmServiceAccounts = 200, - MaxAutoscaleSmSeats = 20, - MaxAutoscaleSmServiceAccounts = 300, + SmSeats = 2, + SmServiceAccounts = 3, PlanType = planType, GatewayCustomerId = "1", GatewaySubscriptionId = "2" }; var organizationUpdate = new SecretsManagerSubscriptionUpdate( - organization, seatAdjustment: 1, maxAutoscaleSeats: 15, serviceAccountAdjustment: 1, maxAutoscaleServiceAccounts: 300); + organization, seatAdjustment: 1, maxAutoscaleSeats: null, serviceAccountAdjustment: 0, maxAutoscaleServiceAccounts: null); var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); - Assert.Contains("Plan does not allow additional Secrets Manager seats.", exception.Message, StringComparison.InvariantCultureIgnoreCase); + Assert.Contains("You have reached the maximum number of Secrets Manager seats (2) for this plan", + exception.Message, StringComparison.InvariantCultureIgnoreCase); await VerifyDependencyNotCalledAsync(sutProvider); } @@ -254,19 +257,18 @@ public async Task UpdateSubscriptionAsync_WithHasAdditionalServiceAccountOptionF { Id = organizationId, UseSecretsManager = true, - SmSeats = 10, - SmServiceAccounts = 200, - MaxAutoscaleSmSeats = 20, - MaxAutoscaleSmServiceAccounts = 300, + SmSeats = 2, + SmServiceAccounts = 3, PlanType = planType, GatewayCustomerId = "1", GatewaySubscriptionId = "2" }; var organizationUpdate = new SecretsManagerSubscriptionUpdate( - organization, seatAdjustment: 0, maxAutoscaleSeats: 15, serviceAccountAdjustment: 1, maxAutoscaleServiceAccounts: 300); + organization, seatAdjustment: 0, maxAutoscaleSeats: null, serviceAccountAdjustment: 1, maxAutoscaleServiceAccounts: null); var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(organizationUpdate)); - Assert.Contains("Plan does not allow additional Service Accounts", exception.Message, StringComparison.InvariantCultureIgnoreCase); + Assert.Contains("You have reached the maximum number of service accounts (3) for this plan", + exception.Message, StringComparison.InvariantCultureIgnoreCase); await VerifyDependencyNotCalledAsync(sutProvider); } @@ -314,13 +316,12 @@ await sutProvider.GetDependency().Received(1) // TODO: call ReferenceEventService - see AC-1481 - await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( - Arg.Is(org => + AssertUpdatedOrganization(() => Arg.Is(org => org.Id == organizationId && org.SmSeats == organizationUpdate.SmSeats && org.MaxAutoscaleSmSeats == organizationUpdate.MaxAutoscaleSmSeats - && org.SmServiceAccounts == (organizationServiceAccounts + organizationUpdate.SmServiceAccountsAdjustment) - && org.MaxAutoscaleSmServiceAccounts == organizationUpdate.MaxAutoscaleSmServiceAccounts)); + && org.SmServiceAccounts == (organizationServiceAccounts + serviceAccountAdjustment) + && org.MaxAutoscaleSmServiceAccounts == organizationUpdate.MaxAutoscaleSmServiceAccounts), sutProvider); await sutProvider.GetDependency().Received(1).SendSecretsManagerMaxSeatLimitReachedEmailAsync(organization, organization.MaxAutoscaleSmSeats.Value, Arg.Any>()); await sutProvider.GetDependency().Received(1).SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(organization, organization.MaxAutoscaleSmServiceAccounts.Value, Arg.Any>()); @@ -370,13 +371,12 @@ await sutProvider.GetDependency().Received(1) // TODO: call ReferenceEventService - see AC-1481 - await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( - Arg.Is(org => + AssertUpdatedOrganization(() => Arg.Is(org => org.Id == organizationId && org.SmSeats == organizationUpdate.SmSeats && org.MaxAutoscaleSmSeats == organizationUpdate.MaxAutoscaleSmSeats - && org.SmServiceAccounts == (organizationServiceAccounts + organizationUpdate.SmServiceAccountsAdjustment) - && org.MaxAutoscaleSmServiceAccounts == organizationUpdate.MaxAutoscaleSmServiceAccounts)); + && org.SmServiceAccounts == (organizationServiceAccounts + serviceAccountAdjustment) + && org.MaxAutoscaleSmServiceAccounts == organizationUpdate.MaxAutoscaleSmServiceAccounts), sutProvider); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().SendSecretsManagerMaxSeatLimitReachedEmailAsync(default, default, default); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(default, default, default); @@ -616,13 +616,110 @@ await sutProvider.GetDependency().Received(1).AdjustServiceAcco plan, expectedSmServiceAccountsExcludingBase); // TODO: call ReferenceEventService - see AC-1481 - await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync( - Arg.Is(o => + AssertUpdatedOrganization(() => Arg.Is(o => o.Id == organizationId && o.SmSeats == organizationSeats && o.MaxAutoscaleSmSeats == organizationMaxAutoscaleSeats && o.SmServiceAccounts == expectedSmServiceAccounts - && o.MaxAutoscaleSmServiceAccounts == organizationMaxAutoscaleServiceAccounts)); + && o.MaxAutoscaleSmServiceAccounts == organizationMaxAutoscaleServiceAccounts), sutProvider); + } + + [Theory] + [BitAutoData(PlanType.EnterpriseAnnually)] + public async Task ServiceAccountAutoscaling_MaxLimitReached_ThrowsBadRequestException( + PlanType planType, + Organization organization, + SutProvider sutProvider) + { + organization.PlanType = planType; + organization.UseSecretsManager = true; + organization.SmServiceAccounts = 9; + organization.MaxAutoscaleSmServiceAccounts = 10; + + var update = new SecretsManagerSubscriptionUpdate(organization, true); + update.AdjustServiceAccounts(2); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); + Assert.Contains("Secrets Manager service account limit has been reached.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData(PlanType.EnterpriseAnnually)] + public async Task ServiceAccountAutoscaling_Subtracting_ThrowsBadRequestException( + PlanType planType, + Organization organization, + SutProvider sutProvider) + { + organization.PlanType = planType; + organization.UseSecretsManager = true; + + var update = new SecretsManagerSubscriptionUpdate(organization, true); + update.AdjustServiceAccounts(-2); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); + Assert.Contains("Cannot use autoscaling to subtract service accounts.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData(PlanType.EnterpriseAnnually)] + public async Task SmSeatAutoscaling_MaxLimitReached_ThrowsBadRequestException( + PlanType planType, + Organization organization, + SutProvider sutProvider) + { + organization.PlanType = planType; + organization.UseSecretsManager = true; + organization.SmSeats = 9; + organization.MaxAutoscaleSmSeats = 10; + + var update = new SecretsManagerSubscriptionUpdate(organization, true); + update.AdjustSeats(2); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); + Assert.Contains("Secrets Manager seat limit has been reached.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData(PlanType.EnterpriseAnnually)] + public async Task SmSeatAutoscaling_Subtracting_ThrowsBadRequestException( + PlanType planType, + Organization organization, + SutProvider sutProvider) + { + organization.PlanType = planType; + organization.UseSecretsManager = true; + + var update = new SecretsManagerSubscriptionUpdate(organization, true); + update.AdjustSeats(-2); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); + Assert.Contains("Cannot use autoscaling to subtract seats.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + + [Theory] + [BitAutoData(false, "Cannot update subscription on a self-hosted instance.")] + [BitAutoData(true, "Cannot autoscale on a self-hosted instance.")] + public async Task UpdatingSubscription_WhenSelfHosted_ThrowsBadRequestException( + bool autoscaling, + string expectedError, + Organization organization, + SutProvider sutProvider) + { + organization.PlanType = PlanType.EnterpriseAnnually; + organization.UseSecretsManager = true; + + var update = new SecretsManagerSubscriptionUpdate(organization, autoscaling); + update.AdjustSeats(2); + + sutProvider.GetDependency().SelfHosted.Returns(true); + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateSubscriptionAsync(update)); + Assert.Contains(expectedError, exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); } private static async Task VerifyDependencyNotCalledAsync(SutProvider sutProvider) @@ -632,10 +729,17 @@ await sutProvider.GetDependency().DidNotReceive() await sutProvider.GetDependency().DidNotReceive() .AdjustServiceAccountsAsync(Arg.Any(), Arg.Any(), Arg.Any()); // TODO: call ReferenceEventService - see AC-1481 - await sutProvider.GetDependency().DidNotReceive() - .ReplaceAndUpdateCacheAsync(Arg.Any()); await sutProvider.GetDependency().DidNotReceive() .SendOrganizationMaxSeatLimitReachedEmailAsync(Arg.Any(), Arg.Any(), Arg.Any>()); + + sutProvider.GetDependency().DidNotReceiveWithAnyArgs().ReplaceAsync(default); + sutProvider.GetDependency().DidNotReceiveWithAnyArgs().UpsertOrganizationAbilityAsync(default); + } + + private void AssertUpdatedOrganization(Func organizationMatcher, SutProvider sutProvider) + { + sutProvider.GetDependency().Received(1).ReplaceAsync(organizationMatcher()); + sutProvider.GetDependency().Received(1).UpsertOrganizationAbilityAsync(organizationMatcher()); } } diff --git a/test/Core.Test/OrganizationFeatures/OrganizationUsers/CountNewSmSeatsRequiredQueryTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationUsers/CountNewSmSeatsRequiredQueryTests.cs new file mode 100644 index 000000000000..375fcc66a289 --- /dev/null +++ b/test/Core.Test/OrganizationFeatures/OrganizationUsers/CountNewSmSeatsRequiredQueryTests.cs @@ -0,0 +1,110 @@ +using Bit.Core.Entities; +using Bit.Core.Exceptions; +using Bit.Core.OrganizationFeatures.OrganizationUsers; +using Bit.Core.Repositories; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.OrganizationFeatures.OrganizationUsers; + +[SutProviderCustomize] +public class CountNewSmSeatsRequiredQueryTests +{ + [Theory] + [BitAutoData(2, 5, 2, 0)] + [BitAutoData(0, 5, 2, 0)] + [BitAutoData(6, 5, 2, 3)] + [BitAutoData(2, 5, 10, 7)] + public async Task CountNewSmSeatsRequiredAsync_ReturnsCorrectCount( + int usersToAdd, + int organizationSmSeats, + int currentOccupiedSmSeats, + int expectedNewSmSeatsRequired, + Organization organization, + SutProvider sutProvider) + { + organization.UseSecretsManager = true; + organization.SmSeats = organizationSmSeats; + + sutProvider.GetDependency() + .GetByIdAsync(organization.Id) + .Returns(organization); + + sutProvider.GetDependency() + .GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id) + .Returns(currentOccupiedSmSeats); + + var result = await sutProvider.Sut.CountNewSmSeatsRequiredAsync(organization.Id, usersToAdd); + + Assert.Equal(expectedNewSmSeatsRequired, result); + } + + [Theory] + [BitAutoData(0)] + [BitAutoData(5)] + public async Task CountNewSmSeatsRequiredAsync_WithNullSmSeats_ReturnsZero( + int usersToAdd, + Organization organization, + SutProvider sutProvider) + { + const int expected = 0; + + organization.UseSecretsManager = true; + organization.SmSeats = null; + + sutProvider.GetDependency() + .GetByIdAsync(organization.Id) + .Returns(organization); + + var result = await sutProvider.Sut.CountNewSmSeatsRequiredAsync(organization.Id, usersToAdd); + + Assert.Equal(expected, result); + } + + [Theory, BitAutoData] + public async Task CountNewSmSeatsRequiredAsync_WithNonExistentOrganizationId_ThrowsNotFound( + Guid organizationId, int usersToAdd, + SutProvider sutProvider) + { + await Assert.ThrowsAsync(async () => await sutProvider.Sut.CountNewSmSeatsRequiredAsync(organizationId, usersToAdd)); + } + + [Theory, BitAutoData] + public async Task CountNewSmSeatsRequiredAsync_WithOrganizationUseSecretsManagerFalse_ThrowsNotFound( + Organization organization, int usersToAdd, + SutProvider sutProvider) + { + organization.UseSecretsManager = false; + + sutProvider.GetDependency() + .GetByIdAsync(organization.Id) + .Returns(organization); + + var exception = await Assert.ThrowsAsync(async () => + await sutProvider.Sut.CountNewSmSeatsRequiredAsync(organization.Id, usersToAdd)); + Assert.Contains("Organization does not use Secrets Manager", exception.Message); + } + + [Theory, BitAutoData] + public async Task CountNewSmSeatsRequiredAsync_WithSecretsManagerBeta_ReturnsZero( + int usersToAdd, + Organization organization, + SutProvider sutProvider) + { + organization.UseSecretsManager = true; + organization.SecretsManagerBeta = true; + + sutProvider.GetDependency() + .GetByIdAsync(organization.Id) + .Returns(organization); + + var result = await sutProvider.Sut.CountNewSmSeatsRequiredAsync(organization.Id, usersToAdd); + + Assert.Equal(0, result); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetOccupiedSmSeatCountByOrganizationIdAsync(default); + } +} diff --git a/test/Core.Test/SecretsManager/Commands/EnableAccessSecretsManager/EnableAccessSecretsManagerCommandTests.cs b/test/Core.Test/SecretsManager/Commands/EnableAccessSecretsManager/EnableAccessSecretsManagerCommandTests.cs deleted file mode 100644 index ae783bc547c3..000000000000 --- a/test/Core.Test/SecretsManager/Commands/EnableAccessSecretsManager/EnableAccessSecretsManagerCommandTests.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Bit.Core.Entities; -using Bit.Core.Repositories; -using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager; -using Bit.Test.Common.AutoFixture; -using Bit.Test.Common.AutoFixture.Attributes; -using Bit.Test.Common.Helpers; -using NSubstitute; -using Xunit; - -namespace Bit.Core.Test.SecretsManager.Commands.EnableAccessSecretsManager; - -[SutProviderCustomize] -public class EnableAccessSecretsManagerCommandTests -{ - [Theory] - [BitAutoData] - public async Task EnableUsers_UsersAlreadyEnabled_DoesNotCallRepository( - SutProvider sutProvider, ICollection data) - { - foreach (var item in data) - { - item.AccessSecretsManager = true; - } - - var result = await sutProvider.Sut.EnableUsersAsync(data); - - await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() - .ReplaceManyAsync(default); - - Assert.Equal(data.Count, result.Count); - Assert.Equal(data.Count, - result.Where(x => x.error == "User already has access to Secrets Manager").ToList().Count); - } - - [Theory] - [BitAutoData] - public async Task EnableUsers_OneUserNotEnabled_CallsRepositoryForOne( - SutProvider sutProvider, ICollection data) - { - var firstUser = new List(); - foreach (var item in data) - { - if (item == data.First()) - { - item.AccessSecretsManager = false; - firstUser.Add(item); - } - else - { - item.AccessSecretsManager = true; - } - } - - var result = await sutProvider.Sut.EnableUsersAsync(data); - - await sutProvider.GetDependency().Received(1) - .ReplaceManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(firstUser))); - - Assert.Equal(data.Count, result.Count); - Assert.Equal(data.Count - 1, - result.Where(x => x.error == "User already has access to Secrets Manager").ToList().Count); - } - - [Theory] - [BitAutoData] - public async Task EnableUsers_Success( - SutProvider sutProvider, ICollection data) - { - foreach (var item in data) - { - item.AccessSecretsManager = false; - } - - var result = await sutProvider.Sut.EnableUsersAsync(data); - - await sutProvider.GetDependency().Received(1) - .ReplaceManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data))); - - Assert.Equal(data.Count, result.Count); - } -} diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index 06dc51b31760..07548a16d8d4 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -14,6 +14,8 @@ using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Models.StaticStore; +using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; +using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; @@ -27,6 +29,7 @@ using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; +using NSubstitute.ExceptionExtensions; using Xunit; using Organization = Bit.Core.Entities.Organization; using OrganizationUser = Bit.Core.Entities.OrganizationUser; @@ -639,6 +642,97 @@ await sutProvider.GetDependency().Received(1) await sutProvider.GetDependency().Received(1).LogOrganizationUserEventsAsync(Arg.Any>()); } + [Theory, BitAutoData] + public async Task InviteUser_WithSecretsManager_Passes(Organization organization, + IEnumerable<(OrganizationUserInvite invite, string externalId)> invites, + [OrganizationUser(type: OrganizationUserType.Owner, status: OrganizationUserStatusType.Confirmed)] OrganizationUser savingUser, + SutProvider sutProvider) + { + InviteUserHelper_ArrangeValidPermissions(organization, savingUser, sutProvider); + + // Set up some invites to grant access to SM + invites.First().invite.AccessSecretsManager = true; + var invitedSmUsers = invites.First().invite.Emails.Count(); + + // Assume we need to add seats for all invited SM users + sutProvider.GetDependency() + .CountNewSmSeatsRequiredAsync(organization.Id, invitedSmUsers).Returns(invitedSmUsers); + + await sutProvider.Sut.InviteUsersAsync(organization.Id, savingUser.Id, invites); + + sutProvider.GetDependency().Received(1) + .UpdateSubscriptionAsync(Arg.Is(update => + update.SmSeats == organization.SmSeats + invitedSmUsers && + !update.SmServiceAccountsChanged && + !update.MaxAutoscaleSmSeatsChanged && + !update.MaxAutoscaleSmSeatsChanged)); + } + + [Theory, BitAutoData] + public async Task InviteUser_WithSecretsManager_WhenErrorIsThrown_RevertsAutoscaling(Organization organization, + IEnumerable<(OrganizationUserInvite invite, string externalId)> invites, + [OrganizationUser(type: OrganizationUserType.Owner, status: OrganizationUserStatusType.Confirmed)] OrganizationUser savingUser, + SutProvider sutProvider) + { + var initialSmSeats = organization.SmSeats; + InviteUserHelper_ArrangeValidPermissions(organization, savingUser, sutProvider); + + // Set up some invites to grant access to SM + invites.First().invite.AccessSecretsManager = true; + var invitedSmUsers = invites.First().invite.Emails.Count(); + + // Assume we need to add seats for all invited SM users + sutProvider.GetDependency() + .CountNewSmSeatsRequiredAsync(organization.Id, invitedSmUsers).Returns(invitedSmUsers); + + // Mock SecretsManagerSubscriptionUpdateCommand to actually change the organization's subscription in memory + sutProvider.GetDependency() + .UpdateSubscriptionAsync(Arg.Any()) + .ReturnsForAnyArgs(Task.FromResult(0)).AndDoes(x => organization.SmSeats += invitedSmUsers); + + // Throw error at the end of the try block + sutProvider.GetDependency().RaiseEventAsync(default).ThrowsForAnyArgs(); + + await Assert.ThrowsAsync(async () => await sutProvider.Sut.InviteUsersAsync(organization.Id, savingUser.Id, invites)); + + // OrgUser is reverted + // Note: we don't know what their guids are so comparing length is the best we can do + var invitedEmails = invites.SelectMany(i => i.invite.Emails); + sutProvider.GetDependency().Received(1).DeleteManyAsync( + Arg.Is>(ids => ids.Count() == invitedEmails.Count())); + + Received.InOrder(() => + { + // Initial autoscaling + sutProvider.GetDependency() + .UpdateSubscriptionAsync(Arg.Is(update => + update.SmSeats == initialSmSeats + invitedSmUsers && + !update.SmServiceAccountsChanged && + !update.MaxAutoscaleSmSeatsChanged && + !update.MaxAutoscaleSmSeatsChanged)); + + // Revert autoscaling + sutProvider.GetDependency() + .UpdateSubscriptionAsync(Arg.Is(update => + update.SmSeats == initialSmSeats && + !update.SmServiceAccountsChanged && + !update.MaxAutoscaleSmSeatsChanged && + !update.MaxAutoscaleSmSeatsChanged)); + }); + } + + private void InviteUserHelper_ArrangeValidPermissions(Organization organization, OrganizationUser savingUser, + SutProvider sutProvider) + { + savingUser.OrganizationId = organization.Id; + organization.UseCustomPermissions = true; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + sutProvider.GetDependency().OrganizationOwner(organization.Id).Returns(true); + sutProvider.GetDependency().ManageUsers(organization.Id).Returns(true); + sutProvider.GetDependency().GetManyByOrganizationAsync(savingUser.OrganizationId, OrganizationUserType.Owner) + .Returns(new List { savingUser }); + } + [Theory, BitAutoData] public async Task SaveUser_NoUserId_Throws(OrganizationUser user, Guid? savingUserId, IEnumerable collections, IEnumerable groups, SutProvider sutProvider) From d905bbbf69f80ccb7a58cba5bc9a63addba07ad6 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Tue, 1 Aug 2023 09:32:46 +1000 Subject: [PATCH 40/41] Move bitwarden_license include reference into conditional block --- src/Admin/Admin.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Admin/Admin.csproj b/src/Admin/Admin.csproj index ac7efce42bc6..5557884b1d6e 100644 --- a/src/Admin/Admin.csproj +++ b/src/Admin/Admin.csproj @@ -3,11 +3,10 @@ bitwarden-Admin - + - @@ -20,6 +19,7 @@ + From a1284549f40b97bc7a1954ca80966ed134397dbf Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Wed, 2 Aug 2023 19:58:12 +0100 Subject: [PATCH 41/41] [AC 1526]Show current SM seat and service account usage in Bitwarden Portal (#3142) * changes base on the tickets request * Code refactoring * Removed the unwanted method * Add implementation to the new method * Resolve some pr comments * resolve lint issue * resolve pr comments * add the new noop files * Add new noop file and resolve some pr comments * resolve pr comments * removed unused method --- .../Repositories/ProjectRepository.cs | 10 ++ .../Repositories/SecretRepository.cs | 10 ++ .../Controllers/OrganizationsController.cs | 29 +++++- src/Admin/Models/OrganizationEditModel.cs | 5 +- src/Admin/Models/OrganizationViewModel.cs | 13 ++- .../Organizations/_ViewInformation.cshtml | 12 +++ src/Admin/packages.lock.json | 30 +++--- .../Repositories/IProjectRepository.cs | 1 + .../Repositories/ISecretRepository.cs | 1 + .../Noop/NoopProjectRepository.cs | 65 +++++++++++++ .../Repositories/Noop/NoopSecretRepository.cs | 91 +++++++++++++++++++ .../Utilities/ServiceCollectionExtensions.cs | 2 + 12 files changed, 248 insertions(+), 21 deletions(-) create mode 100644 src/Core/SecretsManager/Repositories/Noop/NoopProjectRepository.cs create mode 100644 src/Core/SecretsManager/Repositories/Noop/NoopSecretRepository.cs diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs index a350a62e9346..9fdbe42814bd 100644 --- a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs +++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs @@ -40,6 +40,16 @@ public async Task> GetManyByOrganizationId return await projects.ToListAsync(); } + public async Task GetProjectCountByOrganizationIdAsync(Guid organizationId) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + return await dbContext.Project + .CountAsync(ou => ou.OrganizationId == organizationId); + } + } + public async Task> GetManyByOrganizationIdWriteAccessAsync( Guid organizationId, Guid userId, AccessClientType accessType) { diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/SecretRepository.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/SecretRepository.cs index 078147f3ab6f..9fa346fbc835 100644 --- a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/SecretRepository.cs +++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/SecretRepository.cs @@ -57,6 +57,16 @@ public async Task> GetManyByOrganizationIdA return await secrets.ToListAsync(); } + public async Task GetSecretsCountByOrganizationIdAsync(Guid organizationId) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + return await dbContext.Secret + .CountAsync(ou => ou.OrganizationId == organizationId && ou.DeletedDate == null); + } + } + public async Task> GetManyByOrganizationIdInTrashByIdsAsync(Guid organizationId, IEnumerable ids) { using (var scope = ServiceScopeFactory.CreateScope()) diff --git a/src/Admin/Controllers/OrganizationsController.cs b/src/Admin/Controllers/OrganizationsController.cs index 8fc74435a32a..2eb223716d5d 100644 --- a/src/Admin/Controllers/OrganizationsController.cs +++ b/src/Admin/Controllers/OrganizationsController.cs @@ -8,6 +8,7 @@ using Bit.Core.Models.OrganizationConnectionConfigs; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.Repositories; +using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; using Bit.Core.Settings; using Bit.Core.Tools.Enums; @@ -42,6 +43,9 @@ public class OrganizationsController : Controller private readonly ILogger _logger; private readonly IAccessControlService _accessControlService; private readonly ICurrentContext _currentContext; + private readonly ISecretRepository _secretRepository; + private readonly IProjectRepository _projectRepository; + private readonly IServiceAccountRepository _serviceAccountRepository; public OrganizationsController( IOrganizationService organizationService, @@ -62,7 +66,10 @@ public OrganizationsController( IProviderRepository providerRepository, ILogger logger, IAccessControlService accessControlService, - ICurrentContext currentContext) + ICurrentContext currentContext, + ISecretRepository secretRepository, + IProjectRepository projectRepository, + IServiceAccountRepository serviceAccountRepository) { _organizationService = organizationService; _organizationRepository = organizationRepository; @@ -83,6 +90,9 @@ public OrganizationsController( _logger = logger; _accessControlService = accessControlService; _currentContext = currentContext; + _secretRepository = secretRepository; + _projectRepository = projectRepository; + _serviceAccountRepository = serviceAccountRepository; } [RequirePermission(Permission.Org_List_View)] @@ -137,7 +147,14 @@ public async Task View(Guid id) } var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id); var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null; - return View(new OrganizationViewModel(organization, provider, billingSyncConnection, users, ciphers, collections, groups, policies)); + var secrets = organization.UseSecretsManager ? await _secretRepository.GetSecretsCountByOrganizationIdAsync(id) : -1; + var projects = organization.UseSecretsManager ? await _projectRepository.GetProjectCountByOrganizationIdAsync(id) : -1; + var serviceAccounts = organization.UseSecretsManager ? await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(id) : -1; + var smSeats = organization.UseSecretsManager + ? await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id) + : -1; + return View(new OrganizationViewModel(organization, provider, billingSyncConnection, users, ciphers, collections, groups, policies, + secrets, projects, serviceAccounts, smSeats)); } [SelfHosted(NotSelfHostedOnly = true)] @@ -165,8 +182,14 @@ public async Task Edit(Guid id) var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id); var billingInfo = await _paymentService.GetBillingAsync(organization); var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null; + var secrets = organization.UseSecretsManager ? await _secretRepository.GetSecretsCountByOrganizationIdAsync(id) : -1; + var projects = organization.UseSecretsManager ? await _projectRepository.GetProjectCountByOrganizationIdAsync(id) : -1; + var serviceAccounts = organization.UseSecretsManager ? await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(id) : -1; + var smSeats = organization.UseSecretsManager + ? await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id) + : -1; return View(new OrganizationEditModel(organization, provider, users, ciphers, collections, groups, policies, - billingInfo, billingSyncConnection, _globalSettings)); + billingInfo, billingSyncConnection, _globalSettings, secrets, projects, serviceAccounts, smSeats)); } [HttpPost] diff --git a/src/Admin/Models/OrganizationEditModel.cs b/src/Admin/Models/OrganizationEditModel.cs index 93a52347d12f..9b02a72895b7 100644 --- a/src/Admin/Models/OrganizationEditModel.cs +++ b/src/Admin/Models/OrganizationEditModel.cs @@ -27,8 +27,9 @@ public OrganizationEditModel(Provider provider) public OrganizationEditModel(Organization org, Provider provider, IEnumerable orgUsers, IEnumerable ciphers, IEnumerable collections, IEnumerable groups, IEnumerable policies, BillingInfo billingInfo, IEnumerable connections, - GlobalSettings globalSettings) - : base(org, provider, connections, orgUsers, ciphers, collections, groups, policies) + GlobalSettings globalSettings, int secrets, int projects, int serviceAccounts, int smSeats) + : base(org, provider, connections, orgUsers, ciphers, collections, groups, policies, secrets, projects, + serviceAccounts, smSeats) { BillingInfo = billingInfo; BraintreeMerchantId = globalSettings.Braintree.MerchantId; diff --git a/src/Admin/Models/OrganizationViewModel.cs b/src/Admin/Models/OrganizationViewModel.cs index 57edc49a7c75..491ff9191bed 100644 --- a/src/Admin/Models/OrganizationViewModel.cs +++ b/src/Admin/Models/OrganizationViewModel.cs @@ -12,7 +12,9 @@ public OrganizationViewModel() { } public OrganizationViewModel(Organization org, Provider provider, IEnumerable connections, IEnumerable orgUsers, IEnumerable ciphers, IEnumerable collections, - IEnumerable groups, IEnumerable policies) + IEnumerable groups, IEnumerable policies, int secretsCount, int projectCount, int serviceAccountsCount, + int smSeatsCount) + { Organization = org; Provider = provider; @@ -37,6 +39,10 @@ public OrganizationViewModel(Organization org, Provider provider, IEnumerable u.Type == OrganizationUserType.Admin && u.Status == organizationUserStatus) .Select(u => u.Email)); + Secrets = secretsCount; + Projects = projectCount; + ServiceAccounts = serviceAccountsCount; + SmSeats = smSeatsCount; } public Organization Organization { get; set; } @@ -53,4 +59,9 @@ public OrganizationViewModel(Organization org, Provider provider, IEnumerable Organization.UseSecretsManager; } diff --git a/src/Admin/Views/Organizations/_ViewInformation.cshtml b/src/Admin/Views/Organizations/_ViewInformation.cshtml index 24e69d298b0a..df4513ccaa86 100644 --- a/src/Admin/Views/Organizations/_ViewInformation.cshtml +++ b/src/Admin/Views/Organizations/_ViewInformation.cshtml @@ -32,6 +32,18 @@
Collections
@Model.CollectionCount
+
Secrets
+
@(Model.UseSecretsManager ? Model.Secrets: "N/A")
+ +
Projects
+
@(Model.UseSecretsManager ? Model.Projects: "N/A")
+ +
Service Accounts
+
@(Model.UseSecretsManager ? Model.ServiceAccounts: "N/A")
+ +
Secrets Manager Seats
+
@(Model.UseSecretsManager ? Model.SmSeats: "N/A" )
+
Groups
@Model.GroupCount
diff --git a/src/Admin/packages.lock.json b/src/Admin/packages.lock.json index f32f6cafd3de..d601fe600ebd 100644 --- a/src/Admin/packages.lock.json +++ b/src/Admin/packages.lock.json @@ -2821,15 +2821,15 @@ "commercial.core": { "type": "Project", "dependencies": { - "Core": "[2023.7.0, )" + "Core": "[2023.7.1, )" } }, "commercial.infrastructure.entityframework": { "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.7.0, )", - "Infrastructure.EntityFramework": "[2023.7.0, )" + "Core": "[2023.7.1, )", + "Infrastructure.EntityFramework": "[2023.7.1, )" } }, "core": { @@ -2877,7 +2877,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.7.0, )", + "Core": "[2023.7.1, )", "Dapper": "[2.0.123, )" } }, @@ -2885,7 +2885,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.7.0, )", + "Core": "[2023.7.1, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2897,7 +2897,7 @@ "migrator": { "type": "Project", "dependencies": { - "Core": "[2023.7.0, )", + "Core": "[2023.7.1, )", "Microsoft.Extensions.Logging": "[6.0.0, )", "dbup-sqlserver": "[5.0.8, )" } @@ -2905,30 +2905,30 @@ "mysqlmigrations": { "type": "Project", "dependencies": { - "Core": "[2023.7.0, )", - "Infrastructure.EntityFramework": "[2023.7.0, )" + "Core": "[2023.7.1, )", + "Infrastructure.EntityFramework": "[2023.7.1, )" } }, "postgresmigrations": { "type": "Project", "dependencies": { - "Core": "[2023.7.0, )", - "Infrastructure.EntityFramework": "[2023.7.0, )" + "Core": "[2023.7.1, )", + "Infrastructure.EntityFramework": "[2023.7.1, )" } }, "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.7.0, )", - "Infrastructure.Dapper": "[2023.7.0, )", - "Infrastructure.EntityFramework": "[2023.7.0, )" + "Core": "[2023.7.1, )", + "Infrastructure.Dapper": "[2023.7.1, )", + "Infrastructure.EntityFramework": "[2023.7.1, )" } }, "sqlitemigrations": { "type": "Project", "dependencies": { - "Core": "[2023.7.0, )", - "Infrastructure.EntityFramework": "[2023.7.0, )" + "Core": "[2023.7.1, )", + "Infrastructure.EntityFramework": "[2023.7.1, )" } } } diff --git a/src/Core/SecretsManager/Repositories/IProjectRepository.cs b/src/Core/SecretsManager/Repositories/IProjectRepository.cs index e425df6f45f4..62770d972060 100644 --- a/src/Core/SecretsManager/Repositories/IProjectRepository.cs +++ b/src/Core/SecretsManager/Repositories/IProjectRepository.cs @@ -16,4 +16,5 @@ public interface IProjectRepository Task> ImportAsync(IEnumerable projects); Task<(bool Read, bool Write)> AccessToProjectAsync(Guid id, Guid userId, AccessClientType accessType); Task ProjectsAreInOrganization(List projectIds, Guid organizationId); + Task GetProjectCountByOrganizationIdAsync(Guid organizationId); } diff --git a/src/Core/SecretsManager/Repositories/ISecretRepository.cs b/src/Core/SecretsManager/Repositories/ISecretRepository.cs index 68422cb44cd2..e8f0673d1e2f 100644 --- a/src/Core/SecretsManager/Repositories/ISecretRepository.cs +++ b/src/Core/SecretsManager/Repositories/ISecretRepository.cs @@ -21,4 +21,5 @@ public interface ISecretRepository Task UpdateRevisionDates(IEnumerable ids); Task<(bool Read, bool Write)> AccessToSecretAsync(Guid id, Guid userId, AccessClientType accessType); Task EmptyTrash(DateTime nowTime, uint deleteAfterThisNumberOfDays); + Task GetSecretsCountByOrganizationIdAsync(Guid organizationId); } diff --git a/src/Core/SecretsManager/Repositories/Noop/NoopProjectRepository.cs b/src/Core/SecretsManager/Repositories/Noop/NoopProjectRepository.cs new file mode 100644 index 000000000000..f8b16f276a5e --- /dev/null +++ b/src/Core/SecretsManager/Repositories/Noop/NoopProjectRepository.cs @@ -0,0 +1,65 @@ +using Bit.Core.Enums; +using Bit.Core.SecretsManager.Entities; +using Bit.Core.SecretsManager.Models.Data; + +namespace Bit.Core.SecretsManager.Repositories.Noop; + +public class NoopProjectRepository : IProjectRepository +{ + public Task> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId, + AccessClientType accessType) + { + return Task.FromResult(null as IEnumerable); + } + + public Task> GetManyByOrganizationIdWriteAccessAsync(Guid organizationId, Guid userId, + AccessClientType accessType) + { + return Task.FromResult(null as IEnumerable); + } + + public Task> GetManyWithSecretsByIds(IEnumerable ids) + { + return Task.FromResult(null as IEnumerable); + } + + public Task GetByIdAsync(Guid id) + { + return Task.FromResult(null as Project); + } + + public Task CreateAsync(Project project) + { + return Task.FromResult(null as Project); + } + + public Task ReplaceAsync(Project project) + { + return Task.FromResult(0); + } + + public Task DeleteManyByIdAsync(IEnumerable ids) + { + return Task.FromResult(0); + } + + public Task> ImportAsync(IEnumerable projects) + { + return Task.FromResult(null as IEnumerable); + } + + public Task<(bool Read, bool Write)> AccessToProjectAsync(Guid id, Guid userId, AccessClientType accessType) + { + return Task.FromResult((false, false)); + } + + public Task ProjectsAreInOrganization(List projectIds, Guid organizationId) + { + return Task.FromResult(false); + } + + public Task GetProjectCountByOrganizationIdAsync(Guid organizationId) + { + return Task.FromResult(0); + } +} diff --git a/src/Core/SecretsManager/Repositories/Noop/NoopSecretRepository.cs b/src/Core/SecretsManager/Repositories/Noop/NoopSecretRepository.cs new file mode 100644 index 000000000000..8f65322ac11b --- /dev/null +++ b/src/Core/SecretsManager/Repositories/Noop/NoopSecretRepository.cs @@ -0,0 +1,91 @@ +using Bit.Core.Enums; +using Bit.Core.SecretsManager.Entities; +using Bit.Core.SecretsManager.Models.Data; + +namespace Bit.Core.SecretsManager.Repositories.Noop; + +public class NoopSecretRepository : ISecretRepository +{ + public Task> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId, + AccessClientType accessType) + { + return Task.FromResult(null as IEnumerable); + } + + public Task> GetManyByOrganizationIdInTrashAsync(Guid organizationId) + { + return Task.FromResult(null as IEnumerable); + } + + public Task> GetManyByOrganizationIdInTrashByIdsAsync(Guid organizationId, + IEnumerable ids) + { + return Task.FromResult(null as IEnumerable); + } + + public Task> GetManyByIds(IEnumerable ids) + { + return Task.FromResult(null as IEnumerable); + } + + public Task> GetManyByProjectIdAsync(Guid projectId, Guid userId, + AccessClientType accessType) + { + return Task.FromResult(null as IEnumerable); + } + + public Task GetByIdAsync(Guid id) + { + return Task.FromResult(null as Secret); + } + + public Task CreateAsync(Secret secret) + { + return Task.FromResult(null as Secret); + } + + public Task UpdateAsync(Secret secret) + { + return Task.FromResult(null as Secret); + } + + public Task SoftDeleteManyByIdAsync(IEnumerable ids) + { + return Task.FromResult(0); + } + + public Task HardDeleteManyByIdAsync(IEnumerable ids) + { + return Task.FromResult(0); + } + + public Task RestoreManyByIdAsync(IEnumerable ids) + { + return Task.FromResult(0); + } + + public Task> ImportAsync(IEnumerable secrets) + { + return Task.FromResult(null as IEnumerable); + } + + public Task UpdateRevisionDates(IEnumerable ids) + { + return Task.FromResult(0); + } + + public Task<(bool Read, bool Write)> AccessToSecretAsync(Guid id, Guid userId, AccessClientType accessType) + { + return Task.FromResult((false, false)); + } + + public Task EmptyTrash(DateTime nowTime, uint deleteAfterThisNumberOfDays) + { + return Task.FromResult(0); + } + + public Task GetSecretsCountByOrganizationIdAsync(Guid organizationId) + { + return Task.FromResult(0); + } +} diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index 97695d171f5a..ea97af041958 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -332,6 +332,8 @@ public static void AddOosServices(this IServiceCollection services) { services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } public static void AddNoopServices(this IServiceCollection services)