diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1d66ccb4b639..7041323880a1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,9 +2,18 @@ # # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners +# The following owners will be the default owners for everything in the repo. +# Unless a later match takes precedence +* @bitwarden/tech-leads + # DevOps for Actions and other workflow changes. .github/workflows @bitwarden/dept-devops +# DevOps for Docker changes. +**/Dockerfile @bitwarden/dept-devops +**/*.Dockerfile @bitwarden/dept-devops +**/.dockerignore @bitwarden/dept-devops + ## Auth team files ## **/Auth @bitwarden/team-auth-dev bitwarden_license/src/Sso @bitwarden/team-auth-dev @@ -18,4 +27,14 @@ src/Identity @bitwarden/team-auth-dev bitwarden_license/src/Scim @bitwarden/team-admin-console-dev bitwarden_license/src/test/Scim.IntegrationTest @bitwarden/team-admin-console-dev bitwarden_license/src/test/Scim.ScimTest @bitwarden/team-admin-console-dev -**/AdminConsole @bitwarden/team-admin-console-dev \ No newline at end of file +**/AdminConsole @bitwarden/team-admin-console-dev + +# Billing Team +**/*billing* @bitwarden/team-billing-dev +**/*subscription* @bitwarden/team-billing-dev +**/*stripe* @bitwarden/team-billing-dev +**/*paypal* @bitwarden/team-billing-dev +**/*braintree* @bitwarden/team-billing-dev +**/*bitpay* @bitwarden/team-billing-dev +**/*freshdesk* @bitwarden/team-billing-dev +**/*freshsales* @bitwarden/team-billing-dev \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 165efd0c7fe9..8fc28e2921e6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install cloc run: | @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up dotnet uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 @@ -49,7 +49,7 @@ jobs: NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages steps: - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up dotnet uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 @@ -135,7 +135,7 @@ jobs: node: true steps: - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up dotnet uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 @@ -246,7 +246,7 @@ jobs: dotnet: true steps: - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Check Branch to Publish env: @@ -277,7 +277,7 @@ jobs: - name: Retrieve github PAT secrets id: retrieve-secret-pat - uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/get-keyvault-secrets@c970b0fb89bd966749280e832928db62040812bf with: keyvault: "bitwarden-ci" secrets: "github-pat-bitwarden-devops-bot-repo-scope" @@ -337,7 +337,7 @@ jobs: needs: build-docker steps: - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up dotnet uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 @@ -476,7 +476,7 @@ jobs: - win-x64 steps: - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up dotnet uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 @@ -528,7 +528,7 @@ jobs: - name: Retrieve github PAT secrets id: retrieve-secret-pat - uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/get-keyvault-secrets@c970b0fb89bd966749280e832928db62040812bf with: keyvault: "bitwarden-ci" secrets: "github-pat-bitwarden-devops-bot-repo-scope" @@ -603,7 +603,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/get-keyvault-secrets@c970b0fb89bd966749280e832928db62040812bf if: failure() with: keyvault: "bitwarden-ci" diff --git a/.github/workflows/cleanup-after-pr.yml b/.github/workflows/cleanup-after-pr.yml index d24144c0b7f9..ac8a1b624e84 100644 --- a/.github/workflows/cleanup-after-pr.yml +++ b/.github/workflows/cleanup-after-pr.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 ########## ACR ########## - name: Login to Azure - QA Subscription diff --git a/.github/workflows/container-registry-purge.yml b/.github/workflows/container-registry-purge.yml index 622c614c80e0..0a42d465d6fd 100644 --- a/.github/workflows/container-registry-purge.yml +++ b/.github/workflows/container-registry-purge.yml @@ -92,7 +92,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/get-keyvault-secrets@c970b0fb89bd966749280e832928db62040812bf if: failure() with: keyvault: "bitwarden-ci" diff --git a/.github/workflows/database.yml b/.github/workflows/database.yml index 523957ae58e5..3b5b1ac498c5 100644 --- a/.github/workflows/database.yml +++ b/.github/workflows/database.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up dotnet uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 diff --git a/.github/workflows/infrastructure-tests.yml b/.github/workflows/infrastructure-tests.yml index 7669be7b7578..e6f649f9f575 100644 --- a/.github/workflows/infrastructure-tests.yml +++ b/.github/workflows/infrastructure-tests.yml @@ -38,7 +38,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up dotnet uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 diff --git a/.github/workflows/protect-files.yml b/.github/workflows/protect-files.yml index 2c79c8a98353..df595e900c2f 100644 --- a/.github/workflows/protect-files.yml +++ b/.github/workflows/protect-files.yml @@ -30,7 +30,7 @@ jobs: label: "DB-migrations-changed" steps: - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3caa36489563..b22eef8ff524 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,11 +37,11 @@ jobs: fi - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/release-version-check@c970b0fb89bd966749280e832928db62040812bf with: release-type: ${{ github.event.inputs.release_type }} project-type: dotnet @@ -89,7 +89,7 @@ jobs: - name: Download latest Release ${{ matrix.name }} asset if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@bc3bf31f1d9cac9c9d02cae01fc615fa25d38929 + uses: bitwarden/gh-actions/download-artifacts@c970b0fb89bd966749280e832928db62040812bf with: workflow: build.yml workflow_conclusion: success @@ -98,7 +98,7 @@ jobs: - name: Dry Run - Download latest Release ${{ matrix.name }} asset if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@bc3bf31f1d9cac9c9d02cae01fc615fa25d38929 + uses: bitwarden/gh-actions/download-artifacts@c970b0fb89bd966749280e832928db62040812bf with: workflow: build.yml workflow_conclusion: success @@ -211,7 +211,7 @@ jobs: echo "Github Release Option: $RELEASE_OPTION" - name: Checkout repo - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Setup project name id: setup @@ -274,7 +274,7 @@ jobs: steps: - name: Download latest Release Docker Stubs if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@bc3bf31f1d9cac9c9d02cae01fc615fa25d38929 + uses: bitwarden/gh-actions/download-artifacts@c970b0fb89bd966749280e832928db62040812bf with: workflow: build.yml workflow_conclusion: success @@ -287,7 +287,7 @@ jobs: - name: Dry Run - Download latest Release Docker Stubs if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@bc3bf31f1d9cac9c9d02cae01fc615fa25d38929 + uses: bitwarden/gh-actions/download-artifacts@c970b0fb89bd966749280e832928db62040812bf with: workflow: build.yml workflow_conclusion: success diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index 1616404d53a3..21180567727f 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout Branch - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Login to Azure - CI Subscription uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 @@ -23,13 +23,13 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/get-keyvault-secrets@c970b0fb89bd966749280e832928db62040812bf with: keyvault: "bitwarden-ci" secrets: "github-gpg-private-key, github-gpg-private-key-passphrase" - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@d6f3f49f3345e29369fe57596a3ca8f94c4d2ca7 # v5.4.0 + uses: crazy-max/ghaction-import-gpg@82a020f1f7f605c65dd2449b392a52c3fcfef7ef # v6.0.0 with: gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }} passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }} @@ -40,11 +40,14 @@ jobs: run: git switch -c version_bump_${{ github.event.inputs.version_number }} - name: Bump Version - Props - uses: bitwarden/gh-actions/version-bump@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/version-bump@c970b0fb89bd966749280e832928db62040812bf with: version: ${{ github.event.inputs.version_number }} file_path: "Directory.Build.props" + - name: Refresh lockfiles + run: dotnet restore -f --force-evaluate --no-cache + - name: Setup git run: | git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com" diff --git a/.github/workflows/workflow-linter.yml b/.github/workflows/workflow-linter.yml index 13b3eb5110dd..5afd077e8d20 100644 --- a/.github/workflows/workflow-linter.yml +++ b/.github/workflows/workflow-linter.yml @@ -8,4 +8,4 @@ on: jobs: call-workflow: - uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@f096207b7a2f31723165aee6ad03e91716686e78 + uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@c970b0fb89bd966749280e832928db62040812bf diff --git a/.gitignore b/.gitignore index 4b798f3b74a8..dd26998ccea9 100644 --- a/.gitignore +++ b/.gitignore @@ -225,4 +225,4 @@ src/Identity/Identity.zip src/Notifications/Notifications.zip bitwarden_license/src/Portal/Portal.zip bitwarden_license/src/Sso/Sso.zip -src/Api/flags.json +**/src/*/flags.json diff --git a/Directory.Build.props b/Directory.Build.props index 952cacd42b4b..6820a03e3758 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ net6.0 - 2023.9.0 + 2023.9.1 Bit.$(MSBuildProjectName) true enable diff --git a/NuGet.Config b/NuGet.Config deleted file mode 100644 index ecaab16a1e50..000000000000 --- a/NuGet.Config +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/bitwarden_license/src/Commercial.Core/Providers/CreateProviderCommand.cs b/bitwarden_license/src/Commercial.Core/AdminConsole/Providers/CreateProviderCommand.cs similarity index 87% rename from bitwarden_license/src/Commercial.Core/Providers/CreateProviderCommand.cs rename to bitwarden_license/src/Commercial.Core/AdminConsole/Providers/CreateProviderCommand.cs index 36b6fe85c86e..738723a81989 100644 --- a/bitwarden_license/src/Commercial.Core/Providers/CreateProviderCommand.cs +++ b/bitwarden_license/src/Commercial.Core/AdminConsole/Providers/CreateProviderCommand.cs @@ -1,11 +1,12 @@ -using Bit.Core.Entities.Provider; -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Providers.Interfaces; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Services; using Bit.Core.Exceptions; -using Bit.Core.Providers.Interfaces; using Bit.Core.Repositories; -using Bit.Core.Services; -namespace Bit.Commercial.Core.Providers; +namespace Bit.Commercial.Core.AdminConsole.Providers; public class CreateProviderCommand : ICreateProviderCommand { diff --git a/bitwarden_license/src/Commercial.Core/Services/ProviderService.cs b/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs similarity index 97% rename from bitwarden_license/src/Commercial.Core/Services/ProviderService.cs rename to bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs index 9a5f924246c0..a03e92c3f012 100644 --- a/bitwarden_license/src/Commercial.Core/Services/ProviderService.cs +++ b/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs @@ -1,11 +1,13 @@ -using Bit.Core.Context; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Business.Provider; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Services; +using Bit.Core.Context; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Exceptions; using Bit.Core.Models.Business; -using Bit.Core.Models.Business.Provider; using Bit.Core.Models.Data; using Bit.Core.Repositories; using Bit.Core.Services; @@ -13,7 +15,7 @@ using Bit.Core.Utilities; using Microsoft.AspNetCore.DataProtection; -namespace Bit.Commercial.Core.Services; +namespace Bit.Commercial.Core.AdminConsole.Services; public class ProviderService : IProviderService { @@ -354,6 +356,12 @@ public async Task AddOrganization(Guid providerId, Guid organizationId, string k var organization = await _organizationRepository.GetByIdAsync(organizationId); ThrowOnInvalidPlanType(organization.PlanType); + if (organization.UseSecretsManager) + { + throw new BadRequestException( + "The organization is subscribed to Secrets Manager. Please contact Customer Support to manage the subscription."); + } + var providerOrganization = new ProviderOrganization { ProviderId = providerId, diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/AuthorizationHandlers/AccessPolicies/AccessPolicyAuthorizationHandler.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/AuthorizationHandlers/AccessPolicies/AccessPolicyAuthorizationHandler.cs index 2915b4393067..4cb90223e39f 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/AuthorizationHandlers/AccessPolicies/AccessPolicyAuthorizationHandler.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/AuthorizationHandlers/AccessPolicies/AccessPolicyAuthorizationHandler.cs @@ -1,4 +1,5 @@ -using Bit.Core.Context; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Repositories; using Bit.Core.SecretsManager.AuthorizationRequirements; diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/AuthorizationHandlers/ServiceAccounts/ServiceAccountAuthorizationHandler.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/AuthorizationHandlers/ServiceAccounts/ServiceAccountAuthorizationHandler.cs index 2c4e78d10919..e1a4405f1d11 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/AuthorizationHandlers/ServiceAccounts/ServiceAccountAuthorizationHandler.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/AuthorizationHandlers/ServiceAccounts/ServiceAccountAuthorizationHandler.cs @@ -56,6 +56,9 @@ protected override async Task HandleRequirementAsync(AuthorizationHandlerContext case not null when requirement == ServiceAccountOperations.RevokeAccessTokens: await CanRevokeAccessTokensAsync(context, requirement, resource); break; + case not null when requirement == ServiceAccountOperations.ReadEvents: + await CanReadEventsAsync(context, requirement, resource); + break; default: throw new ArgumentException("Unsupported operation requirement type provided.", nameof(requirement)); @@ -169,4 +172,19 @@ await _serviceAccountRepository.AccessToServiceAccountAsync(resource.Id, userId, context.Succeed(requirement); } } + + private async Task CanReadEventsAsync(AuthorizationHandlerContext context, + ServiceAccountOperationRequirement requirement, ServiceAccount resource) + { + var (accessClient, userId) = + await _accessClientQuery.GetAccessClientAsync(context.User, resource.OrganizationId); + var access = + await _serviceAccountRepository.AccessToServiceAccountAsync(resource.Id, userId, + accessClient); + + if (access.Read) + { + context.Succeed(requirement); + } + } } diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Queries/Projects/MaxProjectsQuery.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Queries/Projects/MaxProjectsQuery.cs index fc30c2c6e84b..7afad6e82aa8 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/Queries/Projects/MaxProjectsQuery.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Queries/Projects/MaxProjectsQuery.cs @@ -28,8 +28,8 @@ public MaxProjectsQuery( throw new NotFoundException(); } - var plan = StaticStore.GetSecretsManagerPlan(org.PlanType); - if (plan == null) + var plan = StaticStore.GetPlan(org.PlanType); + if (plan?.SecretsManager == null) { throw new BadRequestException("Existing plan not found."); } @@ -37,7 +37,7 @@ public MaxProjectsQuery( if (plan.Type == PlanType.Free) { var projects = await _projectRepository.GetProjectCountByOrganizationIdAsync(organizationId); - return projects + projectsToAdd > plan.MaxProjects ? (plan.MaxProjects, true) : (plan.MaxProjects, false); + return ((short? max, bool? overMax))(projects + projectsToAdd > plan.SecretsManager.MaxProjects ? (plan.SecretsManager.MaxProjects, true) : (plan.SecretsManager.MaxProjects, false)); } return (null, null); diff --git a/bitwarden_license/src/Commercial.Core/Utilities/ServiceCollectionExtensions.cs b/bitwarden_license/src/Commercial.Core/Utilities/ServiceCollectionExtensions.cs index b9d21915bf59..09788406de4d 100644 --- a/bitwarden_license/src/Commercial.Core/Utilities/ServiceCollectionExtensions.cs +++ b/bitwarden_license/src/Commercial.Core/Utilities/ServiceCollectionExtensions.cs @@ -1,7 +1,7 @@ -using Bit.Commercial.Core.Providers; -using Bit.Commercial.Core.Services; -using Bit.Core.Providers.Interfaces; -using Bit.Core.Services; +using Bit.Commercial.Core.AdminConsole.Providers; +using Bit.Commercial.Core.AdminConsole.Services; +using Bit.Core.AdminConsole.Providers.Interfaces; +using Bit.Core.AdminConsole.Services; using Microsoft.Extensions.DependencyInjection; namespace Bit.Commercial.Core.Utilities; diff --git a/bitwarden_license/src/Commercial.Core/packages.lock.json b/bitwarden_license/src/Commercial.Core/packages.lock.json index 3a26cd9d5ecc..49e4ef124471 100644 --- a/bitwarden_license/src/Commercial.Core/packages.lock.json +++ b/bitwarden_license/src/Commercial.Core/packages.lock.json @@ -141,10 +141,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2433,7 +2434,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/packages.lock.json b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/packages.lock.json index 374ae585cbb5..ca7a2c863276 100644 --- a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/packages.lock.json +++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/packages.lock.json @@ -159,10 +159,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2593,7 +2594,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2627,7 +2628,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", diff --git a/bitwarden_license/src/Scim/Controllers/v2/GroupsController.cs b/bitwarden_license/src/Scim/Controllers/v2/GroupsController.cs index 68bdf8f2999f..5df0b2921644 100644 --- a/bitwarden_license/src/Scim/Controllers/v2/GroupsController.cs +++ b/bitwarden_license/src/Scim/Controllers/v2/GroupsController.cs @@ -1,6 +1,7 @@ -using Bit.Core.Enums; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Enums; using Bit.Core.Exceptions; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; using Bit.Core.Repositories; using Bit.Scim.Groups.Interfaces; using Bit.Scim.Models; diff --git a/bitwarden_license/src/Scim/Groups/GetGroupsListQuery.cs b/bitwarden_license/src/Scim/Groups/GetGroupsListQuery.cs index 1afab3a0fc70..7055736a4cf3 100644 --- a/bitwarden_license/src/Scim/Groups/GetGroupsListQuery.cs +++ b/bitwarden_license/src/Scim/Groups/GetGroupsListQuery.cs @@ -1,5 +1,5 @@ -using Bit.Core.Entities; -using Bit.Core.Repositories; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Repositories; using Bit.Scim.Groups.Interfaces; namespace Bit.Scim.Groups; diff --git a/bitwarden_license/src/Scim/Groups/Interfaces/IGetGroupsListQuery.cs b/bitwarden_license/src/Scim/Groups/Interfaces/IGetGroupsListQuery.cs index d2cf5ef0a21a..07ff04470182 100644 --- a/bitwarden_license/src/Scim/Groups/Interfaces/IGetGroupsListQuery.cs +++ b/bitwarden_license/src/Scim/Groups/Interfaces/IGetGroupsListQuery.cs @@ -1,4 +1,4 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; namespace Bit.Scim.Groups.Interfaces; diff --git a/bitwarden_license/src/Scim/Groups/Interfaces/IPostGroupCommand.cs b/bitwarden_license/src/Scim/Groups/Interfaces/IPostGroupCommand.cs index 76e80f08dd87..011199f033ba 100644 --- a/bitwarden_license/src/Scim/Groups/Interfaces/IPostGroupCommand.cs +++ b/bitwarden_license/src/Scim/Groups/Interfaces/IPostGroupCommand.cs @@ -1,4 +1,5 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.Entities; using Bit.Scim.Models; namespace Bit.Scim.Groups.Interfaces; diff --git a/bitwarden_license/src/Scim/Groups/Interfaces/IPutGroupCommand.cs b/bitwarden_license/src/Scim/Groups/Interfaces/IPutGroupCommand.cs index 052a4e554bad..8c0b9108c017 100644 --- a/bitwarden_license/src/Scim/Groups/Interfaces/IPutGroupCommand.cs +++ b/bitwarden_license/src/Scim/Groups/Interfaces/IPutGroupCommand.cs @@ -1,4 +1,5 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.Entities; using Bit.Scim.Models; namespace Bit.Scim.Groups.Interfaces; diff --git a/bitwarden_license/src/Scim/Groups/PatchGroupCommand.cs b/bitwarden_license/src/Scim/Groups/PatchGroupCommand.cs index 750dcec46cda..f31225754ffa 100644 --- a/bitwarden_license/src/Scim/Groups/PatchGroupCommand.cs +++ b/bitwarden_license/src/Scim/Groups/PatchGroupCommand.cs @@ -1,10 +1,10 @@ using System.Text.Json; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Services; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; -using Bit.Core.Repositories; -using Bit.Core.Services; using Bit.Scim.Groups.Interfaces; using Bit.Scim.Models; diff --git a/bitwarden_license/src/Scim/Groups/PostGroupCommand.cs b/bitwarden_license/src/Scim/Groups/PostGroupCommand.cs index 00da7a8d7c81..80ec5c5e3e4a 100644 --- a/bitwarden_license/src/Scim/Groups/PostGroupCommand.cs +++ b/bitwarden_license/src/Scim/Groups/PostGroupCommand.cs @@ -1,8 +1,10 @@ -using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; using Bit.Core.Repositories; using Bit.Scim.Context; using Bit.Scim.Groups.Interfaces; diff --git a/bitwarden_license/src/Scim/Groups/PutGroupCommand.cs b/bitwarden_license/src/Scim/Groups/PutGroupCommand.cs index 0310139ecbf8..96c67be9184f 100644 --- a/bitwarden_license/src/Scim/Groups/PutGroupCommand.cs +++ b/bitwarden_license/src/Scim/Groups/PutGroupCommand.cs @@ -1,9 +1,10 @@ -using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; -using Bit.Core.Repositories; using Bit.Scim.Context; using Bit.Scim.Groups.Interfaces; using Bit.Scim.Models; diff --git a/bitwarden_license/src/Scim/Models/ScimGroupRequestModel.cs b/bitwarden_license/src/Scim/Models/ScimGroupRequestModel.cs index ac99eca2e9a7..11bd40c58727 100644 --- a/bitwarden_license/src/Scim/Models/ScimGroupRequestModel.cs +++ b/bitwarden_license/src/Scim/Models/ScimGroupRequestModel.cs @@ -1,4 +1,4 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; using Bit.Core.Utilities; namespace Bit.Scim.Models; diff --git a/bitwarden_license/src/Scim/Models/ScimGroupResponseModel.cs b/bitwarden_license/src/Scim/Models/ScimGroupResponseModel.cs index 7ba209df9ba4..697a3d59daf2 100644 --- a/bitwarden_license/src/Scim/Models/ScimGroupResponseModel.cs +++ b/bitwarden_license/src/Scim/Models/ScimGroupResponseModel.cs @@ -1,4 +1,4 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; namespace Bit.Scim.Models; diff --git a/bitwarden_license/src/Scim/packages.lock.json b/bitwarden_license/src/Scim/packages.lock.json index 0da51a039110..16a42f355d3f 100644 --- a/bitwarden_license/src/Scim/packages.lock.json +++ b/bitwarden_license/src/Scim/packages.lock.json @@ -158,10 +158,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2597,7 +2598,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2630,7 +2631,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2638,7 +2639,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2650,9 +2651,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/bitwarden_license/src/Sso/packages.lock.json b/bitwarden_license/src/Sso/packages.lock.json index 79e2fecc3918..ecccbaab16d0 100644 --- a/bitwarden_license/src/Sso/packages.lock.json +++ b/bitwarden_license/src/Sso/packages.lock.json @@ -183,10 +183,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2757,7 +2758,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2790,7 +2791,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2798,7 +2799,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2810,9 +2811,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/bitwarden_license/test/Commercial.Core.Test/AutoFixture/ProviderUserFixtures.cs b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/AutoFixture/ProviderUserFixtures.cs similarity index 83% rename from bitwarden_license/test/Commercial.Core.Test/AutoFixture/ProviderUserFixtures.cs rename to bitwarden_license/test/Commercial.Core.Test/AdminConsole/AutoFixture/ProviderUserFixtures.cs index 48f70c335066..fc47e80c8742 100644 --- a/bitwarden_license/test/Commercial.Core.Test/AutoFixture/ProviderUserFixtures.cs +++ b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/AutoFixture/ProviderUserFixtures.cs @@ -1,9 +1,9 @@ using System.Reflection; using AutoFixture; using AutoFixture.Xunit2; -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Enums.Provider; -namespace Bit.Commercial.Core.Test.AutoFixture; +namespace Bit.Commercial.Core.Test.AdminConsole.AutoFixture; internal class ProviderUser : ICustomization { @@ -18,7 +18,7 @@ public ProviderUser(ProviderUserStatusType status, ProviderUserType type) public void Customize(IFixture fixture) { - fixture.Customize(composer => composer + fixture.Customize(composer => composer .With(o => o.Type, Type) .With(o => o.Status, Status)); } diff --git a/bitwarden_license/test/Commercial.Core.Test/ProviderFeatures/CreateProviderCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/ProviderFeatures/CreateProviderCommandTests.cs similarity index 86% rename from bitwarden_license/test/Commercial.Core.Test/ProviderFeatures/CreateProviderCommandTests.cs rename to bitwarden_license/test/Commercial.Core.Test/AdminConsole/ProviderFeatures/CreateProviderCommandTests.cs index 721ed24d9c2a..399ed6ea1e1b 100644 --- a/bitwarden_license/test/Commercial.Core.Test/ProviderFeatures/CreateProviderCommandTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/ProviderFeatures/CreateProviderCommandTests.cs @@ -1,16 +1,17 @@ -using Bit.Commercial.Core.Providers; +using Bit.Commercial.Core.AdminConsole.Providers; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Services; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; -using Bit.Core.Enums.Provider; using Bit.Core.Exceptions; using Bit.Core.Repositories; -using Bit.Core.Services; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; using Xunit; -namespace Bit.Commercial.Core.Test.ProviderFeatures; +namespace Bit.Commercial.Core.Test.AdminConsole.ProviderFeatures; [SutProviderCustomize] public class CreateProviderCommandTests diff --git a/bitwarden_license/test/Commercial.Core.Test/Services/ProviderServiceTests.cs b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/Services/ProviderServiceTests.cs similarity index 95% rename from bitwarden_license/test/Commercial.Core.Test/Services/ProviderServiceTests.cs rename to bitwarden_license/test/Commercial.Core.Test/AdminConsole/Services/ProviderServiceTests.cs index babfa9c0741a..01538003c6ee 100644 --- a/bitwarden_license/test/Commercial.Core.Test/Services/ProviderServiceTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/Services/ProviderServiceTests.cs @@ -1,13 +1,14 @@ -using Bit.Commercial.Core.Services; -using Bit.Commercial.Core.Test.AutoFixture; +using Bit.Commercial.Core.AdminConsole.Services; +using Bit.Commercial.Core.Test.AdminConsole.AutoFixture; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Business.Provider; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Exceptions; using Bit.Core.Models.Business; -using Bit.Core.Models.Business.Provider; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Utilities; @@ -17,10 +18,10 @@ using NSubstitute; using NSubstitute.ReturnsExtensions; using Xunit; -using Provider = Bit.Core.Entities.Provider.Provider; -using ProviderUser = Bit.Core.Entities.Provider.ProviderUser; +using Provider = Bit.Core.AdminConsole.Entities.Provider.Provider; +using ProviderUser = Bit.Core.AdminConsole.Entities.Provider.ProviderUser; -namespace Bit.Commercial.Core.Test.Services; +namespace Bit.Commercial.Core.Test.AdminConsole.Services; [SutProviderCustomize] public class ProviderServiceTests @@ -431,6 +432,23 @@ public async Task AddOrganization_OrganizationAlreadyBelongsToAProvider_Throws(P Assert.Equal("Organization already belongs to a provider.", exception.Message); } + [Theory, BitAutoData] + public async Task AddOrganization_OrganizationHasSecretsManager_Throws(Provider provider, Organization organization, string key, + SutProvider sutProvider) + { + organization.PlanType = PlanType.EnterpriseAnnually; + organization.UseSecretsManager = true; + + sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); + var providerOrganizationRepository = sutProvider.GetDependency(); + providerOrganizationRepository.GetByOrganizationId(organization.Id).ReturnsNull(); + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.AddOrganization(provider.Id, organization.Id, key)); + Assert.Equal("The organization is subscribed to Secrets Manager. Please contact Customer Support to manage the subscription.", exception.Message); + } + [Theory, BitAutoData] public async Task AddOrganization_Success(Provider provider, Organization organization, string key, SutProvider sutProvider) diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AuthorizationHandlers/AccessPolicies/AccessPolicyAuthorizationHandlerTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AuthorizationHandlers/AccessPolicies/AccessPolicyAuthorizationHandlerTests.cs index 6f95f3c45118..b6779394c1d4 100644 --- a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AuthorizationHandlers/AccessPolicies/AccessPolicyAuthorizationHandlerTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AuthorizationHandlers/AccessPolicies/AccessPolicyAuthorizationHandlerTests.cs @@ -2,6 +2,8 @@ using System.Security.Claims; using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies; using Bit.Commercial.Core.Test.SecretsManager.Enums; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AuthorizationHandlers/ServiceAccounts/ServiceAccountAuthorizationHandlerTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AuthorizationHandlers/ServiceAccounts/ServiceAccountAuthorizationHandlerTests.cs index b7804a700fd5..eec69095d5c8 100644 --- a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AuthorizationHandlers/ServiceAccounts/ServiceAccountAuthorizationHandlerTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AuthorizationHandlers/ServiceAccounts/ServiceAccountAuthorizationHandlerTests.cs @@ -497,4 +497,63 @@ public async Task CanDeleteProject_AccessCheck(PermissionType permissionType, bo Assert.Equal(expected, authzContext.HasSucceeded); } + + [Theory] + [BitAutoData] + public async Task CanReadEvents_AccessToSecretsManagerFalse_DoesNotSucceed( + SutProvider sutProvider, ServiceAccount serviceAccount, + ClaimsPrincipal claimsPrincipal) + { + var requirement = ServiceAccountOperations.ReadEvents; + sutProvider.GetDependency().AccessSecretsManager(serviceAccount.OrganizationId) + .Returns(false); + var authzContext = new AuthorizationHandlerContext(new List { requirement }, + claimsPrincipal, serviceAccount); + + await sutProvider.Sut.HandleAsync(authzContext); + + Assert.False(authzContext.HasSucceeded); + } + + [Theory] + [BitAutoData] + public async Task CanReadEvents_NullResource_DoesNotSucceed( + SutProvider sutProvider, ServiceAccount serviceAccount, + ClaimsPrincipal claimsPrincipal, + Guid userId) + { + var requirement = ServiceAccountOperations.ReadEvents; + SetupPermission(sutProvider, PermissionType.RunAsAdmin, serviceAccount.OrganizationId, userId); + var authzContext = new AuthorizationHandlerContext(new List { requirement }, + claimsPrincipal, null); + + await sutProvider.Sut.HandleAsync(authzContext); + + Assert.False(authzContext.HasSucceeded); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin, true, true, true)] + [BitAutoData(PermissionType.RunAsUserWithPermission, false, false, false)] + [BitAutoData(PermissionType.RunAsUserWithPermission, false, true, false)] + [BitAutoData(PermissionType.RunAsUserWithPermission, true, false, true)] + [BitAutoData(PermissionType.RunAsUserWithPermission, true, true, true)] + public async Task CanReadEvents_AccessCheck(PermissionType permissionType, bool read, bool write, + bool expected, + SutProvider sutProvider, ServiceAccount serviceAccount, + ClaimsPrincipal claimsPrincipal, + Guid userId) + { + var requirement = ServiceAccountOperations.ReadEvents; + SetupPermission(sutProvider, permissionType, serviceAccount.OrganizationId, userId); + sutProvider.GetDependency() + .AccessToServiceAccountAsync(serviceAccount.Id, userId, Arg.Any()) + .Returns((read, write)); + var authzContext = new AuthorizationHandlerContext(new List { requirement }, + claimsPrincipal, serviceAccount); + + await sutProvider.Sut.HandleAsync(authzContext); + + Assert.Equal(expected, authzContext.HasSucceeded); + } } diff --git a/bitwarden_license/test/Commercial.Core.Test/packages.lock.json b/bitwarden_license/test/Commercial.Core.Test/packages.lock.json index 4b48f35c5a82..25d8df608173 100644 --- a/bitwarden_license/test/Commercial.Core.Test/packages.lock.json +++ b/bitwarden_license/test/Commercial.Core.Test/packages.lock.json @@ -201,10 +201,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2666,7 +2667,7 @@ "commercial.core": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )" + "Core": "[2023.9.0, )" } }, "common": { @@ -2674,7 +2675,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -2693,7 +2694,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2728,8 +2729,8 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Common": "[2023.8.3, )", - "Core": "[2023.8.3, )", + "Common": "[2023.9.0, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", diff --git a/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json b/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json index 670b8ff3d3c2..e1d3b4b09e30 100644 --- a/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json +++ b/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json @@ -239,10 +239,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2988,7 +2989,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -3007,7 +3008,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -3040,15 +3041,15 @@ "identity": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )", + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )", "Swashbuckle.AspNetCore.SwaggerGen": "[6.5.0, )" } }, "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -3056,7 +3057,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -3068,8 +3069,8 @@ "integrationtestcommon": { "type": "Project", "dependencies": { - "Common": "[2023.8.3, )", - "Identity": "[2023.8.3, )", + "Common": "[2023.9.0, )", + "Identity": "[2023.9.0, )", "Microsoft.AspNetCore.Mvc.Testing": "[6.0.5, )", "Microsoft.EntityFrameworkCore.InMemory": "[7.0.5, )", "Microsoft.Extensions.Configuration": "[6.0.1, )" @@ -3078,16 +3079,16 @@ "scim": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )" } }, "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/bitwarden_license/test/Scim.Test/Groups/GetGroupsListQueryTests.cs b/bitwarden_license/test/Scim.Test/Groups/GetGroupsListQueryTests.cs index fa8997580c33..1599b6e39080 100644 --- a/bitwarden_license/test/Scim.Test/Groups/GetGroupsListQueryTests.cs +++ b/bitwarden_license/test/Scim.Test/Groups/GetGroupsListQueryTests.cs @@ -1,5 +1,5 @@ -using Bit.Core.Entities; -using Bit.Core.Repositories; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Repositories; using Bit.Scim.Groups; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; diff --git a/bitwarden_license/test/Scim.Test/Groups/PatchGroupCommandTests.cs b/bitwarden_license/test/Scim.Test/Groups/PatchGroupCommandTests.cs index 505a67f570ff..066887fe7aac 100644 --- a/bitwarden_license/test/Scim.Test/Groups/PatchGroupCommandTests.cs +++ b/bitwarden_license/test/Scim.Test/Groups/PatchGroupCommandTests.cs @@ -1,10 +1,11 @@ using System.Text.Json; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Services; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; -using Bit.Core.Repositories; -using Bit.Core.Services; using Bit.Scim.Groups; using Bit.Scim.Models; using Bit.Scim.Utilities; diff --git a/bitwarden_license/test/Scim.Test/Groups/PostGroupCommandTests.cs b/bitwarden_license/test/Scim.Test/Groups/PostGroupCommandTests.cs index 40281e4d1b32..2d06f23a29f0 100644 --- a/bitwarden_license/test/Scim.Test/Groups/PostGroupCommandTests.cs +++ b/bitwarden_license/test/Scim.Test/Groups/PostGroupCommandTests.cs @@ -1,9 +1,10 @@ -using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; -using Bit.Core.Repositories; using Bit.Scim.Context; using Bit.Scim.Groups; using Bit.Scim.Models; diff --git a/bitwarden_license/test/Scim.Test/Groups/PutGroupCommandTests.cs b/bitwarden_license/test/Scim.Test/Groups/PutGroupCommandTests.cs index ecf77d563b9b..05f6672779fb 100644 --- a/bitwarden_license/test/Scim.Test/Groups/PutGroupCommandTests.cs +++ b/bitwarden_license/test/Scim.Test/Groups/PutGroupCommandTests.cs @@ -1,9 +1,10 @@ -using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Enums; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; -using Bit.Core.Repositories; using Bit.Scim.Context; using Bit.Scim.Groups; using Bit.Scim.Models; diff --git a/bitwarden_license/test/Scim.Test/packages.lock.json b/bitwarden_license/test/Scim.Test/packages.lock.json index 8e8023b0a6a4..36253d50a120 100644 --- a/bitwarden_license/test/Scim.Test/packages.lock.json +++ b/bitwarden_license/test/Scim.Test/packages.lock.json @@ -227,10 +227,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2833,7 +2834,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -2852,7 +2853,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2885,7 +2886,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2893,7 +2894,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2905,16 +2906,16 @@ "scim": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )" } }, "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/global.json b/global.json index 527fd31d3fdc..13a0d55fff34 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.413", + "version": "6.0.415", "rollForward": "latestFeature" } } diff --git a/perf/MicroBenchmarks/packages.lock.json b/perf/MicroBenchmarks/packages.lock.json index f2a30e33e957..c8d9a6e53f8d 100644 --- a/perf/MicroBenchmarks/packages.lock.json +++ b/perf/MicroBenchmarks/packages.lock.json @@ -166,10 +166,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2540,7 +2541,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", diff --git a/src/Admin/Controllers/OrganizationsController.cs b/src/Admin/Controllers/OrganizationsController.cs index 44b83c2f3449..b3a232c2c8ba 100644 --- a/src/Admin/Controllers/OrganizationsController.cs +++ b/src/Admin/Controllers/OrganizationsController.cs @@ -2,6 +2,8 @@ using Bit.Admin.Models; using Bit.Admin.Services; using Bit.Admin.Utilities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; @@ -19,6 +21,7 @@ using Bit.Core.Vault.Repositories; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Stripe; namespace Bit.Admin.Controllers; @@ -47,6 +50,7 @@ public class OrganizationsController : Controller private readonly ISecretRepository _secretRepository; private readonly IProjectRepository _projectRepository; private readonly IServiceAccountRepository _serviceAccountRepository; + private readonly IStripeSyncService _stripeSyncService; public OrganizationsController( IOrganizationService organizationService, @@ -70,7 +74,8 @@ public OrganizationsController( ICurrentContext currentContext, ISecretRepository secretRepository, IProjectRepository projectRepository, - IServiceAccountRepository serviceAccountRepository) + IServiceAccountRepository serviceAccountRepository, + IStripeSyncService stripeSyncService) { _organizationService = organizationService; _organizationRepository = organizationRepository; @@ -94,6 +99,7 @@ public OrganizationsController( _secretRepository = secretRepository; _projectRepository = projectRepository; _serviceAccountRepository = serviceAccountRepository; + _stripeSyncService = stripeSyncService; } [RequirePermission(Permission.Org_List_View)] @@ -201,13 +207,22 @@ public async Task Edit(Guid id, OrganizationEditModel model) var organization = await GetOrganization(id, model); if (organization.UseSecretsManager && - !organization.SecretsManagerBeta - && StaticStore.GetSecretsManagerPlan(organization.PlanType) == null - ) + !organization.SecretsManagerBeta && + !StaticStore.GetPlan(organization.PlanType).SupportsSecretsManager) { throw new BadRequestException("Plan does not support Secrets Manager"); } + try + { + await _stripeSyncService.UpdateCustomerEmailAddress(organization.GatewayCustomerId, organization.BillingEmail); + } + catch (StripeException stripeException) + { + _logger.LogError(stripeException, "Failed to update billing email address in Stripe for Organization with ID '{organizationId}'", organization.Id); + throw; + } + await _organizationRepository.ReplaceAsync(organization); await _applicationCacheService.UpsertOrganizationAbilityAsync(organization); await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationEditedByAdmin, organization, _currentContext) @@ -215,6 +230,7 @@ await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventTy EventRaisedByUser = _userService.GetUserName(User), SalesAssistedTrialStarted = model.SalesAssistedTrialStarted, }); + return RedirectToAction("Edit", new { id }); } diff --git a/src/Admin/Controllers/ProvidersController.cs b/src/Admin/Controllers/ProvidersController.cs index 3dbf1ca53bb3..8fa02e9b329f 100644 --- a/src/Admin/Controllers/ProvidersController.cs +++ b/src/Admin/Controllers/ProvidersController.cs @@ -1,9 +1,11 @@ using Bit.Admin.Enums; using Bit.Admin.Models; using Bit.Admin.Utilities; -using Bit.Core.Entities.Provider; -using Bit.Core.Enums.Provider; -using Bit.Core.Providers.Interfaces; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Providers.Interfaces; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Services; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; diff --git a/src/Admin/Models/CreateProviderModel.cs b/src/Admin/Models/CreateProviderModel.cs index 9dcd218fa625..8eddd014cd5c 100644 --- a/src/Admin/Models/CreateProviderModel.cs +++ b/src/Admin/Models/CreateProviderModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -using Bit.Core.Entities.Provider; -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; using Bit.SharedWeb.Utilities; namespace Bit.Admin.Models; diff --git a/src/Admin/Models/OrganizationEditModel.cs b/src/Admin/Models/OrganizationEditModel.cs index 76222fbb18ba..234d8bb1574c 100644 --- a/src/Admin/Models/OrganizationEditModel.cs +++ b/src/Admin/Models/OrganizationEditModel.cs @@ -1,8 +1,9 @@ using System.ComponentModel.DataAnnotations; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Models.Business; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Settings; @@ -158,11 +159,12 @@ public OrganizationEditModel(Organization org, Provider provider, IEnumerable> GetPlansHelper() => - StaticStore.SecretManagerPlans.Select(p => - new Dictionary + StaticStore.Plans + .Where(p => p.SupportsSecretsManager) + .Select(p => new Dictionary { { "type", p.Type }, - { "baseServiceAccount", p.BaseServiceAccount } + { "baseServiceAccount", p.SecretsManager.BaseServiceAccount } }); public Organization CreateOrganization(Provider provider) diff --git a/src/Admin/Models/OrganizationViewModel.cs b/src/Admin/Models/OrganizationViewModel.cs index 7320b8594d7c..0bad3650fd73 100644 --- a/src/Admin/Models/OrganizationViewModel.cs +++ b/src/Admin/Models/OrganizationViewModel.cs @@ -1,5 +1,6 @@ -using Bit.Core.Entities; -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Vault.Entities; diff --git a/src/Admin/Models/ProviderEditModel.cs b/src/Admin/Models/ProviderEditModel.cs index 38fdb742a85a..b5219d8ed989 100644 --- a/src/Admin/Models/ProviderEditModel.cs +++ b/src/Admin/Models/ProviderEditModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -using Bit.Core.Entities.Provider; -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; namespace Bit.Admin.Models; diff --git a/src/Admin/Models/ProviderViewModel.cs b/src/Admin/Models/ProviderViewModel.cs index 766101e884b9..38e7eac4a2c2 100644 --- a/src/Admin/Models/ProviderViewModel.cs +++ b/src/Admin/Models/ProviderViewModel.cs @@ -1,6 +1,6 @@ -using Bit.Core.Entities.Provider; -using Bit.Core.Enums.Provider; -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; namespace Bit.Admin.Models; diff --git a/src/Admin/Models/ProvidersModel.cs b/src/Admin/Models/ProvidersModel.cs index dccf4a4d7610..5fb8e8dfab16 100644 --- a/src/Admin/Models/ProvidersModel.cs +++ b/src/Admin/Models/ProvidersModel.cs @@ -1,4 +1,4 @@ -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities.Provider; namespace Bit.Admin.Models; diff --git a/src/Admin/Views/Organizations/_ProviderInformation.cshtml b/src/Admin/Views/Organizations/_ProviderInformation.cshtml index 217f9951b0c3..4bd2118ea0a3 100644 --- a/src/Admin/Views/Organizations/_ProviderInformation.cshtml +++ b/src/Admin/Views/Organizations/_ProviderInformation.cshtml @@ -1,5 +1,5 @@ @using Bit.SharedWeb.Utilities -@model Bit.Core.Entities.Provider.Provider +@model Bit.Core.AdminConsole.Entities.Provider.Provider
Provider Name
@Model.Name
diff --git a/src/Admin/Views/Providers/Admins.cshtml b/src/Admin/Views/Providers/Admins.cshtml index 533cb3034ea2..4d110aca5bcc 100644 --- a/src/Admin/Views/Providers/Admins.cshtml +++ b/src/Admin/Views/Providers/Admins.cshtml @@ -1,4 +1,5 @@ @using Bit.Admin.Enums; +@using Bit.Core.AdminConsole.Enums.Provider @inject Bit.Admin.Services.IAccessControlService AccessControlService @model ProviderViewModel diff --git a/src/Admin/Views/Providers/Create.cshtml b/src/Admin/Views/Providers/Create.cshtml index 107e93150b41..2e69da3ad7b3 100644 --- a/src/Admin/Views/Providers/Create.cshtml +++ b/src/Admin/Views/Providers/Create.cshtml @@ -1,4 +1,5 @@ @using Bit.SharedWeb.Utilities +@using Bit.Core.AdminConsole.Enums.Provider @model CreateProviderModel @{ ViewData["Title"] = "Create Provider"; diff --git a/src/Admin/Views/Providers/Organizations.cshtml b/src/Admin/Views/Providers/Organizations.cshtml index 06fc490bcc0d..b9c751480d44 100644 --- a/src/Admin/Views/Providers/Organizations.cshtml +++ b/src/Admin/Views/Providers/Organizations.cshtml @@ -1,3 +1,4 @@ +@using Bit.Core.AdminConsole.Enums.Provider @model ProviderViewModel @await Html.PartialAsync("_ProviderScripts") diff --git a/src/Admin/Views/Providers/_ViewInformation.cshtml b/src/Admin/Views/Providers/_ViewInformation.cshtml index ff4a2e8106ac..b01502e6d668 100644 --- a/src/Admin/Views/Providers/_ViewInformation.cshtml +++ b/src/Admin/Views/Providers/_ViewInformation.cshtml @@ -1,4 +1,5 @@ @using Bit.SharedWeb.Utilities +@using Bit.Core.AdminConsole.Enums.Provider @model ProviderViewModel
Id
diff --git a/src/Admin/Views/Shared/_OrganizationForm.cshtml b/src/Admin/Views/Shared/_OrganizationForm.cshtml index 9b76d1c2a80c..d0530849acbd 100644 --- a/src/Admin/Views/Shared/_OrganizationForm.cshtml +++ b/src/Admin/Views/Shared/_OrganizationForm.cshtml @@ -1,5 +1,6 @@ @using Bit.SharedWeb.Utilities @using Bit.Admin.Enums; +@using Bit.Core.AdminConsole.Enums.Provider @inject Bit.Admin.Services.IAccessControlService AccessControlService; @model OrganizationEditModel @@ -277,7 +278,13 @@ } else { - + } diff --git a/src/Admin/Views/_ViewImports.cshtml b/src/Admin/Views/_ViewImports.cshtml index b3dac997e284..073a922f5e45 100644 --- a/src/Admin/Views/_ViewImports.cshtml +++ b/src/Admin/Views/_ViewImports.cshtml @@ -1,7 +1,6 @@ @using Microsoft.AspNetCore.Identity @using Bit.Admin @using Bit.Admin.Models -@using Bit.Core.Enums.Provider @using Bit.Core.Enums @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper "*, Admin" diff --git a/src/Admin/packages.lock.json b/src/Admin/packages.lock.json index 6c420e2a173b..b830fdad389a 100644 --- a/src/Admin/packages.lock.json +++ b/src/Admin/packages.lock.json @@ -178,10 +178,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2791,15 +2792,15 @@ "commercial.core": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )" + "Core": "[2023.9.0, )" } }, "commercial.infrastructure.entityframework": { "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } }, "core": { @@ -2814,7 +2815,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2847,7 +2848,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2855,7 +2856,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2867,7 +2868,7 @@ "migrator": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.Extensions.Logging": "[6.0.0, )", "dbup-sqlserver": "[5.0.8, )" } @@ -2875,30 +2876,30 @@ "mysqlmigrations": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } }, "postgresmigrations": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } }, "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } }, "sqlitemigrations": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/src/Api/Controllers/GroupsController.cs b/src/Api/AdminConsole/Controllers/GroupsController.cs similarity index 95% rename from src/Api/Controllers/GroupsController.cs rename to src/Api/AdminConsole/Controllers/GroupsController.cs index 0220cfd5e249..9946c192b1fa 100644 --- a/src/Api/Controllers/GroupsController.cs +++ b/src/Api/AdminConsole/Controllers/GroupsController.cs @@ -1,14 +1,16 @@ -using Bit.Api.Models.Request; +using Bit.Api.AdminConsole.Models.Request; +using Bit.Api.AdminConsole.Models.Response; using Bit.Api.Models.Response; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Services; using Bit.Core.Context; using Bit.Core.Exceptions; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; using Bit.Core.Repositories; -using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Bit.Api.Controllers; +namespace Bit.Api.AdminConsole.Controllers; [Route("organizations/{orgId}/groups")] [Authorize("Application")] diff --git a/src/Api/Controllers/OrganizationUsersController.cs b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs similarity index 98% rename from src/Api/Controllers/OrganizationUsersController.cs rename to src/Api/AdminConsole/Controllers/OrganizationUsersController.cs index d1c4ebacd601..85436b41ba87 100644 --- a/src/Api/Controllers/OrganizationUsersController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs @@ -1,6 +1,8 @@ -using Bit.Api.Models.Request.Organizations; +using Bit.Api.AdminConsole.Models.Request.Organizations; +using Bit.Api.AdminConsole.Models.Response.Organizations; +using Bit.Api.Models.Request.Organizations; using Bit.Api.Models.Response; -using Bit.Api.Models.Response.Organizations; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -14,7 +16,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Bit.Api.Controllers; +namespace Bit.Api.AdminConsole.Controllers; [Route("organizations/{orgId}/users")] [Authorize("Application")] diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/AdminConsole/Controllers/OrganizationsController.cs similarity index 98% rename from src/Api/Controllers/OrganizationsController.cs rename to src/Api/AdminConsole/Controllers/OrganizationsController.cs index 3c8e938eb47d..eb3a95ebc2a5 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationsController.cs @@ -1,4 +1,8 @@ using System.Text.Json; +using Bit.Api.AdminConsole.Models.Request; +using Bit.Api.AdminConsole.Models.Request.Organizations; +using Bit.Api.AdminConsole.Models.Response; +using Bit.Api.AdminConsole.Models.Response.Organizations; using Bit.Api.Auth.Models.Request.Accounts; using Bit.Api.Auth.Models.Request.Organizations; using Bit.Api.Auth.Models.Response.Organizations; @@ -6,8 +10,8 @@ using Bit.Api.Models.Request.Accounts; using Bit.Api.Models.Request.Organizations; using Bit.Api.Models.Response; -using Bit.Api.Models.Response.Organizations; using Bit.Core; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Repositories; using Bit.Core.Auth.Services; @@ -26,7 +30,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Bit.Api.Controllers; +namespace Bit.Api.AdminConsole.Controllers; [Route("organizations")] [Authorize("Application")] @@ -540,7 +544,7 @@ public async Task ApiKey(string id, [FromBody] Organization if (model.Type == OrganizationApiKeyType.BillingSync || model.Type == OrganizationApiKeyType.Scim) { // Non-enterprise orgs should not be able to create or view an apikey of billing sync/scim key types - var plan = StaticStore.GetPasswordManagerPlan(organization.PlanType); + var plan = StaticStore.GetPlan(organization.PlanType); if (plan.Product != ProductType.Enterprise) { throw new NotFoundException(); diff --git a/src/Api/Controllers/PoliciesController.cs b/src/Api/AdminConsole/Controllers/PoliciesController.cs similarity index 98% rename from src/Api/Controllers/PoliciesController.cs rename to src/Api/AdminConsole/Controllers/PoliciesController.cs index e352e109083a..6f93c22def17 100644 --- a/src/Api/Controllers/PoliciesController.cs +++ b/src/Api/AdminConsole/Controllers/PoliciesController.cs @@ -1,4 +1,4 @@ -using Bit.Api.Models.Request; +using Bit.Api.AdminConsole.Models.Request; using Bit.Api.Models.Response; using Bit.Core.Context; using Bit.Core.Enums; @@ -12,7 +12,7 @@ using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; -namespace Bit.Api.Controllers; +namespace Bit.Api.AdminConsole.Controllers; [Route("organizations/{orgId}/policies")] [Authorize("Application")] diff --git a/src/Api/Controllers/ProviderOrganizationsController.cs b/src/Api/AdminConsole/Controllers/ProviderOrganizationsController.cs similarity index 93% rename from src/Api/Controllers/ProviderOrganizationsController.cs rename to src/Api/AdminConsole/Controllers/ProviderOrganizationsController.cs index 6f8283280285..4d734e7cad3c 100644 --- a/src/Api/Controllers/ProviderOrganizationsController.cs +++ b/src/Api/AdminConsole/Controllers/ProviderOrganizationsController.cs @@ -1,15 +1,16 @@ -using Bit.Api.Models.Request.Providers; +using Bit.Api.AdminConsole.Models.Request.Providers; +using Bit.Api.AdminConsole.Models.Response.Providers; using Bit.Api.Models.Response; -using Bit.Api.Models.Response.Providers; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Services; using Bit.Core.Context; using Bit.Core.Exceptions; -using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Bit.Api.Controllers; +namespace Bit.Api.AdminConsole.Controllers; [Route("providers/{providerId:guid}/organizations")] [Authorize("Application")] diff --git a/src/Api/Controllers/ProviderUsersController.cs b/src/Api/AdminConsole/Controllers/ProviderUsersController.cs similarity index 96% rename from src/Api/Controllers/ProviderUsersController.cs rename to src/Api/AdminConsole/Controllers/ProviderUsersController.cs index 9dbcc49e3f6e..73639bb1a429 100644 --- a/src/Api/Controllers/ProviderUsersController.cs +++ b/src/Api/AdminConsole/Controllers/ProviderUsersController.cs @@ -1,15 +1,16 @@ -using Bit.Api.Models.Request.Providers; +using Bit.Api.AdminConsole.Models.Request.Providers; +using Bit.Api.AdminConsole.Models.Response.Providers; using Bit.Api.Models.Response; -using Bit.Api.Models.Response.Providers; +using Bit.Core.AdminConsole.Models.Business.Provider; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Services; using Bit.Core.Context; using Bit.Core.Exceptions; -using Bit.Core.Models.Business.Provider; -using Bit.Core.Repositories; using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Bit.Api.Controllers; +namespace Bit.Api.AdminConsole.Controllers; [Route("providers/{providerId:guid}/users")] [Authorize("Application")] diff --git a/src/Api/Controllers/ProvidersController.cs b/src/Api/AdminConsole/Controllers/ProvidersController.cs similarity index 91% rename from src/Api/Controllers/ProvidersController.cs rename to src/Api/AdminConsole/Controllers/ProvidersController.cs index 5daf9ce491d6..cd39a90a840e 100644 --- a/src/Api/Controllers/ProvidersController.cs +++ b/src/Api/AdminConsole/Controllers/ProvidersController.cs @@ -1,14 +1,15 @@ -using Bit.Api.Models.Request.Providers; -using Bit.Api.Models.Response.Providers; +using Bit.Api.AdminConsole.Models.Request.Providers; +using Bit.Api.AdminConsole.Models.Response.Providers; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Services; using Bit.Core.Context; using Bit.Core.Exceptions; -using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Bit.Api.Controllers; +namespace Bit.Api.AdminConsole.Controllers; [Route("providers")] [Authorize("Application")] diff --git a/src/Api/Models/Request/GroupRequestModel.cs b/src/Api/AdminConsole/Models/Request/GroupRequestModel.cs similarity index 86% rename from src/Api/Models/Request/GroupRequestModel.cs rename to src/Api/AdminConsole/Models/Request/GroupRequestModel.cs index aa52a08af24f..97c344f95fdd 100644 --- a/src/Api/Models/Request/GroupRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/GroupRequestModel.cs @@ -1,7 +1,8 @@ using System.ComponentModel.DataAnnotations; -using Bit.Core.Entities; +using Bit.Api.Models.Request; +using Bit.Core.AdminConsole.Entities; -namespace Bit.Api.Models.Request; +namespace Bit.Api.AdminConsole.Models.Request; public class GroupRequestModel { diff --git a/src/Api/Models/Request/Organizations/ImportOrganizationUsersRequestModel.cs b/src/Api/AdminConsole/Models/Request/ImportOrganizationUsersRequestModel.cs similarity index 92% rename from src/Api/Models/Request/Organizations/ImportOrganizationUsersRequestModel.cs rename to src/Api/AdminConsole/Models/Request/ImportOrganizationUsersRequestModel.cs index 3f1e2b244148..48d34c1710b4 100644 --- a/src/Api/Models/Request/Organizations/ImportOrganizationUsersRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/ImportOrganizationUsersRequestModel.cs @@ -1,7 +1,8 @@ using System.ComponentModel.DataAnnotations; +using Bit.Core.AdminConsole.Models.Business; using Bit.Core.Models.Business; -namespace Bit.Api.Models.Request.Organizations; +namespace Bit.Api.AdminConsole.Models.Request; public class ImportOrganizationUsersRequestModel { @@ -24,7 +25,7 @@ public ImportedGroup ToImportedGroup(Guid organizationId) { var importedGroup = new ImportedGroup { - Group = new Core.Entities.Group + Group = new Core.AdminConsole.Entities.Group { OrganizationId = organizationId, Name = Name, diff --git a/src/Api/Models/Request/Accounts/OrganizationApiKeyRequestModel.cs b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationApiKeyRequestModel.cs similarity index 77% rename from src/Api/Models/Request/Accounts/OrganizationApiKeyRequestModel.cs rename to src/Api/AdminConsole/Models/Request/Organizations/OrganizationApiKeyRequestModel.cs index 5e4dff2ef669..af5c4e19d04d 100644 --- a/src/Api/Models/Request/Accounts/OrganizationApiKeyRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationApiKeyRequestModel.cs @@ -1,7 +1,7 @@ using Bit.Api.Auth.Models.Request.Accounts; using Bit.Core.Enums; -namespace Bit.Api.Models.Request.Accounts; +namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class OrganizationApiKeyRequestModel : SecretVerificationRequestModel { diff --git a/src/Api/Models/Request/Organizations/OrganizationCreateRequestModel.cs b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationCreateRequestModel.cs similarity index 98% rename from src/Api/Models/Request/Organizations/OrganizationCreateRequestModel.cs rename to src/Api/AdminConsole/Models/Request/Organizations/OrganizationCreateRequestModel.cs index 35151dc4f625..2ea4e8b84eea 100644 --- a/src/Api/Models/Request/Organizations/OrganizationCreateRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationCreateRequestModel.cs @@ -4,7 +4,7 @@ using Bit.Core.Models.Business; using Bit.Core.Utilities; -namespace Bit.Api.Models.Request.Organizations; +namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class OrganizationCreateRequestModel : IValidatableObject { diff --git a/src/Api/Models/Request/Organizations/OrganizationKeysRequestModel.cs b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationKeysRequestModel.cs similarity index 96% rename from src/Api/Models/Request/Organizations/OrganizationKeysRequestModel.cs rename to src/Api/AdminConsole/Models/Request/Organizations/OrganizationKeysRequestModel.cs index a22b4eaa6f01..3a343caa21a1 100644 --- a/src/Api/Models/Request/Organizations/OrganizationKeysRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationKeysRequestModel.cs @@ -2,7 +2,7 @@ using Bit.Core.Entities; using Bit.Core.Models.Business; -namespace Bit.Api.Models.Request.Organizations; +namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class OrganizationKeysRequestModel { diff --git a/src/Api/Models/Request/Organizations/OrganizationSeatRequestModel.cs b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationSeatRequestModel.cs similarity index 88% rename from src/Api/Models/Request/Organizations/OrganizationSeatRequestModel.cs rename to src/Api/AdminConsole/Models/Request/Organizations/OrganizationSeatRequestModel.cs index b3849f0a40c4..309adfceb759 100644 --- a/src/Api/Models/Request/Organizations/OrganizationSeatRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationSeatRequestModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Bit.Api.Models.Request.Organizations; +namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class OrganizationSeatRequestModel : IValidatableObject { diff --git a/src/Api/Models/Request/Organizations/OrganizationUpdateRequestModel.cs b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationUpdateRequestModel.cs similarity index 94% rename from src/Api/Models/Request/Organizations/OrganizationUpdateRequestModel.cs rename to src/Api/AdminConsole/Models/Request/Organizations/OrganizationUpdateRequestModel.cs index 79983a55499a..be526ee6ccb7 100644 --- a/src/Api/Models/Request/Organizations/OrganizationUpdateRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationUpdateRequestModel.cs @@ -3,7 +3,7 @@ using Bit.Core.Models.Data; using Bit.Core.Settings; -namespace Bit.Api.Models.Request.Organizations; +namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class OrganizationUpdateRequestModel { diff --git a/src/Api/Models/Request/Organizations/OrganizationUpgradeRequestModel.cs b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationUpgradeRequestModel.cs similarity index 96% rename from src/Api/Models/Request/Organizations/OrganizationUpgradeRequestModel.cs rename to src/Api/AdminConsole/Models/Request/Organizations/OrganizationUpgradeRequestModel.cs index 0c5277ec6e2f..dae1b1d4293d 100644 --- a/src/Api/Models/Request/Organizations/OrganizationUpgradeRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationUpgradeRequestModel.cs @@ -2,7 +2,7 @@ using Bit.Core.Enums; using Bit.Core.Models.Business; -namespace Bit.Api.Models.Request.Organizations; +namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class OrganizationUpgradeRequestModel { diff --git a/src/Api/Models/Request/Organizations/OrganizationUserRequestModels.cs b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationUserRequestModels.cs similarity index 97% rename from src/Api/Models/Request/Organizations/OrganizationUserRequestModels.cs rename to src/Api/AdminConsole/Models/Request/Organizations/OrganizationUserRequestModels.cs index 1206d3c973a8..bf10d85c0b89 100644 --- a/src/Api/Models/Request/Organizations/OrganizationUserRequestModels.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationUserRequestModels.cs @@ -1,11 +1,12 @@ using System.ComponentModel.DataAnnotations; +using Bit.Api.Models.Request; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Utilities; -namespace Bit.Api.Models.Request.Organizations; +namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class OrganizationUserInviteRequestModel { diff --git a/src/Api/Models/Request/PolicyRequestModel.cs b/src/Api/AdminConsole/Models/Request/PolicyRequestModel.cs similarity index 94% rename from src/Api/Models/Request/PolicyRequestModel.cs rename to src/Api/AdminConsole/Models/Request/PolicyRequestModel.cs index bc303cd40f80..b3ba3a6bca97 100644 --- a/src/Api/Models/Request/PolicyRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/PolicyRequestModel.cs @@ -3,7 +3,7 @@ using Bit.Core.Entities; using Bit.Core.Enums; -namespace Bit.Api.Models.Request; +namespace Bit.Api.AdminConsole.Models.Request; public class PolicyRequestModel { diff --git a/src/Api/Models/Request/Providers/ProviderOrganizationAddRequestModel.cs b/src/Api/AdminConsole/Models/Request/Providers/ProviderOrganizationAddRequestModel.cs similarity index 79% rename from src/Api/Models/Request/Providers/ProviderOrganizationAddRequestModel.cs rename to src/Api/AdminConsole/Models/Request/Providers/ProviderOrganizationAddRequestModel.cs index b6ea8759e054..207d84b787ee 100644 --- a/src/Api/Models/Request/Providers/ProviderOrganizationAddRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Providers/ProviderOrganizationAddRequestModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Bit.Api.Models.Request.Providers; +namespace Bit.Api.AdminConsole.Models.Request.Providers; public class ProviderOrganizationAddRequestModel { diff --git a/src/Api/Models/Request/Providers/ProviderOrganizationCreateRequestModel.cs b/src/Api/AdminConsole/Models/Request/Providers/ProviderOrganizationCreateRequestModel.cs similarity index 75% rename from src/Api/Models/Request/Providers/ProviderOrganizationCreateRequestModel.cs rename to src/Api/AdminConsole/Models/Request/Providers/ProviderOrganizationCreateRequestModel.cs index 7fead717be9d..bf75c611e224 100644 --- a/src/Api/Models/Request/Providers/ProviderOrganizationCreateRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Providers/ProviderOrganizationCreateRequestModel.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; -using Bit.Api.Models.Request.Organizations; +using Bit.Api.AdminConsole.Models.Request.Organizations; using Bit.Core.Utilities; -namespace Bit.Api.Models.Request.Providers; +namespace Bit.Api.AdminConsole.Models.Request.Providers; public class ProviderOrganizationCreateRequestModel { diff --git a/src/Api/Models/Request/Providers/ProviderSetupRequestModel.cs b/src/Api/AdminConsole/Models/Request/Providers/ProviderSetupRequestModel.cs similarity index 86% rename from src/Api/Models/Request/Providers/ProviderSetupRequestModel.cs rename to src/Api/AdminConsole/Models/Request/Providers/ProviderSetupRequestModel.cs index 51191f947c07..e091a55632e5 100644 --- a/src/Api/Models/Request/Providers/ProviderSetupRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Providers/ProviderSetupRequestModel.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities.Provider; -namespace Bit.Api.Models.Request.Providers; +namespace Bit.Api.AdminConsole.Models.Request.Providers; public class ProviderSetupRequestModel { diff --git a/src/Api/Models/Request/Providers/ProviderUpdateRequestModel.cs b/src/Api/AdminConsole/Models/Request/Providers/ProviderUpdateRequestModel.cs similarity index 88% rename from src/Api/Models/Request/Providers/ProviderUpdateRequestModel.cs rename to src/Api/AdminConsole/Models/Request/Providers/ProviderUpdateRequestModel.cs index ceec796dc4ec..90bdb1e048a9 100644 --- a/src/Api/Models/Request/Providers/ProviderUpdateRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Providers/ProviderUpdateRequestModel.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.Settings; -namespace Bit.Api.Models.Request.Providers; +namespace Bit.Api.AdminConsole.Models.Request.Providers; public class ProviderUpdateRequestModel { diff --git a/src/Api/Models/Request/Providers/ProviderUserRequestModels.cs b/src/Api/AdminConsole/Models/Request/Providers/ProviderUserRequestModels.cs similarity index 89% rename from src/Api/Models/Request/Providers/ProviderUserRequestModels.cs rename to src/Api/AdminConsole/Models/Request/Providers/ProviderUserRequestModels.cs index 9c451d8adc7c..dd225309164f 100644 --- a/src/Api/Models/Request/Providers/ProviderUserRequestModels.cs +++ b/src/Api/AdminConsole/Models/Request/Providers/ProviderUserRequestModels.cs @@ -1,9 +1,9 @@ using System.ComponentModel.DataAnnotations; -using Bit.Core.Entities.Provider; -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.Utilities; -namespace Bit.Api.Models.Request.Providers; +namespace Bit.Api.AdminConsole.Models.Request.Providers; public class ProviderUserInviteRequestModel { diff --git a/src/Api/Models/Response/GroupResponseModel.cs b/src/Api/AdminConsole/Models/Response/GroupResponseModel.cs similarity index 89% rename from src/Api/Models/Response/GroupResponseModel.cs rename to src/Api/AdminConsole/Models/Response/GroupResponseModel.cs index 83dffb75524d..b5d00fca9818 100644 --- a/src/Api/Models/Response/GroupResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/GroupResponseModel.cs @@ -1,8 +1,9 @@ -using Bit.Core.Entities; +using Bit.Api.Models.Response; +using Bit.Core.AdminConsole.Entities; using Bit.Core.Models.Api; using Bit.Core.Models.Data; -namespace Bit.Api.Models.Response; +namespace Bit.Api.AdminConsole.Models.Response; public class GroupResponseModel : ResponseModel { diff --git a/src/Api/Models/Response/Organizations/OrganizationApiKeyInformationResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationApiKeyInformationResponseModel.cs similarity index 87% rename from src/Api/Models/Response/Organizations/OrganizationApiKeyInformationResponseModel.cs rename to src/Api/AdminConsole/Models/Response/Organizations/OrganizationApiKeyInformationResponseModel.cs index a25cb8935522..ba1b290db788 100644 --- a/src/Api/Models/Response/Organizations/OrganizationApiKeyInformationResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationApiKeyInformationResponseModel.cs @@ -2,7 +2,7 @@ using Bit.Core.Enums; using Bit.Core.Models.Api; -namespace Bit.Api.Models.Response.Organizations; +namespace Bit.Api.AdminConsole.Models.Response.Organizations; public class OrganizationApiKeyInformation : ResponseModel { diff --git a/src/Api/Models/Response/Organizations/OrganizationAutoEnrollStatusResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationAutoEnrollStatusResponseModel.cs similarity index 86% rename from src/Api/Models/Response/Organizations/OrganizationAutoEnrollStatusResponseModel.cs rename to src/Api/AdminConsole/Models/Response/Organizations/OrganizationAutoEnrollStatusResponseModel.cs index 2318d3ff0582..c853b78a6c50 100644 --- a/src/Api/Models/Response/Organizations/OrganizationAutoEnrollStatusResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationAutoEnrollStatusResponseModel.cs @@ -1,6 +1,6 @@ using Bit.Core.Models.Api; -namespace Bit.Api.Models.Response.Organizations; +namespace Bit.Api.AdminConsole.Models.Response.Organizations; public class OrganizationAutoEnrollStatusResponseModel : ResponseModel { diff --git a/src/Api/Models/Response/Organizations/OrganizationKeysResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationKeysResponseModel.cs similarity index 88% rename from src/Api/Models/Response/Organizations/OrganizationKeysResponseModel.cs rename to src/Api/AdminConsole/Models/Response/Organizations/OrganizationKeysResponseModel.cs index 35c2f77e7ddd..a2e4fe058d0b 100644 --- a/src/Api/Models/Response/Organizations/OrganizationKeysResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationKeysResponseModel.cs @@ -1,7 +1,7 @@ using Bit.Core.Entities; using Bit.Core.Models.Api; -namespace Bit.Api.Models.Response.Organizations; +namespace Bit.Api.AdminConsole.Models.Response.Organizations; public class OrganizationKeysResponseModel : ResponseModel { diff --git a/src/Api/Models/Response/Organizations/OrganizationPublicKeyResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationPublicKeyResponseModel.cs similarity index 87% rename from src/Api/Models/Response/Organizations/OrganizationPublicKeyResponseModel.cs rename to src/Api/AdminConsole/Models/Response/Organizations/OrganizationPublicKeyResponseModel.cs index 0451433e1ba2..dc124796188e 100644 --- a/src/Api/Models/Response/Organizations/OrganizationPublicKeyResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationPublicKeyResponseModel.cs @@ -1,7 +1,7 @@ using Bit.Core.Entities; using Bit.Core.Models.Api; -namespace Bit.Api.Models.Response.Organizations; +namespace Bit.Api.AdminConsole.Models.Response.Organizations; public class OrganizationPublicKeyResponseModel : ResponseModel { diff --git a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs similarity index 94% rename from src/Api/Models/Response/Organizations/OrganizationResponseModel.cs rename to src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs index 11f5a795a48a..c4e848b35d4e 100644 --- a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs @@ -1,11 +1,12 @@ -using Bit.Core.Entities; +using Bit.Api.Models.Response; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.Api; using Bit.Core.Models.Business; using Bit.Core.Utilities; using Constants = Bit.Core.Constants; -namespace Bit.Api.Models.Response.Organizations; +namespace Bit.Api.AdminConsole.Models.Response.Organizations; public class OrganizationResponseModel : ResponseModel { @@ -26,12 +27,7 @@ public OrganizationResponseModel(Organization organization, string obj = "organi BusinessCountry = organization.BusinessCountry; BusinessTaxNumber = organization.BusinessTaxNumber; BillingEmail = organization.BillingEmail; - Plan = new PlanResponseModel(StaticStore.PasswordManagerPlans.FirstOrDefault(plan => plan.Type == organization.PlanType)); - var matchingPlan = StaticStore.GetSecretsManagerPlan(organization.PlanType); - if (matchingPlan != null) - { - SecretsManagerPlan = new PlanResponseModel(matchingPlan); - } + Plan = new PlanResponseModel(StaticStore.GetPlan(organization.PlanType)); PlanType = organization.PlanType; Seats = organization.Seats; MaxAutoscaleSeats = organization.MaxAutoscaleSeats; @@ -116,6 +112,7 @@ public OrganizationSubscriptionResponseModel(Organization organization, Subscrip { Subscription = subscription.Subscription != null ? new BillingSubscription(subscription.Subscription) : null; UpcomingInvoice = subscription.UpcomingInvoice != null ? new BillingSubscriptionUpcomingInvoice(subscription.UpcomingInvoice) : null; + CustomerDiscount = subscription.CustomerDiscount != null ? new BillingCustomerDiscount(subscription.CustomerDiscount) : null; Expiration = DateTime.UtcNow.AddYears(1); // Not used, so just give it a value. if (hideSensitiveData) @@ -146,6 +143,7 @@ public OrganizationSubscriptionResponseModel(Organization organization, Organiza public string StorageName { get; set; } public double? StorageGb { get; set; } + public BillingCustomerDiscount CustomerDiscount { get; set; } public BillingSubscription Subscription { get; set; } public BillingSubscriptionUpcomingInvoice UpcomingInvoice { get; set; } diff --git a/src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationUserResponseModel.cs similarity index 98% rename from src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs rename to src/Api/AdminConsole/Models/Response/Organizations/OrganizationUserResponseModel.cs index 06a39f3f901c..ee1d790fa66d 100644 --- a/src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationUserResponseModel.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using Bit.Api.Models.Response; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.Api; @@ -6,7 +7,7 @@ using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Utilities; -namespace Bit.Api.Models.Response.Organizations; +namespace Bit.Api.AdminConsole.Models.Response.Organizations; public class OrganizationUserResponseModel : ResponseModel { diff --git a/src/Api/Models/Response/ProfileOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs similarity index 96% rename from src/Api/Models/Response/ProfileOrganizationResponseModel.cs rename to src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs index a0ededa0b6b1..d0ce18290f5c 100644 --- a/src/Api/Models/Response/ProfileOrganizationResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs @@ -1,13 +1,13 @@ -using Bit.Core.Auth.Enums; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models.Data; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Models.Api; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Utilities; -namespace Bit.Api.Models.Response; +namespace Bit.Api.AdminConsole.Models.Response; public class ProfileOrganizationResponseModel : ResponseModel { @@ -55,7 +55,7 @@ public ProfileOrganizationResponseModel(OrganizationUserOrganizationDetails orga FamilySponsorshipAvailable = FamilySponsorshipFriendlyName == null && StaticStore.GetSponsoredPlan(PlanSponsorshipType.FamiliesForEnterprise) .UsersCanSponsor(organization); - PlanProductType = StaticStore.GetPasswordManagerPlan(organization.PlanType).Product; + PlanProductType = StaticStore.GetPlan(organization.PlanType).Product; FamilySponsorshipLastSyncDate = organization.FamilySponsorshipLastSyncDate; FamilySponsorshipToDelete = organization.FamilySponsorshipToDelete; FamilySponsorshipValidUntil = organization.FamilySponsorshipValidUntil; diff --git a/src/Api/Models/Response/ProfileProviderOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs similarity index 90% rename from src/Api/Models/Response/ProfileProviderOrganizationResponseModel.cs rename to src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs index 32bfd81db268..7ff9805b5819 100644 --- a/src/Api/Models/Response/ProfileProviderOrganizationResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs @@ -1,8 +1,9 @@ -using Bit.Core.Enums; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.Enums; using Bit.Core.Models.Data; using Bit.Core.Utilities; -namespace Bit.Api.Models.Response; +namespace Bit.Api.AdminConsole.Models.Response; public class ProfileProviderOrganizationResponseModel : ProfileOrganizationResponseModel { @@ -42,6 +43,6 @@ public ProfileProviderOrganizationResponseModel(ProviderUserOrganizationDetails UserId = organization.UserId; ProviderId = organization.ProviderId; ProviderName = organization.ProviderName; - PlanProductType = StaticStore.GetPasswordManagerPlan(organization.PlanType).Product; + PlanProductType = StaticStore.GetPlan(organization.PlanType).Product; } } diff --git a/src/Api/Models/Response/Providers/ProfileProviderResponseModel.cs b/src/Api/AdminConsole/Models/Response/Providers/ProfileProviderResponseModel.cs similarity index 86% rename from src/Api/Models/Response/Providers/ProfileProviderResponseModel.cs rename to src/Api/AdminConsole/Models/Response/Providers/ProfileProviderResponseModel.cs index e2d494dad105..66e0acae0cdd 100644 --- a/src/Api/Models/Response/Providers/ProfileProviderResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Providers/ProfileProviderResponseModel.cs @@ -1,9 +1,10 @@ -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.Models.Api; using Bit.Core.Models.Data; using Bit.Core.Utilities; -namespace Bit.Api.Models.Response.Providers; +namespace Bit.Api.AdminConsole.Models.Response.Providers; public class ProfileProviderResponseModel : ResponseModel { diff --git a/src/Api/Models/Response/Providers/ProviderOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/Providers/ProviderOrganizationResponseModel.cs similarity index 94% rename from src/Api/Models/Response/Providers/ProviderOrganizationResponseModel.cs rename to src/Api/AdminConsole/Models/Response/Providers/ProviderOrganizationResponseModel.cs index 9bc7d52dc677..da56d49ce864 100644 --- a/src/Api/Models/Response/Providers/ProviderOrganizationResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Providers/ProviderOrganizationResponseModel.cs @@ -1,8 +1,8 @@ -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.Models.Api; -using Bit.Core.Models.Data; -namespace Bit.Api.Models.Response.Providers; +namespace Bit.Api.AdminConsole.Models.Response.Providers; public class ProviderOrganizationResponseModel : ResponseModel { diff --git a/src/Api/Models/Response/Providers/ProviderResponseModel.cs b/src/Api/AdminConsole/Models/Response/Providers/ProviderResponseModel.cs similarity index 91% rename from src/Api/Models/Response/Providers/ProviderResponseModel.cs rename to src/Api/AdminConsole/Models/Response/Providers/ProviderResponseModel.cs index ce62fdaa682a..bc55093a03bd 100644 --- a/src/Api/Models/Response/Providers/ProviderResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Providers/ProviderResponseModel.cs @@ -1,7 +1,7 @@ -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.Models.Api; -namespace Bit.Api.Models.Response.Providers; +namespace Bit.Api.AdminConsole.Models.Response.Providers; public class ProviderResponseModel : ResponseModel { diff --git a/src/Api/Models/Response/Providers/ProviderUserResponseModel.cs b/src/Api/AdminConsole/Models/Response/Providers/ProviderUserResponseModel.cs similarity index 92% rename from src/Api/Models/Response/Providers/ProviderUserResponseModel.cs rename to src/Api/AdminConsole/Models/Response/Providers/ProviderUserResponseModel.cs index b47b9cdd2b61..1eaeb8e0bd9e 100644 --- a/src/Api/Models/Response/Providers/ProviderUserResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Providers/ProviderUserResponseModel.cs @@ -1,10 +1,11 @@ -using Bit.Core.Entities.Provider; -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.Models.Api; using Bit.Core.Models.Data; using Bit.Core.Utilities; -namespace Bit.Api.Models.Response.Providers; +namespace Bit.Api.AdminConsole.Models.Response.Providers; public class ProviderUserResponseModel : ResponseModel { diff --git a/src/Api/Public/Controllers/GroupsController.cs b/src/Api/AdminConsole/Public/Controllers/GroupsController.cs similarity index 96% rename from src/Api/Public/Controllers/GroupsController.cs rename to src/Api/AdminConsole/Public/Controllers/GroupsController.cs index 7080405451c4..01c38a084f51 100644 --- a/src/Api/Public/Controllers/GroupsController.cs +++ b/src/Api/AdminConsole/Public/Controllers/GroupsController.cs @@ -1,13 +1,15 @@ using System.Net; -using Bit.Api.Models.Public.Request; +using Bit.Api.AdminConsole.Public.Models.Request; +using Bit.Api.AdminConsole.Public.Models.Response; using Bit.Api.Models.Public.Response; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; using Bit.Core.Repositories; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Bit.Api.Public.Controllers; +namespace Bit.Api.AdminConsole.Public.Controllers; [Route("public/groups")] [Authorize("Organization")] diff --git a/src/Api/Public/Controllers/MembersController.cs b/src/Api/AdminConsole/Public/Controllers/MembersController.cs similarity index 98% rename from src/Api/Public/Controllers/MembersController.cs rename to src/Api/AdminConsole/Public/Controllers/MembersController.cs index 74dd4b83e10b..52cf952f7beb 100644 --- a/src/Api/Public/Controllers/MembersController.cs +++ b/src/Api/AdminConsole/Public/Controllers/MembersController.cs @@ -1,6 +1,8 @@ using System.Net; -using Bit.Api.Models.Public.Request; +using Bit.Api.AdminConsole.Public.Models.Request; +using Bit.Api.AdminConsole.Public.Models.Response; using Bit.Api.Models.Public.Response; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Models.Business; using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; @@ -9,7 +11,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Bit.Api.Public.Controllers; +namespace Bit.Api.AdminConsole.Public.Controllers; [Route("public/members")] [Authorize("Organization")] diff --git a/src/Api/Public/Controllers/OrganizationController.cs b/src/Api/AdminConsole/Public/Controllers/OrganizationController.cs similarity index 93% rename from src/Api/Public/Controllers/OrganizationController.cs rename to src/Api/AdminConsole/Public/Controllers/OrganizationController.cs index ce0683b95dca..65dc15e77199 100644 --- a/src/Api/Public/Controllers/OrganizationController.cs +++ b/src/Api/AdminConsole/Public/Controllers/OrganizationController.cs @@ -1,5 +1,6 @@ using System.Net; -using Bit.Api.Models.Public.Request; +using Bit.Api.AdminConsole.Public.Models.Request; +using Bit.Api.AdminConsole.Public.Models.Response; using Bit.Api.Models.Public.Response; using Bit.Core.Context; using Bit.Core.Exceptions; @@ -8,7 +9,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Bit.Api.Public.Controllers; +namespace Bit.Api.AdminConsole.Public.Controllers; [Route("public/organization")] [Authorize("Organization")] diff --git a/src/Api/Public/Controllers/PoliciesController.cs b/src/Api/AdminConsole/Public/Controllers/PoliciesController.cs similarity index 96% rename from src/Api/Public/Controllers/PoliciesController.cs rename to src/Api/AdminConsole/Public/Controllers/PoliciesController.cs index b208938ed721..bcabb969c6ba 100644 --- a/src/Api/Public/Controllers/PoliciesController.cs +++ b/src/Api/AdminConsole/Public/Controllers/PoliciesController.cs @@ -1,5 +1,6 @@ using System.Net; -using Bit.Api.Models.Public.Request; +using Bit.Api.AdminConsole.Public.Models.Request; +using Bit.Api.AdminConsole.Public.Models.Response; using Bit.Api.Models.Public.Response; using Bit.Core.Context; using Bit.Core.Enums; @@ -8,7 +9,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Bit.Api.Public.Controllers; +namespace Bit.Api.AdminConsole.Public.Controllers; [Route("public/policies")] [Authorize("Organization")] diff --git a/src/Api/Models/Public/GroupBaseModel.cs b/src/Api/AdminConsole/Public/Models/GroupBaseModel.cs similarity index 94% rename from src/Api/Models/Public/GroupBaseModel.cs rename to src/Api/AdminConsole/Public/Models/GroupBaseModel.cs index 2b09e2952b65..ff1813aff4d0 100644 --- a/src/Api/Models/Public/GroupBaseModel.cs +++ b/src/Api/AdminConsole/Public/Models/GroupBaseModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Bit.Api.Models.Public; +namespace Bit.Api.AdminConsole.Public.Models; public abstract class GroupBaseModel { diff --git a/src/Api/Models/Public/MemberBaseModel.cs b/src/Api/AdminConsole/Public/Models/MemberBaseModel.cs similarity index 97% rename from src/Api/Models/Public/MemberBaseModel.cs rename to src/Api/AdminConsole/Public/Models/MemberBaseModel.cs index af57d80645dd..d7eaf98509f4 100644 --- a/src/Api/Models/Public/MemberBaseModel.cs +++ b/src/Api/AdminConsole/Public/Models/MemberBaseModel.cs @@ -3,7 +3,7 @@ using Bit.Core.Enums; using Bit.Core.Models.Data.Organizations.OrganizationUsers; -namespace Bit.Api.Models.Public; +namespace Bit.Api.AdminConsole.Public.Models; public abstract class MemberBaseModel { diff --git a/src/Api/Models/Public/PolicyBaseModel.cs b/src/Api/AdminConsole/Public/Models/PolicyBaseModel.cs similarity index 88% rename from src/Api/Models/Public/PolicyBaseModel.cs rename to src/Api/AdminConsole/Public/Models/PolicyBaseModel.cs index 2ad8e76005e8..f474d87ec98e 100644 --- a/src/Api/Models/Public/PolicyBaseModel.cs +++ b/src/Api/AdminConsole/Public/Models/PolicyBaseModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Bit.Api.Models.Public; +namespace Bit.Api.AdminConsole.Public.Models; public abstract class PolicyBaseModel { diff --git a/src/Api/Models/Public/Request/GroupCreateUpdateRequestModel.cs b/src/Api/AdminConsole/Public/Models/Request/GroupCreateUpdateRequestModel.cs similarity index 87% rename from src/Api/Models/Public/Request/GroupCreateUpdateRequestModel.cs rename to src/Api/AdminConsole/Public/Models/Request/GroupCreateUpdateRequestModel.cs index aa100b6a7f51..01850003d7cc 100644 --- a/src/Api/Models/Public/Request/GroupCreateUpdateRequestModel.cs +++ b/src/Api/AdminConsole/Public/Models/Request/GroupCreateUpdateRequestModel.cs @@ -1,7 +1,7 @@ using Bit.Api.Auth.Models.Public.Request; -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; -namespace Bit.Api.Models.Public.Request; +namespace Bit.Api.AdminConsole.Public.Models.Request; public class GroupCreateUpdateRequestModel : GroupBaseModel { diff --git a/src/Api/Models/Public/Request/MemberCreateRequestModel.cs b/src/Api/AdminConsole/Public/Models/Request/MemberCreateRequestModel.cs similarity index 90% rename from src/Api/Models/Public/Request/MemberCreateRequestModel.cs rename to src/Api/AdminConsole/Public/Models/Request/MemberCreateRequestModel.cs index 447434e4701b..cdbc99d0d192 100644 --- a/src/Api/Models/Public/Request/MemberCreateRequestModel.cs +++ b/src/Api/AdminConsole/Public/Models/Request/MemberCreateRequestModel.cs @@ -2,7 +2,7 @@ using Bit.Core.Entities; using Bit.Core.Utilities; -namespace Bit.Api.Models.Public.Request; +namespace Bit.Api.AdminConsole.Public.Models.Request; public class MemberCreateRequestModel : MemberUpdateRequestModel { diff --git a/src/Api/Models/Public/Request/MemberUpdateRequestModel.cs b/src/Api/AdminConsole/Public/Models/Request/MemberUpdateRequestModel.cs similarity index 93% rename from src/Api/Models/Public/Request/MemberUpdateRequestModel.cs rename to src/Api/AdminConsole/Public/Models/Request/MemberUpdateRequestModel.cs index 733e1285e67d..18aced43718b 100644 --- a/src/Api/Models/Public/Request/MemberUpdateRequestModel.cs +++ b/src/Api/AdminConsole/Public/Models/Request/MemberUpdateRequestModel.cs @@ -1,7 +1,7 @@ using Bit.Api.Auth.Models.Public.Request; using Bit.Core.Entities; -namespace Bit.Api.Models.Public.Request; +namespace Bit.Api.AdminConsole.Public.Models.Request; public class MemberUpdateRequestModel : MemberBaseModel { diff --git a/src/Api/Models/Public/Request/OrganizationImportRequestModel.cs b/src/Api/AdminConsole/Public/Models/Request/OrganizationImportRequestModel.cs similarity index 96% rename from src/Api/Models/Public/Request/OrganizationImportRequestModel.cs rename to src/Api/AdminConsole/Public/Models/Request/OrganizationImportRequestModel.cs index 70bf649a25ed..2adda81e4962 100644 --- a/src/Api/Models/Public/Request/OrganizationImportRequestModel.cs +++ b/src/Api/AdminConsole/Public/Models/Request/OrganizationImportRequestModel.cs @@ -1,10 +1,11 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Models.Business; using Bit.Core.Models.Business; using Bit.Core.Utilities; -namespace Bit.Api.Models.Public.Request; +namespace Bit.Api.AdminConsole.Public.Models.Request; public class OrganizationImportRequestModel { diff --git a/src/Api/Models/Public/Request/PolicyUpdateRequestModel.cs b/src/Api/AdminConsole/Public/Models/Request/PolicyUpdateRequestModel.cs similarity index 90% rename from src/Api/Models/Public/Request/PolicyUpdateRequestModel.cs rename to src/Api/AdminConsole/Public/Models/Request/PolicyUpdateRequestModel.cs index 251b9358d1c5..1866da6ecad9 100644 --- a/src/Api/Models/Public/Request/PolicyUpdateRequestModel.cs +++ b/src/Api/AdminConsole/Public/Models/Request/PolicyUpdateRequestModel.cs @@ -1,7 +1,7 @@ using System.Text.Json; using Bit.Core.Entities; -namespace Bit.Api.Models.Public.Request; +namespace Bit.Api.AdminConsole.Public.Models.Request; public class PolicyUpdateRequestModel : PolicyBaseModel { diff --git a/src/Api/Models/Public/Request/UpdateGroupIdsRequestModel.cs b/src/Api/AdminConsole/Public/Models/Request/UpdateGroupIdsRequestModel.cs similarity index 77% rename from src/Api/Models/Public/Request/UpdateGroupIdsRequestModel.cs rename to src/Api/AdminConsole/Public/Models/Request/UpdateGroupIdsRequestModel.cs index 7a818e5bbcc0..c55be36fff99 100644 --- a/src/Api/Models/Public/Request/UpdateGroupIdsRequestModel.cs +++ b/src/Api/AdminConsole/Public/Models/Request/UpdateGroupIdsRequestModel.cs @@ -1,4 +1,4 @@ -namespace Bit.Api.Models.Public.Request; +namespace Bit.Api.AdminConsole.Public.Models.Request; public class UpdateGroupIdsRequestModel { diff --git a/src/Api/Models/Public/Request/UpdateMemberIdsRequestModel.cs b/src/Api/AdminConsole/Public/Models/Request/UpdateMemberIdsRequestModel.cs similarity index 78% rename from src/Api/Models/Public/Request/UpdateMemberIdsRequestModel.cs rename to src/Api/AdminConsole/Public/Models/Request/UpdateMemberIdsRequestModel.cs index 87a2418318de..4124719929fd 100644 --- a/src/Api/Models/Public/Request/UpdateMemberIdsRequestModel.cs +++ b/src/Api/AdminConsole/Public/Models/Request/UpdateMemberIdsRequestModel.cs @@ -1,4 +1,4 @@ -namespace Bit.Api.Models.Public.Request; +namespace Bit.Api.AdminConsole.Public.Models.Request; public class UpdateMemberIdsRequestModel { diff --git a/src/Api/Models/Public/Response/GroupResponseModel.cs b/src/Api/AdminConsole/Public/Models/Response/GroupResponseModel.cs similarity index 90% rename from src/Api/Models/Public/Response/GroupResponseModel.cs rename to src/Api/AdminConsole/Public/Models/Response/GroupResponseModel.cs index 82c053c6650c..b1100ef02cca 100644 --- a/src/Api/Models/Public/Response/GroupResponseModel.cs +++ b/src/Api/AdminConsole/Public/Models/Response/GroupResponseModel.cs @@ -1,9 +1,10 @@ using System.ComponentModel.DataAnnotations; using Bit.Api.Auth.Models.Public.Response; -using Bit.Core.Entities; +using Bit.Api.Models.Public.Response; +using Bit.Core.AdminConsole.Entities; using Bit.Core.Models.Data; -namespace Bit.Api.Models.Public.Response; +namespace Bit.Api.AdminConsole.Public.Models.Response; /// /// A user group. diff --git a/src/Api/Models/Public/Response/MemberResponseModel.cs b/src/Api/AdminConsole/Public/Models/Response/MemberResponseModel.cs similarity index 97% rename from src/Api/Models/Public/Response/MemberResponseModel.cs rename to src/Api/AdminConsole/Public/Models/Response/MemberResponseModel.cs index b2e094cfa665..4f2ff1c17840 100644 --- a/src/Api/Models/Public/Response/MemberResponseModel.cs +++ b/src/Api/AdminConsole/Public/Models/Response/MemberResponseModel.cs @@ -1,11 +1,12 @@ using System.ComponentModel.DataAnnotations; using Bit.Api.Auth.Models.Public.Response; +using Bit.Api.Models.Public.Response; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.OrganizationUsers; -namespace Bit.Api.Models.Public.Response; +namespace Bit.Api.AdminConsole.Public.Models.Response; /// /// An organization member. diff --git a/src/Api/Models/Public/Response/PolicyResponseModel.cs b/src/Api/AdminConsole/Public/Models/Response/PolicyResponseModel.cs similarity index 92% rename from src/Api/Models/Public/Response/PolicyResponseModel.cs rename to src/Api/AdminConsole/Public/Models/Response/PolicyResponseModel.cs index b30c283229bd..3b196874d575 100644 --- a/src/Api/Models/Public/Response/PolicyResponseModel.cs +++ b/src/Api/AdminConsole/Public/Models/Response/PolicyResponseModel.cs @@ -1,9 +1,10 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json; +using Bit.Api.Models.Public.Response; using Bit.Core.Entities; using Bit.Core.Enums; -namespace Bit.Api.Models.Public.Response; +namespace Bit.Api.AdminConsole.Public.Models.Response; /// /// A policy. diff --git a/src/Api/Auth/Controllers/EmergencyAccessController.cs b/src/Api/Auth/Controllers/EmergencyAccessController.cs index cb58e8dfec96..ec2a9fc6b762 100644 --- a/src/Api/Auth/Controllers/EmergencyAccessController.cs +++ b/src/Api/Auth/Controllers/EmergencyAccessController.cs @@ -1,6 +1,6 @@ -using Bit.Api.Auth.Models.Request; +using Bit.Api.AdminConsole.Models.Request.Organizations; +using Bit.Api.Auth.Models.Request; using Bit.Api.Auth.Models.Response; -using Bit.Api.Models.Request.Organizations; using Bit.Api.Models.Response; using Bit.Api.Vault.Models.Response; using Bit.Core.Auth.Services; diff --git a/src/Api/Controllers/AccountsController.cs b/src/Api/Controllers/AccountsController.cs index d199c223debe..804aeb6518c1 100644 --- a/src/Api/Controllers/AccountsController.cs +++ b/src/Api/Controllers/AccountsController.cs @@ -1,15 +1,17 @@ -using Bit.Api.Auth.Models.Request.Accounts; +using Bit.Api.AdminConsole.Models.Response; +using Bit.Api.Auth.Models.Request.Accounts; using Bit.Api.Models.Request; using Bit.Api.Models.Request.Accounts; using Bit.Api.Models.Response; using Bit.Api.Utilities; using Bit.Core; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Auth.Models.Api.Request.Accounts; using Bit.Core.Auth.Models.Api.Response.Accounts; using Bit.Core.Auth.Services; using Bit.Core.Auth.Utilities; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Exceptions; using Bit.Core.Models.Api.Response; using Bit.Core.Models.Business; diff --git a/src/Api/Controllers/EventsController.cs b/src/Api/Controllers/EventsController.cs index 585568db61b4..921ee844004a 100644 --- a/src/Api/Controllers/EventsController.cs +++ b/src/Api/Controllers/EventsController.cs @@ -1,4 +1,6 @@ using Bit.Api.Models.Response; +using Bit.Api.Utilities; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Exceptions; using Bit.Core.Models.Data; @@ -41,7 +43,7 @@ public EventsController( public async Task> GetUser( [FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null) { - var dateRange = GetDateRange(start, end); + var dateRange = ApiHelpers.GetDateRange(start, end); var userId = _userService.GetProperUserId(User).Value; var result = await _eventRepository.GetManyByUserAsync(userId, dateRange.Item1, dateRange.Item2, new PageOptions { ContinuationToken = continuationToken }); @@ -75,7 +77,7 @@ public async Task> GetCipher(string id, throw new NotFoundException(); } - var dateRange = GetDateRange(start, end); + var dateRange = ApiHelpers.GetDateRange(start, end); var result = await _eventRepository.GetManyByCipherAsync(cipher, dateRange.Item1, dateRange.Item2, new PageOptions { ContinuationToken = continuationToken }); var responses = result.Data.Select(e => new EventResponseModel(e)); @@ -92,7 +94,7 @@ public async Task> GetOrganization(string throw new NotFoundException(); } - var dateRange = GetDateRange(start, end); + var dateRange = ApiHelpers.GetDateRange(start, end); var result = await _eventRepository.GetManyByOrganizationAsync(orgId, dateRange.Item1, dateRange.Item2, new PageOptions { ContinuationToken = continuationToken }); var responses = result.Data.Select(e => new EventResponseModel(e)); @@ -110,7 +112,7 @@ public async Task> GetOrganizationUser(str throw new NotFoundException(); } - var dateRange = GetDateRange(start, end); + var dateRange = ApiHelpers.GetDateRange(start, end); var result = await _eventRepository.GetManyByOrganizationActingUserAsync(organizationUser.OrganizationId, organizationUser.UserId.Value, dateRange.Item1, dateRange.Item2, new PageOptions { ContinuationToken = continuationToken }); @@ -127,7 +129,7 @@ public async Task> GetProvider(Guid provid throw new NotFoundException(); } - var dateRange = GetDateRange(start, end); + var dateRange = ApiHelpers.GetDateRange(start, end); var result = await _eventRepository.GetManyByProviderAsync(providerId, dateRange.Item1, dateRange.Item2, new PageOptions { ContinuationToken = continuationToken }); var responses = result.Data.Select(e => new EventResponseModel(e)); @@ -145,33 +147,11 @@ public async Task> GetProviderUser(Guid pr throw new NotFoundException(); } - var dateRange = GetDateRange(start, end); + var dateRange = ApiHelpers.GetDateRange(start, end); var result = await _eventRepository.GetManyByProviderActingUserAsync(providerUser.ProviderId, providerUser.UserId.Value, dateRange.Item1, dateRange.Item2, new PageOptions { ContinuationToken = continuationToken }); var responses = result.Data.Select(e => new EventResponseModel(e)); return new ListResponseModel(responses, result.ContinuationToken); } - - private Tuple GetDateRange(DateTime? start, DateTime? end) - { - if (!end.HasValue || !start.HasValue) - { - end = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1); - start = DateTime.UtcNow.Date.AddDays(-30); - } - else if (start.Value > end.Value) - { - var newEnd = start; - start = end; - end = newEnd; - } - - if ((end.Value - start.Value) > TimeSpan.FromDays(367)) - { - throw new BadRequestException("Range too large."); - } - - return new Tuple(start.Value, end.Value); - } } diff --git a/src/Api/Controllers/OrganizationConnectionsController.cs b/src/Api/Controllers/OrganizationConnectionsController.cs index 4c04a24973fb..e9ea31c8ffca 100644 --- a/src/Api/Controllers/OrganizationConnectionsController.cs +++ b/src/Api/Controllers/OrganizationConnectionsController.cs @@ -78,7 +78,12 @@ public async Task CreateConnection([FromBod [HttpPut("{organizationConnectionId}")] public async Task UpdateConnection(Guid organizationConnectionId, [FromBody] OrganizationConnectionRequestModel model) { - var existingOrganizationConnection = await _organizationConnectionRepository.GetByIdAsync(organizationConnectionId); + if (model == null) + { + throw new NotFoundException(); + } + + var existingOrganizationConnection = await _organizationConnectionRepository.GetByIdOrganizationIdAsync(organizationConnectionId, model.OrganizationId); if (existingOrganizationConnection == null) { throw new NotFoundException(); diff --git a/src/Api/Controllers/OrganizationDomainController.cs b/src/Api/Controllers/OrganizationDomainController.cs index 23e4f51bc70e..60abdad7d42c 100644 --- a/src/Api/Controllers/OrganizationDomainController.cs +++ b/src/Api/Controllers/OrganizationDomainController.cs @@ -19,7 +19,7 @@ public class OrganizationDomainController : Controller private readonly ICreateOrganizationDomainCommand _createOrganizationDomainCommand; private readonly IVerifyOrganizationDomainCommand _verifyOrganizationDomainCommand; private readonly IDeleteOrganizationDomainCommand _deleteOrganizationDomainCommand; - private readonly IGetOrganizationDomainByIdQuery _getOrganizationDomainByIdQuery; + private readonly IGetOrganizationDomainByIdOrganizationIdQuery _getOrganizationDomainByIdAndOrganizationIdQuery; private readonly IGetOrganizationDomainByOrganizationIdQuery _getOrganizationDomainByOrganizationIdQuery; private readonly ICurrentContext _currentContext; private readonly IOrganizationRepository _organizationRepository; @@ -29,7 +29,7 @@ public OrganizationDomainController( ICreateOrganizationDomainCommand createOrganizationDomainCommand, IVerifyOrganizationDomainCommand verifyOrganizationDomainCommand, IDeleteOrganizationDomainCommand deleteOrganizationDomainCommand, - IGetOrganizationDomainByIdQuery getOrganizationDomainByIdQuery, + IGetOrganizationDomainByIdOrganizationIdQuery getOrganizationDomainByIdAndOrganizationIdQuery, IGetOrganizationDomainByOrganizationIdQuery getOrganizationDomainByOrganizationIdQuery, ICurrentContext currentContext, IOrganizationRepository organizationRepository, @@ -38,7 +38,7 @@ public OrganizationDomainController( _createOrganizationDomainCommand = createOrganizationDomainCommand; _verifyOrganizationDomainCommand = verifyOrganizationDomainCommand; _deleteOrganizationDomainCommand = deleteOrganizationDomainCommand; - _getOrganizationDomainByIdQuery = getOrganizationDomainByIdQuery; + _getOrganizationDomainByIdAndOrganizationIdQuery = getOrganizationDomainByIdAndOrganizationIdQuery; _getOrganizationDomainByOrganizationIdQuery = getOrganizationDomainByOrganizationIdQuery; _currentContext = currentContext; _organizationRepository = organizationRepository; @@ -46,71 +46,78 @@ public OrganizationDomainController( } [HttpGet("{orgId}/domain")] - public async Task> Get(string orgId) + public async Task> Get(Guid orgId) { - var orgIdGuid = new Guid(orgId); - await ValidateOrganizationAccessAsync(orgIdGuid); + await ValidateOrganizationAccessAsync(orgId); var domains = await _getOrganizationDomainByOrganizationIdQuery - .GetDomainsByOrganizationId(orgIdGuid); + .GetDomainsByOrganizationIdAsync(orgId); var response = domains.Select(x => new OrganizationDomainResponseModel(x)).ToList(); return new ListResponseModel(response); } [HttpGet("{orgId}/domain/{id}")] - public async Task Get(string orgId, string id) + public async Task Get(Guid orgId, Guid id) { - var orgIdGuid = new Guid(orgId); - var IdGuid = new Guid(id); - await ValidateOrganizationAccessAsync(orgIdGuid); + await ValidateOrganizationAccessAsync(orgId); - var domain = await _getOrganizationDomainByIdQuery.GetOrganizationDomainById(IdGuid); - if (domain is null) + var organizationDomain = await _getOrganizationDomainByIdAndOrganizationIdQuery + .GetOrganizationDomainByIdOrganizationIdAsync(id, orgId); + if (organizationDomain is null) { throw new NotFoundException(); } - return new OrganizationDomainResponseModel(domain); + return new OrganizationDomainResponseModel(organizationDomain); } [HttpPost("{orgId}/domain")] - public async Task Post(string orgId, + public async Task Post(Guid orgId, [FromBody] OrganizationDomainRequestModel model) { - var orgIdGuid = new Guid(orgId); - await ValidateOrganizationAccessAsync(orgIdGuid); + await ValidateOrganizationAccessAsync(orgId); var organizationDomain = new OrganizationDomain { - OrganizationId = orgIdGuid, + OrganizationId = orgId, Txt = model.Txt, DomainName = model.DomainName.ToLower() }; - var domain = await _createOrganizationDomainCommand.CreateAsync(organizationDomain); - return new OrganizationDomainResponseModel(domain); + organizationDomain = await _createOrganizationDomainCommand.CreateAsync(organizationDomain); + + return new OrganizationDomainResponseModel(organizationDomain); } [HttpPost("{orgId}/domain/{id}/verify")] - public async Task Verify(string orgId, string id) + public async Task Verify(Guid orgId, Guid id) { - var orgIdGuid = new Guid(orgId); - var idGuid = new Guid(id); - await ValidateOrganizationAccessAsync(orgIdGuid); + await ValidateOrganizationAccessAsync(orgId); + + var organizationDomain = await _organizationDomainRepository.GetDomainByIdOrganizationIdAsync(id, orgId); + if (organizationDomain is null) + { + throw new NotFoundException(); + } - var domain = await _verifyOrganizationDomainCommand.VerifyOrganizationDomain(idGuid); - return new OrganizationDomainResponseModel(domain); + organizationDomain = await _verifyOrganizationDomainCommand.VerifyOrganizationDomainAsync(organizationDomain); + + return new OrganizationDomainResponseModel(organizationDomain); } [HttpDelete("{orgId}/domain/{id}")] [HttpPost("{orgId}/domain/{id}/remove")] - public async Task RemoveDomain(string orgId, string id) + public async Task RemoveDomain(Guid orgId, Guid id) { - var orgIdGuid = new Guid(orgId); - var idGuid = new Guid(id); - await ValidateOrganizationAccessAsync(orgIdGuid); + await ValidateOrganizationAccessAsync(orgId); + + var domain = await _organizationDomainRepository.GetDomainByIdOrganizationIdAsync(id, orgId); + if (domain is null) + { + throw new NotFoundException(); + } - await _deleteOrganizationDomainCommand.DeleteAsync(idGuid); + await _deleteOrganizationDomainCommand.DeleteAsync(domain); } [AllowAnonymous] diff --git a/src/Api/Controllers/PlansController.cs b/src/Api/Controllers/PlansController.cs index a7ae7c0b3060..d738e60cfb20 100644 --- a/src/Api/Controllers/PlansController.cs +++ b/src/Api/Controllers/PlansController.cs @@ -19,30 +19,12 @@ public PlansController(ITaxRateRepository taxRateRepository) [HttpGet("")] [AllowAnonymous] public ListResponseModel Get() - { - var data = StaticStore.PasswordManagerPlans; - var responses = data.Select(plan => new PlanResponseModel(plan)); - return new ListResponseModel(responses); - } - - [HttpGet("all")] - [AllowAnonymous] - public ListResponseModel GetAllPlans() { var data = StaticStore.Plans; var responses = data.Select(plan => new PlanResponseModel(plan)); return new ListResponseModel(responses); } - [HttpGet("sm-plans")] - [AllowAnonymous] - public ListResponseModel GetSecretsManagerPlans() - { - var data = StaticStore.SecretManagerPlans; - var responses = data.Select(plan => new PlanResponseModel(plan)); - return new ListResponseModel(responses); - } - [HttpGet("sales-tax-rates")] public async Task> GetTaxRates() { diff --git a/src/Api/Controllers/SelfHosted/SelfHostedOrganizationLicensesController.cs b/src/Api/Controllers/SelfHosted/SelfHostedOrganizationLicensesController.cs index c821204c75b8..783c4b71f42c 100644 --- a/src/Api/Controllers/SelfHosted/SelfHostedOrganizationLicensesController.cs +++ b/src/Api/Controllers/SelfHosted/SelfHostedOrganizationLicensesController.cs @@ -1,6 +1,6 @@ -using Bit.Api.Models.Request; +using Bit.Api.AdminConsole.Models.Response.Organizations; +using Bit.Api.Models.Request; using Bit.Api.Models.Request.Organizations; -using Bit.Api.Models.Response.Organizations; using Bit.Api.Utilities; using Bit.Core.Context; using Bit.Core.Enums; diff --git a/src/Api/Models/Request/Organizations/OrganizationCreateLicenseRequestModel.cs b/src/Api/Models/Request/Organizations/OrganizationCreateLicenseRequestModel.cs index 2d9175158f4e..5ee7a632a6cd 100644 --- a/src/Api/Models/Request/Organizations/OrganizationCreateLicenseRequestModel.cs +++ b/src/Api/Models/Request/Organizations/OrganizationCreateLicenseRequestModel.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using Bit.Api.AdminConsole.Models.Request.Organizations; using Bit.Core.Utilities; namespace Bit.Api.Models.Request.Organizations; diff --git a/src/Api/Models/Response/PlanResponseModel.cs b/src/Api/Models/Response/PlanResponseModel.cs index f3b39f113373..7d007421ef18 100644 --- a/src/Api/Models/Response/PlanResponseModel.cs +++ b/src/Api/Models/Response/PlanResponseModel.cs @@ -21,15 +21,6 @@ public PlanResponseModel(Plan plan, string obj = "plan") NameLocalizationKey = plan.NameLocalizationKey; DescriptionLocalizationKey = plan.DescriptionLocalizationKey; CanBeUsedByBusiness = plan.CanBeUsedByBusiness; - BaseSeats = plan.BaseSeats; - BaseStorageGb = plan.BaseStorageGb; - MaxCollections = plan.MaxCollections; - MaxUsers = plan.MaxUsers; - HasAdditionalSeatsOption = plan.HasAdditionalSeatsOption; - HasAdditionalStorageOption = plan.HasAdditionalStorageOption; - MaxAdditionalSeats = plan.MaxAdditionalSeats; - MaxAdditionalStorage = plan.MaxAdditionalStorage; - HasPremiumAccessOption = plan.HasPremiumAccessOption; TrialPeriodDays = plan.TrialPeriodDays; HasSelfHost = plan.HasSelfHost; HasPolicies = plan.HasPolicies; @@ -45,22 +36,12 @@ public PlanResponseModel(Plan plan, string obj = "plan") DisplaySortOrder = plan.DisplaySortOrder; LegacyYear = plan.LegacyYear; Disabled = plan.Disabled; - StripePlanId = plan.StripePlanId; - StripeSeatPlanId = plan.StripeSeatPlanId; - StripeStoragePlanId = plan.StripeStoragePlanId; - BasePrice = plan.BasePrice; - SeatPrice = plan.SeatPrice; - AdditionalStoragePricePerGb = plan.AdditionalStoragePricePerGb; - PremiumAccessOptionPrice = plan.PremiumAccessOptionPrice; + if (plan.SecretsManager != null) + { + SecretsManager = new SecretsManagerPlanFeaturesResponseModel(plan.SecretsManager); + } - AdditionalPricePerServiceAccount = plan.AdditionalPricePerServiceAccount; - BaseServiceAccount = plan.BaseServiceAccount; - MaxServiceAccounts = plan.MaxServiceAccounts; - MaxAdditionalServiceAccounts = plan.MaxAdditionalServiceAccount; - HasAdditionalServiceAccountOption = plan.HasAdditionalServiceAccountOption; - MaxProjects = plan.MaxProjects; - BitwardenProduct = plan.BitwardenProduct; - StripeServiceAccountPlanId = plan.StripeServiceAccountPlanId; + PasswordManager = new PasswordManagerPlanFeaturesResponseModel(plan.PasswordManager); } public PlanType Type { get; set; } @@ -70,16 +51,6 @@ public PlanResponseModel(Plan plan, string obj = "plan") public string NameLocalizationKey { get; set; } public string DescriptionLocalizationKey { get; set; } public bool CanBeUsedByBusiness { get; set; } - public int BaseSeats { get; set; } - public short? BaseStorageGb { get; set; } - public short? MaxCollections { get; set; } - public short? MaxUsers { get; set; } - - public bool HasAdditionalSeatsOption { get; set; } - public int? MaxAdditionalSeats { get; set; } - public bool HasAdditionalStorageOption { get; set; } - public short? MaxAdditionalStorage { get; set; } - public bool HasPremiumAccessOption { get; set; } public int? TrialPeriodDays { get; set; } public bool HasSelfHost { get; set; } @@ -98,21 +69,95 @@ public PlanResponseModel(Plan plan, string obj = "plan") public int DisplaySortOrder { get; set; } public int? LegacyYear { get; set; } public bool Disabled { get; set; } + public SecretsManagerPlanFeaturesResponseModel SecretsManager { get; protected init; } + public PasswordManagerPlanFeaturesResponseModel PasswordManager { get; protected init; } + + public class SecretsManagerPlanFeaturesResponseModel + { + public SecretsManagerPlanFeaturesResponseModel(Plan.SecretsManagerPlanFeatures plan) + { + MaxServiceAccounts = plan.MaxServiceAccounts; + AllowServiceAccountsAutoscale = plan is { AllowServiceAccountsAutoscale: true }; + StripeServiceAccountPlanId = plan.StripeServiceAccountPlanId; + AdditionalPricePerServiceAccount = plan.AdditionalPricePerServiceAccount; + BaseServiceAccount = plan.BaseServiceAccount; + MaxAdditionalServiceAccount = plan.MaxAdditionalServiceAccount; + HasAdditionalServiceAccountOption = plan is { HasAdditionalServiceAccountOption: true }; + StripeSeatPlanId = plan.StripeSeatPlanId; + HasAdditionalSeatsOption = plan is { HasAdditionalSeatsOption: true }; + BasePrice = plan.BasePrice; + SeatPrice = plan.SeatPrice; + BaseSeats = plan.BaseSeats; + MaxSeats = plan.MaxSeats; + MaxAdditionalSeats = plan.MaxAdditionalSeats; + AllowSeatAutoscale = plan.AllowSeatAutoscale; + MaxProjects = plan.MaxProjects; + } + // Service accounts + public short? MaxServiceAccounts { get; init; } + public bool AllowServiceAccountsAutoscale { get; init; } + public string StripeServiceAccountPlanId { get; init; } + public decimal? AdditionalPricePerServiceAccount { get; init; } + public short? BaseServiceAccount { get; init; } + public short? MaxAdditionalServiceAccount { get; init; } + public bool HasAdditionalServiceAccountOption { get; init; } + // Seats + public string StripeSeatPlanId { get; init; } + public bool HasAdditionalSeatsOption { get; init; } + public decimal BasePrice { get; init; } + public decimal SeatPrice { get; init; } + public int BaseSeats { get; init; } + public short? MaxSeats { get; init; } + public int? MaxAdditionalSeats { get; init; } + public bool AllowSeatAutoscale { get; init; } - public string StripePlanId { get; set; } - public string StripeSeatPlanId { get; set; } - public string StripeStoragePlanId { get; set; } - public string StripePremiumAccessPlanId { get; set; } - public decimal BasePrice { get; set; } - 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? MaxServiceAccounts { get; set; } - public short? MaxAdditionalServiceAccounts { get; set; } - public bool HasAdditionalServiceAccountOption { get; set; } - public short? MaxProjects { get; set; } - public BitwardenProductType BitwardenProduct { get; set; } + // Features + public int MaxProjects { get; init; } + } + + public record PasswordManagerPlanFeaturesResponseModel + { + public PasswordManagerPlanFeaturesResponseModel(Plan.PasswordManagerPlanFeatures plan) + { + StripePlanId = plan.StripePlanId; + StripeSeatPlanId = plan.StripeSeatPlanId; + BasePrice = plan.BasePrice; + SeatPrice = plan.SeatPrice; + AllowSeatAutoscale = plan.AllowSeatAutoscale; + HasAdditionalSeatsOption = plan.HasAdditionalSeatsOption; + MaxAdditionalSeats = plan.MaxAdditionalSeats; + BaseSeats = plan.BaseSeats; + HasPremiumAccessOption = plan.HasPremiumAccessOption; + StripePremiumAccessPlanId = plan.StripePremiumAccessPlanId; + PremiumAccessOptionPrice = plan.PremiumAccessOptionPrice; + MaxSeats = plan.MaxSeats; + BaseStorageGb = plan.BaseStorageGb; + HasAdditionalStorageOption = plan.HasAdditionalStorageOption; + AdditionalStoragePricePerGb = plan.AdditionalStoragePricePerGb; + StripeStoragePlanId = plan.StripeStoragePlanId; + MaxAdditionalStorage = plan.MaxAdditionalStorage; + MaxCollections = plan.MaxCollections; + } + // Seats + public string StripePlanId { get; init; } + public string StripeSeatPlanId { get; init; } + public decimal BasePrice { get; init; } + public decimal SeatPrice { get; init; } + public bool AllowSeatAutoscale { get; init; } + public bool HasAdditionalSeatsOption { get; init; } + public int? MaxAdditionalSeats { get; init; } + public int BaseSeats { get; init; } + public bool HasPremiumAccessOption { get; init; } + public string StripePremiumAccessPlanId { get; init; } + public decimal PremiumAccessOptionPrice { get; init; } + public short? MaxSeats { get; init; } + // Storage + public short? BaseStorageGb { get; init; } + public bool HasAdditionalStorageOption { get; init; } + public decimal AdditionalStoragePricePerGb { get; init; } + public string StripeStoragePlanId { get; init; } + public short? MaxAdditionalStorage { get; init; } + // Feature + public short? MaxCollections { get; init; } + } } diff --git a/src/Api/Models/Response/ProfileResponseModel.cs b/src/Api/Models/Response/ProfileResponseModel.cs index 1fbf002e2761..b5a32fa3267f 100644 --- a/src/Api/Models/Response/ProfileResponseModel.cs +++ b/src/Api/Models/Response/ProfileResponseModel.cs @@ -1,7 +1,8 @@ -using Bit.Api.Models.Response.Providers; +using Bit.Api.AdminConsole.Models.Response; +using Bit.Api.AdminConsole.Models.Response.Providers; +using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.Entities; using Bit.Core.Models.Api; -using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.OrganizationUsers; namespace Bit.Api.Models.Response; diff --git a/src/Api/Models/Response/SubscriptionResponseModel.cs b/src/Api/Models/Response/SubscriptionResponseModel.cs index 4c0ee9338b34..883f7ac900f8 100644 --- a/src/Api/Models/Response/SubscriptionResponseModel.cs +++ b/src/Api/Models/Response/SubscriptionResponseModel.cs @@ -1,5 +1,4 @@ using Bit.Core.Entities; -using Bit.Core.Enums; using Bit.Core.Models.Api; using Bit.Core.Models.Business; using Bit.Core.Utilities; @@ -46,6 +45,20 @@ public SubscriptionResponseModel(User user, UserLicense license = null) public bool UsingInAppPurchase { get; set; } } +public class BillingCustomerDiscount +{ + public BillingCustomerDiscount(SubscriptionInfo.BillingCustomerDiscount discount) + { + Id = discount.Id; + Active = discount.Active; + PercentOff = discount.PercentOff; + } + + public string Id { get; } + public bool Active { get; } + public decimal? PercentOff { get; } +} + public class BillingSubscription { public BillingSubscription(SubscriptionInfo.BillingSubscription sub) @@ -84,7 +97,6 @@ public BillingSubscriptionItem(SubscriptionInfo.BillingSubscription.BillingSubsc Quantity = item.Quantity; SponsoredSubscriptionItem = item.SponsoredSubscriptionItem; AddonSubscriptionItem = item.AddonSubscriptionItem; - BitwardenProduct = item.BitwardenProduct; } public string Name { get; set; } @@ -93,7 +105,6 @@ public BillingSubscriptionItem(SubscriptionInfo.BillingSubscription.BillingSubsc public string Interval { get; set; } public bool SponsoredSubscriptionItem { get; set; } public bool AddonSubscriptionItem { get; set; } - public BitwardenProductType BitwardenProduct { get; set; } } } diff --git a/src/Api/SecretsManager/Controllers/AccessPoliciesController.cs b/src/Api/SecretsManager/Controllers/AccessPoliciesController.cs index 64dacfa0a9b2..c8d43293e1e2 100644 --- a/src/Api/SecretsManager/Controllers/AccessPoliciesController.cs +++ b/src/Api/SecretsManager/Controllers/AccessPoliciesController.cs @@ -1,6 +1,7 @@ using Bit.Api.Models.Response; using Bit.Api.SecretsManager.Models.Request; using Bit.Api.SecretsManager.Models.Response; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -10,14 +11,12 @@ using Bit.Core.SecretsManager.Entities; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; -using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Bit.Api.SecretsManager.Controllers; [Authorize("secrets")] -[SelfHosted(NotSelfHostedOnly = true)] [Route("access-policies")] public class AccessPoliciesController : Controller { diff --git a/src/Api/SecretsManager/Controllers/ProjectsController.cs b/src/Api/SecretsManager/Controllers/ProjectsController.cs index e59918593a44..a436e9601a4f 100644 --- a/src/Api/SecretsManager/Controllers/ProjectsController.cs +++ b/src/Api/SecretsManager/Controllers/ProjectsController.cs @@ -10,14 +10,12 @@ using Bit.Core.SecretsManager.Queries.Projects.Interfaces; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; -using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Bit.Api.SecretsManager.Controllers; [Authorize("secrets")] -[SelfHosted(NotSelfHostedOnly = true)] public class ProjectsController : Controller { private readonly ICurrentContext _currentContext; diff --git a/src/Api/SecretsManager/Controllers/SecretsController.cs b/src/Api/SecretsManager/Controllers/SecretsController.cs index a88a784bed8e..d8e09fe17b0d 100644 --- a/src/Api/SecretsManager/Controllers/SecretsController.cs +++ b/src/Api/SecretsManager/Controllers/SecretsController.cs @@ -14,14 +14,12 @@ using Bit.Core.Tools.Enums; using Bit.Core.Tools.Models.Business; using Bit.Core.Tools.Services; -using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Bit.Api.SecretsManager.Controllers; [Authorize("secrets")] -[SelfHosted(NotSelfHostedOnly = true)] public class SecretsController : Controller { private readonly ICurrentContext _currentContext; diff --git a/src/Api/SecretsManager/Controllers/SecretsManagerEventsController.cs b/src/Api/SecretsManager/Controllers/SecretsManagerEventsController.cs new file mode 100644 index 000000000000..91d350b68092 --- /dev/null +++ b/src/Api/SecretsManager/Controllers/SecretsManagerEventsController.cs @@ -0,0 +1,52 @@ +using Bit.Api.Models.Response; +using Bit.Api.Utilities; +using Bit.Core.Exceptions; +using Bit.Core.Models.Data; +using Bit.Core.Repositories; +using Bit.Core.SecretsManager.AuthorizationRequirements; +using Bit.Core.SecretsManager.Repositories; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Bit.Api.SecretsManager.Controllers; + +[Authorize("secrets")] +public class SecretsManagerEventsController : Controller +{ + private readonly IAuthorizationService _authorizationService; + private readonly IEventRepository _eventRepository; + private readonly IServiceAccountRepository _serviceAccountRepository; + + public SecretsManagerEventsController( + IEventRepository eventRepository, + IServiceAccountRepository serviceAccountRepository, + IAuthorizationService authorizationService) + { + _authorizationService = authorizationService; + _serviceAccountRepository = serviceAccountRepository; + _eventRepository = eventRepository; + } + + [HttpGet("sm/events/service-accounts/{serviceAccountId}")] + public async Task> GetServiceAccountEventsAsync(Guid serviceAccountId, + [FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, + [FromQuery] string continuationToken = null) + { + var serviceAccount = await _serviceAccountRepository.GetByIdAsync(serviceAccountId); + var authorizationResult = + await _authorizationService.AuthorizeAsync(User, serviceAccount, ServiceAccountOperations.ReadEvents); + + if (!authorizationResult.Succeeded) + { + throw new NotFoundException(); + } + + var dateRange = ApiHelpers.GetDateRange(start, end); + + var result = await _eventRepository.GetManyByOrganizationServiceAccountAsync(serviceAccount.OrganizationId, + serviceAccount.Id, dateRange.Item1, dateRange.Item2, + new PageOptions { ContinuationToken = continuationToken }); + var responses = result.Data.Select(e => new EventResponseModel(e)); + return new ListResponseModel(responses, result.ContinuationToken); + } +} diff --git a/src/Api/SecretsManager/Controllers/SecretsManagerPortingController.cs b/src/Api/SecretsManager/Controllers/SecretsManagerPortingController.cs index 3d26c8f702fe..19ef56e5653a 100644 --- a/src/Api/SecretsManager/Controllers/SecretsManagerPortingController.cs +++ b/src/Api/SecretsManager/Controllers/SecretsManagerPortingController.cs @@ -7,14 +7,12 @@ using Bit.Core.SecretsManager.Queries.Projects.Interfaces; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; -using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Bit.Api.SecretsManager.Controllers; [Authorize("secrets")] -[SelfHosted(NotSelfHostedOnly = true)] public class SecretsManagerPortingController : Controller { private readonly ISecretRepository _secretRepository; diff --git a/src/Api/SecretsManager/Controllers/SecretsTrashController.cs b/src/Api/SecretsManager/Controllers/SecretsTrashController.cs index aaaebf5fe416..1bf9d3135a6c 100644 --- a/src/Api/SecretsManager/Controllers/SecretsTrashController.cs +++ b/src/Api/SecretsManager/Controllers/SecretsTrashController.cs @@ -3,14 +3,12 @@ using Bit.Core.Exceptions; using Bit.Core.SecretsManager.Commands.Trash.Interfaces; using Bit.Core.SecretsManager.Repositories; -using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Bit.Api.SecretsManager.Controllers; [Authorize("secrets")] -[SelfHosted(NotSelfHostedOnly = true)] public class TrashController : Controller { private readonly ICurrentContext _currentContext; diff --git a/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs b/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs index bb28276b0332..28fb9671064c 100644 --- a/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs +++ b/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs @@ -14,14 +14,12 @@ using Bit.Core.SecretsManager.Queries.ServiceAccounts.Interfaces; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; -using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Bit.Api.SecretsManager.Controllers; [Authorize("secrets")] -[SelfHosted(NotSelfHostedOnly = true)] [Route("service-accounts")] public class ServiceAccountsController : Controller { diff --git a/src/Api/SecretsManager/Models/Response/PotentialGranteeResponseModel.cs b/src/Api/SecretsManager/Models/Response/PotentialGranteeResponseModel.cs index e62437083026..a64c0d86db28 100644 --- a/src/Api/SecretsManager/Models/Response/PotentialGranteeResponseModel.cs +++ b/src/Api/SecretsManager/Models/Response/PotentialGranteeResponseModel.cs @@ -1,4 +1,4 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; using Bit.Core.Models.Api; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.SecretsManager.Entities; diff --git a/src/Api/Tools/Controllers/ImportCiphersController.cs b/src/Api/Tools/Controllers/ImportCiphersController.cs index d3ed7444f3b7..b480e9c5cbaa 100644 --- a/src/Api/Tools/Controllers/ImportCiphersController.cs +++ b/src/Api/Tools/Controllers/ImportCiphersController.cs @@ -5,7 +5,6 @@ using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; -using Bit.Core.Utilities; using Bit.Core.Vault.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -26,7 +25,6 @@ public ImportCiphersController( ICollectionCipherRepository collectionCipherRepository, ICipherService cipherService, IUserService userService, - IProviderService providerService, ICurrentContext currentContext, ILogger logger, GlobalSettings globalSettings) diff --git a/src/Api/Utilities/ApiHelpers.cs b/src/Api/Utilities/ApiHelpers.cs index 58097089f4dd..f4f1830e1613 100644 --- a/src/Api/Utilities/ApiHelpers.cs +++ b/src/Api/Utilities/ApiHelpers.cs @@ -1,6 +1,7 @@ using System.Text.Json; using Azure.Messaging.EventGrid; using Azure.Messaging.EventGrid.SystemEvents; +using Bit.Core.Exceptions; using Bit.Core.Utilities; using Microsoft.AspNetCore.Mvc; @@ -69,4 +70,35 @@ public async static Task HandleAzureEvents(HttpRequest request, return new OkObjectResult(response); } + + /// + /// Validates and returns a date range. Currently used for fetching events. + /// + /// start date and time + /// end date and time + /// + /// If start or end are null, will return a range of the last 30 days. + /// If a time span greater than 367 days is passed will throw BadRequestException. + /// + public static Tuple GetDateRange(DateTime? start, DateTime? end) + { + if (!end.HasValue || !start.HasValue) + { + end = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1); + start = DateTime.UtcNow.Date.AddDays(-30); + } + else if (start.Value > end.Value) + { + var newEnd = start; + start = end; + end = newEnd; + } + + if ((end.Value - start.Value) > TimeSpan.FromDays(367)) + { + throw new BadRequestException("Range too large."); + } + + return new Tuple(start.Value, end.Value); + } } diff --git a/src/Api/Vault/Controllers/CiphersController.cs b/src/Api/Vault/Controllers/CiphersController.cs index 8bcd6038927d..ad0db8f6b3b5 100644 --- a/src/Api/Vault/Controllers/CiphersController.cs +++ b/src/Api/Vault/Controllers/CiphersController.cs @@ -6,6 +6,7 @@ using Bit.Api.Vault.Models.Request; using Bit.Api.Vault.Models.Response; using Bit.Core; +using Bit.Core.AdminConsole.Services; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Exceptions; @@ -27,6 +28,8 @@ namespace Bit.Api.Vault.Controllers; [Authorize("Application")] public class CiphersController : Controller { + private static readonly Version _fido2KeyCipherMinimumVersion = new Version(Constants.Fido2KeyCipherMinimumVersion); + private readonly ICipherRepository _cipherRepository; private readonly ICollectionCipherRepository _collectionCipherRepository; private readonly ICipherService _cipherService; @@ -36,6 +39,7 @@ public class CiphersController : Controller private readonly ICurrentContext _currentContext; private readonly ILogger _logger; private readonly GlobalSettings _globalSettings; + private readonly Version _cipherKeyEncryptionMinimumVersion = new Version(Constants.CipherKeyEncryptionMinimumVersion); public CiphersController( ICipherRepository cipherRepository, @@ -177,6 +181,9 @@ public async Task Put(Guid id, [FromBody] CipherRequestMode throw new NotFoundException(); } + ValidateClientVersionForItemLevelEncryptionSupport(cipher); + ValidateClientVersionForFido2CredentialSupport(cipher); + var collectionIds = (await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, id)).Select(c => c.CollectionId).ToList(); var modelOrgId = string.IsNullOrWhiteSpace(model.OrganizationId) ? (Guid?)null : new Guid(model.OrganizationId); @@ -198,6 +205,10 @@ public async Task PutAdmin(Guid id, [FromBody] CipherRe { var userId = _userService.GetProperUserId(User).Value; var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(id); + + ValidateClientVersionForItemLevelEncryptionSupport(cipher); + ValidateClientVersionForFido2CredentialSupport(cipher); + if (cipher == null || !cipher.OrganizationId.HasValue || !await _currentContext.EditAnyCollection(cipher.OrganizationId.Value)) { @@ -261,6 +272,9 @@ public async Task PutShare(string id, [FromBody] CipherShar throw new NotFoundException(); } + ValidateClientVersionForItemLevelEncryptionSupport(cipher); + ValidateClientVersionForFido2CredentialSupport(cipher); + var original = cipher.Clone(); await _cipherService.ShareAsync(original, model.Cipher.ToCipher(cipher), new Guid(model.Cipher.OrganizationId), model.CollectionIds.Select(c => new Guid(c)), userId, model.Cipher.LastKnownRevisionDate); @@ -523,7 +537,12 @@ public async Task PutShareMany([FromBody] CipherBulkShareRequestModel model) throw new BadRequestException("Trying to move ciphers that you do not own."); } - shareCiphers.Add((cipher.ToCipher(ciphersDict[cipher.Id.Value]), cipher.LastKnownRevisionDate)); + var existingCipher = ciphersDict[cipher.Id.Value]; + + ValidateClientVersionForItemLevelEncryptionSupport(existingCipher); + ValidateClientVersionForFido2CredentialSupport(existingCipher); + + shareCiphers.Add((cipher.ToCipher(existingCipher), cipher.LastKnownRevisionDate)); } await _cipherService.ShareManyAsync(shareCiphers, organizationId, @@ -576,6 +595,8 @@ await _cipherRepository.GetOrganizationDetailsByIdAsync(idGuid) : throw new NotFoundException(); } + ValidateClientVersionForItemLevelEncryptionSupport(cipher); + if (request.FileSize > CipherService.MAX_FILE_SIZE) { throw new BadRequestException($"Max file size is {CipherService.MAX_FILE_SIZE_READABLE}."); @@ -795,4 +816,24 @@ private void ValidateAttachment() throw new BadRequestException("Invalid content."); } } + + private void ValidateClientVersionForItemLevelEncryptionSupport(Cipher cipher) + { + if (cipher.Key != null && _currentContext.ClientVersion < _cipherKeyEncryptionMinimumVersion) + { + throw new BadRequestException("Cannot edit item. Update to the latest version of Bitwarden and try again."); + } + } + + private void ValidateClientVersionForFido2CredentialSupport(Cipher cipher) + { + if (cipher.Type == Core.Vault.Enums.CipherType.Login) + { + var loginData = JsonSerializer.Deserialize(cipher.Data); + if (loginData?.Fido2Credentials != null && _currentContext.ClientVersion < _fido2KeyCipherMinimumVersion) + { + throw new BadRequestException("Cannot edit item. Update to the latest version of Bitwarden and try again."); + } + } + } } diff --git a/src/Api/Vault/Controllers/SyncController.cs b/src/Api/Vault/Controllers/SyncController.cs index ec9ab4f96dd8..01a1fbb5543b 100644 --- a/src/Api/Vault/Controllers/SyncController.cs +++ b/src/Api/Vault/Controllers/SyncController.cs @@ -1,7 +1,8 @@ using Bit.Api.Vault.Models.Response; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Exceptions; using Bit.Core.Models.Data; using Bit.Core.Repositories; diff --git a/src/Api/Vault/Models/CipherFido2CredentialModel.cs b/src/Api/Vault/Models/CipherFido2CredentialModel.cs new file mode 100644 index 000000000000..32f6104a9aa4 --- /dev/null +++ b/src/Api/Vault/Models/CipherFido2CredentialModel.cs @@ -0,0 +1,89 @@ +using System.ComponentModel.DataAnnotations; +using Bit.Core.Utilities; +using Bit.Core.Vault.Models.Data; + +namespace Bit.Api.Vault.Models; + +public class CipherFido2CredentialModel +{ + public CipherFido2CredentialModel() { } + + public CipherFido2CredentialModel(CipherLoginFido2CredentialData data) + { + CredentialId = data.CredentialId; + KeyType = data.KeyType; + KeyAlgorithm = data.KeyAlgorithm; + KeyCurve = data.KeyCurve; + KeyValue = data.KeyValue; + RpId = data.RpId; + RpName = data.RpName; + UserHandle = data.UserHandle; + UserDisplayName = data.UserDisplayName; + Counter = data.Counter; + Discoverable = data.Discoverable; + CreationDate = data.CreationDate; + } + + [EncryptedString] + [EncryptedStringLength(1000)] + public string CredentialId { get; set; } + [EncryptedString] + [EncryptedStringLength(1000)] + public string KeyType { get; set; } + [EncryptedString] + [EncryptedStringLength(1000)] + public string KeyAlgorithm { get; set; } + [EncryptedString] + [EncryptedStringLength(1000)] + public string KeyCurve { get; set; } + [EncryptedString] + [EncryptedStringLength(1000)] + public string KeyValue { get; set; } + [EncryptedString] + [EncryptedStringLength(1000)] + public string RpId { get; set; } + [EncryptedString] + [EncryptedStringLength(1000)] + public string RpName { get; set; } + [EncryptedString] + [EncryptedStringLength(1000)] + public string UserHandle { get; set; } + [EncryptedString] + [EncryptedStringLength(1000)] + public string UserDisplayName { get; set; } + [EncryptedString] + [EncryptedStringLength(1000)] + public string Counter { get; set; } + [EncryptedString] + [EncryptedStringLength(1000)] + public string Discoverable { get; set; } + [Required] + public DateTime CreationDate { get; set; } + + public CipherLoginFido2CredentialData ToCipherLoginFido2CredentialData() + { + return new CipherLoginFido2CredentialData + { + CredentialId = CredentialId, + KeyType = KeyType, + KeyAlgorithm = KeyAlgorithm, + KeyCurve = KeyCurve, + KeyValue = KeyValue, + RpId = RpId, + RpName = RpName, + UserHandle = UserHandle, + UserDisplayName = UserDisplayName, + Counter = Counter, + Discoverable = Discoverable, + CreationDate = CreationDate + }; + } +} + +static class CipherFido2CredentialModelExtensions +{ + public static CipherLoginFido2CredentialData[] ToCipherLoginFido2CredentialData(this CipherFido2CredentialModel[] models) + { + return models.Select(m => m.ToCipherLoginFido2CredentialData()).ToArray(); + } +} diff --git a/src/Api/Vault/Models/CipherLoginModel.cs b/src/Api/Vault/Models/CipherLoginModel.cs index 08bdc082b2b9..d1ea16751347 100644 --- a/src/Api/Vault/Models/CipherLoginModel.cs +++ b/src/Api/Vault/Models/CipherLoginModel.cs @@ -16,6 +16,11 @@ public CipherLoginModel(CipherLoginData data) Uri = data.Uri; } + if (data.Fido2Credentials != null) + { + Fido2Credentials = data.Fido2Credentials.Select(key => new CipherFido2CredentialModel(key)).ToArray(); + } + Username = data.Username; Password = data.Password; PasswordRevisionDate = data.PasswordRevisionDate; @@ -55,6 +60,7 @@ public string Uri [EncryptedStringLength(1000)] public string Totp { get; set; } public bool? AutofillOnPageLoad { get; set; } + public CipherFido2CredentialModel[] Fido2Credentials { get; set; } public class CipherLoginUriModel { diff --git a/src/Api/Vault/Models/Request/CipherRequestModel.cs b/src/Api/Vault/Models/Request/CipherRequestModel.cs index a7cc399fe448..b62f2ff96e35 100644 --- a/src/Api/Vault/Models/Request/CipherRequestModel.cs +++ b/src/Api/Vault/Models/Request/CipherRequestModel.cs @@ -18,6 +18,7 @@ public class CipherRequestModel public string FolderId { get; set; } public bool Favorite { get; set; } public CipherRepromptType Reprompt { get; set; } + public string Key { get; set; } [Required] [EncryptedString] [EncryptedStringLength(1000)] @@ -86,6 +87,7 @@ public Cipher ToCipher(Cipher existingCipher) } existingCipher.Reprompt = Reprompt; + existingCipher.Key = Key; var hasAttachments2 = (Attachments2?.Count ?? 0) > 0; var hasAttachments = (Attachments?.Count ?? 0) > 0; @@ -164,6 +166,7 @@ private CipherLoginData ToCipherLoginData() PasswordRevisionDate = Login.PasswordRevisionDate, Totp = Login.Totp, AutofillOnPageLoad = Login.AutofillOnPageLoad, + Fido2Credentials = Login.Fido2Credentials == null ? null : Login.Fido2Credentials.ToCipherLoginFido2CredentialData(), }; } diff --git a/src/Api/Vault/Models/Response/AttachmentResponseModel.cs b/src/Api/Vault/Models/Response/AttachmentResponseModel.cs index c7e3caabb9e8..f3c0261e9832 100644 --- a/src/Api/Vault/Models/Response/AttachmentResponseModel.cs +++ b/src/Api/Vault/Models/Response/AttachmentResponseModel.cs @@ -1,5 +1,4 @@ -using System.Text.Json.Serialization; -using Bit.Core.Models.Api; +using Bit.Core.Models.Api; using Bit.Core.Settings; using Bit.Core.Utilities; using Bit.Core.Vault.Entities; @@ -15,7 +14,7 @@ public AttachmentResponseModel(AttachmentResponseData data) : base("attachment") Url = data.Url; FileName = data.Data.FileName; Key = data.Data.Key; - Size = data.Data.Size; + Size = data.Data.Size.ToString(); SizeName = CoreHelpers.ReadableBytesSize(data.Data.Size); } @@ -27,7 +26,7 @@ public AttachmentResponseModel(string id, CipherAttachment.MetaData data, Cipher Url = $"{globalSettings.Attachment.BaseUrl}/{cipher.Id}/{id}"; FileName = data.FileName; Key = data.Key; - Size = data.Size; + Size = data.Size.ToString(); SizeName = CoreHelpers.ReadableBytesSize(data.Size); } @@ -35,8 +34,7 @@ public AttachmentResponseModel(string id, CipherAttachment.MetaData data, Cipher public string Url { get; set; } public string FileName { get; set; } public string Key { get; set; } - [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] - public long Size { get; set; } + public string Size { get; set; } public string SizeName { get; set; } public static IEnumerable FromCipher(Cipher cipher, IGlobalSettings globalSettings) diff --git a/src/Api/Vault/Models/Response/CipherResponseModel.cs b/src/Api/Vault/Models/Response/CipherResponseModel.cs index ed79c04780c6..ab79cc36008c 100644 --- a/src/Api/Vault/Models/Response/CipherResponseModel.cs +++ b/src/Api/Vault/Models/Response/CipherResponseModel.cs @@ -63,6 +63,7 @@ public CipherMiniResponseModel(Cipher cipher, IGlobalSettings globalSettings, bo CreationDate = cipher.CreationDate; DeletedDate = cipher.DeletedDate; Reprompt = cipher.Reprompt.GetValueOrDefault(CipherRepromptType.None); + Key = cipher.Key; } public Guid Id { get; set; } @@ -83,6 +84,7 @@ public CipherMiniResponseModel(Cipher cipher, IGlobalSettings globalSettings, bo public DateTime CreationDate { get; set; } public DateTime? DeletedDate { get; set; } public CipherRepromptType Reprompt { get; set; } + public string Key { get; set; } } public class CipherResponseModel : CipherMiniResponseModel diff --git a/src/Api/Vault/Models/Response/SyncResponseModel.cs b/src/Api/Vault/Models/Response/SyncResponseModel.cs index 386b7445be74..817f233e1174 100644 --- a/src/Api/Vault/Models/Response/SyncResponseModel.cs +++ b/src/Api/Vault/Models/Response/SyncResponseModel.cs @@ -1,5 +1,6 @@ using Bit.Api.Models.Response; using Bit.Api.Tools.Models.Response; +using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.Entities; using Bit.Core.Models.Api; using Bit.Core.Models.Api.Response; diff --git a/src/Api/packages.lock.json b/src/Api/packages.lock.json index 44b805fd7ec4..409ac010c21c 100644 --- a/src/Api/packages.lock.json +++ b/src/Api/packages.lock.json @@ -281,10 +281,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2771,15 +2772,15 @@ "commercial.core": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )" + "Core": "[2023.9.0, )" } }, "commercial.infrastructure.entityframework": { "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } }, "core": { @@ -2794,7 +2795,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2827,7 +2828,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2835,7 +2836,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2847,9 +2848,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/src/Billing/Constants/HandledStripeWebhook.cs b/src/Billing/Constants/HandledStripeWebhook.cs index 7b894a295e3c..707a5dd5d5ea 100644 --- a/src/Billing/Constants/HandledStripeWebhook.cs +++ b/src/Billing/Constants/HandledStripeWebhook.cs @@ -11,4 +11,5 @@ public static class HandledStripeWebhook public const string PaymentFailed = "invoice.payment_failed"; public const string InvoiceCreated = "invoice.created"; public const string PaymentMethodAttached = "payment_method.attached"; + public const string CustomerUpdated = "customer.updated"; } diff --git a/src/Billing/Controllers/StripeController.cs b/src/Billing/Controllers/StripeController.cs index 00a8fa5ac6cd..e71e025dffc5 100644 --- a/src/Billing/Controllers/StripeController.cs +++ b/src/Billing/Controllers/StripeController.cs @@ -1,4 +1,5 @@ using Bit.Billing.Constants; +using Bit.Billing.Services; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; @@ -44,12 +45,14 @@ public class StripeController : Controller private readonly IAppleIapService _appleIapService; private readonly IMailService _mailService; private readonly ILogger _logger; - private readonly Braintree.BraintreeGateway _btGateway; + private readonly BraintreeGateway _btGateway; private readonly IReferenceEventService _referenceEventService; private readonly ITaxRateRepository _taxRateRepository; private readonly IUserRepository _userRepository; private readonly ICurrentContext _currentContext; private readonly GlobalSettings _globalSettings; + private readonly IStripeEventService _stripeEventService; + private readonly IStripeFacade _stripeFacade; public StripeController( GlobalSettings globalSettings, @@ -67,7 +70,9 @@ public StripeController( ILogger logger, ITaxRateRepository taxRateRepository, IUserRepository userRepository, - ICurrentContext currentContext) + ICurrentContext currentContext, + IStripeEventService stripeEventService, + IStripeFacade stripeFacade) { _billingSettings = billingSettings?.Value; _hostingEnvironment = hostingEnvironment; @@ -83,7 +88,7 @@ public StripeController( _taxRateRepository = taxRateRepository; _userRepository = userRepository; _logger = logger; - _btGateway = new Braintree.BraintreeGateway + _btGateway = new BraintreeGateway { Environment = globalSettings.Braintree.Production ? Braintree.Environment.PRODUCTION : Braintree.Environment.SANDBOX, @@ -93,6 +98,8 @@ public StripeController( }; _currentContext = currentContext; _globalSettings = globalSettings; + _stripeEventService = stripeEventService; + _stripeFacade = stripeFacade; } [HttpPost("webhook")] @@ -103,7 +110,7 @@ public async Task PostWebhook([FromQuery] string key) return new BadRequestResult(); } - Stripe.Event parsedEvent; + Event parsedEvent; using (var sr = new StreamReader(HttpContext.Request.Body)) { var json = await sr.ReadToEndAsync(); @@ -125,7 +132,7 @@ public async Task PostWebhook([FromQuery] string key) } // If the customer and server cloud regions don't match, early return 200 to avoid unnecessary errors - if (!await ValidateCloudRegionAsync(parsedEvent)) + if (!await _stripeEventService.ValidateCloudRegion(parsedEvent)) { return new OkResult(); } @@ -135,7 +142,7 @@ public async Task PostWebhook([FromQuery] string key) if (subDeleted || subUpdated) { - var subscription = await GetSubscriptionAsync(parsedEvent, true); + var subscription = await _stripeEventService.GetSubscription(parsedEvent, true); var ids = GetIdsFromMetaData(subscription.Metadata); var organizationId = ids.Item1 ?? Guid.Empty; var userId = ids.Item2 ?? Guid.Empty; @@ -204,53 +211,76 @@ await _userService.UpdatePremiumExpirationAsync(userId, } else if (parsedEvent.Type.Equals(HandledStripeWebhook.UpcomingInvoice)) { - var invoice = await GetInvoiceAsync(parsedEvent); - var subscriptionService = new SubscriptionService(); - var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId); + var invoice = await _stripeEventService.GetInvoice(parsedEvent); + + if (string.IsNullOrEmpty(invoice.SubscriptionId)) + { + _logger.LogWarning("Received 'invoice.upcoming' Event with ID '{eventId}' that did not include a Subscription ID", parsedEvent.Id); + return new OkResult(); + } + + var subscription = await _stripeFacade.GetSubscription(invoice.SubscriptionId); + if (subscription == null) { - throw new Exception("Invoice subscription is null. " + invoice.Id); + throw new Exception( + $"Received null Subscription from Stripe for ID '{invoice.SubscriptionId}' while processing Event with ID '{parsedEvent.Id}'"); } - subscription = await VerifyCorrectTaxRateForCharge(invoice, subscription); + var updatedSubscription = await VerifyCorrectTaxRateForCharge(invoice, subscription); - string email = null; - var ids = GetIdsFromMetaData(subscription.Metadata); - // org - if (ids.Item1.HasValue) + var (organizationId, userId) = GetIdsFromMetaData(updatedSubscription.Metadata); + + var invoiceLineItemDescriptions = invoice.Lines.Select(i => i.Description).ToList(); + + async Task SendEmails(IEnumerable emails) + { + var validEmails = emails.Where(e => !string.IsNullOrEmpty(e)); + + if (invoice.NextPaymentAttempt.HasValue) + { + await _mailService.SendInvoiceUpcoming( + validEmails, + invoice.AmountDue / 100M, + invoice.NextPaymentAttempt.Value, + invoiceLineItemDescriptions, + true); + } + } + + if (organizationId.HasValue) { - // sponsored org - if (IsSponsoredSubscription(subscription)) + if (IsSponsoredSubscription(updatedSubscription)) { - await _validateSponsorshipCommand.ValidateSponsorshipAsync(ids.Item1.Value); + await _validateSponsorshipCommand.ValidateSponsorshipAsync(organizationId.Value); } - var org = await _organizationRepository.GetByIdAsync(ids.Item1.Value); - if (org != null && OrgPlanForInvoiceNotifications(org)) + var organization = await _organizationRepository.GetByIdAsync(organizationId.Value); + + if (organization == null || !OrgPlanForInvoiceNotifications(organization)) { - email = org.BillingEmail; + return new OkResult(); } + + await SendEmails(new List { organization.BillingEmail }); + + var ownerEmails = await _organizationRepository.GetOwnerEmailAddressesById(organization.Id); + + await SendEmails(ownerEmails); } - // user - else if (ids.Item2.HasValue) + else if (userId.HasValue) { - var user = await _userService.GetUserByIdAsync(ids.Item2.Value); + var user = await _userService.GetUserByIdAsync(userId.Value); + if (user.Premium) { - email = user.Email; + await SendEmails(new List { user.Email }); } } - - if (!string.IsNullOrWhiteSpace(email) && invoice.NextPaymentAttempt.HasValue) - { - var items = invoice.Lines.Select(i => i.Description).ToList(); - await _mailService.SendInvoiceUpcomingAsync(email, invoice.AmountDue / 100M, - invoice.NextPaymentAttempt.Value, items, true); - } } else if (parsedEvent.Type.Equals(HandledStripeWebhook.ChargeSucceeded)) { - var charge = await GetChargeAsync(parsedEvent); + var charge = await _stripeEventService.GetCharge(parsedEvent); var chargeTransaction = await _transactionRepository.GetByGatewayIdAsync( GatewayType.Stripe, charge.Id); if (chargeTransaction != null) @@ -377,7 +407,7 @@ await _mailService.SendInvoiceUpcomingAsync(email, invoice.AmountDue / 100M, } else if (parsedEvent.Type.Equals(HandledStripeWebhook.ChargeRefunded)) { - var charge = await GetChargeAsync(parsedEvent); + var charge = await _stripeEventService.GetCharge(parsedEvent); var chargeTransaction = await _transactionRepository.GetByGatewayIdAsync( GatewayType.Stripe, charge.Id); if (chargeTransaction == null) @@ -427,7 +457,7 @@ await _transactionRepository.CreateAsync(new Transaction } else if (parsedEvent.Type.Equals(HandledStripeWebhook.PaymentSucceeded)) { - var invoice = await GetInvoiceAsync(parsedEvent, true); + var invoice = await _stripeEventService.GetInvoice(parsedEvent, true); if (invoice.Paid && invoice.BillingReason == "subscription_create") { var subscriptionService = new SubscriptionService(); @@ -443,7 +473,7 @@ await _transactionRepository.CreateAsync(new Transaction // org if (ids.Item1.HasValue) { - if (subscription.Items.Any(i => StaticStore.PasswordManagerPlans.Any(p => p.StripePlanId == i.Plan.Id))) + if (subscription.Items.Any(i => StaticStore.Plans.Any(p => p.PasswordManager.StripePlanId == i.Plan.Id))) { await _organizationService.EnableAsync(ids.Item1.Value, subscription.CurrentPeriodEnd); @@ -479,11 +509,11 @@ await _referenceEventService.RaiseEventAsync( } else if (parsedEvent.Type.Equals(HandledStripeWebhook.PaymentFailed)) { - await HandlePaymentFailed(await GetInvoiceAsync(parsedEvent, true)); + await HandlePaymentFailed(await _stripeEventService.GetInvoice(parsedEvent, true)); } else if (parsedEvent.Type.Equals(HandledStripeWebhook.InvoiceCreated)) { - var invoice = await GetInvoiceAsync(parsedEvent, true); + var invoice = await _stripeEventService.GetInvoice(parsedEvent, true); if (!invoice.Paid && UnpaidAutoChargeInvoiceForSubscriptionCycle(invoice)) { await AttemptToPayInvoiceAsync(invoice); @@ -491,113 +521,41 @@ await _referenceEventService.RaiseEventAsync( } else if (parsedEvent.Type.Equals(HandledStripeWebhook.PaymentMethodAttached)) { - var paymentMethod = await GetPaymentMethodAsync(parsedEvent); + var paymentMethod = await _stripeEventService.GetPaymentMethod(parsedEvent); await HandlePaymentMethodAttachedAsync(paymentMethod); } - else - { - _logger.LogWarning("Unsupported event received. " + parsedEvent.Type); - } - - return new OkResult(); - } - - /// - /// Ensures that the customer associated with the parsed event's data is in the correct region for this server. - /// We use the customer instead of the subscription given that all subscriptions have customers, but not all - /// customers have subscriptions - /// - /// - /// true if the customer's region and the server's region match, otherwise false - /// - private async Task ValidateCloudRegionAsync(Event parsedEvent) - { - var serverRegion = _globalSettings.BaseServiceUri.CloudRegion; - var eventType = parsedEvent.Type; - var expandOptions = new List { "customer" }; - - try + else if (parsedEvent.Type.Equals(HandledStripeWebhook.CustomerUpdated)) { - Dictionary customerMetadata; - switch (eventType) - { - case HandledStripeWebhook.SubscriptionDeleted: - case HandledStripeWebhook.SubscriptionUpdated: - customerMetadata = (await GetSubscriptionAsync(parsedEvent, true, expandOptions))?.Customer - ?.Metadata; - break; - case HandledStripeWebhook.ChargeSucceeded: - case HandledStripeWebhook.ChargeRefunded: - customerMetadata = (await GetChargeAsync(parsedEvent, true, expandOptions))?.Customer?.Metadata; - break; - case HandledStripeWebhook.UpcomingInvoice: - customerMetadata = (await GetInvoiceAsync(parsedEvent))?.Customer?.Metadata; - break; - case HandledStripeWebhook.PaymentSucceeded: - case HandledStripeWebhook.PaymentFailed: - case HandledStripeWebhook.InvoiceCreated: - customerMetadata = (await GetInvoiceAsync(parsedEvent, true, expandOptions))?.Customer?.Metadata; - break; - case HandledStripeWebhook.PaymentMethodAttached: - customerMetadata = (await GetPaymentMethodAsync(parsedEvent, true, expandOptions)) - ?.Customer - ?.Metadata; - break; - default: - customerMetadata = null; - break; - } + var customer = + await _stripeEventService.GetCustomer(parsedEvent, true, new List { "subscriptions" }); - if (customerMetadata is null) + if (customer.Subscriptions == null || !customer.Subscriptions.Any()) { - return false; + return new OkResult(); } - var customerRegion = GetCustomerRegionFromMetadata(customerMetadata); + var subscription = customer.Subscriptions.First(); - return customerRegion == serverRegion; - } - catch (Exception e) - { - _logger.LogError(e, "Encountered unexpected error while validating cloud region"); - throw; - } - } + var (organizationId, _) = GetIdsFromMetaData(subscription.Metadata); - /// - /// Gets the customer's region from the metadata. - /// - /// The metadata of the customer. - /// The region of the customer. If the region is not specified, it returns "US", if metadata is null, - /// it returns null. It is case insensitive. - private static string GetCustomerRegionFromMetadata(IDictionary customerMetadata) - { - const string defaultRegion = "US"; + if (!organizationId.HasValue) + { + return new OkResult(); + } - if (customerMetadata is null) - { - return null; - } + var organization = await _organizationRepository.GetByIdAsync(organizationId.Value); + organization.BillingEmail = customer.Email; + await _organizationRepository.ReplaceAsync(organization); - if (customerMetadata.TryGetValue("region", out var value)) - { - return value; + await _referenceEventService.RaiseEventAsync( + new ReferenceEvent(ReferenceEventType.OrganizationEditedInStripe, organization, _currentContext)); } - - var miscasedRegionKey = customerMetadata.Keys - .FirstOrDefault(key => - key.Equals("region", StringComparison.OrdinalIgnoreCase)); - - if (miscasedRegionKey is null) + else { - return defaultRegion; + _logger.LogWarning("Unsupported event received. " + parsedEvent.Type); } - _ = customerMetadata.TryGetValue(miscasedRegionKey, out var regionValue); - - return !string.IsNullOrWhiteSpace(regionValue) - ? regionValue - : defaultRegion; + return new OkResult(); } private async Task HandlePaymentMethodAttachedAsync(PaymentMethod paymentMethod) @@ -975,109 +933,6 @@ private bool UnpaidAutoChargeInvoiceForSubscriptionCycle(Invoice invoice) invoice.BillingReason == "subscription_cycle" && invoice.SubscriptionId != null; } - private async Task GetChargeAsync(Event parsedEvent, bool fresh = false, List expandOptions = null) - { - if (!(parsedEvent.Data.Object is Charge eventCharge)) - { - throw new Exception("Charge is null (from parsed event). " + parsedEvent.Id); - } - if (!fresh) - { - return eventCharge; - } - var chargeService = new ChargeService(); - var chargeGetOptions = new ChargeGetOptions { Expand = expandOptions }; - var charge = await chargeService.GetAsync(eventCharge.Id, chargeGetOptions); - if (charge == null) - { - throw new Exception("Charge is null. " + eventCharge.Id); - } - return charge; - } - - private async Task GetInvoiceAsync(Stripe.Event parsedEvent, bool fresh = false, List expandOptions = null) - { - if (!(parsedEvent.Data.Object is Invoice eventInvoice)) - { - throw new Exception("Invoice is null (from parsed event). " + parsedEvent.Id); - } - if (!fresh) - { - return eventInvoice; - } - var invoiceService = new InvoiceService(); - var invoiceGetOptions = new InvoiceGetOptions { Expand = expandOptions }; - var invoice = await invoiceService.GetAsync(eventInvoice.Id, invoiceGetOptions); - if (invoice == null) - { - throw new Exception("Invoice is null. " + eventInvoice.Id); - } - return invoice; - } - - private async Task GetSubscriptionAsync(Stripe.Event parsedEvent, bool fresh = false, - List expandOptions = null) - { - if (parsedEvent.Data.Object is not Subscription eventSubscription) - { - throw new Exception("Subscription is null (from parsed event). " + parsedEvent.Id); - } - if (!fresh) - { - return eventSubscription; - } - var subscriptionService = new SubscriptionService(); - var subscriptionGetOptions = new SubscriptionGetOptions { Expand = expandOptions }; - var subscription = await subscriptionService.GetAsync(eventSubscription.Id, subscriptionGetOptions); - if (subscription == null) - { - throw new Exception("Subscription is null. " + eventSubscription.Id); - } - return subscription; - } - - private async Task GetCustomerAsync(string customerId) - { - if (string.IsNullOrWhiteSpace(customerId)) - { - throw new Exception("Customer ID cannot be empty when attempting to get a customer from Stripe"); - } - - var customerService = new CustomerService(); - var customer = await customerService.GetAsync(customerId); - if (customer == null) - { - throw new Exception($"Customer is null. {customerId}"); - } - - return customer; - } - - private async Task GetPaymentMethodAsync(Event parsedEvent, bool fresh = false, - List expandOptions = null) - { - if (parsedEvent.Data.Object is not PaymentMethod eventPaymentMethod) - { - throw new Exception("Invoice is null (from parsed event). " + parsedEvent.Id); - } - - if (!fresh) - { - return eventPaymentMethod; - } - - var paymentMethodService = new PaymentMethodService(); - var paymentMethodGetOptions = new PaymentMethodGetOptions { Expand = expandOptions }; - var paymentMethod = await paymentMethodService.GetAsync(eventPaymentMethod.Id, paymentMethodGetOptions); - - if (paymentMethod == null) - { - throw new Exception($"Payment method is null. {eventPaymentMethod.Id}"); - } - - return paymentMethod; - } - private async Task VerifyCorrectTaxRateForCharge(Invoice invoice, Subscription subscription) { if (!string.IsNullOrWhiteSpace(invoice?.CustomerAddress?.Country) && !string.IsNullOrWhiteSpace(invoice?.CustomerAddress?.PostalCode)) diff --git a/src/Billing/Services/IStripeEventService.cs b/src/Billing/Services/IStripeEventService.cs new file mode 100644 index 000000000000..6e2239cf9866 --- /dev/null +++ b/src/Billing/Services/IStripeEventService.cs @@ -0,0 +1,80 @@ +using Stripe; + +namespace Bit.Billing.Services; + +public interface IStripeEventService +{ + /// + /// Extracts the object from the Stripe . When is true, + /// uses the charge ID extracted from the event to retrieve the most up-to-update charge from Stripe's API + /// and optionally expands it with the provided options. + /// + /// The Stripe webhook event. + /// Determines whether or not to retrieve a fresh copy of the charge object from Stripe. + /// Optionally provided to expand the fresh charge object retrieved from Stripe. + /// A Stripe . + /// Thrown when the Stripe event does not contain a charge object. + /// Thrown when is true and Stripe's API returns a null charge object. + Task GetCharge(Event stripeEvent, bool fresh = false, List expand = null); + + /// + /// Extracts the object from the Stripe . When is true, + /// uses the customer ID extracted from the event to retrieve the most up-to-update customer from Stripe's API + /// and optionally expands it with the provided options. + /// + /// The Stripe webhook event. + /// Determines whether or not to retrieve a fresh copy of the customer object from Stripe. + /// Optionally provided to expand the fresh customer object retrieved from Stripe. + /// A Stripe . + /// Thrown when the Stripe event does not contain a customer object. + /// Thrown when is true and Stripe's API returns a null customer object. + Task GetCustomer(Event stripeEvent, bool fresh = false, List expand = null); + + /// + /// Extracts the object from the Stripe . When is true, + /// uses the invoice ID extracted from the event to retrieve the most up-to-update invoice from Stripe's API + /// and optionally expands it with the provided options. + /// + /// The Stripe webhook event. + /// Determines whether or not to retrieve a fresh copy of the invoice object from Stripe. + /// Optionally provided to expand the fresh invoice object retrieved from Stripe. + /// A Stripe . + /// Thrown when the Stripe event does not contain an invoice object. + /// Thrown when is true and Stripe's API returns a null invoice object. + Task GetInvoice(Event stripeEvent, bool fresh = false, List expand = null); + + /// + /// Extracts the object from the Stripe . When is true, + /// uses the payment method ID extracted from the event to retrieve the most up-to-update payment method from Stripe's API + /// and optionally expands it with the provided options. + /// + /// The Stripe webhook event. + /// Determines whether or not to retrieve a fresh copy of the payment method object from Stripe. + /// Optionally provided to expand the fresh payment method object retrieved from Stripe. + /// A Stripe . + /// Thrown when the Stripe event does not contain an payment method object. + /// Thrown when is true and Stripe's API returns a null payment method object. + Task GetPaymentMethod(Event stripeEvent, bool fresh = false, List expand = null); + + /// + /// Extracts the object from the Stripe . When is true, + /// uses the subscription ID extracted from the event to retrieve the most up-to-update subscription from Stripe's API + /// and optionally expands it with the provided options. + /// + /// The Stripe webhook event. + /// Determines whether or not to retrieve a fresh copy of the subscription object from Stripe. + /// Optionally provided to expand the fresh subscription object retrieved from Stripe. + /// A Stripe . + /// Thrown when the Stripe event does not contain an subscription object. + /// Thrown when is true and Stripe's API returns a null subscription object. + Task GetSubscription(Event stripeEvent, bool fresh = false, List expand = null); + + /// + /// Ensures that the customer associated with the Stripe is in the correct region for this server. + /// We use the customer instead of the subscription given that all subscriptions have customers, but not all + /// customers have subscriptions. + /// + /// The Stripe webhook event. + /// True if the customer's region and the server's region match, otherwise false. + Task ValidateCloudRegion(Event stripeEvent); +} diff --git a/src/Billing/Services/IStripeFacade.cs b/src/Billing/Services/IStripeFacade.cs new file mode 100644 index 000000000000..cbe36b6a46b7 --- /dev/null +++ b/src/Billing/Services/IStripeFacade.cs @@ -0,0 +1,36 @@ +using Stripe; + +namespace Bit.Billing.Services; + +public interface IStripeFacade +{ + Task GetCharge( + string chargeId, + ChargeGetOptions chargeGetOptions = null, + RequestOptions requestOptions = null, + CancellationToken cancellationToken = default); + + Task GetCustomer( + string customerId, + CustomerGetOptions customerGetOptions = null, + RequestOptions requestOptions = null, + CancellationToken cancellationToken = default); + + Task GetInvoice( + string invoiceId, + InvoiceGetOptions invoiceGetOptions = null, + RequestOptions requestOptions = null, + CancellationToken cancellationToken = default); + + Task GetPaymentMethod( + string paymentMethodId, + PaymentMethodGetOptions paymentMethodGetOptions = null, + RequestOptions requestOptions = null, + CancellationToken cancellationToken = default); + + Task GetSubscription( + string subscriptionId, + SubscriptionGetOptions subscriptionGetOptions = null, + RequestOptions requestOptions = null, + CancellationToken cancellationToken = default); +} diff --git a/src/Billing/Services/Implementations/StripeEventService.cs b/src/Billing/Services/Implementations/StripeEventService.cs new file mode 100644 index 000000000000..ce7ab311ffd0 --- /dev/null +++ b/src/Billing/Services/Implementations/StripeEventService.cs @@ -0,0 +1,244 @@ +using Bit.Billing.Constants; +using Bit.Core.Settings; +using Stripe; + +namespace Bit.Billing.Services.Implementations; + +public class StripeEventService : IStripeEventService +{ + private readonly GlobalSettings _globalSettings; + private readonly ILogger _logger; + private readonly IStripeFacade _stripeFacade; + + public StripeEventService( + GlobalSettings globalSettings, + ILogger logger, + IStripeFacade stripeFacade) + { + _globalSettings = globalSettings; + _logger = logger; + _stripeFacade = stripeFacade; + } + + public async Task GetCharge(Event stripeEvent, bool fresh = false, List expand = null) + { + var eventCharge = Extract(stripeEvent); + + if (!fresh) + { + return eventCharge; + } + + if (string.IsNullOrEmpty(eventCharge.Id)) + { + _logger.LogWarning("Cannot retrieve up-to-date Charge for Event with ID '{eventId}' because no Charge ID was included in the Event.", stripeEvent.Id); + return eventCharge; + } + + var charge = await _stripeFacade.GetCharge(eventCharge.Id, new ChargeGetOptions { Expand = expand }); + + if (charge == null) + { + throw new Exception( + $"Received null Charge from Stripe for ID '{eventCharge.Id}' while processing Event with ID '{stripeEvent.Id}'"); + } + + return charge; + } + + public async Task GetCustomer(Event stripeEvent, bool fresh = false, List expand = null) + { + var eventCustomer = Extract(stripeEvent); + + if (!fresh) + { + return eventCustomer; + } + + if (string.IsNullOrEmpty(eventCustomer.Id)) + { + _logger.LogWarning("Cannot retrieve up-to-date Customer for Event with ID '{eventId}' because no Customer ID was included in the Event.", stripeEvent.Id); + return eventCustomer; + } + + var customer = await _stripeFacade.GetCustomer(eventCustomer.Id, new CustomerGetOptions { Expand = expand }); + + if (customer == null) + { + throw new Exception( + $"Received null Customer from Stripe for ID '{eventCustomer.Id}' while processing Event with ID '{stripeEvent.Id}'"); + } + + return customer; + } + + public async Task GetInvoice(Event stripeEvent, bool fresh = false, List expand = null) + { + var eventInvoice = Extract(stripeEvent); + + if (!fresh) + { + return eventInvoice; + } + + if (string.IsNullOrEmpty(eventInvoice.Id)) + { + _logger.LogWarning("Cannot retrieve up-to-date Invoice for Event with ID '{eventId}' because no Invoice ID was included in the Event.", stripeEvent.Id); + return eventInvoice; + } + + var invoice = await _stripeFacade.GetInvoice(eventInvoice.Id, new InvoiceGetOptions { Expand = expand }); + + if (invoice == null) + { + throw new Exception( + $"Received null Invoice from Stripe for ID '{eventInvoice.Id}' while processing Event with ID '{stripeEvent.Id}'"); + } + + return invoice; + } + + public async Task GetPaymentMethod(Event stripeEvent, bool fresh = false, List expand = null) + { + var eventPaymentMethod = Extract(stripeEvent); + + if (!fresh) + { + return eventPaymentMethod; + } + + if (string.IsNullOrEmpty(eventPaymentMethod.Id)) + { + _logger.LogWarning("Cannot retrieve up-to-date Payment Method for Event with ID '{eventId}' because no Payment Method ID was included in the Event.", stripeEvent.Id); + return eventPaymentMethod; + } + + var paymentMethod = await _stripeFacade.GetPaymentMethod(eventPaymentMethod.Id, new PaymentMethodGetOptions { Expand = expand }); + + if (paymentMethod == null) + { + throw new Exception( + $"Received null Payment Method from Stripe for ID '{eventPaymentMethod.Id}' while processing Event with ID '{stripeEvent.Id}'"); + } + + return paymentMethod; + } + + public async Task GetSubscription(Event stripeEvent, bool fresh = false, List expand = null) + { + var eventSubscription = Extract(stripeEvent); + + if (!fresh) + { + return eventSubscription; + } + + if (string.IsNullOrEmpty(eventSubscription.Id)) + { + _logger.LogWarning("Cannot retrieve up-to-date Subscription for Event with ID '{eventId}' because no Subscription ID was included in the Event.", stripeEvent.Id); + return eventSubscription; + } + + var subscription = await _stripeFacade.GetSubscription(eventSubscription.Id, new SubscriptionGetOptions { Expand = expand }); + + if (subscription == null) + { + throw new Exception( + $"Received null Subscription from Stripe for ID '{eventSubscription.Id}' while processing Event with ID '{stripeEvent.Id}'"); + } + + return subscription; + } + + public async Task ValidateCloudRegion(Event stripeEvent) + { + var serverRegion = _globalSettings.BaseServiceUri.CloudRegion; + + var customerExpansion = new List { "customer" }; + + var customerMetadata = stripeEvent.Type switch + { + HandledStripeWebhook.SubscriptionDeleted or HandledStripeWebhook.SubscriptionUpdated => + (await GetSubscription(stripeEvent, true, customerExpansion))?.Customer?.Metadata, + + HandledStripeWebhook.ChargeSucceeded or HandledStripeWebhook.ChargeRefunded => + (await GetCharge(stripeEvent, true, customerExpansion))?.Customer?.Metadata, + + HandledStripeWebhook.UpcomingInvoice => + await GetCustomerMetadataFromUpcomingInvoiceEvent(stripeEvent), + + HandledStripeWebhook.PaymentSucceeded or HandledStripeWebhook.PaymentFailed or HandledStripeWebhook.InvoiceCreated => + (await GetInvoice(stripeEvent, true, customerExpansion))?.Customer?.Metadata, + + HandledStripeWebhook.PaymentMethodAttached => + (await GetPaymentMethod(stripeEvent, true, customerExpansion))?.Customer?.Metadata, + + HandledStripeWebhook.CustomerUpdated => + (await GetCustomer(stripeEvent, true))?.Metadata, + + _ => null + }; + + if (customerMetadata == null) + { + return false; + } + + var customerRegion = GetCustomerRegion(customerMetadata); + + return customerRegion == serverRegion; + + /* In Stripe, when we receive an invoice.upcoming event, the event does not include an Invoice ID because + the invoice hasn't been created yet. Therefore, rather than retrieving the fresh Invoice with a 'customer' + expansion, we need to use the Customer ID on the event to retrieve the metadata. */ + async Task> GetCustomerMetadataFromUpcomingInvoiceEvent(Event localStripeEvent) + { + var invoice = await GetInvoice(localStripeEvent); + + var customer = !string.IsNullOrEmpty(invoice.CustomerId) + ? await _stripeFacade.GetCustomer(invoice.CustomerId) + : null; + + return customer?.Metadata; + } + } + + private static T Extract(Event stripeEvent) + { + if (stripeEvent.Data.Object is not T type) + { + throw new Exception($"Stripe event with ID '{stripeEvent.Id}' does not have object matching type '{typeof(T).Name}'"); + } + + return type; + } + + private static string GetCustomerRegion(IDictionary customerMetadata) + { + const string defaultRegion = "US"; + + if (customerMetadata is null) + { + return null; + } + + if (customerMetadata.TryGetValue("region", out var value)) + { + return value; + } + + var miscasedRegionKey = customerMetadata.Keys + .FirstOrDefault(key => key.Equals("region", StringComparison.OrdinalIgnoreCase)); + + if (miscasedRegionKey is null) + { + return defaultRegion; + } + + _ = customerMetadata.TryGetValue(miscasedRegionKey, out var regionValue); + + return !string.IsNullOrWhiteSpace(regionValue) + ? regionValue + : defaultRegion; + } +} diff --git a/src/Billing/Services/Implementations/StripeFacade.cs b/src/Billing/Services/Implementations/StripeFacade.cs new file mode 100644 index 000000000000..2ea4d0b93f82 --- /dev/null +++ b/src/Billing/Services/Implementations/StripeFacade.cs @@ -0,0 +1,47 @@ +using Stripe; + +namespace Bit.Billing.Services.Implementations; + +public class StripeFacade : IStripeFacade +{ + private readonly ChargeService _chargeService = new(); + private readonly CustomerService _customerService = new(); + private readonly InvoiceService _invoiceService = new(); + private readonly PaymentMethodService _paymentMethodService = new(); + private readonly SubscriptionService _subscriptionService = new(); + + public async Task GetCharge( + string chargeId, + ChargeGetOptions chargeGetOptions = null, + RequestOptions requestOptions = null, + CancellationToken cancellationToken = default) => + await _chargeService.GetAsync(chargeId, chargeGetOptions, requestOptions, cancellationToken); + + public async Task GetCustomer( + string customerId, + CustomerGetOptions customerGetOptions = null, + RequestOptions requestOptions = null, + CancellationToken cancellationToken = default) => + await _customerService.GetAsync(customerId, customerGetOptions, requestOptions, cancellationToken); + + public async Task GetInvoice( + string invoiceId, + InvoiceGetOptions invoiceGetOptions = null, + RequestOptions requestOptions = null, + CancellationToken cancellationToken = default) => + await _invoiceService.GetAsync(invoiceId, invoiceGetOptions, requestOptions, cancellationToken); + + public async Task GetPaymentMethod( + string paymentMethodId, + PaymentMethodGetOptions paymentMethodGetOptions = null, + RequestOptions requestOptions = null, + CancellationToken cancellationToken = default) => + await _paymentMethodService.GetAsync(paymentMethodId, paymentMethodGetOptions, requestOptions, cancellationToken); + + public async Task GetSubscription( + string subscriptionId, + SubscriptionGetOptions subscriptionGetOptions = null, + RequestOptions requestOptions = null, + CancellationToken cancellationToken = default) => + await _subscriptionService.GetAsync(subscriptionId, subscriptionGetOptions, requestOptions, cancellationToken); +} diff --git a/src/Billing/Startup.cs b/src/Billing/Startup.cs index d665677d62ab..cbde05ce06ac 100644 --- a/src/Billing/Startup.cs +++ b/src/Billing/Startup.cs @@ -1,4 +1,6 @@ using System.Globalization; +using Bit.Billing.Services; +using Bit.Billing.Services.Implementations; using Bit.Core.Context; using Bit.Core.SecretsManager.Repositories; using Bit.Core.SecretsManager.Repositories.Noop; @@ -80,6 +82,9 @@ public void ConfigureServices(IServiceCollection services) // Set up HttpClients services.AddHttpClient("FreshdeskApi"); + + services.AddScoped(); + services.AddScoped(); } public void Configure( diff --git a/src/Billing/Utilities/PayPalIpnClient.cs b/src/Billing/Utilities/PayPalIpnClient.cs index 15f7a2f1567a..1762f92a11fc 100644 --- a/src/Billing/Utilities/PayPalIpnClient.cs +++ b/src/Billing/Utilities/PayPalIpnClient.cs @@ -10,18 +10,22 @@ public class PayPalIpnClient { private readonly HttpClient _httpClient = new HttpClient(); private readonly Uri _ipnUri; + private readonly ILogger _logger; - public PayPalIpnClient(IOptions billingSettings) + public PayPalIpnClient(IOptions billingSettings, ILogger logger) { var bSettings = billingSettings?.Value; - _ipnUri = new Uri(bSettings.PayPal.Production ? "https://www.paypal.com/cgi-bin/webscr" : - "https://www.sandbox.paypal.com/cgi-bin/webscr"); + _ipnUri = new Uri(bSettings.PayPal.Production ? "https://ipnpb.paypal.com/cgi-bin/webscr" : + "https://ipnpb.sandbox.paypal.com/cgi-bin/webscr"); + _logger = logger; } public async Task VerifyIpnAsync(string ipnBody) { + _logger.LogInformation("Verifying IPN with PayPal at {Timestamp}: {VerificationUri}", DateTime.UtcNow, _ipnUri); if (ipnBody == null) { + _logger.LogError("No IPN body."); throw new ArgumentException("No IPN body."); } @@ -30,6 +34,7 @@ public async Task VerifyIpnAsync(string ipnBody) Method = HttpMethod.Post, RequestUri = _ipnUri }; + _httpClient.DefaultRequestHeaders.Add("User-Agent", "CSharp-IPN-VerificationScript"); var cmdIpnBody = string.Concat("cmd=_notify-validate&", ipnBody); request.Content = new StringContent(cmdIpnBody, Encoding.UTF8, "application/x-www-form-urlencoded"); var response = await _httpClient.SendAsync(request); @@ -42,14 +47,14 @@ public async Task VerifyIpnAsync(string ipnBody) { return true; } - else if (responseContent.Equals("INVALID")) + + if (responseContent.Equals("INVALID")) { + _logger.LogWarning("Received an INVALID response from PayPal: {ResponseContent}", responseContent); return false; } - else - { - throw new Exception("Failed to verify IPN."); - } + _logger.LogError("Failed to verify IPN: {ResponseContent}", responseContent); + throw new Exception("Failed to verify IPN."); } public class IpnTransaction diff --git a/src/Billing/packages.lock.json b/src/Billing/packages.lock.json index 0da51a039110..16a42f355d3f 100644 --- a/src/Billing/packages.lock.json +++ b/src/Billing/packages.lock.json @@ -158,10 +158,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2597,7 +2598,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2630,7 +2631,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2638,7 +2639,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2650,9 +2651,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/src/Core/Context/CurrentContextProvider.cs b/src/Core/AdminConsole/Context/CurrentContextProvider.cs similarity index 79% rename from src/Core/Context/CurrentContextProvider.cs rename to src/Core/AdminConsole/Context/CurrentContextProvider.cs index 57792840cc4f..78a5565e80ca 100644 --- a/src/Core/Context/CurrentContextProvider.cs +++ b/src/Core/AdminConsole/Context/CurrentContextProvider.cs @@ -1,9 +1,9 @@ -using Bit.Core.Entities.Provider; -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.Models.Data; using Bit.Core.Utilities; -namespace Bit.Core.Context; +namespace Bit.Core.AdminConsole.Context; public class CurrentContextProvider { diff --git a/src/Core/Entities/Group.cs b/src/Core/AdminConsole/Entities/Group.cs similarity index 90% rename from src/Core/Entities/Group.cs rename to src/Core/AdminConsole/Entities/Group.cs index 3c15380fa48a..19b9b6d005f9 100644 --- a/src/Core/Entities/Group.cs +++ b/src/Core/AdminConsole/Entities/Group.cs @@ -1,8 +1,9 @@ using System.ComponentModel.DataAnnotations; +using Bit.Core.Entities; using Bit.Core.Models; using Bit.Core.Utilities; -namespace Bit.Core.Entities; +namespace Bit.Core.AdminConsole.Entities; public class Group : ITableObject, IExternal { diff --git a/src/Core/Entities/GroupUser.cs b/src/Core/AdminConsole/Entities/GroupUser.cs similarity index 71% rename from src/Core/Entities/GroupUser.cs rename to src/Core/AdminConsole/Entities/GroupUser.cs index 3497c2c74458..ad889c93ca49 100644 --- a/src/Core/Entities/GroupUser.cs +++ b/src/Core/AdminConsole/Entities/GroupUser.cs @@ -1,4 +1,4 @@ -namespace Bit.Core.Entities; +namespace Bit.Core.AdminConsole.Entities; public class GroupUser { diff --git a/src/Core/Entities/Provider/Provider.cs b/src/Core/AdminConsole/Entities/Provider/Provider.cs similarity index 88% rename from src/Core/Entities/Provider/Provider.cs rename to src/Core/AdminConsole/Entities/Provider/Provider.cs index 82b1f727e437..7aeb422cf3e3 100644 --- a/src/Core/Entities/Provider/Provider.cs +++ b/src/Core/AdminConsole/Entities/Provider/Provider.cs @@ -1,7 +1,8 @@ -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.Entities; using Bit.Core.Utilities; -namespace Bit.Core.Entities.Provider; +namespace Bit.Core.AdminConsole.Entities.Provider; public class Provider : ITableObject { diff --git a/src/Core/Entities/Provider/ProviderOrganization.cs b/src/Core/AdminConsole/Entities/Provider/ProviderOrganization.cs similarity index 83% rename from src/Core/Entities/Provider/ProviderOrganization.cs rename to src/Core/AdminConsole/Entities/Provider/ProviderOrganization.cs index 6cafef67b7b1..8396846ad45d 100644 --- a/src/Core/Entities/Provider/ProviderOrganization.cs +++ b/src/Core/AdminConsole/Entities/Provider/ProviderOrganization.cs @@ -1,6 +1,7 @@ -using Bit.Core.Utilities; +using Bit.Core.Entities; +using Bit.Core.Utilities; -namespace Bit.Core.Entities.Provider; +namespace Bit.Core.AdminConsole.Entities.Provider; public class ProviderOrganization : ITableObject { diff --git a/src/Core/Entities/Provider/ProviderUser.cs b/src/Core/AdminConsole/Entities/Provider/ProviderUser.cs similarity index 84% rename from src/Core/Entities/Provider/ProviderUser.cs rename to src/Core/AdminConsole/Entities/Provider/ProviderUser.cs index 9b86d591c98d..6e5b12934b59 100644 --- a/src/Core/Entities/Provider/ProviderUser.cs +++ b/src/Core/AdminConsole/Entities/Provider/ProviderUser.cs @@ -1,7 +1,8 @@ -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.Entities; using Bit.Core.Utilities; -namespace Bit.Core.Entities.Provider; +namespace Bit.Core.AdminConsole.Entities.Provider; public class ProviderUser : ITableObject { diff --git a/src/Core/Enums/Provider/ProviderStatusType.cs b/src/Core/AdminConsole/Enums/Provider/ProviderStatusType.cs similarity index 60% rename from src/Core/Enums/Provider/ProviderStatusType.cs rename to src/Core/AdminConsole/Enums/Provider/ProviderStatusType.cs index bcb1f8cd2d50..794bd36bf54a 100644 --- a/src/Core/Enums/Provider/ProviderStatusType.cs +++ b/src/Core/AdminConsole/Enums/Provider/ProviderStatusType.cs @@ -1,4 +1,4 @@ -namespace Bit.Core.Enums.Provider; +namespace Bit.Core.AdminConsole.Enums.Provider; public enum ProviderStatusType : byte { diff --git a/src/Core/Enums/Provider/ProviderType.cs b/src/Core/AdminConsole/Enums/Provider/ProviderType.cs similarity index 87% rename from src/Core/Enums/Provider/ProviderType.cs rename to src/Core/AdminConsole/Enums/Provider/ProviderType.cs index 020f39eba1e3..a159fe2b6b74 100644 --- a/src/Core/Enums/Provider/ProviderType.cs +++ b/src/Core/AdminConsole/Enums/Provider/ProviderType.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Bit.Core.Enums.Provider; +namespace Bit.Core.AdminConsole.Enums.Provider; public enum ProviderType : byte { diff --git a/src/Core/Enums/Provider/ProviderUserStatusType.cs b/src/Core/AdminConsole/Enums/Provider/ProviderUserStatusType.cs similarity index 66% rename from src/Core/Enums/Provider/ProviderUserStatusType.cs rename to src/Core/AdminConsole/Enums/Provider/ProviderUserStatusType.cs index 60571386d734..bb7b1022f768 100644 --- a/src/Core/Enums/Provider/ProviderUserStatusType.cs +++ b/src/Core/AdminConsole/Enums/Provider/ProviderUserStatusType.cs @@ -1,4 +1,4 @@ -namespace Bit.Core.Enums.Provider; +namespace Bit.Core.AdminConsole.Enums.Provider; public enum ProviderUserStatusType : byte { diff --git a/src/Core/Enums/Provider/ProviderUserType.cs b/src/Core/AdminConsole/Enums/Provider/ProviderUserType.cs similarity index 62% rename from src/Core/Enums/Provider/ProviderUserType.cs rename to src/Core/AdminConsole/Enums/Provider/ProviderUserType.cs index d13591290dce..a903d28a2173 100644 --- a/src/Core/Enums/Provider/ProviderUserType.cs +++ b/src/Core/AdminConsole/Enums/Provider/ProviderUserType.cs @@ -1,4 +1,4 @@ -namespace Bit.Core.Enums.Provider; +namespace Bit.Core.AdminConsole.Enums.Provider; public enum ProviderUserType : byte { diff --git a/src/Core/Models/Business/ImportedGroup.cs b/src/Core/AdminConsole/Models/Business/ImportedGroup.cs similarity index 58% rename from src/Core/Models/Business/ImportedGroup.cs rename to src/Core/AdminConsole/Models/Business/ImportedGroup.cs index bd0e38933989..bd4e81bf5b57 100644 --- a/src/Core/Models/Business/ImportedGroup.cs +++ b/src/Core/AdminConsole/Models/Business/ImportedGroup.cs @@ -1,6 +1,6 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; -namespace Bit.Core.Models.Business; +namespace Bit.Core.AdminConsole.Models.Business; public class ImportedGroup { diff --git a/src/Core/Models/Business/Provider/ProviderUserInvite.cs b/src/Core/AdminConsole/Models/Business/Provider/ProviderUserInvite.cs similarity index 90% rename from src/Core/Models/Business/Provider/ProviderUserInvite.cs rename to src/Core/AdminConsole/Models/Business/Provider/ProviderUserInvite.cs index 0fbe3d87c2ab..53cdefb3f96a 100644 --- a/src/Core/Models/Business/Provider/ProviderUserInvite.cs +++ b/src/Core/AdminConsole/Models/Business/Provider/ProviderUserInvite.cs @@ -1,6 +1,6 @@ -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Enums.Provider; -namespace Bit.Core.Models.Business.Provider; +namespace Bit.Core.AdminConsole.Models.Business.Provider; public class ProviderUserInvite { diff --git a/src/Core/Models/Data/GroupWithCollections.cs b/src/Core/AdminConsole/Models/Data/GroupWithCollections.cs similarity index 58% rename from src/Core/Models/Data/GroupWithCollections.cs rename to src/Core/AdminConsole/Models/Data/GroupWithCollections.cs index 3fa08bc45b84..e9ba5125740a 100644 --- a/src/Core/Models/Data/GroupWithCollections.cs +++ b/src/Core/AdminConsole/Models/Data/GroupWithCollections.cs @@ -1,7 +1,7 @@ using System.Data; -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; -namespace Bit.Core.Models.Data; +namespace Bit.Core.AdminConsole.Models.Data; public class GroupWithCollections : Group { diff --git a/src/Core/Models/Data/Provider/ProviderAbility.cs b/src/Core/AdminConsole/Models/Data/Provider/ProviderAbility.cs similarity index 70% rename from src/Core/Models/Data/Provider/ProviderAbility.cs rename to src/Core/AdminConsole/Models/Data/Provider/ProviderAbility.cs index b7e45eaed476..459981681b9d 100644 --- a/src/Core/Models/Data/Provider/ProviderAbility.cs +++ b/src/Core/AdminConsole/Models/Data/Provider/ProviderAbility.cs @@ -1,12 +1,10 @@ -using Bit.Core.Entities.Provider; - -namespace Bit.Core.Models.Data; +namespace Bit.Core.AdminConsole.Models.Data.Provider; public class ProviderAbility { public ProviderAbility() { } - public ProviderAbility(Provider provider) + public ProviderAbility(Entities.Provider.Provider provider) { Id = provider.Id; UseEvents = provider.UseEvents; diff --git a/src/Core/Models/Data/Provider/ProviderOrganizationOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/Provider/ProviderOrganizationOrganizationDetails.cs similarity index 91% rename from src/Core/Models/Data/Provider/ProviderOrganizationOrganizationDetails.cs rename to src/Core/AdminConsole/Models/Data/Provider/ProviderOrganizationOrganizationDetails.cs index cf4f11b00131..dc6d80b8801e 100644 --- a/src/Core/Models/Data/Provider/ProviderOrganizationOrganizationDetails.cs +++ b/src/Core/AdminConsole/Models/Data/Provider/ProviderOrganizationOrganizationDetails.cs @@ -1,6 +1,6 @@ using Bit.Core.Enums; -namespace Bit.Core.Models.Data; +namespace Bit.Core.AdminConsole.Models.Data.Provider; public class ProviderOrganizationOrganizationDetails { diff --git a/src/Core/Models/Data/Provider/ProviderOrganizationProviderDetails.cs b/src/Core/AdminConsole/Models/Data/Provider/ProviderOrganizationProviderDetails.cs similarity index 72% rename from src/Core/Models/Data/Provider/ProviderOrganizationProviderDetails.cs rename to src/Core/AdminConsole/Models/Data/Provider/ProviderOrganizationProviderDetails.cs index 8ae9559c8637..e0a6faabbf12 100644 --- a/src/Core/Models/Data/Provider/ProviderOrganizationProviderDetails.cs +++ b/src/Core/AdminConsole/Models/Data/Provider/ProviderOrganizationProviderDetails.cs @@ -1,6 +1,6 @@ -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Enums.Provider; -namespace Bit.Core.Models.Data; +namespace Bit.Core.AdminConsole.Models.Data.Provider; public class ProviderOrganizationProviderDetails { diff --git a/src/Core/Models/Data/Provider/ProviderUserOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs similarity index 89% rename from src/Core/Models/Data/Provider/ProviderUserOrganizationDetails.cs rename to src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs index 2d06fc429691..92f420a5b554 100644 --- a/src/Core/Models/Data/Provider/ProviderUserOrganizationDetails.cs +++ b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs @@ -1,6 +1,6 @@ -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Enums.Provider; -namespace Bit.Core.Models.Data; +namespace Bit.Core.AdminConsole.Models.Data.Provider; public class ProviderUserOrganizationDetails { @@ -34,5 +34,5 @@ public class ProviderUserOrganizationDetails public Guid? ProviderId { get; set; } public Guid? ProviderUserId { get; set; } public string ProviderName { get; set; } - public Enums.PlanType PlanType { get; set; } + public Core.Enums.PlanType PlanType { get; set; } } diff --git a/src/Core/Models/Data/Provider/ProviderUserProviderDetails.cs b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserProviderDetails.cs similarity index 82% rename from src/Core/Models/Data/Provider/ProviderUserProviderDetails.cs rename to src/Core/AdminConsole/Models/Data/Provider/ProviderUserProviderDetails.cs index 16f2e1dda574..2b7d240a5c35 100644 --- a/src/Core/Models/Data/Provider/ProviderUserProviderDetails.cs +++ b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserProviderDetails.cs @@ -1,6 +1,6 @@ -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Enums.Provider; -namespace Bit.Core.Models.Data; +namespace Bit.Core.AdminConsole.Models.Data.Provider; public class ProviderUserProviderDetails { diff --git a/src/Core/Models/Data/Provider/ProviderUserPublicKey.cs b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserPublicKey.cs similarity index 72% rename from src/Core/Models/Data/Provider/ProviderUserPublicKey.cs rename to src/Core/AdminConsole/Models/Data/Provider/ProviderUserPublicKey.cs index 0b161fd860de..a9b37b20502a 100644 --- a/src/Core/Models/Data/Provider/ProviderUserPublicKey.cs +++ b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserPublicKey.cs @@ -1,4 +1,4 @@ -namespace Bit.Core.Models.Data; +namespace Bit.Core.AdminConsole.Models.Data.Provider; public class ProviderUserPublicKey { diff --git a/src/Core/Models/Data/Provider/ProviderUserUserDetails.cs b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserUserDetails.cs similarity index 78% rename from src/Core/Models/Data/Provider/ProviderUserUserDetails.cs rename to src/Core/AdminConsole/Models/Data/Provider/ProviderUserUserDetails.cs index 51df1d44ef56..94aa3fa0f33b 100644 --- a/src/Core/Models/Data/Provider/ProviderUserUserDetails.cs +++ b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserUserDetails.cs @@ -1,6 +1,6 @@ -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Enums.Provider; -namespace Bit.Core.Models.Data; +namespace Bit.Core.AdminConsole.Models.Data.Provider; public class ProviderUserUserDetails { diff --git a/src/Core/OrganizationFeatures/Groups/CreateGroupCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/Groups/CreateGroupCommand.cs similarity index 89% rename from src/Core/OrganizationFeatures/Groups/CreateGroupCommand.cs rename to src/Core/AdminConsole/OrganizationFeatures/Groups/CreateGroupCommand.cs index 10633a7e961b..94e03afd8c10 100644 --- a/src/Core/OrganizationFeatures/Groups/CreateGroupCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Groups/CreateGroupCommand.cs @@ -1,16 +1,18 @@ -using Bit.Core.Context; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Data; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Tools.Enums; using Bit.Core.Tools.Models.Business; using Bit.Core.Tools.Services; -namespace Bit.Core.OrganizationFeatures.Groups; +namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups; public class CreateGroupCommand : ICreateGroupCommand { @@ -46,7 +48,7 @@ public async Task CreateGroupAsync(Group group, Organization organization, await GroupRepositoryUpdateUsersAsync(group, users); } - await _eventService.LogGroupEventAsync(group, Enums.EventType.Group_Created); + await _eventService.LogGroupEventAsync(group, Core.Enums.EventType.Group_Created); } public async Task CreateGroupAsync(Group group, Organization organization, EventSystemUser systemUser, @@ -61,7 +63,7 @@ public async Task CreateGroupAsync(Group group, Organization organization, Event await GroupRepositoryUpdateUsersAsync(group, users, systemUser); } - await _eventService.LogGroupEventAsync(group, Enums.EventType.Group_Created, systemUser); + await _eventService.LogGroupEventAsync(group, Core.Enums.EventType.Group_Created, systemUser); } private async Task GroupRepositoryCreateGroupAsync(Group group, Organization organization, IEnumerable collections = null) diff --git a/src/Core/OrganizationFeatures/Groups/DeleteGroupCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/Groups/DeleteGroupCommand.cs similarity index 90% rename from src/Core/OrganizationFeatures/Groups/DeleteGroupCommand.cs rename to src/Core/AdminConsole/OrganizationFeatures/Groups/DeleteGroupCommand.cs index 86fb615f82ba..85f68eaf0aaa 100644 --- a/src/Core/OrganizationFeatures/Groups/DeleteGroupCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Groups/DeleteGroupCommand.cs @@ -1,11 +1,11 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Enums; using Bit.Core.Exceptions; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; -using Bit.Core.Repositories; using Bit.Core.Services; -namespace Bit.Core.OrganizationFeatures.Groups; +namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups; public class DeleteGroupCommand : IDeleteGroupCommand { diff --git a/src/Core/OrganizationFeatures/Groups/Interfaces/ICreateGroupCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/Groups/Interfaces/ICreateGroupCommand.cs similarity index 77% rename from src/Core/OrganizationFeatures/Groups/Interfaces/ICreateGroupCommand.cs rename to src/Core/AdminConsole/OrganizationFeatures/Groups/Interfaces/ICreateGroupCommand.cs index d8da08e8f2ca..54f5bc2ec22b 100644 --- a/src/Core/OrganizationFeatures/Groups/Interfaces/ICreateGroupCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Groups/Interfaces/ICreateGroupCommand.cs @@ -1,8 +1,9 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.Data; -namespace Bit.Core.OrganizationFeatures.Groups.Interfaces; +namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; public interface ICreateGroupCommand { diff --git a/src/Core/OrganizationFeatures/Groups/Interfaces/IDeleteGroupCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/Groups/Interfaces/IDeleteGroupCommand.cs similarity index 72% rename from src/Core/OrganizationFeatures/Groups/Interfaces/IDeleteGroupCommand.cs rename to src/Core/AdminConsole/OrganizationFeatures/Groups/Interfaces/IDeleteGroupCommand.cs index 6dc9d01f997c..566cdb1f24aa 100644 --- a/src/Core/OrganizationFeatures/Groups/Interfaces/IDeleteGroupCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Groups/Interfaces/IDeleteGroupCommand.cs @@ -1,7 +1,7 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; using Bit.Core.Enums; -namespace Bit.Core.OrganizationFeatures.Groups.Interfaces; +namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; public interface IDeleteGroupCommand { diff --git a/src/Core/OrganizationFeatures/Groups/Interfaces/IUpdateGroupCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/Groups/Interfaces/IUpdateGroupCommand.cs similarity index 77% rename from src/Core/OrganizationFeatures/Groups/Interfaces/IUpdateGroupCommand.cs rename to src/Core/AdminConsole/OrganizationFeatures/Groups/Interfaces/IUpdateGroupCommand.cs index 7abb012fad26..97b32fd950bd 100644 --- a/src/Core/OrganizationFeatures/Groups/Interfaces/IUpdateGroupCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Groups/Interfaces/IUpdateGroupCommand.cs @@ -1,8 +1,9 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.Data; -namespace Bit.Core.OrganizationFeatures.Groups.Interfaces; +namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; public interface IUpdateGroupCommand { diff --git a/src/Core/OrganizationFeatures/Groups/UpdateGroupCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/Groups/UpdateGroupCommand.cs similarity index 89% rename from src/Core/OrganizationFeatures/Groups/UpdateGroupCommand.cs rename to src/Core/AdminConsole/OrganizationFeatures/Groups/UpdateGroupCommand.cs index 857adcc8c0b3..3deb6994ac53 100644 --- a/src/Core/OrganizationFeatures/Groups/UpdateGroupCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Groups/UpdateGroupCommand.cs @@ -1,12 +1,14 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Data; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; -namespace Bit.Core.OrganizationFeatures.Groups; +namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups; public class UpdateGroupCommand : IUpdateGroupCommand { @@ -36,7 +38,7 @@ public async Task UpdateGroupAsync(Group group, Organization organization, await GroupRepositoryUpdateUsersAsync(group, userIds); } - await _eventService.LogGroupEventAsync(group, Enums.EventType.Group_Updated); + await _eventService.LogGroupEventAsync(group, Core.Enums.EventType.Group_Updated); } public async Task UpdateGroupAsync(Group group, Organization organization, EventSystemUser systemUser, @@ -51,7 +53,7 @@ public async Task UpdateGroupAsync(Group group, Organization organization, Event await GroupRepositoryUpdateUsersAsync(group, userIds, systemUser); } - await _eventService.LogGroupEventAsync(group, Enums.EventType.Group_Updated, systemUser); + await _eventService.LogGroupEventAsync(group, Core.Enums.EventType.Group_Updated, systemUser); } private async Task GroupRepositoryUpdateGroupAsync(Group group, IEnumerable collections = null) diff --git a/src/Core/Providers/Interfaces/ICreateProviderCommand.cs b/src/Core/AdminConsole/Providers/Interfaces/ICreateProviderCommand.cs similarity index 60% rename from src/Core/Providers/Interfaces/ICreateProviderCommand.cs rename to src/Core/AdminConsole/Providers/Interfaces/ICreateProviderCommand.cs index 4121b1533564..93b3e387a3af 100644 --- a/src/Core/Providers/Interfaces/ICreateProviderCommand.cs +++ b/src/Core/AdminConsole/Providers/Interfaces/ICreateProviderCommand.cs @@ -1,6 +1,6 @@ -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities.Provider; -namespace Bit.Core.Providers.Interfaces; +namespace Bit.Core.AdminConsole.Providers.Interfaces; public interface ICreateProviderCommand { diff --git a/src/Core/Repositories/IGroupRepository.cs b/src/Core/AdminConsole/Repositories/IGroupRepository.cs similarity index 90% rename from src/Core/Repositories/IGroupRepository.cs rename to src/Core/AdminConsole/Repositories/IGroupRepository.cs index f1b506de07f0..7e691db17316 100644 --- a/src/Core/Repositories/IGroupRepository.cs +++ b/src/Core/AdminConsole/Repositories/IGroupRepository.cs @@ -1,7 +1,8 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; using Bit.Core.Models.Data; +using Bit.Core.Repositories; -namespace Bit.Core.Repositories; +namespace Bit.Core.AdminConsole.Repositories; public interface IGroupRepository : IRepository { diff --git a/src/Core/Repositories/IProviderOrganizationRepository.cs b/src/Core/AdminConsole/Repositories/IProviderOrganizationRepository.cs similarity index 76% rename from src/Core/Repositories/IProviderOrganizationRepository.cs rename to src/Core/AdminConsole/Repositories/IProviderOrganizationRepository.cs index 8550a3d7bc04..6f267295efad 100644 --- a/src/Core/Repositories/IProviderOrganizationRepository.cs +++ b/src/Core/AdminConsole/Repositories/IProviderOrganizationRepository.cs @@ -1,7 +1,8 @@ -using Bit.Core.Entities.Provider; -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.Repositories; -namespace Bit.Core.Repositories; +namespace Bit.Core.AdminConsole.Repositories; public interface IProviderOrganizationRepository : IRepository { diff --git a/src/Core/Repositories/IProviderRepository.cs b/src/Core/AdminConsole/Repositories/IProviderRepository.cs similarity index 63% rename from src/Core/Repositories/IProviderRepository.cs rename to src/Core/AdminConsole/Repositories/IProviderRepository.cs index 8939bba966db..c9da0b2faad4 100644 --- a/src/Core/Repositories/IProviderRepository.cs +++ b/src/Core/AdminConsole/Repositories/IProviderRepository.cs @@ -1,7 +1,8 @@ -using Bit.Core.Entities.Provider; -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.Repositories; -namespace Bit.Core.Repositories; +namespace Bit.Core.AdminConsole.Repositories; public interface IProviderRepository : IRepository { diff --git a/src/Core/Repositories/IProviderUserRepository.cs b/src/Core/AdminConsole/Repositories/IProviderUserRepository.cs similarity index 85% rename from src/Core/Repositories/IProviderUserRepository.cs rename to src/Core/AdminConsole/Repositories/IProviderUserRepository.cs index 9f0374fee3d2..34443f7d5f4d 100644 --- a/src/Core/Repositories/IProviderUserRepository.cs +++ b/src/Core/AdminConsole/Repositories/IProviderUserRepository.cs @@ -1,8 +1,9 @@ -using Bit.Core.Entities.Provider; -using Bit.Core.Enums.Provider; -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.Repositories; -namespace Bit.Core.Repositories; +namespace Bit.Core.AdminConsole.Repositories; public interface IProviderUserRepository : IRepository { diff --git a/src/Core/Services/IGroupService.cs b/src/Core/AdminConsole/Services/IGroupService.cs similarity index 85% rename from src/Core/Services/IGroupService.cs rename to src/Core/AdminConsole/Services/IGroupService.cs index 14967b6e85ec..d189a63de548 100644 --- a/src/Core/Services/IGroupService.cs +++ b/src/Core/AdminConsole/Services/IGroupService.cs @@ -1,7 +1,7 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; using Bit.Core.Enums; -namespace Bit.Core.Services; +namespace Bit.Core.AdminConsole.Services; public interface IGroupService { diff --git a/src/Core/Services/IProviderService.cs b/src/Core/AdminConsole/Services/IProviderService.cs similarity index 89% rename from src/Core/Services/IProviderService.cs rename to src/Core/AdminConsole/Services/IProviderService.cs index c1ac8b5a33aa..f71403e80c46 100644 --- a/src/Core/Services/IProviderService.cs +++ b/src/Core/AdminConsole/Services/IProviderService.cs @@ -1,9 +1,9 @@ -using Bit.Core.Entities; -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Business.Provider; +using Bit.Core.Entities; using Bit.Core.Models.Business; -using Bit.Core.Models.Business.Provider; -namespace Bit.Core.Services; +namespace Bit.Core.AdminConsole.Services; public interface IProviderService { diff --git a/src/Core/Services/Implementations/GroupService.cs b/src/Core/AdminConsole/Services/Implementations/GroupService.cs similarity index 92% rename from src/Core/Services/Implementations/GroupService.cs rename to src/Core/AdminConsole/Services/Implementations/GroupService.cs index c878c321d969..62ab4ed487c4 100644 --- a/src/Core/Services/Implementations/GroupService.cs +++ b/src/Core/AdminConsole/Services/Implementations/GroupService.cs @@ -1,9 +1,12 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Repositories; +using Bit.Core.Services; -namespace Bit.Core.Services; +namespace Bit.Core.AdminConsole.Services.Implementations; public class GroupService : IGroupService { diff --git a/src/Core/Services/NoopImplementations/NoopProviderService.cs b/src/Core/AdminConsole/Services/NoopImplementations/NoopProviderService.cs similarity index 92% rename from src/Core/Services/NoopImplementations/NoopProviderService.cs rename to src/Core/AdminConsole/Services/NoopImplementations/NoopProviderService.cs index 596ec9171838..b573764b3fd0 100644 --- a/src/Core/Services/NoopImplementations/NoopProviderService.cs +++ b/src/Core/AdminConsole/Services/NoopImplementations/NoopProviderService.cs @@ -1,9 +1,9 @@ -using Bit.Core.Entities; -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Business.Provider; +using Bit.Core.Entities; using Bit.Core.Models.Business; -using Bit.Core.Models.Business.Provider; -namespace Bit.Core.Services; +namespace Bit.Core.AdminConsole.Services.NoopImplementations; public class NoopProviderService : IProviderService { diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index d54c38fc0513..4b5c90261452 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -20,6 +20,10 @@ public static class Constants /// their subscription has expired. /// public const int OrganizationSelfHostSubscriptionGracePeriodDays = 60; + + public const string Fido2KeyCipherMinimumVersion = "2023.10.0"; + + public const string CipherKeyEncryptionMinimumVersion = "2023.9.2"; } public static class TokenPurposes @@ -38,7 +42,10 @@ public static class FeatureFlagKeys public const string DisplayLowKdfIterationWarning = "display-kdf-iteration-warning"; public const string PasswordlessLogin = "passwordless-login"; public const string TrustedDeviceEncryption = "trusted-device-encryption"; + public const string Fido2VaultCredentials = "fido2-vault-credentials"; public const string AutofillV2 = "autofill-v2"; + public const string BrowserFilelessImport = "browser-fileless-import"; + public const string AutofillOverlay = "autofill-overlay"; public static List GetAllKeys() { @@ -53,7 +60,8 @@ public static Dictionary GetLocalOverrideFlagValues() // place overriding values when needed locally (offline), or return null return new Dictionary() { - { TrustedDeviceEncryption, "true" } + { TrustedDeviceEncryption, "true" }, + { Fido2VaultCredentials, "true" } }; } } diff --git a/src/Core/Context/CurrentContext.cs b/src/Core/Context/CurrentContext.cs index 4751beb797e7..870d5f0480fc 100644 --- a/src/Core/Context/CurrentContext.cs +++ b/src/Core/Context/CurrentContext.cs @@ -1,7 +1,10 @@ using System.Security.Claims; +using Bit.Core.AdminConsole.Context; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Identity; using Bit.Core.Models.Data; using Bit.Core.Repositories; diff --git a/src/Core/Context/CurrentContextOrganization.cs b/src/Core/Context/CurrentContextOrganization.cs index cf2c8b40c736..fbef49c5aa65 100644 --- a/src/Core/Context/CurrentContextOrganization.cs +++ b/src/Core/Context/CurrentContextOrganization.cs @@ -14,7 +14,7 @@ public CurrentContextOrganization(OrganizationUserOrganizationDetails orgUser) Id = orgUser.OrganizationId; Type = orgUser.Type; Permissions = CoreHelpers.LoadClassFromJsonData(orgUser.Permissions); - AccessSecretsManager = orgUser.AccessSecretsManager && orgUser.UseSecretsManager; + AccessSecretsManager = orgUser.AccessSecretsManager && orgUser.UseSecretsManager && orgUser.Enabled; } public Guid Id { get; set; } diff --git a/src/Core/Context/ICurrentContext.cs b/src/Core/Context/ICurrentContext.cs index c2e362d435de..ee3708142184 100644 --- a/src/Core/Context/ICurrentContext.cs +++ b/src/Core/Context/ICurrentContext.cs @@ -1,4 +1,6 @@ using System.Security.Claims; +using Bit.Core.AdminConsole.Context; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Identity; diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 95b5fe8eeb56..dff657d7e816 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -51,7 +51,7 @@ - + diff --git a/src/Core/Entities/Organization.cs b/src/Core/Entities/Organization.cs index fc76b399c7bd..7deab3299ed6 100644 --- a/src/Core/Entities/Organization.cs +++ b/src/Core/Entities/Organization.cs @@ -250,5 +250,9 @@ public void UpdateFromLicense(OrganizationLicense license) ExpirationDate = license.Expires; LicenseKey = license.LicenseKey; RevisionDate = DateTime.UtcNow; + UsePasswordManager = license.UsePasswordManager; + UseSecretsManager = license.UseSecretsManager; + SmSeats = license.SmSeats; + SmServiceAccounts = license.SmServiceAccounts; } } diff --git a/src/Core/Enums/BitwardenProductType.cs b/src/Core/Enums/BitwardenProductType.cs deleted file mode 100644 index d0c358671f74..000000000000 --- a/src/Core/Enums/BitwardenProductType.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Bit.Core.Enums; - -public enum BitwardenProductType : byte -{ - [Display(Name = "Password Manager")] - PasswordManager = 0, - [Display(Name = "Secrets Manager")] - SecretsManager = 1, -} diff --git a/src/Core/Models/Business/OrganizationLicense.cs b/src/Core/Models/Business/OrganizationLicense.cs index c72430d94461..c8060263c810 100644 --- a/src/Core/Models/Business/OrganizationLicense.cs +++ b/src/Core/Models/Business/OrganizationLicense.cs @@ -47,6 +47,10 @@ public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, UsersGetPremium = org.UsersGetPremium; UseCustomPermissions = org.UseCustomPermissions; Issued = DateTime.UtcNow; + UsePasswordManager = org.UsePasswordManager; + UseSecretsManager = org.UseSecretsManager; + SmSeats = org.SmSeats; + SmServiceAccounts = org.SmServiceAccounts; if (subscriptionInfo?.Subscription == null) { @@ -125,6 +129,10 @@ public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, public DateTime? Refresh { get; set; } public DateTime? Expires { get; set; } public DateTime? ExpirationWithoutGracePeriod { get; set; } + public bool UsePasswordManager { get; set; } + public bool UseSecretsManager { get; set; } + public int? SmSeats { get; set; } + public int? SmServiceAccounts { get; set; } public bool Trial { get; set; } public LicenseType? LicenseType { get; set; } public string Hash { get; set; } @@ -137,10 +145,10 @@ public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, /// /// Intentionally set one version behind to allow self hosted users some time to update before /// getting out of date license errors - private const int CURRENT_LICENSE_FILE_VERSION = 11; + private const int CURRENT_LICENSE_FILE_VERSION = 12; private bool ValidLicenseVersion { - get => Version is >= 1 and <= 12; + get => Version is >= 1 and <= 13; } public byte[] GetDataBytes(bool forHash = false) @@ -176,6 +184,8 @@ public byte[] GetDataBytes(bool forHash = false) (Version >= 11 || !p.Name.Equals(nameof(UseCustomPermissions))) && // ExpirationWithoutGracePeriod was added in Version 12 (Version >= 12 || !p.Name.Equals(nameof(ExpirationWithoutGracePeriod))) && + // UseSecretsManager was added in Version 13 + (Version >= 13 || !p.Name.Equals(nameof(UseSecretsManager))) && ( !forHash || ( @@ -315,6 +325,14 @@ public bool VerifyData(Organization organization, IGlobalSettings globalSettings valid = organization.UseCustomPermissions == UseCustomPermissions; } + if (valid && Version >= 13) + { + valid = organization.UseSecretsManager == UseSecretsManager && + organization.UsePasswordManager == UsePasswordManager && + organization.SmSeats == SmSeats && + organization.SmServiceAccounts == SmServiceAccounts; + } + return valid; } else diff --git a/src/Core/Models/Business/SeatSubscriptionUpdate.cs b/src/Core/Models/Business/SeatSubscriptionUpdate.cs new file mode 100644 index 000000000000..8b4c613d617a --- /dev/null +++ b/src/Core/Models/Business/SeatSubscriptionUpdate.cs @@ -0,0 +1,49 @@ +using Bit.Core.Entities; +using Stripe; + +namespace Bit.Core.Models.Business; + +public class SeatSubscriptionUpdate : SubscriptionUpdate +{ + private readonly int _previousSeats; + private readonly StaticStore.Plan _plan; + private readonly long? _additionalSeats; + protected override List PlanIds => new() { _plan.PasswordManager.StripeSeatPlanId }; + public SeatSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats) + { + _plan = plan; + _additionalSeats = additionalSeats; + _previousSeats = organization.Seats.GetValueOrDefault(); + } + + public override List UpgradeItemsOptions(Subscription subscription) + { + var item = SubscriptionItem(subscription, PlanIds.Single()); + return new() + { + new SubscriptionItemOptions + { + Id = item?.Id, + Plan = PlanIds.Single(), + Quantity = _additionalSeats, + Deleted = (item?.Id != null && _additionalSeats == 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 = _previousSeats, + Deleted = _previousSeats == 0 ? true : (bool?)null, + } + }; + } +} diff --git a/src/Core/Models/Business/SecretsManagerSubscribeUpdate.cs b/src/Core/Models/Business/SecretsManagerSubscribeUpdate.cs new file mode 100644 index 000000000000..8f3fb89349cb --- /dev/null +++ b/src/Core/Models/Business/SecretsManagerSubscribeUpdate.cs @@ -0,0 +1,78 @@ +using Bit.Core.Entities; +using Stripe; + +namespace Bit.Core.Models.Business; + +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.SecretsManager.StripeSeatPlanId, _plan.SecretsManager.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.SecretsManager.StripeSeatPlanId, + Quantity = _additionalSeats + }); + } + + if (_additionalServiceAccounts > 0) + { + updatedItems.Add(new SubscriptionItemOptions + { + Price = _plan.SecretsManager.StripeServiceAccountPlanId, + Quantity = _additionalServiceAccounts + }); + } + } + + private void RemovePreviousSecretsManagerItems(List updatedItems) + { + updatedItems.Add(new SubscriptionItemOptions + { + Price = _plan.SecretsManager.StripeSeatPlanId, + Quantity = _previousSeats, + Deleted = _previousSeats == 0 ? true : (bool?)null, + }); + + updatedItems.Add(new SubscriptionItemOptions + { + Price = _plan.SecretsManager.StripeServiceAccountPlanId, + Quantity = _previousServiceAccounts, + Deleted = _previousServiceAccounts == 0 ? true : (bool?)null, + }); + } +} diff --git a/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs b/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs index 1cb7a41df8d8..3787470588b9 100644 --- a/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs +++ b/src/Core/Models/Business/SecretsManagerSubscriptionUpdate.cs @@ -43,18 +43,18 @@ public class SecretsManagerSubscriptionUpdate /// 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 => SmSeats.HasValue ? SmSeats.Value - Plan.BaseSeats : 0; + public int SmSeatsExcludingBase => SmSeats.HasValue ? SmSeats.Value - Plan.SecretsManager.BaseSeats : 0; /// /// 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 => SmServiceAccounts.HasValue ? SmServiceAccounts.Value - Plan.BaseServiceAccount.GetValueOrDefault() : 0; + public int SmServiceAccountsExcludingBase => SmServiceAccounts.HasValue ? SmServiceAccounts.Value - Plan.SecretsManager!.BaseServiceAccount : 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 Plan Plan => Utilities.StaticStore.GetPlan(Organization.PlanType); public bool SmSeatAutoscaleLimitReached => SmSeats.HasValue && MaxAutoscaleSmSeats.HasValue && SmSeats == MaxAutoscaleSmSeats; public bool SmServiceAccountAutoscaleLimitReached => SmServiceAccounts.HasValue && @@ -70,7 +70,7 @@ public SecretsManagerSubscriptionUpdate(Organization organization, bool autoscal Organization = organization; - if (Plan == null) + if (!Plan.SupportsSecretsManager) { throw new NotFoundException("Invalid Secrets Manager plan."); } diff --git a/src/Core/Models/Business/ServiceAccountSubscriptionUpdate.cs b/src/Core/Models/Business/ServiceAccountSubscriptionUpdate.cs new file mode 100644 index 000000000000..b49c9cd6c5a0 --- /dev/null +++ b/src/Core/Models/Business/ServiceAccountSubscriptionUpdate.cs @@ -0,0 +1,50 @@ +using Bit.Core.Entities; +using Stripe; + +namespace Bit.Core.Models.Business; + +public class ServiceAccountSubscriptionUpdate : SubscriptionUpdate +{ + private long? _prevServiceAccounts; + private readonly StaticStore.Plan _plan; + private readonly long? _additionalServiceAccounts; + protected override List PlanIds => new() { _plan.SecretsManager.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, + } + }; + } +} diff --git a/src/Core/Models/Business/SmSeatSubscriptionUpdate.cs b/src/Core/Models/Business/SmSeatSubscriptionUpdate.cs new file mode 100644 index 000000000000..ddc126a261a2 --- /dev/null +++ b/src/Core/Models/Business/SmSeatSubscriptionUpdate.cs @@ -0,0 +1,50 @@ +using Bit.Core.Entities; +using Stripe; + +namespace Bit.Core.Models.Business; + +public class SmSeatSubscriptionUpdate : SubscriptionUpdate +{ + private readonly int _previousSeats; + private readonly StaticStore.Plan _plan; + private readonly long? _additionalSeats; + protected override List PlanIds => new() { _plan.SecretsManager.StripeSeatPlanId }; + + public SmSeatSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats) + { + _plan = plan; + _additionalSeats = additionalSeats; + _previousSeats = organization.SmSeats.GetValueOrDefault(); + } + + public override List UpgradeItemsOptions(Subscription subscription) + { + var item = SubscriptionItem(subscription, PlanIds.Single()); + return new() + { + new SubscriptionItemOptions + { + Id = item?.Id, + Plan = PlanIds.Single(), + Quantity = _additionalSeats, + Deleted = (item?.Id != null && _additionalSeats == 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 = _previousSeats, + Deleted = _previousSeats == 0 ? true : (bool?)null, + } + }; + } +} diff --git a/src/Core/Models/Business/SponsorOrganizationSubscriptionUpdate.cs b/src/Core/Models/Business/SponsorOrganizationSubscriptionUpdate.cs new file mode 100644 index 000000000000..88af72f1997a --- /dev/null +++ b/src/Core/Models/Business/SponsorOrganizationSubscriptionUpdate.cs @@ -0,0 +1,83 @@ +using Stripe; + +namespace Bit.Core.Models.Business; + +public class SponsorOrganizationSubscriptionUpdate : SubscriptionUpdate +{ + private readonly string _existingPlanStripeId; + private readonly string _sponsoredPlanStripeId; + private readonly bool _applySponsorship; + protected override List PlanIds => new() { _existingPlanStripeId, _sponsoredPlanStripeId }; + + public SponsorOrganizationSubscriptionUpdate(StaticStore.Plan existingPlan, StaticStore.SponsoredPlan sponsoredPlan, bool applySponsorship) + { + _existingPlanStripeId = existingPlan.PasswordManager.StripePlanId; + _sponsoredPlanStripeId = sponsoredPlan?.StripePlanId; + _applySponsorship = applySponsorship; + } + + public override List RevertItemsOptions(Subscription subscription) + { + var result = new List(); + if (!string.IsNullOrWhiteSpace(AddStripePlanId)) + { + result.Add(new SubscriptionItemOptions + { + Id = AddStripeItem(subscription)?.Id, + Plan = AddStripePlanId, + Quantity = 0, + Deleted = true, + }); + } + + if (!string.IsNullOrWhiteSpace(RemoveStripePlanId)) + { + result.Add(new SubscriptionItemOptions + { + Id = RemoveStripeItem(subscription)?.Id, + Plan = RemoveStripePlanId, + Quantity = 1, + Deleted = false, + }); + } + return result; + } + + public override List UpgradeItemsOptions(Subscription subscription) + { + var result = new List(); + if (RemoveStripeItem(subscription) != null) + { + result.Add(new SubscriptionItemOptions + { + Id = RemoveStripeItem(subscription)?.Id, + Plan = RemoveStripePlanId, + Quantity = 0, + Deleted = true, + }); + } + + if (!string.IsNullOrWhiteSpace(AddStripePlanId)) + { + result.Add(new SubscriptionItemOptions + { + Id = AddStripeItem(subscription)?.Id, + Plan = AddStripePlanId, + Quantity = 1, + Deleted = false, + }); + } + return result; + } + + private string RemoveStripePlanId => _applySponsorship ? _existingPlanStripeId : _sponsoredPlanStripeId; + private string AddStripePlanId => _applySponsorship ? _sponsoredPlanStripeId : _existingPlanStripeId; + private Stripe.SubscriptionItem RemoveStripeItem(Subscription subscription) => + _applySponsorship ? + SubscriptionItem(subscription, _existingPlanStripeId) : + SubscriptionItem(subscription, _sponsoredPlanStripeId); + private Stripe.SubscriptionItem AddStripeItem(Subscription subscription) => + _applySponsorship ? + SubscriptionItem(subscription, _sponsoredPlanStripeId) : + SubscriptionItem(subscription, _existingPlanStripeId); +} diff --git a/src/Core/Models/Business/StorageSubscriptionUpdate.cs b/src/Core/Models/Business/StorageSubscriptionUpdate.cs new file mode 100644 index 000000000000..30ab2428e2ad --- /dev/null +++ b/src/Core/Models/Business/StorageSubscriptionUpdate.cs @@ -0,0 +1,53 @@ +using Stripe; + +namespace Bit.Core.Models.Business; + +public class StorageSubscriptionUpdate : SubscriptionUpdate +{ + private long? _prevStorage; + private readonly string _plan; + private readonly long? _additionalStorage; + protected override List PlanIds => new() { _plan }; + + public StorageSubscriptionUpdate(string plan, long? additionalStorage) + { + _plan = plan; + _additionalStorage = additionalStorage; + } + + public override List UpgradeItemsOptions(Subscription subscription) + { + var item = SubscriptionItem(subscription, PlanIds.Single()); + _prevStorage = item?.Quantity ?? 0; + return new() + { + new SubscriptionItemOptions + { + Id = item?.Id, + Plan = _plan, + Quantity = _additionalStorage, + Deleted = (item?.Id != null && _additionalStorage == 0) ? true : (bool?)null, + } + }; + } + + public override List RevertItemsOptions(Subscription subscription) + { + if (!_prevStorage.HasValue) + { + throw new Exception("Unknown previous value, must first call UpgradeItemsOptions"); + } + + var item = SubscriptionItem(subscription, PlanIds.Single()); + return new() + { + new SubscriptionItemOptions + { + Id = item?.Id, + Plan = _plan, + Quantity = _prevStorage.Value, + Deleted = _prevStorage.Value == 0 ? true : (bool?)null, + } + }; + } +} diff --git a/src/Core/Models/Business/SubscriptionCreateOptions.cs b/src/Core/Models/Business/SubscriptionCreateOptions.cs index 86524a55b6cb..0ea4c4c0b574 100644 --- a/src/Core/Models/Business/SubscriptionCreateOptions.cs +++ b/src/Core/Models/Business/SubscriptionCreateOptions.cs @@ -1,12 +1,12 @@ using Bit.Core.Entities; -using Bit.Core.Enums; using Stripe; +using Plan = Bit.Core.Models.StaticStore.Plan; namespace Bit.Core.Models.Business; public class OrganizationSubscriptionOptionsBase : Stripe.SubscriptionCreateOptions { - public OrganizationSubscriptionOptionsBase(Organization org, List plans, TaxInfo taxInfo, int additionalSeats, + public OrganizationSubscriptionOptionsBase(Organization org, StaticStore.Plan plan, TaxInfo taxInfo, int additionalSeats, int additionalStorageGb, bool premiumAccessAddon, int additionalSmSeats, int additionalServiceAccounts) { Items = new List(); @@ -14,79 +14,80 @@ public OrganizationSubscriptionOptionsBase(Organization org, List { taxInfo.StripeTaxRateId }; } } - private void AddServiceAccount(int additionalServiceAccounts, StaticStore.Plan plan) + private void AddSecretsManagerSeat(Plan plan, int additionalSmSeats) { - if (additionalServiceAccounts > 0 && plan.StripeServiceAccountPlanId != null) + if (additionalSmSeats > 0 && plan.SecretsManager.StripeSeatPlanId != null) { Items.Add(new SubscriptionItemOptions - { - Plan = plan.StripeServiceAccountPlanId, - Quantity = additionalServiceAccounts - }); + { Plan = plan.SecretsManager.StripeSeatPlanId, Quantity = additionalSmSeats }); } } - private void AddAdditionalStorage(int additionalStorageGb, StaticStore.Plan plan) + private void AddPasswordManagerSeat(Plan plan, int additionalSeats) { - if (additionalStorageGb > 0) + if (additionalSeats > 0 && plan.PasswordManager.StripeSeatPlanId != null) + { + Items.Add(new SubscriptionItemOptions + { Plan = plan.PasswordManager.StripeSeatPlanId, Quantity = additionalSeats }); + } + } + + private void AddServiceAccount(StaticStore.Plan plan, int additionalServiceAccounts) + { + if (additionalServiceAccounts > 0 && plan.SecretsManager.StripeServiceAccountPlanId != null) { Items.Add(new SubscriptionItemOptions { - Plan = plan.StripeStoragePlanId, - Quantity = additionalStorageGb + Plan = plan.SecretsManager.StripeServiceAccountPlanId, + Quantity = additionalServiceAccounts }); } } - private void AddPremiumAccessAddon(bool premiumAccessAddon, StaticStore.Plan plan) + private void AddAdditionalStorage(StaticStore.Plan plan, int additionalStorageGb) { - if (premiumAccessAddon && plan.StripePremiumAccessPlanId != null) + if (additionalStorageGb > 0) { - Items.Add(new SubscriptionItemOptions { Plan = plan.StripePremiumAccessPlanId, Quantity = 1 }); + Items.Add(new SubscriptionItemOptions + { + Plan = plan.PasswordManager.StripeStoragePlanId, + Quantity = additionalStorageGb + }); } } - private void AddAdditionalSeatToSubscription(int additionalSeats, StaticStore.Plan plan) + private void AddPremiumAccessAddon(StaticStore.Plan plan, bool premiumAccessAddon) { - if (additionalSeats > 0 && plan.StripeSeatPlanId != null) + if (premiumAccessAddon && plan.PasswordManager.StripePremiumAccessPlanId != null) { - Items.Add(new SubscriptionItemOptions { Plan = plan.StripeSeatPlanId, Quantity = additionalSeats }); + Items.Add(new SubscriptionItemOptions { Plan = plan.PasswordManager.StripePremiumAccessPlanId, Quantity = 1 }); } } private void AddPlanIdToSubscription(StaticStore.Plan plan) { - if (plan.StripePlanId != null) + if (plan.PasswordManager.StripePlanId != null) { - Items.Add(new SubscriptionItemOptions { Plan = plan.StripePlanId, Quantity = 1 }); + Items.Add(new SubscriptionItemOptions { Plan = plan.PasswordManager.StripePlanId, Quantity = 1 }); } } } @@ -94,14 +95,14 @@ private void AddPlanIdToSubscription(StaticStore.Plan plan) public class OrganizationPurchaseSubscriptionOptions : OrganizationSubscriptionOptionsBase { public OrganizationPurchaseSubscriptionOptions( - Organization org, List plans, + Organization org, StaticStore.Plan plan, TaxInfo taxInfo, int additionalSeats, int additionalStorageGb, bool premiumAccessAddon, int additionalSmSeats, int additionalServiceAccounts) : - base(org, plans, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon, additionalSmSeats, additionalServiceAccounts) + base(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon, additionalSmSeats, additionalServiceAccounts) { OffSession = true; - TrialPeriodDays = plans.FirstOrDefault(x => x.BitwardenProduct == BitwardenProductType.PasswordManager)!.TrialPeriodDays; + TrialPeriodDays = plan.TrialPeriodDays; } } @@ -109,8 +110,8 @@ public class OrganizationUpgradeSubscriptionOptions : OrganizationSubscriptionOp { public OrganizationUpgradeSubscriptionOptions( string customerId, Organization org, - List plans, OrganizationUpgrade upgrade) : - base(org, plans, upgrade.TaxInfo, upgrade.AdditionalSeats, upgrade.AdditionalStorageGb, + StaticStore.Plan plan, OrganizationUpgrade upgrade) : + base(org, plan, upgrade.TaxInfo, upgrade.AdditionalSeats, upgrade.AdditionalStorageGb, upgrade.PremiumAccessAddon, upgrade.AdditionalSmSeats.GetValueOrDefault(), upgrade.AdditionalServiceAccounts.GetValueOrDefault()) { diff --git a/src/Core/Models/Business/SubscriptionInfo.cs b/src/Core/Models/Business/SubscriptionInfo.cs index c72e291de0d6..8a0a6add74dd 100644 --- a/src/Core/Models/Business/SubscriptionInfo.cs +++ b/src/Core/Models/Business/SubscriptionInfo.cs @@ -1,14 +1,30 @@ -using Bit.Core.Enums; -using Stripe; +using Stripe; namespace Bit.Core.Models.Business; public class SubscriptionInfo { + public BillingCustomerDiscount CustomerDiscount { get; set; } public BillingSubscription Subscription { get; set; } public BillingUpcomingInvoice UpcomingInvoice { get; set; } public bool UsingInAppPurchase { get; set; } + public class BillingCustomerDiscount + { + public BillingCustomerDiscount() { } + + public BillingCustomerDiscount(Discount discount) + { + Id = discount.Id; + Active = discount.Start != null && discount.End == null; + PercentOff = discount.Coupon?.PercentOff; + } + + public string Id { get; } + public bool Active { get; } + public decimal? PercentOff { get; } + } + public class BillingSubscription { public BillingSubscription(Subscription sub) @@ -49,16 +65,12 @@ public BillingSubscriptionItem(SubscriptionItem item) 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; } diff --git a/src/Core/Models/Business/SubscriptionUpdate.cs b/src/Core/Models/Business/SubscriptionUpdate.cs index 93122b3cf201..497a455d6c8d 100644 --- a/src/Core/Models/Business/SubscriptionUpdate.cs +++ b/src/Core/Models/Business/SubscriptionUpdate.cs @@ -1,6 +1,4 @@ -using Bit.Core.Entities; -using Bit.Core.Enums; -using Stripe; +using Stripe; namespace Bit.Core.Models.Business; @@ -29,309 +27,3 @@ public bool UpdateNeeded(Subscription subscription) protected static SubscriptionItem SubscriptionItem(Subscription subscription, string planId) => planId == null ? null : subscription.Items?.Data?.FirstOrDefault(i => i.Plan.Id == planId); } - -public class SeatSubscriptionUpdate : SubscriptionUpdate -{ - private readonly int _previousSeats; - private readonly StaticStore.Plan _plan; - private readonly long? _additionalSeats; - protected override List PlanIds => new() { _plan.StripeSeatPlanId }; - - - public SeatSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats) - { - _plan = plan; - _additionalSeats = additionalSeats; - 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) - { - var item = SubscriptionItem(subscription, PlanIds.Single()); - return new() - { - new SubscriptionItemOptions - { - Id = item?.Id, - Plan = PlanIds.Single(), - Quantity = _additionalSeats, - Deleted = (item?.Id != null && _additionalSeats == 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 = _previousSeats, - Deleted = _previousSeats == 0 ? true : (bool?)null, - } - }; - } -} - -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; - private readonly string _plan; - private readonly long? _additionalStorage; - protected override List PlanIds => new() { _plan }; - - public StorageSubscriptionUpdate(string plan, long? additionalStorage) - { - _plan = plan; - _additionalStorage = additionalStorage; - } - - public override List UpgradeItemsOptions(Subscription subscription) - { - var item = SubscriptionItem(subscription, PlanIds.Single()); - _prevStorage = item?.Quantity ?? 0; - return new() - { - new SubscriptionItemOptions - { - Id = item?.Id, - Plan = _plan, - Quantity = _additionalStorage, - Deleted = (item?.Id != null && _additionalStorage == 0) ? true : (bool?)null, - } - }; - } - - public override List RevertItemsOptions(Subscription subscription) - { - if (!_prevStorage.HasValue) - { - throw new Exception("Unknown previous value, must first call UpgradeItemsOptions"); - } - - var item = SubscriptionItem(subscription, PlanIds.Single()); - return new() - { - new SubscriptionItemOptions - { - Id = item?.Id, - Plan = _plan, - Quantity = _prevStorage.Value, - Deleted = _prevStorage.Value == 0 ? true : (bool?)null, - } - }; - } -} - -public class SponsorOrganizationSubscriptionUpdate : SubscriptionUpdate -{ - private readonly string _existingPlanStripeId; - private readonly string _sponsoredPlanStripeId; - private readonly bool _applySponsorship; - protected override List PlanIds => new() { _existingPlanStripeId, _sponsoredPlanStripeId }; - - public SponsorOrganizationSubscriptionUpdate(StaticStore.Plan existingPlan, StaticStore.SponsoredPlan sponsoredPlan, bool applySponsorship) - { - _existingPlanStripeId = existingPlan.StripePlanId; - _sponsoredPlanStripeId = sponsoredPlan?.StripePlanId; - _applySponsorship = applySponsorship; - } - - public override List RevertItemsOptions(Subscription subscription) - { - var result = new List(); - if (!string.IsNullOrWhiteSpace(AddStripePlanId)) - { - result.Add(new SubscriptionItemOptions - { - Id = AddStripeItem(subscription)?.Id, - Plan = AddStripePlanId, - Quantity = 0, - Deleted = true, - }); - } - - if (!string.IsNullOrWhiteSpace(RemoveStripePlanId)) - { - result.Add(new SubscriptionItemOptions - { - Id = RemoveStripeItem(subscription)?.Id, - Plan = RemoveStripePlanId, - Quantity = 1, - Deleted = false, - }); - } - return result; - } - - public override List UpgradeItemsOptions(Subscription subscription) - { - var result = new List(); - if (RemoveStripeItem(subscription) != null) - { - result.Add(new SubscriptionItemOptions - { - Id = RemoveStripeItem(subscription)?.Id, - Plan = RemoveStripePlanId, - Quantity = 0, - Deleted = true, - }); - } - - if (!string.IsNullOrWhiteSpace(AddStripePlanId)) - { - result.Add(new SubscriptionItemOptions - { - Id = AddStripeItem(subscription)?.Id, - Plan = AddStripePlanId, - Quantity = 1, - Deleted = false, - }); - } - return result; - } - - private string RemoveStripePlanId => _applySponsorship ? _existingPlanStripeId : _sponsoredPlanStripeId; - private string AddStripePlanId => _applySponsorship ? _sponsoredPlanStripeId : _existingPlanStripeId; - private Stripe.SubscriptionItem RemoveStripeItem(Subscription subscription) => - _applySponsorship ? - SubscriptionItem(subscription, _existingPlanStripeId) : - SubscriptionItem(subscription, _sponsoredPlanStripeId); - private Stripe.SubscriptionItem AddStripeItem(Subscription subscription) => - _applySponsorship ? - SubscriptionItem(subscription, _sponsoredPlanStripeId) : - 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/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs b/src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs index 0470d5ebc3b3..4beb15885c4f 100644 --- a/src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs +++ b/src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs @@ -1,4 +1,4 @@ -using Bit.Core.Enums.Provider; +using Bit.Core.AdminConsole.Enums.Provider; namespace Bit.Core.Models.Data.Organizations.OrganizationUsers; diff --git a/src/Core/Models/StaticStore/Plan.cs b/src/Core/Models/StaticStore/Plan.cs index f542e5e21480..4f8b0435fff2 100644 --- a/src/Core/Models/StaticStore/Plan.cs +++ b/src/Core/Models/StaticStore/Plan.cs @@ -2,64 +2,84 @@ namespace Bit.Core.Models.StaticStore; -public class Plan +public abstract record Plan { - public PlanType Type { get; set; } - public ProductType Product { get; set; } - public string Name { get; set; } - public bool IsAnnual { get; set; } - public string NameLocalizationKey { get; set; } - public string DescriptionLocalizationKey { get; set; } - public bool CanBeUsedByBusiness { get; set; } - public int BaseSeats { get; set; } - 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 PlanType Type { get; protected init; } + public ProductType Product { get; protected init; } + public string Name { get; protected init; } + public bool IsAnnual { get; protected init; } + public string NameLocalizationKey { get; protected init; } + public string DescriptionLocalizationKey { get; protected init; } + public bool CanBeUsedByBusiness { get; protected init; } + public int? TrialPeriodDays { get; protected init; } + public bool HasSelfHost { get; protected init; } + public bool HasPolicies { get; protected init; } + public bool HasGroups { get; protected init; } + public bool HasDirectory { get; protected init; } + public bool HasEvents { get; protected init; } + public bool HasTotp { get; protected init; } + public bool Has2fa { get; protected init; } + public bool HasApi { get; protected init; } + public bool HasSso { get; protected init; } + public bool HasKeyConnector { get; protected init; } + public bool HasScim { get; protected init; } + public bool HasResetPassword { get; protected init; } + public bool UsersGetPremium { get; protected init; } + public bool HasCustomPermissions { get; protected init; } + public int UpgradeSortOrder { get; protected init; } + public int DisplaySortOrder { get; protected init; } + public int? LegacyYear { get; protected init; } + public bool Disabled { get; protected init; } + public PasswordManagerPlanFeatures PasswordManager { get; protected init; } + public SecretsManagerPlanFeatures SecretsManager { get; protected init; } + public bool SupportsSecretsManager => SecretsManager != null; - public bool AllowServiceAccountsAutoscale { get; set; } + public record SecretsManagerPlanFeatures + { + // Service accounts + public short? MaxServiceAccounts { get; init; } + public bool AllowServiceAccountsAutoscale { get; init; } + public string StripeServiceAccountPlanId { get; init; } + public decimal? AdditionalPricePerServiceAccount { get; init; } + public short BaseServiceAccount { get; init; } + public short? MaxAdditionalServiceAccount { get; init; } + public bool HasAdditionalServiceAccountOption { get; init; } + // Seats + public string StripeSeatPlanId { get; init; } + public bool HasAdditionalSeatsOption { get; init; } + public decimal BasePrice { get; init; } + public decimal SeatPrice { get; init; } + public int BaseSeats { get; init; } + public short? MaxSeats { get; init; } + public int? MaxAdditionalSeats { get; init; } + public bool AllowSeatAutoscale { get; init; } - public bool HasAdditionalSeatsOption { get; set; } - public int? MaxAdditionalSeats { get; set; } - public bool HasAdditionalStorageOption { get; set; } - public short? MaxAdditionalStorage { get; set; } - public bool HasPremiumAccessOption { get; set; } - public int? TrialPeriodDays { get; set; } + // Features + public int MaxProjects { get; init; } + } - public bool HasSelfHost { get; set; } - public bool HasPolicies { get; set; } - public bool HasGroups { get; set; } - public bool HasDirectory { get; set; } - public bool HasEvents { get; set; } - public bool HasTotp { get; set; } - public bool Has2fa { get; set; } - public bool HasApi { get; set; } - public bool HasSso { get; set; } - public bool HasKeyConnector { get; set; } - public bool HasScim { get; set; } - public bool HasResetPassword { get; set; } - public bool UsersGetPremium { get; set; } - public bool HasCustomPermissions { get; set; } - - public int UpgradeSortOrder { get; set; } - public int DisplaySortOrder { get; set; } - public int? LegacyYear { get; set; } - public bool Disabled { get; set; } - - public string StripePlanId { get; set; } - public string StripeSeatPlanId { get; set; } - public string StripeStoragePlanId { get; set; } - public string StripeServiceAccountPlanId { get; set; } - public string StripePremiumAccessPlanId { get; set; } - public decimal BasePrice { get; set; } - public decimal SeatPrice { get; set; } - public decimal AdditionalStoragePricePerGb { get; set; } - public decimal PremiumAccessOptionPrice { get; set; } - public decimal? AdditionalPricePerServiceAccount { get; set; } - public short? BaseServiceAccount { get; set; } - public short? MaxAdditionalServiceAccount { get; set; } - public bool HasAdditionalServiceAccountOption { get; set; } - public short? MaxProjects { get; set; } - public BitwardenProductType BitwardenProduct { get; set; } + public record PasswordManagerPlanFeatures + { + // Seats + public string StripePlanId { get; init; } + public string StripeSeatPlanId { get; init; } + public decimal BasePrice { get; init; } + public decimal SeatPrice { get; init; } + public bool AllowSeatAutoscale { get; init; } + public bool HasAdditionalSeatsOption { get; init; } + public int? MaxAdditionalSeats { get; init; } + public int BaseSeats { get; init; } + public bool HasPremiumAccessOption { get; init; } + public string StripePremiumAccessPlanId { get; init; } + public decimal PremiumAccessOptionPrice { get; init; } + public short? MaxSeats { get; init; } + // Storage + public short? BaseStorageGb { get; init; } + public bool HasAdditionalStorageOption { get; init; } + public decimal AdditionalStoragePricePerGb { get; init; } + public string StripeStoragePlanId { get; init; } + public short? MaxAdditionalStorage { get; init; } + // Feature + public short? MaxCollections { get; init; } + } } diff --git a/src/Core/Models/StaticStore/Plans/CustomPlan.cs b/src/Core/Models/StaticStore/Plans/CustomPlan.cs new file mode 100644 index 000000000000..77eee781e7c1 --- /dev/null +++ b/src/Core/Models/StaticStore/Plans/CustomPlan.cs @@ -0,0 +1,20 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.StaticStore.Plans; + +public record CustomPlan : Models.StaticStore.Plan +{ + public CustomPlan() + { + Type = PlanType.Custom; + PasswordManager = new CustomPasswordManagerFeatures(); + } + + private record CustomPasswordManagerFeatures : PasswordManagerPlanFeatures + { + public CustomPasswordManagerFeatures() + { + AllowSeatAutoscale = true; + } + } +} diff --git a/src/Core/Models/StaticStore/Plans/Enterprise2019Plan.cs b/src/Core/Models/StaticStore/Plans/Enterprise2019Plan.cs new file mode 100644 index 000000000000..8cbb579ea239 --- /dev/null +++ b/src/Core/Models/StaticStore/Plans/Enterprise2019Plan.cs @@ -0,0 +1,65 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.StaticStore.Plans; + +public record Enterprise2019Plan : Models.StaticStore.Plan +{ + public Enterprise2019Plan(bool isAnnual) + { + Type = isAnnual ? PlanType.EnterpriseAnnually2019 : PlanType.EnterpriseMonthly2019; + Product = ProductType.Enterprise; + Name = isAnnual ? "Enterprise (Annually) 2019" : "Enterprise (Monthly) 2019"; + IsAnnual = isAnnual; + NameLocalizationKey = "planNameEnterprise"; + DescriptionLocalizationKey = "planDescEnterprise"; + CanBeUsedByBusiness = true; + + TrialPeriodDays = 7; + + HasPolicies = true; + HasSelfHost = true; + HasGroups = true; + HasDirectory = true; + HasEvents = true; + HasTotp = true; + Has2fa = true; + HasApi = true; + UsersGetPremium = true; + HasCustomPermissions = true; + + UpgradeSortOrder = 3; + DisplaySortOrder = 3; + LegacyYear = 2020; + + PasswordManager = new Enterprise2019PasswordManagerFeatures(isAnnual); + } + + private record Enterprise2019PasswordManagerFeatures : PasswordManagerPlanFeatures + { + public Enterprise2019PasswordManagerFeatures(bool isAnnual) + { + BaseSeats = 0; + BaseStorageGb = 1; + + HasAdditionalStorageOption = true; + HasAdditionalSeatsOption = true; + + AllowSeatAutoscale = true; + + if (isAnnual) + { + StripeStoragePlanId = "storage-gb-annually"; + StripeSeatPlanId = "enterprise-org-seat-annually"; + SeatPrice = 36; + AdditionalStoragePricePerGb = 4; + } + else + { + StripeSeatPlanId = "enterprise-org-seat-monthly"; + StripeStoragePlanId = "storage-gb-monthly"; + SeatPrice = 4M; + AdditionalStoragePricePerGb = 0.5M; + } + } + } +} diff --git a/src/Core/Models/StaticStore/Plans/EnterprisePlan.cs b/src/Core/Models/StaticStore/Plans/EnterprisePlan.cs new file mode 100644 index 000000000000..fbb319883916 --- /dev/null +++ b/src/Core/Models/StaticStore/Plans/EnterprisePlan.cs @@ -0,0 +1,100 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.StaticStore.Plans; + +public record EnterprisePlan : Models.StaticStore.Plan +{ + public EnterprisePlan(bool isAnnual) + { + Type = isAnnual ? PlanType.EnterpriseAnnually : PlanType.EnterpriseMonthly; + Product = ProductType.Enterprise; + Name = isAnnual ? "Enterprise (Annually)" : "Enterprise (Monthly)"; + IsAnnual = isAnnual; + NameLocalizationKey = "planNameEnterprise"; + DescriptionLocalizationKey = "planDescEnterprise"; + CanBeUsedByBusiness = true; + + TrialPeriodDays = 7; + + HasPolicies = true; + HasSelfHost = true; + HasGroups = true; + HasDirectory = true; + HasEvents = true; + HasTotp = true; + Has2fa = true; + HasApi = true; + HasSso = true; + HasKeyConnector = true; + HasScim = true; + HasResetPassword = true; + UsersGetPremium = true; + HasCustomPermissions = true; + + UpgradeSortOrder = 3; + DisplaySortOrder = 3; + + PasswordManager = new EnterprisePasswordManagerFeatures(isAnnual); + SecretsManager = new EnterpriseSecretsManagerFeatures(isAnnual); + } + + private record EnterpriseSecretsManagerFeatures : SecretsManagerPlanFeatures + { + public EnterpriseSecretsManagerFeatures(bool isAnnual) + { + BaseSeats = 0; + BasePrice = 0; + BaseServiceAccount = 200; + + HasAdditionalSeatsOption = true; + HasAdditionalServiceAccountOption = true; + + AllowSeatAutoscale = true; + AllowServiceAccountsAutoscale = true; + + if (isAnnual) + { + StripeSeatPlanId = "secrets-manager-enterprise-seat-annually"; + StripeServiceAccountPlanId = "secrets-manager-service-account-annually"; + SeatPrice = 144; + AdditionalPricePerServiceAccount = 6; + } + else + { + StripeSeatPlanId = "secrets-manager-enterprise-seat-monthly"; + StripeServiceAccountPlanId = "secrets-manager-service-account-monthly"; + SeatPrice = 13; + AdditionalPricePerServiceAccount = 0.5M; + } + } + } + + private record EnterprisePasswordManagerFeatures : PasswordManagerPlanFeatures + { + public EnterprisePasswordManagerFeatures(bool isAnnual) + { + BaseSeats = 0; + BaseStorageGb = 1; + + HasAdditionalStorageOption = true; + HasAdditionalSeatsOption = true; + + AllowSeatAutoscale = true; + + if (isAnnual) + { + AdditionalStoragePricePerGb = 4; + StripeStoragePlanId = "storage-gb-annually"; + StripeSeatPlanId = "2020-enterprise-org-seat-annually"; + SeatPrice = 60; + } + else + { + StripeSeatPlanId = "2020-enterprise-seat-monthly"; + StripeStoragePlanId = "storage-gb-monthly"; + SeatPrice = 6; + AdditionalStoragePricePerGb = 0.5M; + } + } + } +} diff --git a/src/Core/Models/StaticStore/Plans/Families2019Plan.cs b/src/Core/Models/StaticStore/Plans/Families2019Plan.cs new file mode 100644 index 000000000000..14ddb3405bae --- /dev/null +++ b/src/Core/Models/StaticStore/Plans/Families2019Plan.cs @@ -0,0 +1,49 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.StaticStore.Plans; + +public record Families2019Plan : Models.StaticStore.Plan +{ + public Families2019Plan() + { + Type = PlanType.FamiliesAnnually2019; + Product = ProductType.Families; + Name = "Families 2019"; + IsAnnual = true; + NameLocalizationKey = "planNameFamilies"; + DescriptionLocalizationKey = "planDescFamilies"; + + TrialPeriodDays = 7; + + HasSelfHost = true; + HasTotp = true; + + UpgradeSortOrder = 1; + DisplaySortOrder = 1; + LegacyYear = 2020; + + PasswordManager = new Families2019PasswordManagerFeatures(); + } + + private record Families2019PasswordManagerFeatures : PasswordManagerPlanFeatures + { + public Families2019PasswordManagerFeatures() + { + BaseSeats = 5; + BaseStorageGb = 1; + MaxSeats = 5; + + HasAdditionalStorageOption = true; + HasPremiumAccessOption = true; + + StripePlanId = "personal-org-annually"; + StripeStoragePlanId = "storage-gb-annually"; + StripePremiumAccessPlanId = "personal-org-premium-access-annually"; + BasePrice = 12; + AdditionalStoragePricePerGb = 4; + PremiumAccessOptionPrice = 40; + + AllowSeatAutoscale = false; + } + } +} diff --git a/src/Core/Models/StaticStore/Plans/FamiliesPlan.cs b/src/Core/Models/StaticStore/Plans/FamiliesPlan.cs new file mode 100644 index 000000000000..9a6e90cf00e3 --- /dev/null +++ b/src/Core/Models/StaticStore/Plans/FamiliesPlan.cs @@ -0,0 +1,46 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.StaticStore.Plans; + +public record FamiliesPlan : Models.StaticStore.Plan +{ + public FamiliesPlan() + { + Type = PlanType.FamiliesAnnually; + Product = ProductType.Families; + Name = "Families"; + IsAnnual = true; + NameLocalizationKey = "planNameFamilies"; + DescriptionLocalizationKey = "planDescFamilies"; + + TrialPeriodDays = 7; + + HasSelfHost = true; + HasTotp = true; + UsersGetPremium = true; + + UpgradeSortOrder = 1; + DisplaySortOrder = 1; + + PasswordManager = new TeamsPasswordManagerFeatures(); + } + + private record TeamsPasswordManagerFeatures : PasswordManagerPlanFeatures + { + public TeamsPasswordManagerFeatures() + { + BaseSeats = 6; + BaseStorageGb = 1; + MaxSeats = 6; + + HasAdditionalStorageOption = true; + + StripePlanId = "2020-families-org-annually"; + StripeStoragePlanId = "storage-gb-annually"; + BasePrice = 40; + AdditionalStoragePricePerGb = 4; + + AllowSeatAutoscale = false; + } + } +} diff --git a/src/Core/Models/StaticStore/Plans/FreePlan.cs b/src/Core/Models/StaticStore/Plans/FreePlan.cs new file mode 100644 index 000000000000..2b647f91e26d --- /dev/null +++ b/src/Core/Models/StaticStore/Plans/FreePlan.cs @@ -0,0 +1,47 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.StaticStore.Plans; + +public record FreePlan : Models.StaticStore.Plan +{ + public FreePlan() + { + Type = PlanType.Free; + Product = ProductType.Free; + Name = "Free"; + NameLocalizationKey = "planNameFree"; + DescriptionLocalizationKey = "planDescFree"; + + UpgradeSortOrder = -1; // Always the lowest plan, cannot be upgraded to + DisplaySortOrder = -1; + + PasswordManager = new FreePasswordManagerFeatures(); + SecretsManager = new FreeSecretsManagerFeatures(); + } + + private record FreeSecretsManagerFeatures : SecretsManagerPlanFeatures + { + public FreeSecretsManagerFeatures() + { + BaseSeats = 2; + BaseServiceAccount = 3; + MaxProjects = 3; + MaxSeats = 2; + MaxServiceAccounts = 3; + + AllowSeatAutoscale = false; + } + } + + private record FreePasswordManagerFeatures : PasswordManagerPlanFeatures + { + public FreePasswordManagerFeatures() + { + BaseSeats = 2; + MaxCollections = 2; + MaxSeats = 2; + + AllowSeatAutoscale = false; + } + } +} diff --git a/src/Core/Models/StaticStore/Plans/Teams2019Plan.cs b/src/Core/Models/StaticStore/Plans/Teams2019Plan.cs new file mode 100644 index 000000000000..f806f377354d --- /dev/null +++ b/src/Core/Models/StaticStore/Plans/Teams2019Plan.cs @@ -0,0 +1,60 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.StaticStore.Plans; + +public record Teams2019Plan : Models.StaticStore.Plan +{ + public Teams2019Plan(bool isAnnual) + { + Type = isAnnual ? PlanType.TeamsAnnually2019 : PlanType.TeamsMonthly2019; + Product = ProductType.Teams; + Name = isAnnual ? "Teams (Annually) 2019" : "Teams (Monthly) 2019"; + IsAnnual = isAnnual; + NameLocalizationKey = "planNameTeams"; + DescriptionLocalizationKey = "planDescTeams"; + CanBeUsedByBusiness = true; + + TrialPeriodDays = 7; + + HasTotp = true; + + UpgradeSortOrder = 2; + DisplaySortOrder = 2; + LegacyYear = 2020; + + PasswordManager = new Teams2019PasswordManagerFeatures(isAnnual); + } + + private record Teams2019PasswordManagerFeatures : PasswordManagerPlanFeatures + { + public Teams2019PasswordManagerFeatures(bool isAnnual) + { + BaseSeats = 5; + BaseStorageGb = 1; + + HasAdditionalStorageOption = true; + HasAdditionalSeatsOption = true; + + AllowSeatAutoscale = true; + + if (isAnnual) + { + StripePlanId = "teams-org-annually"; + StripeStoragePlanId = "storage-gb-annually"; + StripeSeatPlanId = "teams-org-seat-annually"; + SeatPrice = 24; + BasePrice = 60; + AdditionalStoragePricePerGb = 4; + } + else + { + StripePlanId = "teams-org-monthly"; + StripeSeatPlanId = "teams-org-seat-monthly"; + StripeStoragePlanId = "storage-gb-monthly"; + BasePrice = 8; + SeatPrice = 2.5M; + AdditionalStoragePricePerGb = 0.5M; + } + } + } +} diff --git a/src/Core/Models/StaticStore/Plans/TeamsPlan.cs b/src/Core/Models/StaticStore/Plans/TeamsPlan.cs new file mode 100644 index 000000000000..0e325242e3f1 --- /dev/null +++ b/src/Core/Models/StaticStore/Plans/TeamsPlan.cs @@ -0,0 +1,94 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.StaticStore.Plans; + +public record TeamsPlan : Models.StaticStore.Plan +{ + public TeamsPlan(bool isAnnual) + { + Type = isAnnual ? PlanType.TeamsAnnually : PlanType.TeamsMonthly; + Product = ProductType.Teams; + Name = isAnnual ? "Teams (Annually)" : "Teams (Monthly)"; + IsAnnual = isAnnual; + NameLocalizationKey = "planNameTeams"; + DescriptionLocalizationKey = "planDescTeams"; + CanBeUsedByBusiness = true; + + TrialPeriodDays = 7; + + HasGroups = true; + HasDirectory = true; + HasEvents = true; + HasTotp = true; + Has2fa = true; + HasApi = true; + UsersGetPremium = true; + + UpgradeSortOrder = 2; + DisplaySortOrder = 2; + + PasswordManager = new TeamsPasswordManagerFeatures(isAnnual); + SecretsManager = new TeamsSecretsManagerFeatures(isAnnual); + } + + private record TeamsSecretsManagerFeatures : SecretsManagerPlanFeatures + { + public TeamsSecretsManagerFeatures(bool isAnnual) + { + BaseSeats = 0; + BasePrice = 0; + BaseServiceAccount = 50; + + HasAdditionalSeatsOption = true; + HasAdditionalServiceAccountOption = true; + + AllowSeatAutoscale = true; + AllowServiceAccountsAutoscale = true; + + if (isAnnual) + { + StripeSeatPlanId = "secrets-manager-teams-seat-annually"; + StripeServiceAccountPlanId = "secrets-manager-service-account-annually"; + SeatPrice = 72; + AdditionalPricePerServiceAccount = 6; + } + else + { + StripeSeatPlanId = "secrets-manager-teams-seat-monthly"; + StripeServiceAccountPlanId = "secrets-manager-service-account-monthly"; + SeatPrice = 7; + AdditionalPricePerServiceAccount = 0.5M; + } + } + } + + private record TeamsPasswordManagerFeatures : PasswordManagerPlanFeatures + { + public TeamsPasswordManagerFeatures(bool isAnnual) + { + BaseSeats = 0; + BaseStorageGb = 1; + BasePrice = 0; + + HasAdditionalStorageOption = true; + HasAdditionalSeatsOption = true; + + AllowSeatAutoscale = true; + + if (isAnnual) + { + StripeStoragePlanId = "storage-gb-annually"; + StripeSeatPlanId = "2020-teams-org-seat-annually"; + SeatPrice = 36; + AdditionalStoragePricePerGb = 4; + } + else + { + StripeSeatPlanId = "2020-teams-org-seat-monthly"; + StripeStoragePlanId = "storage-gb-monthly"; + SeatPrice = 4; + AdditionalStoragePricePerGb = 0.5M; + } + } + } +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommand.cs b/src/Core/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommand.cs index c420609130ee..f0162f7a50eb 100644 --- a/src/Core/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommand.cs @@ -1,5 +1,5 @@ -using Bit.Core.Enums; -using Bit.Core.Exceptions; +using Bit.Core.Entities; +using Bit.Core.Enums; using Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; @@ -18,15 +18,9 @@ public DeleteOrganizationDomainCommand(IOrganizationDomainRepository organizatio _eventService = eventService; } - public async Task DeleteAsync(Guid id) + public async Task DeleteAsync(OrganizationDomain organizationDomain) { - var domain = await _organizationDomainRepository.GetByIdAsync(id); - if (domain is null) - { - throw new NotFoundException(); - } - - await _organizationDomainRepository.DeleteAsync(domain); - await _eventService.LogOrganizationDomainEventAsync(domain, EventType.OrganizationDomain_Removed); + await _organizationDomainRepository.DeleteAsync(organizationDomain); + await _eventService.LogOrganizationDomainEventAsync(organizationDomain, EventType.OrganizationDomain_Removed); } } diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQuery.cs b/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdOrganizationIdQuery.cs similarity index 52% rename from src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQuery.cs rename to src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdOrganizationIdQuery.cs index 8037fa8ec2b0..1ed22076850f 100644 --- a/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQuery.cs +++ b/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdOrganizationIdQuery.cs @@ -4,15 +4,15 @@ namespace Bit.Core.OrganizationFeatures.OrganizationDomains; -public class GetOrganizationDomainByIdQuery : IGetOrganizationDomainByIdQuery +public class GetOrganizationDomainByIdOrganizationIdQuery : IGetOrganizationDomainByIdOrganizationIdQuery { private readonly IOrganizationDomainRepository _organizationDomainRepository; - public GetOrganizationDomainByIdQuery(IOrganizationDomainRepository organizationDomainRepository) + public GetOrganizationDomainByIdOrganizationIdQuery(IOrganizationDomainRepository organizationDomainRepository) { _organizationDomainRepository = organizationDomainRepository; } - public async Task GetOrganizationDomainById(Guid id) - => await _organizationDomainRepository.GetByIdAsync(id); + public async Task GetOrganizationDomainByIdOrganizationIdAsync(Guid id, Guid organizationId) + => await _organizationDomainRepository.GetDomainByIdOrganizationIdAsync(id, organizationId); } diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQuery.cs b/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQuery.cs index 6b94dbf173ed..c1fef05088d2 100644 --- a/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQuery.cs +++ b/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQuery.cs @@ -13,6 +13,6 @@ public GetOrganizationDomainByOrganizationIdQuery(IOrganizationDomainRepository _organizationDomainRepository = organizationDomainRepository; } - public async Task> GetDomainsByOrganizationId(Guid orgId) + public async Task> GetDomainsByOrganizationIdAsync(Guid orgId) => await _organizationDomainRepository.GetDomainsByOrganizationIdAsync(orgId); } diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IDeleteOrganizationDomainCommand.cs b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IDeleteOrganizationDomainCommand.cs index 4a5cc1c556e1..8b11bc61ed00 100644 --- a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IDeleteOrganizationDomainCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IDeleteOrganizationDomainCommand.cs @@ -1,6 +1,8 @@ -namespace Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; +using Bit.Core.Entities; + +namespace Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; public interface IDeleteOrganizationDomainCommand { - Task DeleteAsync(Guid id); + Task DeleteAsync(OrganizationDomain organizationDomain); } diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByIdOrganizationIdQuery.cs b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByIdOrganizationIdQuery.cs new file mode 100644 index 000000000000..12fe643a6b7e --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByIdOrganizationIdQuery.cs @@ -0,0 +1,8 @@ +using Bit.Core.Entities; + +namespace Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; + +public interface IGetOrganizationDomainByIdOrganizationIdQuery +{ + Task GetOrganizationDomainByIdOrganizationIdAsync(Guid id, Guid organizationId); +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByIdQuery.cs b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByIdQuery.cs deleted file mode 100644 index 765007f42f00..000000000000 --- a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByIdQuery.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Bit.Core.Entities; - -namespace Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; - -public interface IGetOrganizationDomainByIdQuery -{ - Task GetOrganizationDomainById(Guid id); -} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByOrganizationIdQuery.cs b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByOrganizationIdQuery.cs index 1377cb48fbe6..d1fb642a5392 100644 --- a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByOrganizationIdQuery.cs +++ b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByOrganizationIdQuery.cs @@ -4,5 +4,5 @@ namespace Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; public interface IGetOrganizationDomainByOrganizationIdQuery { - Task> GetDomainsByOrganizationId(Guid orgId); + Task> GetDomainsByOrganizationIdAsync(Guid orgId); } diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IVerifyOrganizationDomainCommand.cs b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IVerifyOrganizationDomainCommand.cs index 1d070cf3c12c..fe4c38aa8712 100644 --- a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IVerifyOrganizationDomainCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IVerifyOrganizationDomainCommand.cs @@ -4,5 +4,5 @@ namespace Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; public interface IVerifyOrganizationDomainCommand { - Task VerifyOrganizationDomain(Guid id); + Task VerifyOrganizationDomainAsync(OrganizationDomain organizationDomain); } diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs b/src/Core/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs index cfa4ab1481d3..508a085f61e3 100644 --- a/src/Core/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs @@ -27,14 +27,8 @@ public VerifyOrganizationDomainCommand( _logger = logger; } - public async Task VerifyOrganizationDomain(Guid id) + public async Task VerifyOrganizationDomainAsync(OrganizationDomain domain) { - var domain = await _organizationDomainRepository.GetByIdAsync(id); - if (domain is null) - { - throw new NotFoundException(); - } - if (domain.VerifiedDate is not null) { domain.SetLastCheckedDate(); diff --git a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs index 1756149236a3..b7bd08db83f8 100644 --- a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs @@ -1,8 +1,8 @@ using Bit.Core.AdminConsole.OrganizationAuth; using Bit.Core.AdminConsole.OrganizationAuth.Interfaces; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; using Bit.Core.Models.Business.Tokenables; -using Bit.Core.OrganizationFeatures.Groups; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationApiKeys; using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationCollections; @@ -118,7 +118,7 @@ private static void AddOrganizationDomainCommandsQueries(this IServiceCollection { services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); } diff --git a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/CloudSyncSponsorshipsCommand.cs b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/CloudSyncSponsorshipsCommand.cs index 5c0f1474abd7..d0569278bbaa 100644 --- a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/CloudSyncSponsorshipsCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/CloudSyncSponsorshipsCommand.cs @@ -54,7 +54,7 @@ await DoSyncAsync(sponsoringOrg, sponsorshipsData) : { var requiredSponsoringProductType = StaticStore.GetSponsoredPlan(selfHostedSponsorship.PlanSponsorshipType)?.SponsoringProductType; if (requiredSponsoringProductType == null - || StaticStore.GetPasswordManagerPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value) + || StaticStore.GetPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value) { continue; // prevent unsupported sponsorships } diff --git a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/SetUpSponsorshipCommand.cs b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/SetUpSponsorshipCommand.cs index 81a8bac96632..9230e7d13dbd 100644 --- a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/SetUpSponsorshipCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/SetUpSponsorshipCommand.cs @@ -51,7 +51,7 @@ public async Task SetUpSponsorshipAsync(OrganizationSponsorship sponsorship, var requiredSponsoredProductType = StaticStore.GetSponsoredPlan(sponsorship.PlanSponsorshipType.Value)?.SponsoredProductType; if (requiredSponsoredProductType == null || sponsoredOrganization == null || - StaticStore.GetPasswordManagerPlan(sponsoredOrganization.PlanType).Product != requiredSponsoredProductType.Value) + StaticStore.GetPlan(sponsoredOrganization.PlanType).Product != requiredSponsoredProductType.Value) { throw new BadRequestException("Can only redeem sponsorship offer on families organizations."); } diff --git a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/ValidateSponsorshipCommand.cs b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/ValidateSponsorshipCommand.cs index af2f0af65d41..3f2d7af5ebd5 100644 --- a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/ValidateSponsorshipCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/Cloud/ValidateSponsorshipCommand.cs @@ -56,7 +56,7 @@ public async Task ValidateSponsorshipAsync(Guid sponsoredOrganizationId) return false; } - var sponsoringOrgPlan = Utilities.StaticStore.GetPasswordManagerPlan(sponsoringOrganization.PlanType); + var sponsoringOrgPlan = Utilities.StaticStore.GetPlan(sponsoringOrganization.PlanType); if (OrgDisabledForMoreThanGracePeriod(sponsoringOrganization) || sponsoredPlan.SponsoringProductType != sponsoringOrgPlan.Product || existingSponsorship.ToDelete || diff --git a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/CreateSponsorshipCommand.cs b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/CreateSponsorshipCommand.cs index bfc33ebe879a..69e6c3232c45 100644 --- a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/CreateSponsorshipCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/CreateSponsorshipCommand.cs @@ -32,7 +32,7 @@ public async Task CreateSponsorshipAsync(Organization s var requiredSponsoringProductType = StaticStore.GetSponsoredPlan(sponsorshipType)?.SponsoringProductType; if (requiredSponsoringProductType == null || sponsoringOrg == null || - StaticStore.GetPasswordManagerPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value) + StaticStore.GetPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value) { throw new BadRequestException("Specified Organization cannot sponsor other organizations."); } diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs index 3741148af450..951246bd25e8 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/AddSecretsManagerSubscriptionCommand.cs @@ -1,4 +1,6 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Business; @@ -12,19 +14,23 @@ public class AddSecretsManagerSubscriptionCommand : IAddSecretsManagerSubscripti { private readonly IPaymentService _paymentService; private readonly IOrganizationService _organizationService; + private readonly IProviderRepository _providerRepository; + public AddSecretsManagerSubscriptionCommand( IPaymentService paymentService, - IOrganizationService organizationService) + IOrganizationService organizationService, + IProviderRepository providerRepository) { _paymentService = paymentService; _organizationService = organizationService; + _providerRepository = providerRepository; } public async Task SignUpAsync(Organization organization, int additionalSmSeats, int additionalServiceAccounts) { - ValidateOrganization(organization); + await ValidateOrganization(organization); - var plan = StaticStore.GetSecretsManagerPlan(organization.PlanType); + var plan = StaticStore.GetPlan(organization.PlanType); var signup = SetOrganizationUpgrade(organization, additionalSmSeats, additionalServiceAccounts); _organizationService.ValidateSecretsManagerPlan(plan, signup); @@ -33,8 +39,8 @@ public async Task SignUpAsync(Organization organization, int additionalSmSeats, await _paymentService.AddSecretsManagerToSubscription(organization, plan, additionalSmSeats, additionalServiceAccounts); } - organization.SmSeats = plan.BaseSeats + additionalSmSeats; - organization.SmServiceAccounts = plan.BaseServiceAccount.GetValueOrDefault() + additionalServiceAccounts; + organization.SmSeats = plan.SecretsManager.BaseSeats + additionalSmSeats; + organization.SmServiceAccounts = plan.SecretsManager.BaseServiceAccount + additionalServiceAccounts; organization.UseSecretsManager = true; await _organizationService.ReplaceAndUpdateCacheAsync(organization); @@ -55,7 +61,7 @@ private static OrganizationUpgrade SetOrganizationUpgrade(Organization organizat return signup; } - private static void ValidateOrganization(Organization organization) + private async Task ValidateOrganization(Organization organization) { if (organization == null) { @@ -73,7 +79,7 @@ private static void ValidateOrganization(Organization organization) throw new BadRequestException("Organization already uses Secrets Manager."); } - var plan = StaticStore.GetSecretsManagerPlan(organization.PlanType); + var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType && p.SupportsSecretsManager); if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId) && plan.Product != ProductType.Free) { throw new BadRequestException("No payment method found."); @@ -83,5 +89,12 @@ private static void ValidateOrganization(Organization organization) { throw new BadRequestException("No subscription found."); } + + var provider = await _providerRepository.GetByOrganizationIdAsync(organization.Id); + if (provider is { Type: ProviderType.Msp }) + { + throw new BadRequestException( + "Organizations with a Managed Service Provider do not support Secrets Manager."); + } } } diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs index ab96e55f700a..30ccbe57893d 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpdateSecretsManagerSubscriptionCommand.cs @@ -66,7 +66,7 @@ private async Task FinalizeSubscriptionAdjustmentAsync(SecretsManagerSubscriptio { if (update.SmSeatsChanged) { - await _paymentService.AdjustSeatsAsync(update.Organization, update.Plan, update.SmSeatsExcludingBase, update.ProrationDate); + await _paymentService.AdjustSmSeatsAsync(update.Organization, update.Plan, update.SmSeatsExcludingBase, update.ProrationDate); // TODO: call ReferenceEventService - see AC-1481 } @@ -205,10 +205,10 @@ private async Task ValidateSmSeatsUpdateAsync(SecretsManagerSubscriptionUpdate u } // Check plan maximum seats - if (!plan.HasAdditionalSeatsOption || - (plan.MaxAdditionalSeats.HasValue && update.SmSeatsExcludingBase > plan.MaxAdditionalSeats.Value)) + if (!plan.SecretsManager.HasAdditionalSeatsOption || + (plan.SecretsManager.MaxAdditionalSeats.HasValue && update.SmSeatsExcludingBase > plan.SecretsManager.MaxAdditionalSeats.Value)) { - var planMaxSeats = plan.BaseSeats + plan.MaxAdditionalSeats.GetValueOrDefault(); + var planMaxSeats = plan.SecretsManager.BaseSeats + plan.SecretsManager.MaxAdditionalSeats.GetValueOrDefault(); throw new BadRequestException($"You have reached the maximum number of Secrets Manager seats ({planMaxSeats}) for this plan."); } @@ -222,9 +222,9 @@ private async Task ValidateSmSeatsUpdateAsync(SecretsManagerSubscriptionUpdate u } // Check minimum seats included with plan - if (plan.BaseSeats > update.SmSeats.Value) + if (plan.SecretsManager.BaseSeats > update.SmSeats.Value) { - throw new BadRequestException($"Plan has a minimum of {plan.BaseSeats} Secrets Manager seats."); + throw new BadRequestException($"Plan has a minimum of {plan.SecretsManager.BaseSeats} Secrets Manager seats."); } // Check minimum seats required by business logic @@ -262,11 +262,11 @@ private async Task ValidateSmServiceAccountsUpdateAsync(SecretsManagerSubscripti } // Check plan maximum service accounts - if (!plan.HasAdditionalServiceAccountOption || - (plan.MaxAdditionalServiceAccount.HasValue && update.SmServiceAccountsExcludingBase > plan.MaxAdditionalServiceAccount.Value)) + if (!plan.SecretsManager.HasAdditionalServiceAccountOption || + (plan.SecretsManager.MaxAdditionalServiceAccount.HasValue && update.SmServiceAccountsExcludingBase > plan.SecretsManager.MaxAdditionalServiceAccount.Value)) { - var planMaxServiceAccounts = plan.BaseServiceAccount.GetValueOrDefault() + - plan.MaxAdditionalServiceAccount.GetValueOrDefault(); + var planMaxServiceAccounts = plan.SecretsManager.BaseServiceAccount + + plan.SecretsManager.MaxAdditionalServiceAccount.GetValueOrDefault(); throw new BadRequestException($"You have reached the maximum number of service accounts ({planMaxServiceAccounts}) for this plan."); } @@ -281,9 +281,9 @@ private async Task ValidateSmServiceAccountsUpdateAsync(SecretsManagerSubscripti } // Check minimum service accounts included with plan - if (plan.BaseServiceAccount.HasValue && plan.BaseServiceAccount.Value > update.SmServiceAccounts.Value) + if (plan.SecretsManager.BaseServiceAccount > update.SmServiceAccounts.Value) { - throw new BadRequestException($"Plan has a minimum of {plan.BaseServiceAccount} service accounts."); + throw new BadRequestException($"Plan has a minimum of {plan.SecretsManager.BaseServiceAccount} service accounts."); } // Check minimum service accounts required by business logic @@ -319,15 +319,15 @@ private void ValidateMaxAutoscaleSmSeatsUpdateAsync(SecretsManagerSubscriptionUp throw new BadRequestException($"Cannot set max Secrets Manager seat autoscaling below current Secrets Manager seat count."); } - if (plan.MaxUsers.HasValue && update.MaxAutoscaleSmSeats.Value > plan.MaxUsers) + if (plan.SecretsManager.MaxSeats.HasValue && update.MaxAutoscaleSmSeats.Value > plan.SecretsManager.MaxSeats) { throw new BadRequestException(string.Concat( - $"Your plan has a Secrets Manager seat limit of {plan.MaxUsers}, ", + $"Your plan has a Secrets Manager seat limit of {plan.SecretsManager.MaxSeats}, ", $"but you have specified a max autoscale count of {update.MaxAutoscaleSmSeats}.", "Reduce your max autoscale count.")); } - if (!plan.AllowSeatAutoscale) + if (!plan.SecretsManager.AllowSeatAutoscale) { throw new BadRequestException("Your plan does not allow Secrets Manager seat autoscaling."); } @@ -349,15 +349,15 @@ private void ValidateMaxAutoscaleSmServiceAccountUpdate(SecretsManagerSubscripti $"Cannot set max service accounts autoscaling below current service accounts count."); } - if (!plan.AllowServiceAccountsAutoscale) + if (!plan.SecretsManager.AllowServiceAccountsAutoscale) { throw new BadRequestException("Your plan does not allow service accounts autoscaling."); } - if (plan.MaxServiceAccounts.HasValue && update.MaxAutoscaleSmServiceAccounts.Value > plan.MaxServiceAccounts) + if (plan.SecretsManager.MaxServiceAccounts.HasValue && update.MaxAutoscaleSmServiceAccounts.Value > plan.SecretsManager.MaxServiceAccounts) { throw new BadRequestException(string.Concat( - $"Your plan has a service account limit of {plan.MaxServiceAccounts}, ", + $"Your plan has a service account limit of {plan.SecretsManager.MaxServiceAccounts}, ", $"but you have specified a max autoscale count of {update.MaxAutoscaleSmServiceAccounts}.", "Reduce your max autoscale count.")); } diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs index dec05ac9e936..b9e3c74da71c 100644 --- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs @@ -1,4 +1,5 @@ using Bit.Core.AdminConsole.Models.OrganizationConnectionConfigs; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Repositories; using Bit.Core.Context; @@ -73,69 +74,67 @@ public async Task> UpgradePlanAsync(Guid organizationId, Org throw new BadRequestException("Your account has no payment method available."); } - var existingPasswordManagerPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); - if (existingPasswordManagerPlan == null) + var existingPlan = StaticStore.GetPlan(organization.PlanType); + if (existingPlan == null) { throw new BadRequestException("Existing plan not found."); } - var newPasswordManagerPlan = - StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled); - if (newPasswordManagerPlan == null) + var newPlan = StaticStore.Plans.FirstOrDefault(p => p.Type == upgrade.Plan && !p.Disabled); + if (newPlan == null) { throw new BadRequestException("Plan not found."); } - if (existingPasswordManagerPlan.Type == newPasswordManagerPlan.Type) + if (existingPlan.Type == newPlan.Type) { throw new BadRequestException("Organization is already on this plan."); } - if (existingPasswordManagerPlan.UpgradeSortOrder >= newPasswordManagerPlan.UpgradeSortOrder) + if (existingPlan.UpgradeSortOrder >= newPlan.UpgradeSortOrder) { throw new BadRequestException("You cannot upgrade to this plan."); } - if (existingPasswordManagerPlan.Type != PlanType.Free) + if (existingPlan.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); + _organizationService.ValidatePasswordManagerPlan(newPlan, upgrade); + if (upgrade.UseSecretsManager) { - _organizationService.ValidateSecretsManagerPlan(newSecretsManagerPlan, upgrade); + _organizationService.ValidateSecretsManagerPlan(newPlan, upgrade); } - var newPasswordManagerPlanSeats = (short)(newPasswordManagerPlan.BaseSeats + - (newPasswordManagerPlan.HasAdditionalSeatsOption ? upgrade.AdditionalSeats : 0)); - if (!organization.Seats.HasValue || organization.Seats.Value > newPasswordManagerPlanSeats) + var updatedPasswordManagerSeats = (short)(newPlan.PasswordManager.BaseSeats + + (newPlan.PasswordManager.HasAdditionalSeatsOption ? upgrade.AdditionalSeats : 0)); + if (!organization.Seats.HasValue || organization.Seats.Value > updatedPasswordManagerSeats) { var occupiedSeats = await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); - if (occupiedSeats > newPasswordManagerPlanSeats) + if (occupiedSeats > updatedPasswordManagerSeats) { throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " + - $"Your new plan only has ({newPasswordManagerPlanSeats}) seats. Remove some users."); + $"Your new plan only has ({updatedPasswordManagerSeats}) seats. Remove some users."); } } - if (newPasswordManagerPlan.MaxCollections.HasValue && (!organization.MaxCollections.HasValue || + if (newPlan.PasswordManager.MaxCollections.HasValue && (!organization.MaxCollections.HasValue || organization.MaxCollections.Value > - newPasswordManagerPlan.MaxCollections.Value)) + newPlan.PasswordManager.MaxCollections.Value)) { var collectionCount = await _collectionRepository.GetCountByOrganizationIdAsync(organization.Id); - if (collectionCount > newPasswordManagerPlan.MaxCollections.Value) + if (collectionCount > newPlan.PasswordManager.MaxCollections.Value) { throw new BadRequestException($"Your organization currently has {collectionCount} collections. " + - $"Your new plan allows for a maximum of ({newPasswordManagerPlan.MaxCollections.Value}) collections. " + + $"Your new plan allows for a maximum of ({newPlan.PasswordManager.MaxCollections.Value}) collections. " + "Remove some collections."); } } - if (!newPasswordManagerPlan.HasGroups && organization.UseGroups) + if (!newPlan.HasGroups && organization.UseGroups) { var groups = await _groupRepository.GetManyByOrganizationIdAsync(organization.Id); if (groups.Any()) @@ -145,7 +144,7 @@ public async Task> UpgradePlanAsync(Guid organizationId, Org } } - if (!newPasswordManagerPlan.HasPolicies && organization.UsePolicies) + if (!newPlan.HasPolicies && organization.UsePolicies) { var policies = await _policyRepository.GetManyByOrganizationIdAsync(organization.Id); if (policies.Any(p => p.Enabled)) @@ -155,7 +154,7 @@ public async Task> UpgradePlanAsync(Guid organizationId, Org } } - if (!newPasswordManagerPlan.HasSso && organization.UseSso) + if (!newPlan.HasSso && organization.UseSso) { var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id); if (ssoConfig != null && ssoConfig.Enabled) @@ -165,7 +164,7 @@ public async Task> UpgradePlanAsync(Guid organizationId, Org } } - if (!newPasswordManagerPlan.HasKeyConnector && organization.UseKeyConnector) + if (!newPlan.HasKeyConnector && organization.UseKeyConnector) { var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id); if (ssoConfig != null && ssoConfig.GetData().MemberDecryptionType == MemberDecryptionType.KeyConnector) @@ -175,7 +174,7 @@ public async Task> UpgradePlanAsync(Guid organizationId, Org } } - if (!newPasswordManagerPlan.HasResetPassword && organization.UseResetPassword) + if (!newPlan.HasResetPassword && organization.UseResetPassword) { var resetPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword); @@ -186,7 +185,7 @@ public async Task> UpgradePlanAsync(Guid organizationId, Org } } - if (!newPasswordManagerPlan.HasScim && organization.UseScim) + if (!newPlan.HasScim && organization.UseScim) { var scimConnections = await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organization.Id, OrganizationConnectionType.Scim); @@ -197,7 +196,7 @@ public async Task> UpgradePlanAsync(Guid organizationId, Org } } - if (!newPasswordManagerPlan.HasCustomPermissions && organization.UseCustomPermissions) + if (!newPlan.HasCustomPermissions && organization.UseCustomPermissions) { var organizationCustomUsers = await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, @@ -209,9 +208,9 @@ await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, } } - if (upgrade.UseSecretsManager && newSecretsManagerPlan != null) + if (upgrade.UseSecretsManager) { - await ValidateSecretsManagerSeatsAndServiceAccountAsync(upgrade, organization, newSecretsManagerPlan); + await ValidateSecretsManagerSeatsAndServiceAccountAsync(upgrade, organization, newPlan); } // TODO: Check storage? @@ -220,12 +219,8 @@ await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, 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); + newPlan, upgrade); success = string.IsNullOrWhiteSpace(paymentIntentClientSecret); } else @@ -235,34 +230,34 @@ await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, } 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 + organization.PlanType = newPlan.Type; + organization.Seats = (short)(newPlan.PasswordManager.BaseSeats + upgrade.AdditionalSeats); + organization.MaxCollections = newPlan.PasswordManager.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.PasswordManager.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; + : (short)(newPlan.PasswordManager.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.Enabled = success; organization.PublicKey = upgrade.PublicKey; organization.PrivateKey = upgrade.PrivateKey; @@ -271,8 +266,8 @@ await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, if (upgrade.UseSecretsManager) { - organization.SmSeats = newSecretsManagerPlan.BaseSeats + upgrade.AdditionalSmSeats.GetValueOrDefault(); - organization.SmServiceAccounts = newSecretsManagerPlan.BaseServiceAccount.GetValueOrDefault() + + organization.SmSeats = newPlan.SecretsManager.BaseSeats + upgrade.AdditionalSmSeats.GetValueOrDefault(); + organization.SmServiceAccounts = newPlan.SecretsManager.BaseServiceAccount + upgrade.AdditionalServiceAccounts.GetValueOrDefault(); } @@ -283,10 +278,10 @@ await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, await _referenceEventService.RaiseEventAsync( new ReferenceEvent(ReferenceEventType.UpgradePlan, organization, _currentContext) { - PlanName = newPasswordManagerPlan.Name, - PlanType = newPasswordManagerPlan.Type, - OldPlanName = existingPasswordManagerPlan.Name, - OldPlanType = existingPasswordManagerPlan.Type, + PlanName = newPlan.Name, + PlanType = newPlan.Type, + OldPlanName = existingPlan.Name, + OldPlanType = existingPlan.Type, Seats = organization.Seats, Storage = organization.MaxStorageGb, // TODO: add reference events for SmSeats and Service Accounts - see AC-1481 @@ -299,8 +294,8 @@ await _referenceEventService.RaiseEventAsync( private async Task ValidateSecretsManagerSeatsAndServiceAccountAsync(OrganizationUpgrade upgrade, Organization organization, Models.StaticStore.Plan newSecretsManagerPlan) { - var newPlanSmSeats = (short)(newSecretsManagerPlan.BaseSeats + - (newSecretsManagerPlan.HasAdditionalSeatsOption + var newPlanSmSeats = (short)(newSecretsManagerPlan.SecretsManager.BaseSeats + + (newSecretsManagerPlan.SecretsManager.HasAdditionalSeatsOption ? upgrade.AdditionalSmSeats : 0)); var occupiedSmSeats = @@ -316,10 +311,10 @@ private async Task ValidateSecretsManagerSeatsAndServiceAccountAsync(Organizatio } } - var additionalServiceAccounts = newSecretsManagerPlan.HasAdditionalServiceAccountOption + var additionalServiceAccounts = newSecretsManagerPlan.SecretsManager.HasAdditionalServiceAccountOption ? upgrade.AdditionalServiceAccounts : 0; - var newPlanServiceAccounts = newSecretsManagerPlan.BaseServiceAccount + additionalServiceAccounts; + var newPlanServiceAccounts = newSecretsManagerPlan.SecretsManager.BaseServiceAccount + additionalServiceAccounts; if (!organization.SmServiceAccounts.HasValue || organization.SmServiceAccounts.Value > newPlanServiceAccounts) { @@ -329,7 +324,7 @@ private async Task ValidateSecretsManagerSeatsAndServiceAccountAsync(Organizatio { throw new BadRequestException( $"Your organization currently has {currentServiceAccounts} service accounts. " + - $"Your new plan only allows {newSecretsManagerPlan.MaxServiceAccounts} service accounts. " + + $"Your new plan only allows {newSecretsManagerPlan.SecretsManager.MaxServiceAccounts} service accounts. " + "Remove some service accounts or increase your subscription."); } } diff --git a/src/Core/Repositories/IEventRepository.cs b/src/Core/Repositories/IEventRepository.cs index 493c8c787dc0..dda9b589cc23 100644 --- a/src/Core/Repositories/IEventRepository.cs +++ b/src/Core/Repositories/IEventRepository.cs @@ -19,4 +19,6 @@ Task> GetManyByCipherAsync(Cipher cipher, DateTime startDate PageOptions pageOptions); Task CreateAsync(IEvent e); Task CreateManyAsync(IEnumerable e); + Task> GetManyByOrganizationServiceAccountAsync(Guid organizationId, Guid serviceAccountId, + DateTime startDate, DateTime endDate, PageOptions pageOptions); } diff --git a/src/Core/Repositories/IOrganizationConnectionRepository.cs b/src/Core/Repositories/IOrganizationConnectionRepository.cs index a3bdbb0370b7..b480333f634c 100644 --- a/src/Core/Repositories/IOrganizationConnectionRepository.cs +++ b/src/Core/Repositories/IOrganizationConnectionRepository.cs @@ -5,6 +5,7 @@ namespace Bit.Core.Repositories; public interface IOrganizationConnectionRepository : IRepository { + Task GetByIdOrganizationIdAsync(Guid id, Guid organizationId); Task> GetByOrganizationIdTypeAsync(Guid organizationId, OrganizationConnectionType type); Task> GetEnabledByOrganizationIdTypeAsync(Guid organizationId, OrganizationConnectionType type); } diff --git a/src/Core/Repositories/IOrganizationDomainRepository.cs b/src/Core/Repositories/IOrganizationDomainRepository.cs index dfe535854159..1308c4110932 100644 --- a/src/Core/Repositories/IOrganizationDomainRepository.cs +++ b/src/Core/Repositories/IOrganizationDomainRepository.cs @@ -9,6 +9,7 @@ public interface IOrganizationDomainRepository : IRepository> GetDomainsByOrganizationIdAsync(Guid orgId); Task> GetManyByNextRunDateAsync(DateTime date); Task GetOrganizationDomainSsoDetailsAsync(string email); + Task GetDomainByIdOrganizationIdAsync(Guid id, Guid organizationId); Task GetDomainByOrgIdAndDomainNameAsync(Guid orgId, string domainName); Task> GetExpiredOrganizationDomainsAsync(); Task DeleteExpiredAsync(int expirationPeriod); diff --git a/src/Core/Repositories/IOrganizationRepository.cs b/src/Core/Repositories/IOrganizationRepository.cs index 14126adb0aad..4ac518489bde 100644 --- a/src/Core/Repositories/IOrganizationRepository.cs +++ b/src/Core/Repositories/IOrganizationRepository.cs @@ -14,4 +14,5 @@ public interface IOrganizationRepository : IRepository Task GetByLicenseKeyAsync(string licenseKey); Task GetSelfHostedOrganizationDetailsById(Guid id); Task> SearchUnassignedToProviderAsync(string name, string ownerEmail, int skip, int take); + Task> GetOwnerEmailAddressesById(Guid organizationId); } diff --git a/src/Core/Repositories/TableStorage/EventRepository.cs b/src/Core/Repositories/TableStorage/EventRepository.cs index 3822ce35dc02..7044850033a8 100644 --- a/src/Core/Repositories/TableStorage/EventRepository.cs +++ b/src/Core/Repositories/TableStorage/EventRepository.cs @@ -61,6 +61,14 @@ public async Task> GetManyByCipherAsync(Cipher cipher, DateT return await GetManyAsync(partitionKey, $"CipherId={cipher.Id}__Date={{0}}", startDate, endDate, pageOptions); } + public async Task> GetManyByOrganizationServiceAccountAsync(Guid organizationId, + Guid serviceAccountId, DateTime startDate, DateTime endDate, PageOptions pageOptions) + { + + return await GetManyAsync($"OrganizationId={organizationId}", + $"ServiceAccountId={serviceAccountId}__Date={{0}}", startDate, endDate, pageOptions); + } + public async Task CreateAsync(IEvent e) { if (!(e is EventTableEntity entity)) diff --git a/src/Core/SecretsManager/AuthorizationRequirements/ServiceAccountOperationRequirement.cs b/src/Core/SecretsManager/AuthorizationRequirements/ServiceAccountOperationRequirement.cs index 23f312d0b8db..df09cc71015d 100644 --- a/src/Core/SecretsManager/AuthorizationRequirements/ServiceAccountOperationRequirement.cs +++ b/src/Core/SecretsManager/AuthorizationRequirements/ServiceAccountOperationRequirement.cs @@ -15,4 +15,5 @@ public static class ServiceAccountOperations public static readonly ServiceAccountOperationRequirement ReadAccessTokens = new() { Name = nameof(ReadAccessTokens) }; public static readonly ServiceAccountOperationRequirement CreateAccessToken = new() { Name = nameof(CreateAccessToken) }; public static readonly ServiceAccountOperationRequirement RevokeAccessTokens = new() { Name = nameof(RevokeAccessTokens) }; + public static readonly ServiceAccountOperationRequirement ReadEvents = new() { Name = nameof(ReadEvents) }; } diff --git a/src/Core/SecretsManager/Entities/AccessPolicy.cs b/src/Core/SecretsManager/Entities/AccessPolicy.cs index 6de856d8df5b..f450af5574a3 100644 --- a/src/Core/SecretsManager/Entities/AccessPolicy.cs +++ b/src/Core/SecretsManager/Entities/AccessPolicy.cs @@ -1,4 +1,5 @@ #nullable enable +using Bit.Core.AdminConsole.Entities; using Bit.Core.Entities; using Bit.Core.Utilities; diff --git a/src/Core/Services/IApplicationCacheService.cs b/src/Core/Services/IApplicationCacheService.cs index 7c21fac76f6f..0537251af2ef 100644 --- a/src/Core/Services/IApplicationCacheService.cs +++ b/src/Core/Services/IApplicationCacheService.cs @@ -1,6 +1,6 @@ -using Bit.Core.Entities; -using Bit.Core.Entities.Provider; -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.Entities; using Bit.Core.Models.Data.Organizations; namespace Bit.Core.Services; diff --git a/src/Core/Services/IEventService.cs b/src/Core/Services/IEventService.cs index 10d8b6dbd0cb..38d147b50b91 100644 --- a/src/Core/Services/IEventService.cs +++ b/src/Core/Services/IEventService.cs @@ -1,5 +1,6 @@ -using Bit.Core.Entities; -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.SecretsManager.Entities; using Bit.Core.Vault.Entities; diff --git a/src/Core/Services/IMailService.cs b/src/Core/Services/IMailService.cs index 0e5831082f8a..1b673c9fae9f 100644 --- a/src/Core/Services/IMailService.cs +++ b/src/Core/Services/IMailService.cs @@ -1,7 +1,7 @@ -using Bit.Core.Auth.Entities; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.Auth.Entities; using Bit.Core.Auth.Models.Business; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; using Bit.Core.Models.Mail; namespace Bit.Core.Services; @@ -24,7 +24,17 @@ public interface IMailService Task SendOrganizationConfirmedEmailAsync(string organizationName, string email); Task SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(string organizationName, string email); Task SendPasswordlessSignInAsync(string returnUrl, string token, string email); - Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate, List items, + Task SendInvoiceUpcoming( + string email, + decimal amount, + DateTime dueDate, + List items, + bool mentionInvoices); + Task SendInvoiceUpcoming( + IEnumerable email, + decimal amount, + DateTime dueDate, + List items, bool mentionInvoices); Task SendPaymentFailedAsync(string email, decimal amount, bool mentionInvoices); Task SendAddedCreditAsync(string email, decimal amount); diff --git a/src/Core/Services/IOrganizationService.cs b/src/Core/Services/IOrganizationService.cs index 3c48367d5308..044c4f147000 100644 --- a/src/Core/Services/IOrganizationService.cs +++ b/src/Core/Services/IOrganizationService.cs @@ -1,4 +1,5 @@ using System.Security.Claims; +using Bit.Core.AdminConsole.Models.Business; using Bit.Core.Auth.Enums; using Bit.Core.Entities; using Bit.Core.Enums; diff --git a/src/Core/Services/IPaymentService.cs b/src/Core/Services/IPaymentService.cs index 642a423dc47c..82386a1cf23d 100644 --- a/src/Core/Services/IPaymentService.cs +++ b/src/Core/Services/IPaymentService.cs @@ -9,15 +9,16 @@ public interface IPaymentService { Task CancelAndRecoverChargesAsync(ISubscriber subscriber); Task PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType, - string paymentToken, List plans, short additionalStorageGb, int additionalSeats, + string paymentToken, Plan plan, 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, List plans, OrganizationUpgrade upgrade); + Task UpgradeFreeOrganizationAsync(Organization org, Plan plan, 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); + Task AdjustSmSeatsAsync(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, diff --git a/src/Core/Services/Implementations/CollectionService.cs b/src/Core/Services/Implementations/CollectionService.cs index 006c8c5cfcda..b30645f4d1ff 100644 --- a/src/Core/Services/Implementations/CollectionService.cs +++ b/src/Core/Services/Implementations/CollectionService.cs @@ -98,7 +98,13 @@ public async Task DeleteUserAsync(Collection collection, Guid organizationUserId public async Task> GetOrganizationCollectionsAsync(Guid organizationId) { - if (!await _currentContext.ViewAssignedCollections(organizationId) && !await _currentContext.ManageUsers(organizationId) && !await _currentContext.ManageGroups(organizationId) && !await _currentContext.AccessImportExport(organizationId)) + if ( + !await _currentContext.ViewAssignedCollections(organizationId) && + !await _currentContext.ViewAllCollections(organizationId) && + !await _currentContext.ManageUsers(organizationId) && + !await _currentContext.ManageGroups(organizationId) && + !await _currentContext.AccessImportExport(organizationId) + ) { throw new NotFoundException(); } diff --git a/src/Core/Services/Implementations/EventService.cs b/src/Core/Services/Implementations/EventService.cs index d5dec0a10694..a11354c5177d 100644 --- a/src/Core/Services/Implementations/EventService.cs +++ b/src/Core/Services/Implementations/EventService.cs @@ -1,6 +1,9 @@ -using Bit.Core.Context; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Context; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; using Bit.Core.Enums; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations; diff --git a/src/Core/Services/Implementations/HandlebarsMailService.cs b/src/Core/Services/Implementations/HandlebarsMailService.cs index 98ff7df07bd7..0e55407da73f 100644 --- a/src/Core/Services/Implementations/HandlebarsMailService.cs +++ b/src/Core/Services/Implementations/HandlebarsMailService.cs @@ -1,10 +1,10 @@ using System.Net; using System.Reflection; +using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.Auth.Entities; using Bit.Core.Auth.Models.Business; using Bit.Core.Auth.Models.Mail; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; using Bit.Core.Models.Mail; using Bit.Core.Models.Mail.FamiliesForEnterprise; using Bit.Core.Models.Mail.Provider; @@ -285,10 +285,21 @@ public async Task SendPasswordlessSignInAsync(string returnUrl, string token, st await _mailDeliveryService.SendEmailAsync(message); } - public async Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate, - List items, bool mentionInvoices) + public async Task SendInvoiceUpcoming( + string email, + decimal amount, + DateTime dueDate, + List items, + bool mentionInvoices) => await SendInvoiceUpcoming(new List { email }, amount, dueDate, items, mentionInvoices); + + public async Task SendInvoiceUpcoming( + IEnumerable emails, + decimal amount, + DateTime dueDate, + List items, + bool mentionInvoices) { - var message = CreateDefaultMessage("Your Subscription Will Renew Soon", email); + var message = CreateDefaultMessage("Your Subscription Will Renew Soon", emails); var model = new InvoiceUpcomingViewModel { WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash, diff --git a/src/Core/Services/Implementations/InMemoryApplicationCacheService.cs b/src/Core/Services/Implementations/InMemoryApplicationCacheService.cs index dc23fcdb825f..140392ba2b04 100644 --- a/src/Core/Services/Implementations/InMemoryApplicationCacheService.cs +++ b/src/Core/Services/Implementations/InMemoryApplicationCacheService.cs @@ -1,6 +1,7 @@ -using Bit.Core.Entities; -using Bit.Core.Entities.Provider; -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Entities; using Bit.Core.Models.Data.Organizations; using Bit.Core.Repositories; diff --git a/src/Core/Services/Implementations/InMemoryServiceBusApplicationCacheService.cs b/src/Core/Services/Implementations/InMemoryServiceBusApplicationCacheService.cs index 7067efd82cf2..e4ae0331696c 100644 --- a/src/Core/Services/Implementations/InMemoryServiceBusApplicationCacheService.cs +++ b/src/Core/Services/Implementations/InMemoryServiceBusApplicationCacheService.cs @@ -1,4 +1,5 @@ using Azure.Messaging.ServiceBus; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Repositories; diff --git a/src/Core/Services/Implementations/LaunchDarklyFeatureService.cs b/src/Core/Services/Implementations/LaunchDarklyFeatureService.cs index 501db21d8299..1a43967b3446 100644 --- a/src/Core/Services/Implementations/LaunchDarklyFeatureService.cs +++ b/src/Core/Services/Implementations/LaunchDarklyFeatureService.cs @@ -1,5 +1,6 @@ using Bit.Core.Context; using Bit.Core.Settings; +using LaunchDarkly.Logging; using LaunchDarkly.Sdk.Server; using LaunchDarkly.Sdk.Server.Integrations; @@ -14,6 +15,7 @@ public LaunchDarklyFeatureService( IGlobalSettings globalSettings) { var ldConfig = Configuration.Builder(globalSettings.LaunchDarkly?.SdkKey); + ldConfig.Logging(Components.Logging().Level(LogLevel.Error)); if (string.IsNullOrEmpty(globalSettings.LaunchDarkly?.SdkKey)) { diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 10dc2a205608..8063bc0c56eb 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -1,12 +1,15 @@ using System.Security.Claims; using System.Text.Json; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Business; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models.Business; using Bit.Core.Auth.Repositories; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Exceptions; using Bit.Core.Models.Business; using Bit.Core.Models.Data; @@ -175,19 +178,19 @@ public async Task AdjustStorageAsync(Guid organizationId, short storageA throw new NotFoundException(); } - var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); + var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType); if (plan == null) { throw new BadRequestException("Existing plan not found."); } - if (!plan.HasAdditionalStorageOption) + if (!plan.PasswordManager.HasAdditionalStorageOption) { throw new BadRequestException("Plan does not allow additional storage."); } var secret = await BillingHelpers.AdjustStorageAsync(_paymentService, organization, storageAdjustmentGb, - plan.StripeStoragePlanId); + plan.PasswordManager.StripeStoragePlanId); await _referenceEventService.RaiseEventAsync( new ReferenceEvent(ReferenceEventType.AdjustStorage, organization, _currentContext) { @@ -233,21 +236,21 @@ private async Task UpdateAutoscalingAsync(Organization organization, int? maxAut throw new BadRequestException($"Cannot set max seat autoscaling below current seat count."); } - var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); + var plan = StaticStore.GetPlan(organization.PlanType); if (plan == null) { throw new BadRequestException("Existing plan not found."); } - if (!plan.AllowSeatAutoscale) + if (!plan.PasswordManager.AllowSeatAutoscale) { throw new BadRequestException("Your plan does not allow seat autoscaling."); } - if (plan.MaxUsers.HasValue && maxAutoscaleSeats.HasValue && - maxAutoscaleSeats > plan.MaxUsers) + if (plan.PasswordManager.MaxSeats.HasValue && maxAutoscaleSeats.HasValue && + maxAutoscaleSeats > plan.PasswordManager.MaxSeats) { - throw new BadRequestException(string.Concat($"Your plan has a seat limit of {plan.MaxUsers}, ", + throw new BadRequestException(string.Concat($"Your plan has a seat limit of {plan.PasswordManager.MaxSeats}, ", $"but you have specified a max autoscale count of {maxAutoscaleSeats}.", "Reduce your max autoscale seat count.")); } @@ -285,21 +288,21 @@ private async Task AdjustSeatsAsync(Organization organization, int seatA throw new BadRequestException("No subscription found."); } - var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); + var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType); if (plan == null) { throw new BadRequestException("Existing plan not found."); } - if (!plan.HasAdditionalSeatsOption) + if (!plan.PasswordManager.HasAdditionalSeatsOption) { throw new BadRequestException("Plan does not allow additional seats."); } var newSeatTotal = organization.Seats.Value + seatAdjustment; - if (plan.BaseSeats > newSeatTotal) + if (plan.PasswordManager.BaseSeats > newSeatTotal) { - throw new BadRequestException($"Plan has a minimum of {plan.BaseSeats} seats."); + throw new BadRequestException($"Plan has a minimum of {plan.PasswordManager.BaseSeats} seats."); } if (newSeatTotal <= 0) @@ -307,11 +310,11 @@ private async Task AdjustSeatsAsync(Organization organization, int seatA throw new BadRequestException("You must have at least 1 seat."); } - var additionalSeats = newSeatTotal - plan.BaseSeats; - if (plan.MaxAdditionalSeats.HasValue && additionalSeats > plan.MaxAdditionalSeats.Value) + var additionalSeats = newSeatTotal - plan.PasswordManager.BaseSeats; + if (plan.PasswordManager.MaxAdditionalSeats.HasValue && additionalSeats > plan.PasswordManager.MaxAdditionalSeats.Value) { throw new BadRequestException($"Organization plan allows a maximum of " + - $"{plan.MaxAdditionalSeats.Value} additional seats."); + $"{plan.PasswordManager.MaxAdditionalSeats.Value} additional seats."); } if (!organization.Seats.HasValue || organization.Seats.Value > newSeatTotal) @@ -403,14 +406,18 @@ public async Task VerifyBankAsync(Guid organizationId, int amount1, int amount2) public async Task> SignUpAsync(OrganizationSignup signup, bool provider = false) { - var passwordManagerPlan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == signup.Plan); + var plan = StaticStore.GetPlan(signup.Plan); - ValidatePasswordManagerPlan(passwordManagerPlan, signup); + ValidatePasswordManagerPlan(plan, signup); - var secretsManagerPlan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == signup.Plan); if (signup.UseSecretsManager) { - ValidateSecretsManagerPlan(secretsManagerPlan, signup); + if (provider) + { + throw new BadRequestException( + "Organizations with a Managed Service Provider do not support Secrets Manager."); + } + ValidateSecretsManagerPlan(plan, signup); } if (!provider) @@ -425,25 +432,25 @@ public async Task> SignUpAsync(Organizatio Name = signup.Name, BillingEmail = signup.BillingEmail, BusinessName = signup.BusinessName, - 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, + PlanType = plan!.Type, + Seats = (short)(plan.PasswordManager.BaseSeats + signup.AdditionalSeats), + MaxCollections = plan.PasswordManager.MaxCollections, + MaxStorageGb = !plan.PasswordManager.BaseStorageGb.HasValue ? + (short?)null : (short)(plan.PasswordManager.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, Gateway = null, ReferenceData = signup.Owner.ReferenceData, Enabled = true, @@ -459,12 +466,12 @@ public async Task> SignUpAsync(Organizatio if (signup.UseSecretsManager) { - organization.SmSeats = secretsManagerPlan.BaseSeats + signup.AdditionalSmSeats.GetValueOrDefault(); - organization.SmServiceAccounts = secretsManagerPlan.BaseServiceAccount.GetValueOrDefault() + + organization.SmSeats = plan.SecretsManager.BaseSeats + signup.AdditionalSmSeats.GetValueOrDefault(); + organization.SmServiceAccounts = plan.SecretsManager.BaseServiceAccount + signup.AdditionalServiceAccounts.GetValueOrDefault(); } - if (passwordManagerPlan.Type == PlanType.Free && !provider) + if (plan.Type == PlanType.Free && !provider) { var adminCount = await _organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(signup.Owner.Id); @@ -473,14 +480,10 @@ public async Task> SignUpAsync(Organizatio throw new BadRequestException("You can only be an admin of one free organization."); } } - else if (passwordManagerPlan.Type != PlanType.Free) + else if (plan.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, purchaseOrganizationPlan, signup.AdditionalStorageGb, signup.AdditionalSeats, + signup.PaymentToken, plan, signup.AdditionalStorageGb, signup.AdditionalSeats, signup.PremiumAccessAddon, signup.TaxInfo, provider, signup.AdditionalSmSeats.GetValueOrDefault(), signup.AdditionalServiceAccounts.GetValueOrDefault()); } @@ -490,8 +493,8 @@ await _paymentService.PurchaseOrganizationAsync(organization, signup.PaymentMeth await _referenceEventService.RaiseEventAsync( new ReferenceEvent(ReferenceEventType.Signup, organization, _currentContext) { - PlanName = passwordManagerPlan.Name, - PlanType = passwordManagerPlan.Type, + PlanName = plan.Name, + PlanType = plan.Type, Seats = returnValue.Item1.Seats, Storage = returnValue.Item1.MaxStorageGb, // TODO: add reference events for SmSeats and Service Accounts - see AC-1481 @@ -520,7 +523,7 @@ public async Task> SignUpAsync( } if (license.PlanType != PlanType.Custom && - StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == license.PlanType && !p.Disabled) == null) + StaticStore.Plans.FirstOrDefault(p => p.Type == license.PlanType && !p.Disabled) == null) { throw new BadRequestException("Plan not found."); } @@ -568,7 +571,11 @@ public async Task> SignUpAsync( PrivateKey = privateKey, CreationDate = DateTime.UtcNow, RevisionDate = DateTime.UtcNow, - Status = OrganizationStatusType.Created + Status = OrganizationStatusType.Created, + UsePasswordManager = license.UsePasswordManager, + UseSecretsManager = license.UseSecretsManager, + SmSeats = license.SmSeats, + SmServiceAccounts = license.SmServiceAccounts }; var result = await SignUpAsync(organization, owner.Id, ownerKey, collectionName, false); @@ -1946,11 +1953,6 @@ private static void ValidatePlan(Models.StaticStore.Plan plan, int additionalSea throw new BadRequestException($"{productType} Plan not found."); } - if (plan.BaseSeats + additionalSeats <= 0) - { - throw new BadRequestException($"You do not have any {productType} seats!"); - } - if (additionalSeats < 0) { throw new BadRequestException($"You can't subtract {productType} seats!"); @@ -1961,7 +1963,7 @@ public void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, Organizati { ValidatePlan(plan, upgrade.AdditionalSeats, "Password Manager"); - if (plan.BaseSeats + upgrade.AdditionalSeats <= 0) + if (plan.PasswordManager.BaseSeats + upgrade.AdditionalSeats <= 0) { throw new BadRequestException($"You do not have any Password Manager seats!"); } @@ -1971,7 +1973,7 @@ public void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, Organizati throw new BadRequestException($"You can't subtract Password Manager seats!"); } - if (!plan.HasAdditionalStorageOption && upgrade.AdditionalStorageGb > 0) + if (!plan.PasswordManager.HasAdditionalStorageOption && upgrade.AdditionalStorageGb > 0) { throw new BadRequestException("Plan does not allow additional storage."); } @@ -1981,29 +1983,39 @@ public void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, Organizati throw new BadRequestException("You can't subtract storage!"); } - if (!plan.HasPremiumAccessOption && upgrade.PremiumAccessAddon) + if (!plan.PasswordManager.HasPremiumAccessOption && upgrade.PremiumAccessAddon) { throw new BadRequestException("This plan does not allow you to buy the premium access addon."); } - if (!plan.HasAdditionalSeatsOption && upgrade.AdditionalSeats > 0) + if (!plan.PasswordManager.HasAdditionalSeatsOption && upgrade.AdditionalSeats > 0) { throw new BadRequestException("Plan does not allow additional users."); } - if (plan.HasAdditionalSeatsOption && plan.MaxAdditionalSeats.HasValue && - upgrade.AdditionalSeats > plan.MaxAdditionalSeats.Value) + if (plan.PasswordManager.HasAdditionalSeatsOption && plan.PasswordManager.MaxAdditionalSeats.HasValue && + upgrade.AdditionalSeats > plan.PasswordManager.MaxAdditionalSeats.Value) { throw new BadRequestException($"Selected plan allows a maximum of " + - $"{plan.MaxAdditionalSeats.GetValueOrDefault(0)} additional users."); + $"{plan.PasswordManager.MaxAdditionalSeats.GetValueOrDefault(0)} additional users."); } } public void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade) { + if (plan.SupportsSecretsManager == false) + { + throw new BadRequestException("Invalid Secrets Manager plan selected."); + } + ValidatePlan(plan, upgrade.AdditionalSmSeats.GetValueOrDefault(), "Secrets Manager"); - if (!plan.HasAdditionalServiceAccountOption && upgrade.AdditionalServiceAccounts > 0) + if (plan.SecretsManager.BaseSeats + upgrade.AdditionalSmSeats <= 0) + { + throw new BadRequestException($"You do not have any Secrets Manager seats!"); + } + + if (!plan.SecretsManager.HasAdditionalServiceAccountOption && upgrade.AdditionalServiceAccounts > 0) { throw new BadRequestException("Plan does not allow additional Service Accounts."); } @@ -2018,14 +2030,14 @@ public void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, Organizatio throw new BadRequestException("You can't subtract Service Accounts!"); } - switch (plan.HasAdditionalSeatsOption) + switch (plan.SecretsManager.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: + case true when plan.SecretsManager.MaxAdditionalSeats.HasValue && + upgrade.AdditionalSmSeats > plan.SecretsManager.MaxAdditionalSeats.Value: throw new BadRequestException($"Selected plan allows a maximum of " + - $"{plan.MaxAdditionalSeats.GetValueOrDefault(0)} additional users."); + $"{plan.SecretsManager.MaxAdditionalSeats.GetValueOrDefault(0)} additional users."); } } @@ -2448,7 +2460,7 @@ static OrganizationUserStatusType GetPriorActiveOrganizationUserStatusType(Organ public async Task CreatePendingOrganization(Organization organization, string ownerEmail, ClaimsPrincipal user, IUserService userService, bool salesAssistedTrialStarted) { - var plan = StaticStore.PasswordManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); + var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType); if (plan is not { LegacyYear: null }) { throw new BadRequestException("Invalid plan selected."); diff --git a/src/Core/Services/Implementations/StripePaymentService.cs b/src/Core/Services/Implementations/StripePaymentService.cs index 4d2eb4ef855b..0f7965db7702 100644 --- a/src/Core/Services/Implementations/StripePaymentService.cs +++ b/src/Core/Services/Implementations/StripePaymentService.cs @@ -49,7 +49,7 @@ public StripePaymentService( } public async Task PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType, - string paymentToken, List plans, short additionalStorageGb, + string paymentToken, StaticStore.Plan plan, short additionalStorageGb, int additionalSeats, bool premiumAccessAddon, TaxInfo taxInfo, bool provider = false, int additionalSmSeats = 0, int additionalServiceAccount = 0) { @@ -119,7 +119,7 @@ public async Task PurchaseOrganizationAsync(Organization org, PaymentMet } } - var subCreateOptions = new OrganizationPurchaseSubscriptionOptions(org, plans, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon + var subCreateOptions = new OrganizationPurchaseSubscriptionOptions(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon , additionalSmSeats, additionalServiceAccount); Stripe.Customer customer = null; @@ -211,7 +211,7 @@ public async Task PurchaseOrganizationAsync(Organization org, PaymentMet private async Task ChangeOrganizationSponsorship(Organization org, OrganizationSponsorship sponsorship, bool applySponsorship) { - var existingPlan = Utilities.StaticStore.GetPasswordManagerPlan(org.PlanType); + var existingPlan = Utilities.StaticStore.GetPlan(org.PlanType); var sponsoredPlan = sponsorship != null ? Utilities.StaticStore.GetSponsoredPlan(sponsorship.PlanSponsorshipType.Value) : null; @@ -231,7 +231,7 @@ public Task SponsorOrganizationAsync(Organization org, OrganizationSponsorship s public Task RemoveOrganizationSponsorshipAsync(Organization org, OrganizationSponsorship sponsorship) => ChangeOrganizationSponsorship(org, sponsorship, false); - public async Task UpgradeFreeOrganizationAsync(Organization org, List plans, + public async Task UpgradeFreeOrganizationAsync(Organization org, StaticStore.Plan plan, OrganizationUpgrade upgrade) { if (!string.IsNullOrWhiteSpace(org.GatewaySubscriptionId)) @@ -266,7 +266,7 @@ public async Task UpgradeFreeOrganizationAsync(Organization org, List AdjustSeatsAsync(Organization organization, StaticStore.Plan return FinalizeSubscriptionChangeAsync(organization, new SeatSubscriptionUpdate(organization, plan, additionalSeats), prorationDate); } + public Task AdjustSmSeatsAsync(Organization organization, StaticStore.Plan plan, int additionalSeats, DateTime? prorationDate = null) + { + return FinalizeSubscriptionChangeAsync(organization, new SmSeatSubscriptionUpdate(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); @@ -1557,10 +1562,19 @@ public async Task GetSubscriptionAsync(ISubscriber subscriber) { var subscriptionInfo = new SubscriptionInfo(); - if (subscriber.IsUser() && !string.IsNullOrWhiteSpace(subscriber.GatewayCustomerId)) + if (!string.IsNullOrWhiteSpace(subscriber.GatewayCustomerId)) { var customer = await _stripeAdapter.CustomerGetAsync(subscriber.GatewayCustomerId); - subscriptionInfo.UsingInAppPurchase = customer.Metadata.ContainsKey("appleReceipt"); + + if (customer.Discount != null) + { + subscriptionInfo.CustomerDiscount = new SubscriptionInfo.BillingCustomerDiscount(customer.Discount); + } + + if (subscriber.IsUser()) + { + subscriptionInfo.UsingInAppPurchase = customer.Metadata.ContainsKey("appleReceipt"); + } } if (!string.IsNullOrWhiteSpace(subscriber.GatewaySubscriptionId)) diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index eee255422e07..3f29d14afb0f 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -1,5 +1,6 @@ using System.Security.Claims; using System.Text.Json; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Auth.Entities; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models; diff --git a/src/Core/Services/NoopImplementations/NoopEventService.cs b/src/Core/Services/NoopImplementations/NoopEventService.cs index 068e03e35c47..9a9d831c4e65 100644 --- a/src/Core/Services/NoopImplementations/NoopEventService.cs +++ b/src/Core/Services/NoopImplementations/NoopEventService.cs @@ -1,5 +1,6 @@ -using Bit.Core.Entities; -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.SecretsManager.Entities; using Bit.Core.Vault.Entities; diff --git a/src/Core/Services/NoopImplementations/NoopMailService.cs b/src/Core/Services/NoopImplementations/NoopMailService.cs index 97d69cfa48bf..e098d7051e7c 100644 --- a/src/Core/Services/NoopImplementations/NoopMailService.cs +++ b/src/Core/Services/NoopImplementations/NoopMailService.cs @@ -1,7 +1,7 @@ -using Bit.Core.Auth.Entities; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.Auth.Entities; using Bit.Core.Auth.Models.Business; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; using Bit.Core.Models.Mail; namespace Bit.Core.Services; @@ -88,11 +88,19 @@ public Task SendPasswordlessSignInAsync(string returnUrl, string token, string e return Task.FromResult(0); } - public Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate, - List items, bool mentionInvoices) - { - return Task.FromResult(0); - } + public Task SendInvoiceUpcoming( + string email, + decimal amount, + DateTime dueDate, + List items, + bool mentionInvoices) => Task.FromResult(0); + + public Task SendInvoiceUpcoming( + IEnumerable emails, + decimal amount, + DateTime dueDate, + List items, + bool mentionInvoices) => Task.FromResult(0); public Task SendPaymentFailedAsync(string email, decimal amount, bool mentionInvoices) { diff --git a/src/Core/Tools/Enums/ReferenceEventType.cs b/src/Core/Tools/Enums/ReferenceEventType.cs index a8a52e63179f..1e903b6a8728 100644 --- a/src/Core/Tools/Enums/ReferenceEventType.cs +++ b/src/Core/Tools/Enums/ReferenceEventType.cs @@ -42,6 +42,8 @@ public enum ReferenceEventType OrganizationEditedByAdmin, [EnumMember(Value = "organization-created-by-admin")] OrganizationCreatedByAdmin, + [EnumMember(Value = "organization-edited-in-stripe")] + OrganizationEditedInStripe, [EnumMember(Value = "sm-service-account-accessed-secret")] SmServiceAccountAccessedSecret, } diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index c128fa8e46e5..b125fba49ed8 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -10,11 +10,12 @@ using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; using Azure.Storage.Queues.Models; +using Bit.Core.AdminConsole.Context; +using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.Auth.Enums; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Identity; using Bit.Core.Settings; using IdentityModel; diff --git a/src/Core/Utilities/PasswordManagerPlanStore.cs b/src/Core/Utilities/PasswordManagerPlanStore.cs deleted file mode 100644 index 1f9400eba61b..000000000000 --- a/src/Core/Utilities/PasswordManagerPlanStore.cs +++ /dev/null @@ -1,398 +0,0 @@ -using Bit.Core.Enums; -using Bit.Core.Models.StaticStore; - -namespace Bit.Core.Utilities; - -public static class PasswordManagerPlanStore -{ - public static IEnumerable CreatePlan() - { - return new List - { - new Plan - { - Type = PlanType.Free, - Product = ProductType.Free, - BitwardenProduct = BitwardenProductType.PasswordManager, - Name = "Free", - NameLocalizationKey = "planNameFree", - DescriptionLocalizationKey = "planDescFree", - BaseSeats = 2, - MaxCollections = 2, - MaxUsers = 2, - - UpgradeSortOrder = -1, // Always the lowest plan, cannot be upgraded to - DisplaySortOrder = -1, - - AllowSeatAutoscale = false, - }, - new Plan - { - Type = PlanType.FamiliesAnnually2019, - Product = ProductType.Families, - BitwardenProduct = BitwardenProductType.PasswordManager, - Name = "Families 2019", - IsAnnual = true, - NameLocalizationKey = "planNameFamilies", - DescriptionLocalizationKey = "planDescFamilies", - BaseSeats = 5, - BaseStorageGb = 1, - MaxUsers = 5, - - HasAdditionalStorageOption = true, - HasPremiumAccessOption = true, - TrialPeriodDays = 7, - - HasSelfHost = true, - HasTotp = true, - - UpgradeSortOrder = 1, - DisplaySortOrder = 1, - LegacyYear = 2020, - - StripePlanId = "personal-org-annually", - StripeStoragePlanId = "storage-gb-annually", - StripePremiumAccessPlanId = "personal-org-premium-access-annually", - BasePrice = 12, - AdditionalStoragePricePerGb = 4, - PremiumAccessOptionPrice = 40, - - AllowSeatAutoscale = false, - }, - new Plan - { - Type = PlanType.TeamsAnnually2019, - Product = ProductType.Teams, - BitwardenProduct = BitwardenProductType.PasswordManager, - Name = "Teams (Annually) 2019", - IsAnnual = true, - NameLocalizationKey = "planNameTeams", - DescriptionLocalizationKey = "planDescTeams", - CanBeUsedByBusiness = true, - BaseSeats = 5, - BaseStorageGb = 1, - - HasAdditionalSeatsOption = true, - HasAdditionalStorageOption = true, - TrialPeriodDays = 7, - - HasTotp = true, - - UpgradeSortOrder = 2, - DisplaySortOrder = 2, - LegacyYear = 2020, - - StripePlanId = "teams-org-annually", - StripeSeatPlanId = "teams-org-seat-annually", - StripeStoragePlanId = "storage-gb-annually", - BasePrice = 60, - SeatPrice = 24, - AdditionalStoragePricePerGb = 4, - - AllowSeatAutoscale = true, - }, - new Plan - { - Type = PlanType.TeamsMonthly2019, - Product = ProductType.Teams, - BitwardenProduct = BitwardenProductType.PasswordManager, - Name = "Teams (Monthly) 2019", - NameLocalizationKey = "planNameTeams", - DescriptionLocalizationKey = "planDescTeams", - CanBeUsedByBusiness = true, - BaseSeats = 5, - BaseStorageGb = 1, - - HasAdditionalSeatsOption = true, - HasAdditionalStorageOption = true, - TrialPeriodDays = 7, - - HasTotp = true, - - UpgradeSortOrder = 2, - DisplaySortOrder = 2, - LegacyYear = 2020, - - StripePlanId = "teams-org-monthly", - StripeSeatPlanId = "teams-org-seat-monthly", - StripeStoragePlanId = "storage-gb-monthly", - BasePrice = 8, - SeatPrice = 2.5M, - AdditionalStoragePricePerGb = 0.5M, - - AllowSeatAutoscale = true, - }, - new Plan - { - Type = PlanType.EnterpriseAnnually2019, - Name = "Enterprise (Annually) 2019", - IsAnnual = true, - Product = ProductType.Enterprise, - BitwardenProduct = BitwardenProductType.PasswordManager, - NameLocalizationKey = "planNameEnterprise", - DescriptionLocalizationKey = "planDescEnterprise", - CanBeUsedByBusiness = true, - BaseSeats = 0, - BaseStorageGb = 1, - - HasAdditionalSeatsOption = true, - HasAdditionalStorageOption = true, - TrialPeriodDays = 7, - - HasPolicies = true, - HasSelfHost = true, - HasGroups = true, - HasDirectory = true, - HasEvents = true, - HasTotp = true, - Has2fa = true, - HasApi = true, - UsersGetPremium = true, - HasCustomPermissions = true, - - UpgradeSortOrder = 3, - DisplaySortOrder = 3, - LegacyYear = 2020, - - StripePlanId = null, - StripeSeatPlanId = "enterprise-org-seat-annually", - StripeStoragePlanId = "storage-gb-annually", - BasePrice = 0, - SeatPrice = 36, - AdditionalStoragePricePerGb = 4, - - AllowSeatAutoscale = true, - }, - new Plan - { - Type = PlanType.EnterpriseMonthly2019, - Product = ProductType.Enterprise, - BitwardenProduct = BitwardenProductType.PasswordManager, - Name = "Enterprise (Monthly) 2019", - NameLocalizationKey = "planNameEnterprise", - DescriptionLocalizationKey = "planDescEnterprise", - CanBeUsedByBusiness = true, - BaseSeats = 0, - BaseStorageGb = 1, - - HasAdditionalSeatsOption = true, - HasAdditionalStorageOption = true, - TrialPeriodDays = 7, - - HasPolicies = true, - HasGroups = true, - HasDirectory = true, - HasEvents = true, - HasTotp = true, - Has2fa = true, - HasApi = true, - HasSelfHost = true, - UsersGetPremium = true, - HasCustomPermissions = true, - - UpgradeSortOrder = 3, - DisplaySortOrder = 3, - LegacyYear = 2020, - - StripePlanId = null, - StripeSeatPlanId = "enterprise-org-seat-monthly", - StripeStoragePlanId = "storage-gb-monthly", - BasePrice = 0, - SeatPrice = 4M, - AdditionalStoragePricePerGb = 0.5M, - - AllowSeatAutoscale = true, - }, - new Plan - { - Type = PlanType.FamiliesAnnually, - Product = ProductType.Families, - BitwardenProduct = BitwardenProductType.PasswordManager, - Name = "Families", - IsAnnual = true, - NameLocalizationKey = "planNameFamilies", - DescriptionLocalizationKey = "planDescFamilies", - BaseSeats = 6, - BaseStorageGb = 1, - MaxUsers = 6, - - HasAdditionalStorageOption = true, - TrialPeriodDays = 7, - - HasSelfHost = true, - HasTotp = true, - UsersGetPremium = true, - - UpgradeSortOrder = 1, - DisplaySortOrder = 1, - - StripePlanId = "2020-families-org-annually", - StripeStoragePlanId = "storage-gb-annually", - BasePrice = 40, - AdditionalStoragePricePerGb = 4, - - AllowSeatAutoscale = false, - }, - new Plan - { - Type = PlanType.TeamsAnnually, - Product = ProductType.Teams, - BitwardenProduct = BitwardenProductType.PasswordManager, - Name = "Teams (Annually)", - IsAnnual = true, - NameLocalizationKey = "planNameTeams", - DescriptionLocalizationKey = "planDescTeams", - CanBeUsedByBusiness = true, - BaseStorageGb = 1, - BaseSeats = 0, - - HasAdditionalSeatsOption = true, - HasAdditionalStorageOption = true, - TrialPeriodDays = 7, - - Has2fa = true, - HasApi = true, - HasDirectory = true, - HasEvents = true, - HasGroups = true, - HasTotp = true, - UsersGetPremium = true, - - UpgradeSortOrder = 2, - DisplaySortOrder = 2, - - StripeSeatPlanId = "2020-teams-org-seat-annually", - StripeStoragePlanId = "storage-gb-annually", - SeatPrice = 36, - AdditionalStoragePricePerGb = 4, - - AllowSeatAutoscale = true, - }, - new Plan - { - Type = PlanType.TeamsMonthly, - Product = ProductType.Teams, - BitwardenProduct = BitwardenProductType.PasswordManager, - Name = "Teams (Monthly)", - NameLocalizationKey = "planNameTeams", - DescriptionLocalizationKey = "planDescTeams", - CanBeUsedByBusiness = true, - BaseStorageGb = 1, - BaseSeats = 0, - - HasAdditionalSeatsOption = true, - HasAdditionalStorageOption = true, - TrialPeriodDays = 7, - - Has2fa = true, - HasApi = true, - HasDirectory = true, - HasEvents = true, - HasGroups = true, - HasTotp = true, - UsersGetPremium = true, - - UpgradeSortOrder = 2, - DisplaySortOrder = 2, - - StripeSeatPlanId = "2020-teams-org-seat-monthly", - StripeStoragePlanId = "storage-gb-monthly", - SeatPrice = 4, - AdditionalStoragePricePerGb = 0.5M, - - AllowSeatAutoscale = true, - }, - new Plan - { - Type = PlanType.EnterpriseAnnually, - Name = "Enterprise (Annually)", - Product = ProductType.Enterprise, - BitwardenProduct = BitwardenProductType.PasswordManager, - IsAnnual = true, - NameLocalizationKey = "planNameEnterprise", - DescriptionLocalizationKey = "planDescEnterprise", - CanBeUsedByBusiness = true, - BaseSeats = 0, - BaseStorageGb = 1, - - HasAdditionalSeatsOption = true, - HasAdditionalStorageOption = true, - TrialPeriodDays = 7, - - HasPolicies = true, - HasSelfHost = true, - HasGroups = true, - HasDirectory = true, - HasEvents = true, - HasTotp = true, - Has2fa = true, - HasApi = true, - HasSso = true, - HasKeyConnector = true, - HasScim = true, - HasResetPassword = true, - UsersGetPremium = true, - HasCustomPermissions = true, - - UpgradeSortOrder = 3, - DisplaySortOrder = 3, - - StripeSeatPlanId = "2020-enterprise-org-seat-annually", - StripeStoragePlanId = "storage-gb-annually", - BasePrice = 0, - SeatPrice = 60, - AdditionalStoragePricePerGb = 4, - - AllowSeatAutoscale = true, - }, - new Plan - { - Type = PlanType.EnterpriseMonthly, - Product = ProductType.Enterprise, - BitwardenProduct = BitwardenProductType.PasswordManager, - Name = "Enterprise (Monthly)", - NameLocalizationKey = "planNameEnterprise", - DescriptionLocalizationKey = "planDescEnterprise", - CanBeUsedByBusiness = true, - BaseSeats = 0, - BaseStorageGb = 1, - - HasAdditionalSeatsOption = true, - HasAdditionalStorageOption = true, - TrialPeriodDays = 7, - - HasPolicies = true, - HasGroups = true, - HasDirectory = true, - HasEvents = true, - HasTotp = true, - Has2fa = true, - HasApi = true, - HasSelfHost = true, - HasSso = true, - HasKeyConnector = true, - HasScim = true, - HasResetPassword = true, - UsersGetPremium = true, - HasCustomPermissions = true, - - UpgradeSortOrder = 3, - DisplaySortOrder = 3, - - StripeSeatPlanId = "2020-enterprise-seat-monthly", - StripeStoragePlanId = "storage-gb-monthly", - BasePrice = 0, - SeatPrice = 6, - AdditionalStoragePricePerGb = 0.5M, - - AllowSeatAutoscale = true, - }, - new Plan - { - Type = PlanType.Custom, - - AllowSeatAutoscale = true, - }, - }; - } -} diff --git a/src/Core/Utilities/SecretsManagerPlanStore.cs b/src/Core/Utilities/SecretsManagerPlanStore.cs deleted file mode 100644 index c37c4244623d..000000000000 --- a/src/Core/Utilities/SecretsManagerPlanStore.cs +++ /dev/null @@ -1,172 +0,0 @@ -using Bit.Core.Enums; -using Bit.Core.Models.StaticStore; - -namespace Bit.Core.Utilities; - -public static class SecretsManagerPlanStore -{ - public static IEnumerable CreatePlan() - { - return new List - { - new Plan - { - Type = PlanType.EnterpriseMonthly, - Product = ProductType.Enterprise, - BitwardenProduct = BitwardenProductType.SecretsManager, - Name = "Enterprise (Monthly)", - NameLocalizationKey = "planNameEnterprise", - DescriptionLocalizationKey = "planDescEnterprise", - CanBeUsedByBusiness = true, - BaseSeats = 0, - BaseServiceAccount = 200, - HasAdditionalSeatsOption = true, - HasAdditionalServiceAccountOption = true, - TrialPeriodDays = 7, - HasPolicies = true, - HasGroups = true, - HasDirectory = true, - HasEvents = true, - HasTotp = true, - Has2fa = true, - HasApi = true, - HasSelfHost = true, - HasSso = true, - HasKeyConnector = true, - HasScim = true, - HasResetPassword = true, - UsersGetPremium = true, - HasCustomPermissions = true, - UpgradeSortOrder = 3, - DisplaySortOrder = 3, - StripeSeatPlanId = "secrets-manager-enterprise-seat-monthly", - StripeServiceAccountPlanId = "secrets-manager-service-account-monthly", - BasePrice = 0, - SeatPrice = 13, - AdditionalPricePerServiceAccount = 0.5M, - AllowSeatAutoscale = true, - AllowServiceAccountsAutoscale = true - }, - new Plan - { - Type = PlanType.EnterpriseAnnually, - Name = "Enterprise (Annually)", - Product = ProductType.Enterprise, - BitwardenProduct = BitwardenProductType.SecretsManager, - IsAnnual = true, - NameLocalizationKey = "planNameEnterprise", - DescriptionLocalizationKey = "planDescEnterprise", - CanBeUsedByBusiness = true, - BaseSeats = 0, - BaseServiceAccount = 200, - HasAdditionalSeatsOption = true, - HasAdditionalServiceAccountOption = true, - TrialPeriodDays = 7, - HasPolicies = true, - HasSelfHost = true, - HasGroups = true, - HasDirectory = true, - HasEvents = true, - HasTotp = true, - Has2fa = true, - HasApi = true, - HasSso = true, - HasKeyConnector = true, - HasScim = true, - HasResetPassword = true, - UsersGetPremium = true, - HasCustomPermissions = true, - UpgradeSortOrder = 3, - DisplaySortOrder = 3, - StripeSeatPlanId = "secrets-manager-enterprise-seat-annually", - StripeServiceAccountPlanId = "secrets-manager-service-account-annually", - BasePrice = 0, - SeatPrice = 144, - AdditionalPricePerServiceAccount = 6, - AllowSeatAutoscale = true, - AllowServiceAccountsAutoscale = true - }, - new Plan - { - Type = PlanType.TeamsMonthly, - Name = "Teams (Monthly)", - Product = ProductType.Teams, - BitwardenProduct = BitwardenProductType.SecretsManager, - NameLocalizationKey = "planNameTeams", - DescriptionLocalizationKey = "planDescTeams", - CanBeUsedByBusiness = true, - BaseSeats = 0, - BaseServiceAccount = 50, - HasAdditionalSeatsOption = true, - HasAdditionalServiceAccountOption = true, - TrialPeriodDays = 7, - Has2fa = true, - HasApi = true, - HasDirectory = true, - HasEvents = true, - HasGroups = true, - HasTotp = true, - UsersGetPremium = true, - UpgradeSortOrder = 2, - DisplaySortOrder = 2, - StripeSeatPlanId = "secrets-manager-teams-seat-monthly", - StripeServiceAccountPlanId = "secrets-manager-service-account-monthly", - BasePrice = 0, - SeatPrice = 7, - AdditionalPricePerServiceAccount = 0.5M, - AllowSeatAutoscale = true, - AllowServiceAccountsAutoscale = true - }, - new Plan - { - Type = PlanType.TeamsAnnually, - Name = "Teams (Annually)", - Product = ProductType.Teams, - BitwardenProduct = BitwardenProductType.SecretsManager, - IsAnnual = true, - NameLocalizationKey = "planNameTeams", - DescriptionLocalizationKey = "planDescTeams", - CanBeUsedByBusiness = true, - BaseSeats = 0, - BaseServiceAccount = 50, - HasAdditionalSeatsOption = true, - HasAdditionalServiceAccountOption = true, - TrialPeriodDays = 7, - Has2fa = true, - HasApi = true, - HasDirectory = true, - HasEvents = true, - HasGroups = true, - HasTotp = true, - UsersGetPremium = true, - - UpgradeSortOrder = 2, - DisplaySortOrder = 2, - StripeSeatPlanId = "secrets-manager-teams-seat-annually", - StripeServiceAccountPlanId = "secrets-manager-service-account-annually", - BasePrice = 0, - SeatPrice = 72, - AdditionalPricePerServiceAccount = 6, - AllowSeatAutoscale = true, - AllowServiceAccountsAutoscale = true - }, - new Plan - { - Type = PlanType.Free, - Product = ProductType.Free, - BitwardenProduct = BitwardenProductType.SecretsManager, - Name = "Free", - NameLocalizationKey = "planNameFree", - DescriptionLocalizationKey = "planDescFree", - BaseSeats = 2, - BaseServiceAccount = 3, - MaxProjects = 3, - MaxUsers = 2, - MaxServiceAccounts = 3, - UpgradeSortOrder = -1, // Always the lowest plan, cannot be upgraded to - DisplaySortOrder = -1, - AllowSeatAutoscale = false, - } - }; - } -} diff --git a/src/Core/Utilities/StaticStore.cs b/src/Core/Utilities/StaticStore.cs index ad11b2cfd092..6726d9cd319b 100644 --- a/src/Core/Utilities/StaticStore.cs +++ b/src/Core/Utilities/StaticStore.cs @@ -1,6 +1,8 @@ -using Bit.Core.Enums; +using System.Collections.Immutable; +using Bit.Core.Enums; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Models.StaticStore; +using Bit.Core.Models.StaticStore.Plans; namespace Bit.Core.Utilities; @@ -104,21 +106,26 @@ static StaticStore() GlobalDomains.Add(GlobalEquivalentDomainsType.Pinterest, new List { "pinterest.com", "pinterest.com.au", "pinterest.cl", "pinterest.de", "pinterest.dk", "pinterest.es", "pinterest.fr", "pinterest.co.uk", "pinterest.jp", "pinterest.co.kr", "pinterest.nz", "pinterest.pt", "pinterest.se" }); #endregion - #region Plans - - PasswordManagerPlans = PasswordManagerPlanStore.CreatePlan(); - SecretManagerPlans = SecretsManagerPlanStore.CreatePlan(); - - Plans = PasswordManagerPlans.Concat(SecretManagerPlans); - - - #endregion + Plans = new List + { + new EnterprisePlan(true), + new EnterprisePlan(false), + new TeamsPlan(true), + new TeamsPlan(false), + new FamiliesPlan(), + new FreePlan(), + new CustomPlan(), + + new Enterprise2019Plan(true), + new Enterprise2019Plan(false), + new Teams2019Plan(true), + new Teams2019Plan(false), + new Families2019Plan(), + }.ToImmutableList(); } public static IDictionary> GlobalDomains { get; set; } - public static IEnumerable Plans { get; set; } - public static IEnumerable SecretManagerPlans { get; set; } - public static IEnumerable PasswordManagerPlans { get; set; } + public static IEnumerable Plans { get; } public static IEnumerable SponsoredPlans { get; set; } = new[] { new SponsoredPlan @@ -128,21 +135,20 @@ static StaticStore() SponsoringProductType = ProductType.Enterprise, StripePlanId = "2021-family-for-enterprise-annually", UsersCanSponsor = (OrganizationUserOrganizationDetails org) => - GetPasswordManagerPlan(org.PlanType).Product == ProductType.Enterprise, + GetPlan(org.PlanType).Product == ProductType.Enterprise, } }; - public static Plan GetPasswordManagerPlan(PlanType planType) => - PasswordManagerPlans.SingleOrDefault(p => p.Type == planType); - public static Plan GetSecretsManagerPlan(PlanType planType) => - SecretManagerPlans.SingleOrDefault(p => p.Type == planType); + public static Models.StaticStore.Plan GetPlan(PlanType planType) => + Plans.SingleOrDefault(p => p.Type == 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 + /// matches either the or /// in any . /// /// @@ -151,41 +157,8 @@ public static SponsoredPlan GetSponsoredPlan(PlanSponsorshipType planSponsorship /// 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 - ); + return Plans.Any(p => + p.PasswordManager.StripeStoragePlanId == stripePlanId || + (p.SecretsManager?.StripeServiceAccountPlanId == stripePlanId)); } } diff --git a/src/Core/Vault/Entities/Cipher.cs b/src/Core/Vault/Entities/Cipher.cs index 15cbba411150..6a3ce94cf1c9 100644 --- a/src/Core/Vault/Entities/Cipher.cs +++ b/src/Core/Vault/Entities/Cipher.cs @@ -21,6 +21,7 @@ public class Cipher : ITableObject, ICloneable public DateTime RevisionDate { get; set; } = DateTime.UtcNow; public DateTime? DeletedDate { get; set; } public Enums.CipherRepromptType? Reprompt { get; set; } + public string Key { get; set; } public void SetNewId() { diff --git a/src/Core/Vault/Enums/CipherType.cs b/src/Core/Vault/Enums/CipherType.cs index c5a61043d3c5..f3c7a90f454e 100644 --- a/src/Core/Vault/Enums/CipherType.cs +++ b/src/Core/Vault/Enums/CipherType.cs @@ -7,5 +7,5 @@ public enum CipherType : byte Login = 1, SecureNote = 2, Card = 3, - Identity = 4 + Identity = 4, } diff --git a/src/Core/Vault/Models/Data/CipherLoginData.cs b/src/Core/Vault/Models/Data/CipherLoginData.cs index 223f85ef2bb5..e952b39cf231 100644 --- a/src/Core/Vault/Models/Data/CipherLoginData.cs +++ b/src/Core/Vault/Models/Data/CipherLoginData.cs @@ -19,6 +19,7 @@ public string Uri public DateTime? PasswordRevisionDate { get; set; } public string Totp { get; set; } public bool? AutofillOnPageLoad { get; set; } + public CipherLoginFido2CredentialData[] Fido2Credentials { get; set; } public class CipherLoginUriData { diff --git a/src/Core/Vault/Models/Data/CipherLoginFido2CredentialData.cs b/src/Core/Vault/Models/Data/CipherLoginFido2CredentialData.cs new file mode 100644 index 000000000000..c0801d2a6ccc --- /dev/null +++ b/src/Core/Vault/Models/Data/CipherLoginFido2CredentialData.cs @@ -0,0 +1,19 @@ +namespace Bit.Core.Vault.Models.Data; + +public class CipherLoginFido2CredentialData +{ + public CipherLoginFido2CredentialData() { } + + public string CredentialId { get; set; } + public string KeyType { get; set; } + public string KeyAlgorithm { get; set; } + public string KeyCurve { get; set; } + public string KeyValue { get; set; } + public string RpId { get; set; } + public string RpName { get; set; } + public string UserHandle { get; set; } + public string UserDisplayName { get; set; } + public string Counter { get; set; } + public string Discoverable { get; set; } + public DateTime CreationDate { get; set; } +} diff --git a/src/Core/packages.lock.json b/src/Core/packages.lock.json index 57052b8e9522..26d7ee5280fe 100644 --- a/src/Core/packages.lock.json +++ b/src/Core/packages.lock.json @@ -98,11 +98,12 @@ }, "Braintree": { "type": "Direct", - "requested": "[5.12.0, )", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "requested": "[5.19.0, )", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, diff --git a/src/Events/packages.lock.json b/src/Events/packages.lock.json index 0da51a039110..16a42f355d3f 100644 --- a/src/Events/packages.lock.json +++ b/src/Events/packages.lock.json @@ -158,10 +158,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2597,7 +2598,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2630,7 +2631,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2638,7 +2639,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2650,9 +2651,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/src/EventsProcessor/packages.lock.json b/src/EventsProcessor/packages.lock.json index 0da51a039110..16a42f355d3f 100644 --- a/src/EventsProcessor/packages.lock.json +++ b/src/EventsProcessor/packages.lock.json @@ -158,10 +158,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2597,7 +2598,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2630,7 +2631,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2638,7 +2639,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2650,9 +2651,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/src/Icons/packages.lock.json b/src/Icons/packages.lock.json index c69c089c3b3a..0b2c51d0c0c9 100644 --- a/src/Icons/packages.lock.json +++ b/src/Icons/packages.lock.json @@ -167,10 +167,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2606,7 +2607,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2639,7 +2640,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2647,7 +2648,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2659,9 +2660,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/src/Identity/IdentityServer/ClientStore.cs b/src/Identity/IdentityServer/ClientStore.cs index e2fd33c9dbc8..259898f20901 100644 --- a/src/Identity/IdentityServer/ClientStore.cs +++ b/src/Identity/IdentityServer/ClientStore.cs @@ -1,5 +1,6 @@ using System.Collections.ObjectModel; using System.Security.Claims; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Identity; @@ -100,7 +101,7 @@ private async Task CreateApiKeyClientAsync(string clientId) { case ServiceAccountApiKeyDetails key: var org = await _organizationRepository.GetByIdAsync(key.ServiceAccountOrganizationId); - if (!org.UseSecretsManager) + if (!org.UseSecretsManager || !org.Enabled) { return null; } diff --git a/src/Identity/IdentityServer/ProfileService.cs b/src/Identity/IdentityServer/ProfileService.cs index e8cad3c01d94..bbec6739ba5a 100644 --- a/src/Identity/IdentityServer/ProfileService.cs +++ b/src/Identity/IdentityServer/ProfileService.cs @@ -1,4 +1,5 @@ using System.Security.Claims; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Identity; using Bit.Core.Repositories; diff --git a/src/Identity/packages.lock.json b/src/Identity/packages.lock.json index da1ee4585414..3577f2faef16 100644 --- a/src/Identity/packages.lock.json +++ b/src/Identity/packages.lock.json @@ -167,10 +167,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2619,7 +2620,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2652,7 +2653,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2660,7 +2661,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2672,9 +2673,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/src/Infrastructure.Dapper/Repositories/GroupRepository.cs b/src/Infrastructure.Dapper/AdminConsole/Repositories/GroupRepository.cs similarity index 97% rename from src/Infrastructure.Dapper/Repositories/GroupRepository.cs rename to src/Infrastructure.Dapper/AdminConsole/Repositories/GroupRepository.cs index c5ef42c6b702..c1a970f959ca 100644 --- a/src/Infrastructure.Dapper/Repositories/GroupRepository.cs +++ b/src/Infrastructure.Dapper/AdminConsole/Repositories/GroupRepository.cs @@ -1,13 +1,16 @@ using System.Data; using System.Text.Json; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Models.Data; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; using Bit.Core.Models.Data; -using Bit.Core.Repositories; using Bit.Core.Settings; +using Bit.Infrastructure.Dapper.Repositories; using Dapper; using Microsoft.Data.SqlClient; -namespace Bit.Infrastructure.Dapper.Repositories; +namespace Bit.Infrastructure.Dapper.AdminConsole.Repositories; public class GroupRepository : Repository, IGroupRepository { diff --git a/src/Infrastructure.Dapper/Repositories/ProviderOrganizationRepository.cs b/src/Infrastructure.Dapper/AdminConsole/Repositories/ProviderOrganizationRepository.cs similarity index 96% rename from src/Infrastructure.Dapper/Repositories/ProviderOrganizationRepository.cs rename to src/Infrastructure.Dapper/AdminConsole/Repositories/ProviderOrganizationRepository.cs index 604d81a6e2db..66b4ad856067 100644 --- a/src/Infrastructure.Dapper/Repositories/ProviderOrganizationRepository.cs +++ b/src/Infrastructure.Dapper/AdminConsole/Repositories/ProviderOrganizationRepository.cs @@ -1,12 +1,13 @@ using System.Data; -using Bit.Core.Entities.Provider; -using Bit.Core.Models.Data; -using Bit.Core.Repositories; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Settings; +using Bit.Infrastructure.Dapper.Repositories; using Dapper; using Microsoft.Data.SqlClient; -namespace Bit.Infrastructure.Dapper.Repositories; +namespace Bit.Infrastructure.Dapper.AdminConsole.Repositories; public class ProviderOrganizationRepository : Repository, IProviderOrganizationRepository { diff --git a/src/Infrastructure.Dapper/Repositories/ProviderRepository.cs b/src/Infrastructure.Dapper/AdminConsole/Repositories/ProviderRepository.cs similarity index 88% rename from src/Infrastructure.Dapper/Repositories/ProviderRepository.cs rename to src/Infrastructure.Dapper/AdminConsole/Repositories/ProviderRepository.cs index 39bc02931202..812ed30ff1c2 100644 --- a/src/Infrastructure.Dapper/Repositories/ProviderRepository.cs +++ b/src/Infrastructure.Dapper/AdminConsole/Repositories/ProviderRepository.cs @@ -1,12 +1,13 @@ using System.Data; -using Bit.Core.Entities.Provider; -using Bit.Core.Models.Data; -using Bit.Core.Repositories; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Settings; +using Bit.Infrastructure.Dapper.Repositories; using Dapper; using Microsoft.Data.SqlClient; -namespace Bit.Infrastructure.Dapper.Repositories; +namespace Bit.Infrastructure.Dapper.AdminConsole.Repositories; public class ProviderRepository : Repository, IProviderRepository { diff --git a/src/Infrastructure.Dapper/Repositories/ProviderUserRepository.cs b/src/Infrastructure.Dapper/AdminConsole/Repositories/ProviderUserRepository.cs similarity index 95% rename from src/Infrastructure.Dapper/Repositories/ProviderUserRepository.cs rename to src/Infrastructure.Dapper/AdminConsole/Repositories/ProviderUserRepository.cs index 114ed53f5433..dafa78a07f4f 100644 --- a/src/Infrastructure.Dapper/Repositories/ProviderUserRepository.cs +++ b/src/Infrastructure.Dapper/AdminConsole/Repositories/ProviderUserRepository.cs @@ -1,13 +1,14 @@ using System.Data; -using Bit.Core.Entities.Provider; -using Bit.Core.Enums.Provider; -using Bit.Core.Models.Data; -using Bit.Core.Repositories; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Settings; +using Bit.Infrastructure.Dapper.Repositories; using Dapper; using Microsoft.Data.SqlClient; -namespace Bit.Infrastructure.Dapper.Repositories; +namespace Bit.Infrastructure.Dapper.AdminConsole.Repositories; public class ProviderUserRepository : Repository, IProviderUserRepository { diff --git a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs index 3a2233f4cb61..0b9790764dad 100644 --- a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs +++ b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs @@ -1,8 +1,10 @@ -using Bit.Core.Auth.Repositories; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Auth.Repositories; using Bit.Core.Repositories; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Tools.Repositories; using Bit.Core.Vault.Repositories; +using Bit.Infrastructure.Dapper.AdminConsole.Repositories; using Bit.Infrastructure.Dapper.Auth.Repositories; using Bit.Infrastructure.Dapper.Repositories; using Bit.Infrastructure.Dapper.SecretsManager.Repositories; diff --git a/src/Infrastructure.Dapper/Repositories/EventRepository.cs b/src/Infrastructure.Dapper/Repositories/EventRepository.cs index 1c5b805c4c91..b41687daa73e 100644 --- a/src/Infrastructure.Dapper/Repositories/EventRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/EventRepository.cs @@ -118,6 +118,18 @@ public async Task CreateManyAsync(IEnumerable entities) } } + public async Task> GetManyByOrganizationServiceAccountAsync(Guid organizationId, Guid serviceAccountId, + DateTime startDate, DateTime endDate, + PageOptions pageOptions) + { + return await GetManyAsync($"[{Schema}].[Event_ReadPageByOrganizationIdServiceAccountId]", + new Dictionary + { + ["@OrganizationId"] = organizationId, + ["@ServiceAccountId"] = serviceAccountId + }, startDate, endDate, pageOptions); + } + private async Task> GetManyAsync(string sprocName, IDictionary sprocParams, DateTime startDate, DateTime endDate, PageOptions pageOptions) { @@ -187,6 +199,10 @@ private DataTable BuildEventsTable(SqlBulkCopy bulkCopy, IEnumerable even eventsTable.Columns.Add(ipAddressColumn); var dateColumn = new DataColumn(nameof(e.Date), typeof(DateTime)); eventsTable.Columns.Add(dateColumn); + var secretIdColumn = new DataColumn(nameof(e.SecretId), typeof(Guid)); + eventsTable.Columns.Add(secretIdColumn); + var serviceAccountIdColumn = new DataColumn(nameof(e.ServiceAccountId), typeof(Guid)); + eventsTable.Columns.Add(serviceAccountIdColumn); foreach (DataColumn col in eventsTable.Columns) { @@ -217,6 +233,8 @@ private DataTable BuildEventsTable(SqlBulkCopy bulkCopy, IEnumerable even row[deviceTypeColumn] = ev.DeviceType.HasValue ? (object)ev.DeviceType.Value : DBNull.Value; row[ipAddressColumn] = ev.IpAddress != null ? (object)ev.IpAddress : DBNull.Value; row[dateColumn] = ev.Date; + row[secretIdColumn] = ev.SecretId.HasValue ? ev.SecretId.Value : DBNull.Value; + row[serviceAccountIdColumn] = ev.ServiceAccountId.HasValue ? ev.ServiceAccountId.Value : DBNull.Value; eventsTable.Rows.Add(row); } diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationConnectionRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationConnectionRepository.cs index 7ea1c72272a4..0168f57e32fc 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationConnectionRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationConnectionRepository.cs @@ -14,6 +14,23 @@ public OrganizationConnectionRepository(GlobalSettings globalSettings) : base(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString) { } + public async Task GetByIdOrganizationIdAsync(Guid id, Guid organizationId) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[OrganizationConnection_ReadByIdOrganizationId]", + new + { + Id = id, + OrganizationId = organizationId + }, + commandType: CommandType.StoredProcedure); + + return results.FirstOrDefault(); + } + } + public async Task> GetByOrganizationIdTypeAsync(Guid organizationId, OrganizationConnectionType type) { using (var connection = new SqlConnection(ConnectionString)) diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs index c46c994a3365..6202a121d478 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs @@ -69,6 +69,20 @@ public async Task GetOrganizationDomainSsoDeta } } + public async Task GetDomainByIdOrganizationIdAsync(Guid id, Guid orgId) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection + .QueryAsync( + $"[{Schema}].[OrganizationDomain_ReadByIdOrganizationId]", + new { Id = id, OrganizationId = orgId }, + commandType: CommandType.StoredProcedure); + + return results.SingleOrDefault(); + } + } + public async Task GetDomainByOrgIdAndDomainNameAsync(Guid orgId, string domainName) { using (var connection = new SqlConnection(ConnectionString)) diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationRepository.cs index 9d8cad0f9cef..9329e23790e1 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationRepository.cs @@ -149,4 +149,14 @@ public async Task> SearchUnassignedToProviderAsync(str return results.ToList(); } } + + public async Task> GetOwnerEmailAddressesById(Guid organizationId) + { + await using var connection = new SqlConnection(ConnectionString); + + return await connection.QueryAsync( + $"[{Schema}].[{Table}_ReadOwnerEmailAddressesById]", + new { OrganizationId = organizationId }, + commandType: CommandType.StoredProcedure); + } } diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs index 008242c26c2b..a5afbe39049d 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs @@ -1,5 +1,6 @@ using System.Data; using System.Text.Json; +using Bit.Core.AdminConsole.Entities; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.Data; diff --git a/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs b/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs index cc1fd554e21a..dfc62f304909 100644 --- a/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs +++ b/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs @@ -392,7 +392,8 @@ SELECT TOP 0 * SET [Data] = TC.[Data], [Attachments] = TC.[Attachments], - [RevisionDate] = TC.[RevisionDate] + [RevisionDate] = TC.[RevisionDate], + [Key] = TC.[Key] FROM [dbo].[Cipher] C INNER JOIN @@ -506,7 +507,8 @@ SELECT TOP 0 * [Data] = TC.[Data], [Attachments] = TC.[Attachments], [RevisionDate] = TC.[RevisionDate], - [DeletedDate] = TC.[DeletedDate] + [DeletedDate] = TC.[DeletedDate], + [Key] = TC.[Key] FROM [dbo].[Cipher] C INNER JOIN @@ -728,6 +730,8 @@ private DataTable BuildCiphersTable(SqlBulkCopy bulkCopy, IEnumerable ci ciphersTable.Columns.Add(deletedDateColumn); var repromptColumn = new DataColumn(nameof(c.Reprompt), typeof(short)); ciphersTable.Columns.Add(repromptColumn); + var keyColummn = new DataColumn(nameof(c.Key), typeof(string)); + ciphersTable.Columns.Add(keyColummn); foreach (DataColumn col in ciphersTable.Columns) { @@ -754,6 +758,7 @@ private DataTable BuildCiphersTable(SqlBulkCopy bulkCopy, IEnumerable ci row[revisionDateColumn] = cipher.RevisionDate; row[deletedDateColumn] = cipher.DeletedDate.HasValue ? (object)cipher.DeletedDate : DBNull.Value; row[repromptColumn] = cipher.Reprompt; + row[keyColummn] = cipher.Key; ciphersTable.Rows.Add(row); } diff --git a/src/Infrastructure.Dapper/packages.lock.json b/src/Infrastructure.Dapper/packages.lock.json index d5d9c39e8d8b..8700a708a75f 100644 --- a/src/Infrastructure.Dapper/packages.lock.json +++ b/src/Infrastructure.Dapper/packages.lock.json @@ -147,10 +147,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2439,7 +2440,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Models/Provider/Provider.cs b/src/Infrastructure.EntityFramework/AdminConsole/Models/Provider/Provider.cs new file mode 100644 index 000000000000..3801c9ee69ca --- /dev/null +++ b/src/Infrastructure.EntityFramework/AdminConsole/Models/Provider/Provider.cs @@ -0,0 +1,15 @@ +using AutoMapper; + +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider; + +public class Provider : Core.AdminConsole.Entities.Provider.Provider +{ +} + +public class ProviderMapperProfile : Profile +{ + public ProviderMapperProfile() + { + CreateMap().ReverseMap(); + } +} diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Models/Provider/ProviderOrganization.cs b/src/Infrastructure.EntityFramework/AdminConsole/Models/Provider/ProviderOrganization.cs new file mode 100644 index 000000000000..82bc26c5d55a --- /dev/null +++ b/src/Infrastructure.EntityFramework/AdminConsole/Models/Provider/ProviderOrganization.cs @@ -0,0 +1,18 @@ +using AutoMapper; +using Bit.Infrastructure.EntityFramework.Models; + +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider; + +public class ProviderOrganization : Core.AdminConsole.Entities.Provider.ProviderOrganization +{ + public virtual Provider Provider { get; set; } + public virtual Organization Organization { get; set; } +} + +public class ProviderOrganizationMapperProfile : Profile +{ + public ProviderOrganizationMapperProfile() + { + CreateMap().ReverseMap(); + } +} diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Models/Provider/ProviderUser.cs b/src/Infrastructure.EntityFramework/AdminConsole/Models/Provider/ProviderUser.cs new file mode 100644 index 000000000000..1b6de00960c3 --- /dev/null +++ b/src/Infrastructure.EntityFramework/AdminConsole/Models/Provider/ProviderUser.cs @@ -0,0 +1,18 @@ +using AutoMapper; +using Bit.Infrastructure.EntityFramework.Models; + +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider; + +public class ProviderUser : Core.AdminConsole.Entities.Provider.ProviderUser +{ + public virtual User User { get; set; } + public virtual Provider Provider { get; set; } +} + +public class ProviderUserMapperProfile : Profile +{ + public ProviderUserMapperProfile() + { + CreateMap().ReverseMap(); + } +} diff --git a/src/Infrastructure.EntityFramework/Repositories/GroupRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/GroupRepository.cs similarity index 85% rename from src/Infrastructure.EntityFramework/Repositories/GroupRepository.cs rename to src/Infrastructure.EntityFramework/AdminConsole/Repositories/GroupRepository.cs index 9efd5e90c39f..4c1e4d343a02 100644 --- a/src/Infrastructure.EntityFramework/Repositories/GroupRepository.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/GroupRepository.cs @@ -1,19 +1,21 @@ using AutoMapper; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Models.Data; -using Bit.Core.Repositories; using Bit.Infrastructure.EntityFramework.Models; +using Bit.Infrastructure.EntityFramework.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using AdminConsoleEntities = Bit.Core.AdminConsole.Entities; -namespace Bit.Infrastructure.EntityFramework.Repositories; +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; -public class GroupRepository : Repository, IGroupRepository +public class GroupRepository : Repository, IGroupRepository { public GroupRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Groups) { } - public async Task CreateAsync(Core.Entities.Group obj, IEnumerable collections) + public async Task CreateAsync(AdminConsoleEntities.Group obj, IEnumerable collections) { var grp = await base.CreateAsync(obj); using (var scope = ServiceScopeFactory.CreateScope()) @@ -51,7 +53,7 @@ public async Task DeleteUserAsync(Guid groupId, Guid organizationUserId) } } - public async Task>> GetByIdWithCollectionsAsync(Guid id) + public async Task>> GetByIdWithCollectionsAsync(Guid id) { var grp = await base.GetByIdAsync(id); using (var scope = ServiceScopeFactory.CreateScope()) @@ -67,12 +69,12 @@ from cg in dbContext.CollectionGroups ReadOnly = c.ReadOnly, HidePasswords = c.HidePasswords, }).ToList(); - return new Tuple>( + return new Tuple>( grp, collections); } } - public async Task> GetManyByOrganizationIdAsync(Guid organizationId) + public async Task> GetManyByOrganizationIdAsync(Guid organizationId) { using (var scope = ServiceScopeFactory.CreateScope()) { @@ -81,11 +83,11 @@ from cg in dbContext.CollectionGroups from g in dbContext.Groups where g.OrganizationId == organizationId select g).ToListAsync(); - return Mapper.Map>(data); + return Mapper.Map>(data); } } - public async Task>>> + public async Task>>> GetManyWithCollectionsByOrganizationIdAsync(Guid organizationId) { var groups = await GetManyByOrganizationIdAsync(organizationId); @@ -100,7 +102,7 @@ from cg in dbContext.CollectionGroups var collections = query.GroupBy(c => c.GroupId).ToList(); return groups.Select(group => - new Tuple>( + new Tuple>( group, collections .FirstOrDefault(c => c.Key == group.Id)? @@ -115,7 +117,7 @@ from cg in dbContext.CollectionGroups } } - public async Task> GetManyByManyIds(IEnumerable groupIds) + public async Task> GetManyByManyIds(IEnumerable groupIds) { using (var scope = ServiceScopeFactory.CreateScope()) { @@ -124,11 +126,11 @@ from cg in dbContext.CollectionGroups where groupIds.Contains(g.Id) select g; var groups = await query.ToListAsync(); - return Mapper.Map>(groups); + return Mapper.Map>(groups); } } - public async Task> GetManyGroupUsersByOrganizationIdAsync(Guid organizationId) + public async Task> GetManyGroupUsersByOrganizationIdAsync(Guid organizationId) { using (var scope = ServiceScopeFactory.CreateScope()) { @@ -140,7 +142,7 @@ on gu.GroupId equals g.Id where g.OrganizationId == organizationId select gu; var groupUsers = await query.ToListAsync(); - return Mapper.Map>(groupUsers); + return Mapper.Map>(groupUsers); } } @@ -172,7 +174,7 @@ from gu in dbContext.GroupUsers } } - public async Task ReplaceAsync(Core.Entities.Group group, IEnumerable requestedCollections) + public async Task ReplaceAsync(AdminConsoleEntities.Group group, IEnumerable requestedCollections) { await base.ReplaceAsync(group); using (var scope = ServiceScopeFactory.CreateScope()) diff --git a/src/Infrastructure.EntityFramework/Repositories/ProviderOrganizationRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/ProviderOrganizationRepository.cs similarity index 84% rename from src/Infrastructure.EntityFramework/Repositories/ProviderOrganizationRepository.cs rename to src/Infrastructure.EntityFramework/AdminConsole/Repositories/ProviderOrganizationRepository.cs index c5a6d34e2796..77f5f8edc153 100644 --- a/src/Infrastructure.EntityFramework/Repositories/ProviderOrganizationRepository.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/ProviderOrganizationRepository.cs @@ -1,15 +1,16 @@ using AutoMapper; -using Bit.Core.Entities.Provider; -using Bit.Core.Models.Data; -using Bit.Core.Repositories; -using Bit.Infrastructure.EntityFramework.Repositories.Queries; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.AdminConsole.Repositories; +using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories.Queries; +using Bit.Infrastructure.EntityFramework.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -namespace Bit.Infrastructure.EntityFramework.Repositories; +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; public class ProviderOrganizationRepository : - Repository, IProviderOrganizationRepository + Repository, IProviderOrganizationRepository { public ProviderOrganizationRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) : base(serviceScopeFactory, mapper, context => context.ProviderOrganizations) diff --git a/src/Infrastructure.EntityFramework/Repositories/ProviderRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/ProviderRepository.cs similarity index 90% rename from src/Infrastructure.EntityFramework/Repositories/ProviderRepository.cs rename to src/Infrastructure.EntityFramework/AdminConsole/Repositories/ProviderRepository.cs index 8f66100738e7..3c2ac73b83f7 100644 --- a/src/Infrastructure.EntityFramework/Repositories/ProviderRepository.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/ProviderRepository.cs @@ -1,13 +1,14 @@ using AutoMapper; -using Bit.Core.Entities.Provider; -using Bit.Core.Models.Data; -using Bit.Core.Repositories; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.AdminConsole.Repositories; +using Bit.Infrastructure.EntityFramework.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -namespace Bit.Infrastructure.EntityFramework.Repositories; +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; -public class ProviderRepository : Repository, IProviderRepository +public class ProviderRepository : Repository, IProviderRepository { public ProviderRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) diff --git a/src/Infrastructure.EntityFramework/Repositories/ProviderUserRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/ProviderUserRepository.cs similarity index 94% rename from src/Infrastructure.EntityFramework/Repositories/ProviderUserRepository.cs rename to src/Infrastructure.EntityFramework/AdminConsole/Repositories/ProviderUserRepository.cs index e858347fe292..ad4422da63af 100644 --- a/src/Infrastructure.EntityFramework/Repositories/ProviderUserRepository.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/ProviderUserRepository.cs @@ -1,16 +1,18 @@ using AutoMapper; -using Bit.Core.Entities.Provider; -using Bit.Core.Enums.Provider; -using Bit.Core.Models.Data; -using Bit.Core.Repositories; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.AdminConsole.Repositories; +using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories.Queries; +using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Infrastructure.EntityFramework.Repositories.Queries; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -namespace Bit.Infrastructure.EntityFramework.Repositories; +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; public class ProviderUserRepository : - Repository, IProviderUserRepository + Repository, IProviderUserRepository { public ProviderUserRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.ProviderUsers) diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/ProviderOrganizationCountByOrganizationIdsQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderOrganizationCountByOrganizationIdsQuery.cs similarity index 70% rename from src/Infrastructure.EntityFramework/Repositories/Queries/ProviderOrganizationCountByOrganizationIdsQuery.cs rename to src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderOrganizationCountByOrganizationIdsQuery.cs index 4d9357f2f70d..3f39f2fc520e 100644 --- a/src/Infrastructure.EntityFramework/Repositories/Queries/ProviderOrganizationCountByOrganizationIdsQuery.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderOrganizationCountByOrganizationIdsQuery.cs @@ -1,6 +1,8 @@ -using Bit.Core.Entities.Provider; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Infrastructure.EntityFramework.Repositories; +using Bit.Infrastructure.EntityFramework.Repositories.Queries; -namespace Bit.Infrastructure.EntityFramework.Repositories.Queries; +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories.Queries; public class ProviderOrganizationCountByOrganizationIdsQuery : IQuery { diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/ProviderOrganizationOrganizationDetailsReadByProviderIdQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderOrganizationOrganizationDetailsReadByProviderIdQuery.cs similarity index 85% rename from src/Infrastructure.EntityFramework/Repositories/Queries/ProviderOrganizationOrganizationDetailsReadByProviderIdQuery.cs rename to src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderOrganizationOrganizationDetailsReadByProviderIdQuery.cs index 04d690a74340..af42114ad8a2 100644 --- a/src/Infrastructure.EntityFramework/Repositories/Queries/ProviderOrganizationOrganizationDetailsReadByProviderIdQuery.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderOrganizationOrganizationDetailsReadByProviderIdQuery.cs @@ -1,6 +1,8 @@ -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Infrastructure.EntityFramework.Repositories; +using Bit.Infrastructure.EntityFramework.Repositories.Queries; -namespace Bit.Infrastructure.EntityFramework.Repositories.Queries; +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories.Queries; public class ProviderOrganizationOrganizationDetailsReadByProviderIdQuery : IQuery { diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/ProviderOrganizationReadByUserIdQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderOrganizationReadByUserIdQuery.cs similarity index 80% rename from src/Infrastructure.EntityFramework/Repositories/Queries/ProviderOrganizationReadByUserIdQuery.cs rename to src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderOrganizationReadByUserIdQuery.cs index d28e178ab088..896be67a0598 100644 --- a/src/Infrastructure.EntityFramework/Repositories/Queries/ProviderOrganizationReadByUserIdQuery.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderOrganizationReadByUserIdQuery.cs @@ -1,6 +1,8 @@ -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Infrastructure.EntityFramework.Repositories; +using Bit.Infrastructure.EntityFramework.Repositories.Queries; -namespace Bit.Infrastructure.EntityFramework.Repositories.Queries; +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories.Queries; public class ProviderOrganizationReadByUserIdQuery : IQuery { diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs similarity index 87% rename from src/Infrastructure.EntityFramework/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs rename to src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs index d578012f42ab..95a83968bc4e 100644 --- a/src/Infrastructure.EntityFramework/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs @@ -1,6 +1,8 @@ -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Infrastructure.EntityFramework.Repositories; +using Bit.Infrastructure.EntityFramework.Repositories.Queries; -namespace Bit.Infrastructure.EntityFramework.Repositories.Queries; +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories.Queries; public class ProviderUserOrganizationDetailsViewQuery : IQuery { diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/ProviderUserProviderDetailsReadByUserIdStatusQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserProviderDetailsReadByUserIdStatusQuery.cs similarity index 81% rename from src/Infrastructure.EntityFramework/Repositories/Queries/ProviderUserProviderDetailsReadByUserIdStatusQuery.cs rename to src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserProviderDetailsReadByUserIdStatusQuery.cs index 1cae8437ac3a..6469ffc9ce9e 100644 --- a/src/Infrastructure.EntityFramework/Repositories/Queries/ProviderUserProviderDetailsReadByUserIdStatusQuery.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserProviderDetailsReadByUserIdStatusQuery.cs @@ -1,7 +1,9 @@ -using Bit.Core.Enums.Provider; -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Infrastructure.EntityFramework.Repositories; +using Bit.Infrastructure.EntityFramework.Repositories.Queries; -namespace Bit.Infrastructure.EntityFramework.Repositories.Queries; +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories.Queries; public class ProviderUserProviderDetailsReadByUserIdStatusQuery : IQuery { diff --git a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs index cda0207dcf42..9123100aed20 100644 --- a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs +++ b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs @@ -1,9 +1,11 @@ -using Bit.Core.Auth.Repositories; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Auth.Repositories; using Bit.Core.Enums; using Bit.Core.Repositories; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Tools.Repositories; using Bit.Core.Vault.Repositories; +using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; using Bit.Infrastructure.EntityFramework.Auth.Repositories; using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Infrastructure.EntityFramework.SecretsManager.Repositories; diff --git a/src/Infrastructure.EntityFramework/Models/Group.cs b/src/Infrastructure.EntityFramework/Models/Group.cs index eaa41bed8266..8df9dbd7b5a6 100644 --- a/src/Infrastructure.EntityFramework/Models/Group.cs +++ b/src/Infrastructure.EntityFramework/Models/Group.cs @@ -2,7 +2,7 @@ namespace Bit.Infrastructure.EntityFramework.Models; -public class Group : Core.Entities.Group +public class Group : Core.AdminConsole.Entities.Group { public virtual Organization Organization { get; set; } public virtual ICollection GroupUsers { get; set; } @@ -12,6 +12,6 @@ public class GroupMapperProfile : Profile { public GroupMapperProfile() { - CreateMap().ReverseMap(); + CreateMap().ReverseMap(); } } diff --git a/src/Infrastructure.EntityFramework/Models/GroupUser.cs b/src/Infrastructure.EntityFramework/Models/GroupUser.cs index 3f25e7d87645..4499b20f8a86 100644 --- a/src/Infrastructure.EntityFramework/Models/GroupUser.cs +++ b/src/Infrastructure.EntityFramework/Models/GroupUser.cs @@ -2,7 +2,7 @@ namespace Bit.Infrastructure.EntityFramework.Models; -public class GroupUser : Core.Entities.GroupUser +public class GroupUser : Core.AdminConsole.Entities.GroupUser { public virtual Group Group { get; set; } public virtual OrganizationUser OrganizationUser { get; set; } @@ -12,7 +12,7 @@ public class GroupUserMapperProfile : Profile { public GroupUserMapperProfile() { - CreateMap().ReverseMap(); + CreateMap().ReverseMap(); } } diff --git a/src/Infrastructure.EntityFramework/Models/Provider/Provider.cs b/src/Infrastructure.EntityFramework/Models/Provider/Provider.cs deleted file mode 100644 index d639d6d01dc2..000000000000 --- a/src/Infrastructure.EntityFramework/Models/Provider/Provider.cs +++ /dev/null @@ -1,15 +0,0 @@ -using AutoMapper; - -namespace Bit.Infrastructure.EntityFramework.Models; - -public class Provider : Core.Entities.Provider.Provider -{ -} - -public class ProviderMapperProfile : Profile -{ - public ProviderMapperProfile() - { - CreateMap().ReverseMap(); - } -} diff --git a/src/Infrastructure.EntityFramework/Models/Provider/ProviderOrganization.cs b/src/Infrastructure.EntityFramework/Models/Provider/ProviderOrganization.cs deleted file mode 100644 index af23ba978c7f..000000000000 --- a/src/Infrastructure.EntityFramework/Models/Provider/ProviderOrganization.cs +++ /dev/null @@ -1,17 +0,0 @@ -using AutoMapper; - -namespace Bit.Infrastructure.EntityFramework.Models; - -public class ProviderOrganization : Core.Entities.Provider.ProviderOrganization -{ - public virtual Provider Provider { get; set; } - public virtual Organization Organization { get; set; } -} - -public class ProviderOrganizationMapperProfile : Profile -{ - public ProviderOrganizationMapperProfile() - { - CreateMap().ReverseMap(); - } -} diff --git a/src/Infrastructure.EntityFramework/Models/Provider/ProviderUser.cs b/src/Infrastructure.EntityFramework/Models/Provider/ProviderUser.cs deleted file mode 100644 index 5c53c4d97969..000000000000 --- a/src/Infrastructure.EntityFramework/Models/Provider/ProviderUser.cs +++ /dev/null @@ -1,17 +0,0 @@ -using AutoMapper; - -namespace Bit.Infrastructure.EntityFramework.Models; - -public class ProviderUser : Core.Entities.Provider.ProviderUser -{ - public virtual User User { get; set; } - public virtual Provider Provider { get; set; } -} - -public class ProviderUserMapperProfile : Profile -{ - public ProviderUserMapperProfile() - { - CreateMap().ReverseMap(); - } -} diff --git a/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs b/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs index 1abaaefe1190..f3d7c6ce1484 100644 --- a/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs +++ b/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs @@ -1,4 +1,5 @@ using Bit.Core; +using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider; using Bit.Infrastructure.EntityFramework.Auth.Models; using Bit.Infrastructure.EntityFramework.Converters; using Bit.Infrastructure.EntityFramework.Models; diff --git a/src/Infrastructure.EntityFramework/Repositories/DatabaseContextExtensions.cs b/src/Infrastructure.EntityFramework/Repositories/DatabaseContextExtensions.cs index 480855d61c2f..7280642596f1 100644 --- a/src/Infrastructure.EntityFramework/Repositories/DatabaseContextExtensions.cs +++ b/src/Infrastructure.EntityFramework/Repositories/DatabaseContextExtensions.cs @@ -1,6 +1,6 @@ -using Bit.Core.Auth.Enums; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.Auth.Enums; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Infrastructure.EntityFramework.Repositories.Queries; using Microsoft.EntityFrameworkCore; diff --git a/src/Infrastructure.EntityFramework/Repositories/EventRepository.cs b/src/Infrastructure.EntityFramework/Repositories/EventRepository.cs index a2e56b370098..3e3ebb21aec0 100644 --- a/src/Infrastructure.EntityFramework/Repositories/EventRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/EventRepository.cs @@ -49,6 +49,32 @@ public async Task CreateManyAsync(IEnumerable entities) } } + public async Task> GetManyByOrganizationServiceAccountAsync(Guid organizationId, Guid serviceAccountId, + DateTime startDate, DateTime endDate, + PageOptions pageOptions) + { + DateTime? beforeDate = null; + if (!string.IsNullOrWhiteSpace(pageOptions.ContinuationToken) && + long.TryParse(pageOptions.ContinuationToken, out var binaryDate)) + { + beforeDate = DateTime.SpecifyKind(DateTime.FromBinary(binaryDate), DateTimeKind.Utc); + } + + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + var query = new EventReadPageByOrganizationIdServiceAccountIdQuery(organizationId, serviceAccountId, + startDate, endDate, beforeDate, pageOptions); + var events = await query.Run(dbContext).ToListAsync(); + + var result = new PagedResult(); + if (events.Any() && events.Count >= pageOptions.PageSize) + { + result.ContinuationToken = events.Last().Date.ToBinary().ToString(); + } + result.Data.AddRange(events); + return result; + } + public async Task> GetManyByCipherAsync(Cipher cipher, DateTime startDate, DateTime endDate, PageOptions pageOptions) { DateTime? beforeDate = null; diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationConnectionRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationConnectionRepository.cs index 298e28e02926..ad8359758bb5 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationConnectionRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationConnectionRepository.cs @@ -15,6 +15,17 @@ public OrganizationConnectionRepository(IServiceScopeFactory serviceScopeFactory { } + public async Task GetByIdOrganizationIdAsync(Guid id, Guid organizationId) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var connection = await dbContext.OrganizationConnections + .FirstOrDefaultAsync(oc => oc.Id == id && oc.OrganizationId == organizationId); + return Mapper.Map(connection); + } + } + public async Task> GetByOrganizationIdTypeAsync(Guid organizationId, OrganizationConnectionType type) { using (var scope = ServiceScopeFactory.CreateScope()) diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs index ea507d5531ca..11ff8e04781e 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs @@ -93,6 +93,18 @@ from s in sJoin.DefaultIfEmpty() return ssoDetails; } + public async Task GetDomainByIdOrganizationIdAsync(Guid id, Guid orgId) + { + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + var domain = await dbContext.OrganizationDomains + .Where(x => x.Id == id && x.OrganizationId == orgId) + .AsNoTracking() + .FirstOrDefaultAsync(); + + return Mapper.Map(domain); + } + public async Task GetDomainByOrgIdAndDomainNameAsync(Guid orgId, string domainName) { using var scope = ServiceScopeFactory.CreateScope(); diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs index 702f9bea53a9..62f4df63e366 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs @@ -144,27 +144,42 @@ public override async Task DeleteAsync(Core.Entities.Organization organization) var dbContext = GetDatabaseContext(scope); await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(organization.Id); var deleteCiphersTransaction = await dbContext.Database.BeginTransactionAsync(); - dbContext.Ciphers.RemoveRange( - dbContext.Ciphers.Where(c => c.UserId == null && c.OrganizationId == organization.Id)); + await dbContext.Ciphers.Where(c => c.UserId == null && c.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); await deleteCiphersTransaction.CommitAsync(); + var organizationDeleteTransaction = await dbContext.Database.BeginTransactionAsync(); - dbContext.SsoUsers.RemoveRange(dbContext.SsoUsers.Where(su => su.OrganizationId == organization.Id)); - dbContext.SsoConfigs.RemoveRange(dbContext.SsoConfigs.Where(sc => sc.OrganizationId == organization.Id)); - var collectionUsers = from cu in dbContext.CollectionUsers - join ou in dbContext.OrganizationUsers on cu.OrganizationUserId equals ou.Id - where ou.OrganizationId == organization.Id - select cu; - dbContext.CollectionUsers.RemoveRange(collectionUsers); - dbContext.OrganizationUsers.RemoveRange( - dbContext.OrganizationUsers.Where(ou => ou.OrganizationId == organization.Id)); - dbContext.ProviderOrganizations.RemoveRange( - dbContext.ProviderOrganizations.Where(po => po.OrganizationId == organization.Id)); + await dbContext.SsoUsers.Where(su => su.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.SsoConfigs.Where(sc => sc.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.CollectionUsers.Where(cu => cu.OrganizationUser.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.UserProjectAccessPolicy.Where(ap => ap.OrganizationUser.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.UserServiceAccountAccessPolicy.Where(ap => ap.OrganizationUser.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.OrganizationUsers.Where(ou => ou.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.ProviderOrganizations.Where(po => po.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + + await dbContext.GroupServiceAccountAccessPolicy.Where(ap => ap.GrantedServiceAccount.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.Project.Where(p => p.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.Secret.Where(s => s.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.ApiKeys.Where(ak => ak.ServiceAccount.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.ServiceAccount.Where(sa => sa.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); // The below section are 3 SPROCS in SQL Server but are only called by here - dbContext.OrganizationApiKeys.RemoveRange( - dbContext.OrganizationApiKeys.Where(oa => oa.OrganizationId == organization.Id)); - dbContext.OrganizationConnections.RemoveRange( - dbContext.OrganizationConnections.Where(oc => oc.OrganizationId == organization.Id)); + await dbContext.OrganizationApiKeys.Where(oa => oa.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); + await dbContext.OrganizationConnections.Where(oc => oc.OrganizationId == organization.Id) + .ExecuteDeleteAsync(); var sponsoringOrgs = await dbContext.OrganizationSponsorships .Where(os => os.SponsoringOrganizationId == organization.Id) .ToListAsync(); @@ -209,4 +224,24 @@ public async Task GetSelfHostedOrganizationDetail return selfHostedOrganization; } } + + public async Task> GetOwnerEmailAddressesById(Guid organizationId) + { + using var scope = ServiceScopeFactory.CreateScope(); + + var dbContext = GetDatabaseContext(scope); + + var query = + from u in dbContext.Users + join ou in dbContext.OrganizationUsers on u.Id equals ou.UserId + where + ou.OrganizationId == organizationId && + ou.Type == OrganizationUserType.Owner && + ou.Status == OrganizationUserStatusType.Confirmed + group u by u.Email + into grouped + select grouped.Key; + + return await query.ToListAsync(); + } } diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs index 8256696d9686..b3adada814cf 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs @@ -93,6 +93,11 @@ public async Task DeleteAsync(Guid organizationUserId) .Where(gu => gu.OrganizationUserId == organizationUserId); dbContext.GroupUsers.RemoveRange(groupUsers); + dbContext.UserProjectAccessPolicy.RemoveRange( + dbContext.UserProjectAccessPolicy.Where(ap => ap.OrganizationUserId == organizationUserId)); + dbContext.UserServiceAccountAccessPolicy.RemoveRange( + dbContext.UserServiceAccountAccessPolicy.Where(ap => ap.OrganizationUserId == organizationUserId)); + var orgSponsorships = await dbContext.OrganizationSponsorships .Where(os => os.SponsoringOrganizationUserId == organizationUserId) .ToListAsync(); @@ -325,7 +330,7 @@ public async Task> GetManyDetailsByOrga var userIds = users.Select(u => u.Id); var userIdEntities = dbContext.OrganizationUsers.Where(x => userIds.Contains(x.Id)); - // Query groups/collections separately to avoid cartesian explosion + // Query groups/collections separately to avoid cartesian explosion if (includeGroups) { groups = (await (from gu in dbContext.GroupUsers diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/EventReadPageByOrganizationIdServiceAccountIdQuery.cs b/src/Infrastructure.EntityFramework/Repositories/Queries/EventReadPageByOrganizationIdServiceAccountIdQuery.cs new file mode 100644 index 000000000000..01f3a1fe1491 --- /dev/null +++ b/src/Infrastructure.EntityFramework/Repositories/Queries/EventReadPageByOrganizationIdServiceAccountIdQuery.cs @@ -0,0 +1,38 @@ +using Bit.Core.Models.Data; +using Bit.Infrastructure.EntityFramework.Models; + +namespace Bit.Infrastructure.EntityFramework.Repositories.Queries; + +public class EventReadPageByOrganizationIdServiceAccountIdQuery : IQuery +{ + private readonly Guid _organizationId; + private readonly Guid _serviceAccountId; + private readonly DateTime _startDate; + private readonly DateTime _endDate; + private readonly DateTime? _beforeDate; + private readonly PageOptions _pageOptions; + + public EventReadPageByOrganizationIdServiceAccountIdQuery(Guid organizationId, Guid serviceAccountId, + DateTime startDate, DateTime endDate, DateTime? beforeDate, PageOptions pageOptions) + { + _organizationId = organizationId; + _serviceAccountId = serviceAccountId; + _startDate = startDate; + _endDate = endDate; + _beforeDate = beforeDate; + _pageOptions = pageOptions; + } + + public IQueryable Run(DatabaseContext dbContext) + { + var q = from e in dbContext.Events + where e.Date >= _startDate && + (_beforeDate != null || e.Date <= _endDate) && + (_beforeDate == null || e.Date < _beforeDate.Value) && + e.OrganizationId == _organizationId && + e.ServiceAccountId == _serviceAccountId + orderby e.Date descending + select e; + return q.Skip(0).Take(_pageOptions.PageSize); + } +} diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs b/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs index faf84f4f97f5..d1f8b0adf7b9 100644 --- a/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs +++ b/src/Infrastructure.EntityFramework/Repositories/Queries/UserCipherDetailsQuery.cs @@ -73,6 +73,7 @@ from cg in cg_g.DefaultIfEmpty() Reprompt = c.Reprompt, ViewPassword = true, OrganizationUseTotp = false, + Key = c.Key }); return union; } diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/UserReadPublicKeysByProviderUserIdsQuery.cs b/src/Infrastructure.EntityFramework/Repositories/Queries/UserReadPublicKeysByProviderUserIdsQuery.cs index db347b99b4a2..bb54520f4b04 100644 --- a/src/Infrastructure.EntityFramework/Repositories/Queries/UserReadPublicKeysByProviderUserIdsQuery.cs +++ b/src/Infrastructure.EntityFramework/Repositories/Queries/UserReadPublicKeysByProviderUserIdsQuery.cs @@ -1,5 +1,5 @@ -using Bit.Core.Enums.Provider; -using Bit.Core.Models.Data; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; namespace Bit.Infrastructure.EntityFramework.Repositories.Queries; diff --git a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs index ba422c789b7c..b29153e47c27 100644 --- a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs @@ -167,6 +167,10 @@ join ou in dbContext.OrganizationUsers on gu.OrganizationUserId equals ou.Id where ou.UserId == user.Id select gu; dbContext.GroupUsers.RemoveRange(groupUsers); + dbContext.UserProjectAccessPolicy.RemoveRange( + dbContext.UserProjectAccessPolicy.Where(ap => ap.OrganizationUser.UserId == user.Id)); + dbContext.UserServiceAccountAccessPolicy.RemoveRange( + dbContext.UserServiceAccountAccessPolicy.Where(ap => ap.OrganizationUser.UserId == user.Id)); dbContext.OrganizationUsers.RemoveRange(dbContext.OrganizationUsers.Where(ou => ou.UserId == user.Id)); dbContext.ProviderUsers.RemoveRange(dbContext.ProviderUsers.Where(pu => pu.UserId == user.Id)); dbContext.SsoUsers.RemoveRange(dbContext.SsoUsers.Where(su => su.UserId == user.Id)); diff --git a/src/Infrastructure.EntityFramework/SecretsManager/Configurations/AccessPolicyEntityTypeConfiguration.cs b/src/Infrastructure.EntityFramework/SecretsManager/Configurations/AccessPolicyEntityTypeConfiguration.cs index dd7f01f5d5b5..9e8732a8543f 100644 --- a/src/Infrastructure.EntityFramework/SecretsManager/Configurations/AccessPolicyEntityTypeConfiguration.cs +++ b/src/Infrastructure.EntityFramework/SecretsManager/Configurations/AccessPolicyEntityTypeConfiguration.cs @@ -39,6 +39,12 @@ public void Configure(EntityTypeBuilder builder) builder .Property(e => e.GrantedProjectId) .HasColumnName(nameof(UserProjectAccessPolicy.GrantedProjectId)); + + builder + .HasOne(e => e.GrantedProject) + .WithMany(e => e.UserAccessPolicies) + .HasForeignKey(nameof(UserProjectAccessPolicy.GrantedProjectId)) + .OnDelete(DeleteBehavior.Cascade); } } @@ -67,6 +73,18 @@ public void Configure(EntityTypeBuilder builder) builder .Property(e => e.GrantedProjectId) .HasColumnName(nameof(GroupProjectAccessPolicy.GrantedProjectId)); + + builder + .HasOne(e => e.GrantedProject) + .WithMany(e => e.GroupAccessPolicies) + .HasForeignKey(nameof(GroupProjectAccessPolicy.GrantedProjectId)) + .OnDelete(DeleteBehavior.Cascade); + + builder + .HasOne(e => e.Group) + .WithMany() + .HasForeignKey(nameof(GroupProjectAccessPolicy.GroupId)) + .OnDelete(DeleteBehavior.Cascade); } } @@ -81,6 +99,12 @@ public void Configure(EntityTypeBuilder builder builder .Property(e => e.GrantedServiceAccountId) .HasColumnName(nameof(GroupServiceAccountAccessPolicy.GrantedServiceAccountId)); + + builder + .HasOne(e => e.Group) + .WithMany() + .HasForeignKey(nameof(GroupProjectAccessPolicy.GroupId)) + .OnDelete(DeleteBehavior.Cascade); } } @@ -95,5 +119,11 @@ public void Configure(EntityTypeBuilder build builder .Property(e => e.GrantedProjectId) .HasColumnName(nameof(ServiceAccountProjectAccessPolicy.GrantedProjectId)); + + builder + .HasOne(e => e.GrantedProject) + .WithMany(e => e.ServiceAccountAccessPolicies) + .HasForeignKey(nameof(ServiceAccountProjectAccessPolicy.GrantedProjectId)) + .OnDelete(DeleteBehavior.Cascade); } } diff --git a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs index 07d8576fe972..c575838362f4 100644 --- a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs +++ b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs @@ -366,6 +366,7 @@ public async Task> GetManyByUserIdAsync(Guid userId, Reprompt = c.Reprompt, ViewPassword = true, OrganizationUseTotp = false, + Key = c.Key }; } var ciphers = await cipherDetailsView.ToListAsync(); @@ -591,6 +592,7 @@ public async Task ReplaceAsync(Core.Vault.Entities.Cipher cipher, IEnumera trackedCipher.Attachments = cipher.Attachments; trackedCipher.RevisionDate = cipher.RevisionDate; trackedCipher.DeletedDate = cipher.DeletedDate; + trackedCipher.Key = cipher.Key; await transaction.CommitAsync(); diff --git a/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs b/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs index 49cbc20f4249..b9de76d3ff15 100644 --- a/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs +++ b/src/Infrastructure.EntityFramework/Vault/Repositories/Queries/CipherDetailsQuery.cs @@ -29,6 +29,7 @@ public virtual IQueryable Run(DatabaseContext dbContext) RevisionDate = c.RevisionDate, DeletedDate = c.DeletedDate, Reprompt = c.Reprompt, + Key = c.Key, Favorite = _userId.HasValue && c.Favorites != null && c.Favorites.ToLowerInvariant().Contains($"\"{_userId}\":true"), FolderId = (_ignoreFolders || !_userId.HasValue || c.Folders == null || !c.Folders.ToLowerInvariant().Contains(_userId.Value.ToString())) ? null : diff --git a/src/Infrastructure.EntityFramework/packages.lock.json b/src/Infrastructure.EntityFramework/packages.lock.json index 0e133d9ebb41..90a67e5d8627 100644 --- a/src/Infrastructure.EntityFramework/packages.lock.json +++ b/src/Infrastructure.EntityFramework/packages.lock.json @@ -221,10 +221,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2599,7 +2600,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", diff --git a/src/Notifications/packages.lock.json b/src/Notifications/packages.lock.json index c6fd56a84b12..3da9edc48ef3 100644 --- a/src/Notifications/packages.lock.json +++ b/src/Notifications/packages.lock.json @@ -179,10 +179,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2647,7 +2648,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2680,7 +2681,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2688,7 +2689,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2700,9 +2701,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index f3005e7759f8..e49bf9192168 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -3,6 +3,9 @@ using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using AspNetCoreRateLimit; +using Bit.Core.AdminConsole.Services; +using Bit.Core.AdminConsole.Services.Implementations; +using Bit.Core.AdminConsole.Services.NoopImplementations; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Identity; using Bit.Core.Auth.IdentityServer; diff --git a/src/SharedWeb/packages.lock.json b/src/SharedWeb/packages.lock.json index 6b0c71b02276..08d941f5b0cb 100644 --- a/src/SharedWeb/packages.lock.json +++ b/src/SharedWeb/packages.lock.json @@ -158,10 +158,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2597,7 +2598,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2630,7 +2631,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2638,7 +2639,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", diff --git a/src/Sql/SecretsManager/dbo/Stored Procedures/Event/Event_ReadPageByOrganizationIdServiceAccountId.sql b/src/Sql/SecretsManager/dbo/Stored Procedures/Event/Event_ReadPageByOrganizationIdServiceAccountId.sql new file mode 100644 index 000000000000..5dc950ffff62 --- /dev/null +++ b/src/Sql/SecretsManager/dbo/Stored Procedures/Event/Event_ReadPageByOrganizationIdServiceAccountId.sql @@ -0,0 +1,25 @@ +CREATE PROCEDURE [dbo].[Event_ReadPageByOrganizationIdServiceAccountId] + @OrganizationId UNIQUEIDENTIFIER, + @ServiceAccountId UNIQUEIDENTIFIER, + @StartDate DATETIME2(7), + @EndDate DATETIME2(7), + @BeforeDate DATETIME2(7), + @PageSize INT +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[EventView] + WHERE + [Date] >= @StartDate + AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate) + AND (@BeforeDate IS NULL OR [Date] < @BeforeDate) + AND [OrganizationId] = @OrganizationId + AND [ServiceAccountId] = @ServiceAccountId + ORDER BY [Date] DESC + OFFSET 0 ROWS + FETCH NEXT @PageSize ROWS ONLY +END diff --git a/src/Sql/Vault/dbo/Functions/CipherDetails.sql b/src/Sql/Vault/dbo/Functions/CipherDetails.sql index f32e4bdfc031..5577ff4787fc 100644 --- a/src/Sql/Vault/dbo/Functions/CipherDetails.sql +++ b/src/Sql/Vault/dbo/Functions/CipherDetails.sql @@ -26,6 +26,7 @@ SELECT ELSE TRY_CONVERT(UNIQUEIDENTIFIER, JSON_VALUE(C.[Folders], CONCAT('$."', @UserId, '"'))) END [FolderId], C.[DeletedDate], - C.[Reprompt] + C.[Reprompt], + C.[Key] FROM [dbo].[Cipher] C \ No newline at end of file diff --git a/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Create.sql b/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Create.sql index 0aaef08ff257..a4450036fdf7 100644 --- a/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Create.sql +++ b/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Create.sql @@ -15,7 +15,8 @@ @ViewPassword BIT, -- not used @OrganizationUseTotp BIT, -- not used @DeletedDate DATETIME2(7), - @Reprompt TINYINT + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL AS BEGIN SET NOCOUNT ON @@ -35,7 +36,8 @@ BEGIN [CreationDate], [RevisionDate], [DeletedDate], - [Reprompt] + [Reprompt], + [Key] ) VALUES ( @@ -49,7 +51,8 @@ BEGIN @CreationDate, @RevisionDate, @DeletedDate, - @Reprompt + @Reprompt, + @Key ) IF @OrganizationId IS NOT NULL diff --git a/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql b/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql index 8516905eb75e..a88153a71fe5 100644 --- a/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql +++ b/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_CreateWithCollections.sql @@ -16,6 +16,7 @@ @OrganizationUseTotp BIT, -- not used @DeletedDate DATETIME2(7), @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN @@ -23,7 +24,7 @@ BEGIN EXEC [dbo].[CipherDetails_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders, @Attachments, @CreationDate, @RevisionDate, @FolderId, @Favorite, @Edit, @ViewPassword, - @OrganizationUseTotp, @DeletedDate, @Reprompt + @OrganizationUseTotp, @DeletedDate, @Reprompt, @Key DECLARE @UpdateCollectionsSuccess INT EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds diff --git a/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Update.sql b/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Update.sql index 7ec2ae6fa3f1..11113b2a46f2 100644 --- a/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Update.sql +++ b/src/Sql/Vault/dbo/Stored Procedures/Cipher/CipherDetails_Update.sql @@ -6,7 +6,7 @@ @Data NVARCHAR(MAX), @Favorites NVARCHAR(MAX), -- not used @Folders NVARCHAR(MAX), -- not used - @Attachments NVARCHAR(MAX), -- not used + @Attachments NVARCHAR(MAX), @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7), @FolderId UNIQUEIDENTIFIER, @@ -15,7 +15,8 @@ @ViewPassword BIT, -- not used @OrganizationUseTotp BIT, -- not used @DeletedDate DATETIME2(2), - @Reprompt TINYINT + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL AS BEGIN SET NOCOUNT ON @@ -48,10 +49,12 @@ BEGIN ELSE JSON_MODIFY([Favorites], @UserIdPath, NULL) END, + [Attachments] = @Attachments, [Reprompt] = @Reprompt, [CreationDate] = @CreationDate, [RevisionDate] = @RevisionDate, - [DeletedDate] = @DeletedDate + [DeletedDate] = @DeletedDate, + [Key] = @Key WHERE [Id] = @Id diff --git a/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Create.sql b/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Create.sql index 8bf7ce3a7756..c03c27ea7873 100644 --- a/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Create.sql +++ b/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Create.sql @@ -10,7 +10,8 @@ @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7), @DeletedDate DATETIME2(7), - @Reprompt TINYINT + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL AS BEGIN SET NOCOUNT ON @@ -28,7 +29,8 @@ BEGIN [CreationDate], [RevisionDate], [DeletedDate], - [Reprompt] + [Reprompt], + [Key] ) VALUES ( @@ -43,7 +45,8 @@ BEGIN @CreationDate, @RevisionDate, @DeletedDate, - @Reprompt + @Reprompt, + @Key ) IF @OrganizationId IS NOT NULL diff --git a/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql b/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql index 5e64fc4146b6..775ab0e0a0ab 100644 --- a/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql +++ b/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_CreateWithCollections.sql @@ -11,13 +11,14 @@ @RevisionDate DATETIME2(7), @DeletedDate DATETIME2(7), @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN SET NOCOUNT ON EXEC [dbo].[Cipher_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders, - @Attachments, @CreationDate, @RevisionDate, @DeletedDate, @Reprompt + @Attachments, @CreationDate, @RevisionDate, @DeletedDate, @Reprompt, @Key DECLARE @UpdateCollectionsSuccess INT EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds diff --git a/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Update.sql b/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Update.sql index e5d3e591697f..7815aa3053e5 100644 --- a/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Update.sql +++ b/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_Update.sql @@ -10,7 +10,8 @@ @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7), @DeletedDate DATETIME2(7), - @Reprompt TINYINT + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL AS BEGIN SET NOCOUNT ON @@ -28,7 +29,8 @@ BEGIN [CreationDate] = @CreationDate, [RevisionDate] = @RevisionDate, [DeletedDate] = @DeletedDate, - [Reprompt] = @Reprompt + [Reprompt] = @Reprompt, + [Key] = @Key WHERE [Id] = @Id diff --git a/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql b/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql index e2eba210b2dc..0a0c980e4aa1 100644 --- a/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql +++ b/src/Sql/Vault/dbo/Stored Procedures/Cipher/Cipher_UpdateWithCollections.sql @@ -11,6 +11,7 @@ @RevisionDate DATETIME2(7), @DeletedDate DATETIME2(7), @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, @CollectionIds AS [dbo].[GuidIdArray] READONLY AS BEGIN @@ -36,7 +37,8 @@ BEGIN [Data] = @Data, [Attachments] = @Attachments, [RevisionDate] = @RevisionDate, - [DeletedDate] = @DeletedDate + [DeletedDate] = @DeletedDate, + [Key] = @Key -- No need to update CreationDate, Favorites, Folders, or Type since that data will not change WHERE [Id] = @Id diff --git a/src/Sql/Vault/dbo/Tables/Cipher.sql b/src/Sql/Vault/dbo/Tables/Cipher.sql index 3cea623a8ae1..5ecff19e70cc 100644 --- a/src/Sql/Vault/dbo/Tables/Cipher.sql +++ b/src/Sql/Vault/dbo/Tables/Cipher.sql @@ -12,6 +12,7 @@ CREATE TABLE [dbo].[Cipher] ( [RevisionDate] DATETIME2 (7) NOT NULL, [DeletedDate] DATETIME2 (7) NULL, [Reprompt] TINYINT NULL, + [Key] VARCHAR(MAX) NULL, CONSTRAINT [PK_Cipher] PRIMARY KEY CLUSTERED ([Id] ASC), CONSTRAINT [FK_Cipher_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]), CONSTRAINT [FK_Cipher_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) diff --git a/src/Sql/dbo/Stored Procedures/OrganizationConnection_ReadByIdOrganizationId.sql b/src/Sql/dbo/Stored Procedures/OrganizationConnection_ReadByIdOrganizationId.sql new file mode 100644 index 000000000000..42c41f7e117a --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationConnection_ReadByIdOrganizationId.sql @@ -0,0 +1,15 @@ +CREATE PROCEDURE [dbo].[OrganizationConnection_ReadByIdOrganizationId] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[OrganizationConnectionView] + WHERE + [Id] = @Id AND + [OrganizationId] = @OrganizationId +END diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByIdOrganizationId.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByIdOrganizationId.sql new file mode 100644 index 000000000000..7777fe6831d2 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByIdOrganizationId.sql @@ -0,0 +1,16 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_ReadByIdOrganizationId] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationDomain] +WHERE + [Id] = @Id + AND + [OrganizationId] = @OrganizationId +END diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateMany.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateMany.sql deleted file mode 100644 index 917553c34e3b..000000000000 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateMany.sql +++ /dev/null @@ -1,40 +0,0 @@ -CREATE PROCEDURE [dbo].[OrganizationUser_CreateMany] - @OrganizationUsersInput [dbo].[OrganizationUserType] READONLY -AS -BEGIN - SET NOCOUNT ON - - INSERT INTO [dbo].[OrganizationUser] - ( - [Id], - [OrganizationId], - [UserId], - [Email], - [Key], - [Status], - [Type], - [AccessAll], - [ExternalId], - [CreationDate], - [RevisionDate], - [Permissions], - [ResetPasswordKey] - ) - SELECT - OU.[Id], - OU.[OrganizationId], - OU.[UserId], - OU.[Email], - OU.[Key], - OU.[Status], - OU.[Type], - OU.[AccessAll], - OU.[ExternalId], - OU.[CreationDate], - OU.[RevisionDate], - OU.[Permissions], - OU.[ResetPasswordKey] - FROM - @OrganizationUsersInput OU -END -GO diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateMany.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateMany.sql deleted file mode 100644 index aad6ddedc7cc..000000000000 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateMany.sql +++ /dev/null @@ -1,33 +0,0 @@ -CREATE PROCEDURE [dbo].[OrganizationUser_UpdateMany] - @OrganizationUsersInput [dbo].[OrganizationUserType] READONLY -AS -BEGIN - SET NOCOUNT ON - - UPDATE - OU - SET - [OrganizationId] = OUI.[OrganizationId], - [UserId] = OUI.[UserId], - [Email] = OUI.[Email], - [Key] = OUI.[Key], - [Status] = OUI.[Status], - [Type] = OUI.[Type], - [AccessAll] = OUI.[AccessAll], - [ExternalId] = OUI.[ExternalId], - [CreationDate] = OUI.[CreationDate], - [RevisionDate] = OUI.[RevisionDate], - [Permissions] = OUI.[Permissions], - [ResetPasswordKey] = OUI.[ResetPasswordKey] - FROM - [dbo].[OrganizationUser] OU - INNER JOIN - @OrganizationUsersInput OUI ON OU.Id = OUI.Id - - EXEC [dbo].[User_BumpManyAccountRevisionDates] - ( - SELECT UserId - FROM @OrganizationUsersInput - ) -END -GO diff --git a/src/Sql/dbo/Stored Procedures/User_Create.sql b/src/Sql/dbo/Stored Procedures/User_Create.sql index 65c5d219087e..3aabab8c234e 100644 --- a/src/Sql/dbo/Stored Procedures/User_Create.sql +++ b/src/Sql/dbo/Stored Procedures/User_Create.sql @@ -36,7 +36,6 @@ @UsesKeyConnector BIT = 0, @FailedLoginCount INT = 0, @LastFailedLoginDate DATETIME2(7), - @UnknownDeviceVerificationEnabled BIT = 1, @AvatarColor VARCHAR(7) = NULL, @LastPasswordChangeDate DATETIME2(7) = NULL, @LastKdfChangeDate DATETIME2(7) = NULL, @@ -83,7 +82,6 @@ BEGIN [UsesKeyConnector], [FailedLoginCount], [LastFailedLoginDate], - [UnknownDeviceVerificationEnabled], [AvatarColor], [KdfMemory], [KdfParallelism], @@ -129,7 +127,6 @@ BEGIN @UsesKeyConnector, @FailedLoginCount, @LastFailedLoginDate, - @UnknownDeviceVerificationEnabled, @AvatarColor, @KdfMemory, @KdfParallelism, diff --git a/src/Sql/dbo/Stored Procedures/User_Update.sql b/src/Sql/dbo/Stored Procedures/User_Update.sql index 60a5119366e1..5725f243ff67 100644 --- a/src/Sql/dbo/Stored Procedures/User_Update.sql +++ b/src/Sql/dbo/Stored Procedures/User_Update.sql @@ -36,7 +36,6 @@ @UsesKeyConnector BIT = 0, @FailedLoginCount INT, @LastFailedLoginDate DATETIME2(7), - @UnknownDeviceVerificationEnabled BIT = 1, @AvatarColor VARCHAR(7), @LastPasswordChangeDate DATETIME2(7) = NULL, @LastKdfChangeDate DATETIME2(7) = NULL, @@ -85,7 +84,6 @@ BEGIN [UsesKeyConnector] = @UsesKeyConnector, [FailedLoginCount] = @FailedLoginCount, [LastFailedLoginDate] = @LastFailedLoginDate, - [UnknownDeviceVerificationEnabled] = @UnknownDeviceVerificationEnabled, [AvatarColor] = @AvatarColor, [LastPasswordChangeDate] = @LastPasswordChangeDate, [LastKdfChangeDate] = @LastKdfChangeDate, diff --git a/src/Sql/dbo/Tables/User.sql b/src/Sql/dbo/Tables/User.sql index 62ff0edf2b14..0c34784e9781 100644 --- a/src/Sql/dbo/Tables/User.sql +++ b/src/Sql/dbo/Tables/User.sql @@ -36,8 +36,7 @@ [UsesKeyConnector] BIT NOT NULL, [FailedLoginCount] INT CONSTRAINT [D_User_FailedLoginCount] DEFAULT ((0)) NOT NULL, [LastFailedLoginDate] DATETIME2 (7) NULL, - [UnknownDeviceVerificationEnabled] BIT CONSTRAINT [D_User_UnknownDeviceVerificationEnabled] DEFAULT ((1)) NOT NULL, - [AvatarColor] VARCHAR (7) NULL, + [AvatarColor] VARCHAR(7) NULL, [LastPasswordChangeDate] DATETIME2 (7) NULL, [LastKdfChangeDate] DATETIME2 (7) NULL, [LastKeyRotationDate] DATETIME2 (7) NULL, diff --git a/src/Sql/dbo/User Defined Types/OrganizationUserType.sql b/src/Sql/dbo/User Defined Types/OrganizationUserType.sql deleted file mode 100644 index 6d18cf044184..000000000000 --- a/src/Sql/dbo/User Defined Types/OrganizationUserType.sql +++ /dev/null @@ -1,15 +0,0 @@ -CREATE TYPE [dbo].[OrganizationUserType] AS TABLE( - [Id] UNIQUEIDENTIFIER, - [OrganizationId] UNIQUEIDENTIFIER, - [UserId] UNIQUEIDENTIFIER, - [Email] NVARCHAR(256), - [Key] VARCHAR(MAX), - [Status] SMALLINT, - [Type] TINYINT, - [AccessAll] BIT, - [ExternalId] NVARCHAR(300), - [CreationDate] DATETIME2(7), - [RevisionDate] DATETIME2(7), - [Permissions] NVARCHAR(MAX), - [ResetPasswordKey] VARCHAR(MAX) -) diff --git a/src/Sql/dbo_future/Functions/PolicyApplicableToUser.sql b/src/Sql/dbo_future/Functions/PolicyApplicableToUser.sql deleted file mode 100644 index a851a1e7994f..000000000000 --- a/src/Sql/dbo_future/Functions/PolicyApplicableToUser.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Created 2023-03 --- DELETE FILE \ No newline at end of file diff --git a/src/Sql/dbo_future/Stored Procedures/Collection_CreateWithGroups.sql b/src/Sql/dbo_future/Stored Procedures/Collection_CreateWithGroups.sql deleted file mode 100644 index 0c55ab99b70a..000000000000 --- a/src/Sql/dbo_future/Stored Procedures/Collection_CreateWithGroups.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Created 2022-11 --- DELETE FILE \ No newline at end of file diff --git a/src/Sql/dbo_future/Stored Procedures/Collection_ReadWithGroupsById.sql b/src/Sql/dbo_future/Stored Procedures/Collection_ReadWithGroupsById.sql deleted file mode 100644 index 0c55ab99b70a..000000000000 --- a/src/Sql/dbo_future/Stored Procedures/Collection_ReadWithGroupsById.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Created 2022-11 --- DELETE FILE \ No newline at end of file diff --git a/src/Sql/dbo_future/Stored Procedures/Collection_ReadWithGroupsByIdUserId.sql b/src/Sql/dbo_future/Stored Procedures/Collection_ReadWithGroupsByIdUserId.sql deleted file mode 100644 index 0c55ab99b70a..000000000000 --- a/src/Sql/dbo_future/Stored Procedures/Collection_ReadWithGroupsByIdUserId.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Created 2022-11 --- DELETE FILE \ No newline at end of file diff --git a/src/Sql/dbo_future/Stored Procedures/Collection_UpdateWithGroups.sql b/src/Sql/dbo_future/Stored Procedures/Collection_UpdateWithGroups.sql deleted file mode 100644 index 0c55ab99b70a..000000000000 --- a/src/Sql/dbo_future/Stored Procedures/Collection_UpdateWithGroups.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Created 2022-11 --- DELETE FILE \ No newline at end of file diff --git a/src/Sql/dbo_future/Stored Procedures/OrganizationUser_CreateMany.sql b/src/Sql/dbo_future/Stored Procedures/OrganizationUser_CreateMany.sql deleted file mode 100644 index 371848cf9de6..000000000000 --- a/src/Sql/dbo_future/Stored Procedures/OrganizationUser_CreateMany.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Created 2023-01 --- DELETE FILE diff --git a/src/Sql/dbo_future/Stored Procedures/OrganizationUser_UpdateMany.sql b/src/Sql/dbo_future/Stored Procedures/OrganizationUser_UpdateMany.sql deleted file mode 100644 index 371848cf9de6..000000000000 --- a/src/Sql/dbo_future/Stored Procedures/OrganizationUser_UpdateMany.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Created 2023-01 --- DELETE FILE diff --git a/src/Sql/dbo_future/Stored Procedures/Policy_CountByTypeApplicableToUser.sql b/src/Sql/dbo_future/Stored Procedures/Policy_CountByTypeApplicableToUser.sql deleted file mode 100644 index a851a1e7994f..000000000000 --- a/src/Sql/dbo_future/Stored Procedures/Policy_CountByTypeApplicableToUser.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Created 2023-03 --- DELETE FILE \ No newline at end of file diff --git a/src/Sql/dbo_future/Stored Procedures/Policy_ReadByTypeApplicableToUser.sql b/src/Sql/dbo_future/Stored Procedures/Policy_ReadByTypeApplicableToUser.sql deleted file mode 100644 index a851a1e7994f..000000000000 --- a/src/Sql/dbo_future/Stored Procedures/Policy_ReadByTypeApplicableToUser.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Created 2023-03 --- DELETE FILE \ No newline at end of file diff --git a/src/Sql/dbo_future/Stored Procedures/User_Create.sql b/src/Sql/dbo_future/Stored Procedures/User_Create.sql deleted file mode 100644 index 71391539504d..000000000000 --- a/src/Sql/dbo_future/Stored Procedures/User_Create.sql +++ /dev/null @@ -1,120 +0,0 @@ -CREATE PROCEDURE [dbo].[User_Create] - @Id UNIQUEIDENTIFIER OUTPUT, - @Name NVARCHAR(50), - @Email NVARCHAR(256), - @EmailVerified BIT, - @MasterPassword NVARCHAR(300), - @MasterPasswordHint NVARCHAR(50), - @Culture NVARCHAR(10), - @SecurityStamp NVARCHAR(50), - @TwoFactorProviders NVARCHAR(MAX), - @TwoFactorRecoveryCode NVARCHAR(32), - @EquivalentDomains NVARCHAR(MAX), - @ExcludedGlobalEquivalentDomains NVARCHAR(MAX), - @AccountRevisionDate DATETIME2(7), - @Key NVARCHAR(MAX), - @PublicKey NVARCHAR(MAX), - @PrivateKey NVARCHAR(MAX), - @Premium BIT, - @PremiumExpirationDate DATETIME2(7), - @RenewalReminderDate DATETIME2(7), - @Storage BIGINT, - @MaxStorageGb SMALLINT, - @Gateway TINYINT, - @GatewayCustomerId VARCHAR(50), - @GatewaySubscriptionId VARCHAR(50), - @ReferenceData VARCHAR(MAX), - @LicenseKey VARCHAR(100), - @Kdf TINYINT, - @KdfIterations INT, - @CreationDate DATETIME2(7), - @RevisionDate DATETIME2(7), - @ApiKey VARCHAR(30), - @ForcePasswordReset BIT = 0, - @UsesKeyConnector BIT = 0, - @FailedLoginCount INT = 0, - @LastFailedLoginDate DATETIME2(7), - @AvatarColor VARCHAR(7) = NULL -AS -BEGIN - SET NOCOUNT ON - - INSERT INTO [dbo].[User] - ( - [Id], - [Name], - [Email], - [EmailVerified], - [MasterPassword], - [MasterPasswordHint], - [Culture], - [SecurityStamp], - [TwoFactorProviders], - [TwoFactorRecoveryCode], - [EquivalentDomains], - [ExcludedGlobalEquivalentDomains], - [AccountRevisionDate], - [Key], - [PublicKey], - [PrivateKey], - [Premium], - [PremiumExpirationDate], - [RenewalReminderDate], - [Storage], - [MaxStorageGb], - [Gateway], - [GatewayCustomerId], - [GatewaySubscriptionId], - [ReferenceData], - [LicenseKey], - [Kdf], - [KdfIterations], - [CreationDate], - [RevisionDate], - [ApiKey], - [ForcePasswordReset], - [UsesKeyConnector], - [FailedLoginCount], - [LastFailedLoginDate], - [AvatarColor] - ) - VALUES - ( - @Id, - @Name, - @Email, - @EmailVerified, - @MasterPassword, - @MasterPasswordHint, - @Culture, - @SecurityStamp, - @TwoFactorProviders, - @TwoFactorRecoveryCode, - @EquivalentDomains, - @ExcludedGlobalEquivalentDomains, - @AccountRevisionDate, - @Key, - @PublicKey, - @PrivateKey, - @Premium, - @PremiumExpirationDate, - @RenewalReminderDate, - @Storage, - @MaxStorageGb, - @Gateway, - @GatewayCustomerId, - @GatewaySubscriptionId, - @ReferenceData, - @LicenseKey, - @Kdf, - @KdfIterations, - @CreationDate, - @RevisionDate, - @ApiKey, - @ForcePasswordReset, - @UsesKeyConnector, - @FailedLoginCount, - @LastFailedLoginDate, - @AvatarColor - ) -END diff --git a/src/Sql/dbo_future/Stored Procedures/User_Update.sql b/src/Sql/dbo_future/Stored Procedures/User_Update.sql deleted file mode 100644 index 1ef314af8e2b..000000000000 --- a/src/Sql/dbo_future/Stored Procedures/User_Update.sql +++ /dev/null @@ -1,82 +0,0 @@ -CREATE PROCEDURE [dbo].[User_Update] - @Id UNIQUEIDENTIFIER, - @Name NVARCHAR(50), - @Email NVARCHAR(256), - @EmailVerified BIT, - @MasterPassword NVARCHAR(300), - @MasterPasswordHint NVARCHAR(50), - @Culture NVARCHAR(10), - @SecurityStamp NVARCHAR(50), - @TwoFactorProviders NVARCHAR(MAX), - @TwoFactorRecoveryCode NVARCHAR(32), - @EquivalentDomains NVARCHAR(MAX), - @ExcludedGlobalEquivalentDomains NVARCHAR(MAX), - @AccountRevisionDate DATETIME2(7), - @Key NVARCHAR(MAX), - @PublicKey NVARCHAR(MAX), - @PrivateKey NVARCHAR(MAX), - @Premium BIT, - @PremiumExpirationDate DATETIME2(7), - @RenewalReminderDate DATETIME2(7), - @Storage BIGINT, - @MaxStorageGb SMALLINT, - @Gateway TINYINT, - @GatewayCustomerId VARCHAR(50), - @GatewaySubscriptionId VARCHAR(50), - @ReferenceData VARCHAR(MAX), - @LicenseKey VARCHAR(100), - @Kdf TINYINT, - @KdfIterations INT, - @CreationDate DATETIME2(7), - @RevisionDate DATETIME2(7), - @ApiKey VARCHAR(30), - @ForcePasswordReset BIT = 0, - @UsesKeyConnector BIT = 0, - @FailedLoginCount INT, - @LastFailedLoginDate DATETIME2(7), - @AvatarColor VARCHAR(7) -AS -BEGIN - SET NOCOUNT ON - - UPDATE - [dbo].[User] - SET - [Name] = @Name, - [Email] = @Email, - [EmailVerified] = @EmailVerified, - [MasterPassword] = @MasterPassword, - [MasterPasswordHint] = @MasterPasswordHint, - [Culture] = @Culture, - [SecurityStamp] = @SecurityStamp, - [TwoFactorProviders] = @TwoFactorProviders, - [TwoFactorRecoveryCode] = @TwoFactorRecoveryCode, - [EquivalentDomains] = @EquivalentDomains, - [ExcludedGlobalEquivalentDomains] = @ExcludedGlobalEquivalentDomains, - [AccountRevisionDate] = @AccountRevisionDate, - [Key] = @Key, - [PublicKey] = @PublicKey, - [PrivateKey] = @PrivateKey, - [Premium] = @Premium, - [PremiumExpirationDate] = @PremiumExpirationDate, - [RenewalReminderDate] = @RenewalReminderDate, - [Storage] = @Storage, - [MaxStorageGb] = @MaxStorageGb, - [Gateway] = @Gateway, - [GatewayCustomerId] = @GatewayCustomerId, - [GatewaySubscriptionId] = @GatewaySubscriptionId, - [ReferenceData] = @ReferenceData, - [LicenseKey] = @LicenseKey, - [Kdf] = @Kdf, - [KdfIterations] = @KdfIterations, - [CreationDate] = @CreationDate, - [RevisionDate] = @RevisionDate, - [ApiKey] = @ApiKey, - [ForcePasswordReset] = @ForcePasswordReset, - [UsesKeyConnector] = @UsesKeyConnector, - [FailedLoginCount] = @FailedLoginCount, - [LastFailedLoginDate] = @LastFailedLoginDate, - [AvatarColor] = @AvatarColor - WHERE - [Id] = @Id -END diff --git a/src/Sql/dbo_future/Tables/User.sql b/src/Sql/dbo_future/Tables/User.sql deleted file mode 100644 index e2d681d1e289..000000000000 --- a/src/Sql/dbo_future/Tables/User.sql +++ /dev/null @@ -1,49 +0,0 @@ -CREATE TABLE [dbo].[User] ( - [Id] UNIQUEIDENTIFIER NOT NULL, - [Name] NVARCHAR (50) NULL, - [Email] NVARCHAR (256) NOT NULL, - [EmailVerified] BIT NOT NULL, - [MasterPassword] NVARCHAR (300) NULL, - [MasterPasswordHint] NVARCHAR (50) NULL, - [Culture] NVARCHAR (10) NOT NULL, - [SecurityStamp] NVARCHAR (50) NOT NULL, - [TwoFactorProviders] NVARCHAR (MAX) NULL, - [TwoFactorRecoveryCode] NVARCHAR (32) NULL, - [EquivalentDomains] NVARCHAR (MAX) NULL, - [ExcludedGlobalEquivalentDomains] NVARCHAR (MAX) NULL, - [AccountRevisionDate] DATETIME2 (7) NOT NULL, - [Key] VARCHAR (MAX) NULL, - [PublicKey] VARCHAR (MAX) NULL, - [PrivateKey] VARCHAR (MAX) NULL, - [Premium] BIT NOT NULL, - [PremiumExpirationDate] DATETIME2 (7) NULL, - [RenewalReminderDate] DATETIME2 (7) NULL, - [Storage] BIGINT NULL, - [MaxStorageGb] SMALLINT NULL, - [Gateway] TINYINT NULL, - [GatewayCustomerId] VARCHAR (50) NULL, - [GatewaySubscriptionId] VARCHAR (50) NULL, - [ReferenceData] NVARCHAR (MAX) NULL, - [LicenseKey] VARCHAR (100) NULL, - [Kdf] TINYINT NOT NULL, - [KdfIterations] INT NOT NULL, - [CreationDate] DATETIME2 (7) NOT NULL, - [RevisionDate] DATETIME2 (7) NOT NULL, - [ApiKey] VARCHAR (30) NOT NULL, - [ForcePasswordReset] BIT NOT NULL, - [UsesKeyConnector] BIT NOT NULL, - [FailedLoginCount] INT CONSTRAINT [D_User_FailedLoginCount] DEFAULT ((0)) NOT NULL, - [LastFailedLoginDate] DATETIME2 (7) NULL, - [AvatarColor] VARCHAR(7) NULL, - CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC) -); - - -GO -CREATE UNIQUE NONCLUSTERED INDEX [IX_User_Email] - ON [dbo].[User]([Email] ASC); - -GO -CREATE NONCLUSTERED INDEX [IX_User_Premium_PremiumExpirationDate_RenewalReminderDate] - ON [dbo].[User]([Premium] ASC, [PremiumExpirationDate] ASC, [RenewalReminderDate] ASC); - diff --git a/src/Sql/dbo_future/User Defined Types/OrganizationUserType.sql b/src/Sql/dbo_future/User Defined Types/OrganizationUserType.sql deleted file mode 100644 index 371848cf9de6..000000000000 --- a/src/Sql/dbo_future/User Defined Types/OrganizationUserType.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Created 2023-01 --- DELETE FILE diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTests.cs index 171dad4a8f9c..006bcc2c246d 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTests.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTests.cs @@ -56,12 +56,16 @@ private async Task LoginAsync(string email) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task CreateProjectAccessPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task CreateProjectAccessPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var (projectId, serviceAccountId) = await CreateProjectAndServiceAccountAsync(org.Id); @@ -82,7 +86,7 @@ public async Task CreateProjectAccessPolicies_SmNotEnabled_NotFound(bool useSecr public async Task CreateProjectAccessPolicies_NoPermission() { // Create a new account as a user - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -105,7 +109,7 @@ public async Task CreateProjectAccessPolicies_NoPermission() [InlineData(PermissionType.RunAsUserWithPermission)] public async Task CreateProjectAccessPolicies_MismatchedOrgIds_NotFound(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var (projectId, serviceAccountId) = await CreateProjectAndServiceAccountAsync(org.Id, true); @@ -130,7 +134,7 @@ public async Task CreateProjectAccessPolicies_MismatchedOrgIds_NotFound(Permissi [InlineData(PermissionType.RunAsUserWithPermission)] public async Task CreateProjectAccessPolicies_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var (projectId, serviceAccountId) = await CreateProjectAndServiceAccountAsync(org.Id); @@ -167,12 +171,16 @@ public async Task CreateProjectAccessPolicies_Success(PermissionType permissionT } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task UpdateAccessPolicy_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task UpdateAccessPolicy_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var initData = await SetupAccessPolicyRequest(org.Id); @@ -188,7 +196,7 @@ public async Task UpdateAccessPolicy_SmNotEnabled_NotFound(bool useSecrets, bool public async Task UpdateAccessPolicy_NoPermission() { // Create a new account as a user - await _organizationHelper.Initialize(true, true); + await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -208,7 +216,7 @@ public async Task UpdateAccessPolicy_NoPermission() [InlineData(PermissionType.RunAsUserWithPermission)] public async Task UpdateAccessPolicy_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var initData = await SetupAccessPolicyRequest(org.Id); @@ -248,12 +256,16 @@ public async Task UpdateAccessPolicy_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task DeleteAccessPolicy_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task DeleteAccessPolicy_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var initData = await SetupAccessPolicyRequest(org.Id); @@ -265,7 +277,7 @@ public async Task DeleteAccessPolicy_SmNotEnabled_NotFound(bool useSecrets, bool public async Task DeleteAccessPolicy_NoPermission() { // Create a new account as a user - await _organizationHelper.Initialize(true, true); + await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -281,7 +293,7 @@ public async Task DeleteAccessPolicy_NoPermission() [InlineData(PermissionType.RunAsUserWithPermission)] public async Task DeleteAccessPolicy_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var initData = await SetupAccessPolicyRequest(org.Id); @@ -309,7 +321,7 @@ public async Task DeleteAccessPolicy_Success(PermissionType permissionType) [Fact] public async Task GetProjectAccessPolicies_ReturnsEmpty() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var project = await _projectRepository.CreateAsync(new Project @@ -330,12 +342,16 @@ public async Task GetProjectAccessPolicies_ReturnsEmpty() } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task GetProjectAccessPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task GetProjectAccessPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var initData = await SetupAccessPolicyRequest(org.Id); @@ -348,7 +364,7 @@ public async Task GetProjectAccessPolicies_SmNotEnabled_NotFound(bool useSecrets public async Task GetProjectAccessPolicies_NoPermission() { // Create a new account as a user - await _organizationHelper.Initialize(true, true); + await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -364,7 +380,7 @@ public async Task GetProjectAccessPolicies_NoPermission() [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetProjectAccessPolicies(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var initData = await SetupAccessPolicyRequest(org.Id); @@ -392,12 +408,16 @@ public async Task GetProjectAccessPolicies(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task GetPeoplePotentialGrantees_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task GetPeoplePotentialGrantees_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var response = @@ -411,7 +431,7 @@ await _client.GetAsync( [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetPeoplePotentialGrantees_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); if (permissionType == PermissionType.RunAsUserWithPermission) @@ -432,12 +452,16 @@ await _client.GetAsync( } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task GetServiceAccountPotentialGrantees_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task GetServiceAccountPotentialGrantees_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var response = @@ -450,7 +474,7 @@ await _client.GetAsync( public async Task GetServiceAccountPotentialGrantees_OnlyReturnsServiceAccountsWithWriteAccess() { // Create a new account as a user - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -477,7 +501,7 @@ await _client.GetAsync( [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetServiceAccountsPotentialGrantees_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -517,12 +541,16 @@ await _client.GetAsync( } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task GetProjectPotentialGrantees_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task GetProjectPotentialGrantees_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var response = @@ -535,7 +563,7 @@ await _client.GetAsync( public async Task GetProjectPotentialGrantees_OnlyReturnsProjectsWithWriteAccess() { // Create a new account as a user - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -558,7 +586,7 @@ await _client.GetAsync( [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetProjectPotentialGrantees_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var project = await _projectRepository.CreateAsync(new Project @@ -595,12 +623,16 @@ await _client.GetAsync( } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task CreateServiceAccountAccessPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task CreateServiceAccountAccessPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, orgUser) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, orgUser) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -627,7 +659,7 @@ public async Task CreateServiceAccountAccessPolicies_SmNotEnabled_NotFound(bool [InlineData(PermissionType.RunAsUserWithPermission)] public async Task CreateServiceAccountAccessPolicies_MismatchOrgId_NotFound(PermissionType permissionType) { - var (org, orgUser) = await _organizationHelper.Initialize(true, true); + var (org, orgUser) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var ownerOrgUserId = orgUser.Id; @@ -650,7 +682,7 @@ await SetupUserServiceAccountAccessPolicyRequestAsync(permissionType, org.Id, or [InlineData(PermissionType.RunAsUserWithPermission)] public async Task CreateServiceAccountAccessPolicies_Success(PermissionType permissionType) { - var (org, orgUser) = await _organizationHelper.Initialize(true, true); + var (org, orgUser) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var ownerOrgUserId = orgUser.Id; @@ -687,7 +719,7 @@ await SetupUserServiceAccountAccessPolicyRequestAsync(permissionType, org.Id, or public async Task CreateServiceAccountAccessPolicies_NoPermission() { // Create a new account as a user - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -711,12 +743,16 @@ public async Task CreateServiceAccountAccessPolicies_NoPermission() } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task GetServiceAccountAccessPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task GetServiceAccountAccessPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var initData = await SetupAccessPolicyRequest(org.Id); @@ -727,7 +763,7 @@ public async Task GetServiceAccountAccessPolicies_SmNotEnabled_NotFound(bool use [Fact] public async Task GetServiceAccountAccessPolicies_ReturnsEmpty() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -750,7 +786,7 @@ public async Task GetServiceAccountAccessPolicies_ReturnsEmpty() public async Task GetServiceAccountAccessPolicies_NoPermission() { // Create a new account as a user - await _organizationHelper.Initialize(true, true); + await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -766,7 +802,7 @@ public async Task GetServiceAccountAccessPolicies_NoPermission() [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetServiceAccountAccessPolicies(PermissionType permissionType) { - var (org, ownerOrgUser) = await _organizationHelper.Initialize(true, true); + var (org, ownerOrgUser) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var initData = await SetupAccessPolicyRequest(org.Id); @@ -811,12 +847,16 @@ public async Task GetServiceAccountAccessPolicies(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task CreateServiceAccountGrantedPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task CreateServiceAccountGrantedPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -836,7 +876,7 @@ public async Task CreateServiceAccountGrantedPolicies_SmNotEnabled_NotFound(bool public async Task CreateServiceAccountGrantedPolicies_NoPermission() { // Create a new account as a user - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -865,7 +905,7 @@ public async Task CreateServiceAccountGrantedPolicies_NoPermission() [InlineData(PermissionType.RunAsUserWithPermission)] public async Task CreateServiceAccountGrantedPolicies_MismatchedOrgId_NotFound(PermissionType permissionType) { - var (org, orgUser) = await _organizationHelper.Initialize(true, true); + var (org, orgUser) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var ownerOrgUserId = orgUser.Id; @@ -886,7 +926,7 @@ public async Task CreateServiceAccountGrantedPolicies_MismatchedOrgId_NotFound(P [InlineData(PermissionType.RunAsUserWithPermission)] public async Task CreateServiceAccountGrantedPolicies_Success(PermissionType permissionType) { - var (org, orgUser) = await _organizationHelper.Initialize(true, true); + var (org, orgUser) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var ownerOrgUserId = orgUser.Id; @@ -918,12 +958,16 @@ public async Task CreateServiceAccountGrantedPolicies_Success(PermissionType per } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task GetServiceAccountGrantedPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task GetServiceAccountGrantedPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var initData = await SetupAccessPolicyRequest(org.Id); @@ -934,7 +978,7 @@ public async Task GetServiceAccountGrantedPolicies_SmNotEnabled_NotFound(bool us [Fact] public async Task GetServiceAccountGrantedPolicies_ReturnsEmpty() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -957,7 +1001,7 @@ public async Task GetServiceAccountGrantedPolicies_ReturnsEmpty() public async Task GetServiceAccountGrantedPolicies_NoPermission_ReturnsEmpty() { // Create a new account as a user - await _organizationHelper.Initialize(true, true); + await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -977,7 +1021,7 @@ public async Task GetServiceAccountGrantedPolicies_NoPermission_ReturnsEmpty() [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetServiceAccountGrantedPolicies(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var initData = await SetupAccessPolicyRequest(org.Id); diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/ProjectsControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/ProjectsControllerTests.cs index fa88a44b838c..65ec123a2027 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/ProjectsControllerTests.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/ProjectsControllerTests.cs @@ -56,12 +56,16 @@ private async Task LoginAsync(string email) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task ListByOrganization_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task ListByOrganization_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var response = await _client.GetAsync($"/organizations/{org.Id}/projects"); @@ -71,7 +75,7 @@ public async Task ListByOrganization_SmNotEnabled_NotFound(bool useSecrets, bool [Fact] public async Task ListByOrganization_UserWithoutPermission_EmptyList() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -102,12 +106,16 @@ public async Task ListByOrganization_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Create_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Create_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var request = new ProjectCreateRequestModel { Name = _mockEncryptedString }; @@ -134,7 +142,7 @@ public async Task Create_AtMaxProjects_BadRequest(PermissionType permissionType) [InlineData(PermissionType.RunAsUserWithPermission)] public async Task Create_Success(PermissionType permissionType) { - var (org, adminOrgUser) = await _organizationHelper.Initialize(true, true); + var (org, adminOrgUser) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var orgUserId = adminOrgUser.Id; var currentUserId = adminOrgUser.UserId!.Value; @@ -178,12 +186,16 @@ public async Task Create_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Update_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Update_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var initialProject = await _projectRepository.CreateAsync(new Project @@ -231,7 +243,7 @@ public async Task Update_Success(PermissionType permissionType) [Fact] public async Task Update_NonExistingProject_NotFound() { - await _organizationHelper.Initialize(true, true); + await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var request = new ProjectUpdateRequestModel @@ -248,7 +260,7 @@ public async Task Update_NonExistingProject_NotFound() [Fact] public async Task Update_MissingAccessPolicy_NotFound() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -270,12 +282,16 @@ public async Task Update_MissingAccessPolicy_NotFound() } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Get_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Get_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var project = await _projectRepository.CreateAsync(new Project @@ -295,7 +311,7 @@ public async Task Get_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) [Fact] public async Task Get_MissingAccessPolicy_NotFound() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -312,7 +328,7 @@ public async Task Get_MissingAccessPolicy_NotFound() [Fact] public async Task Get_NonExistingProject_NotFound() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -346,12 +362,16 @@ public async Task Get_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Delete_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Delete_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var projectIds = await CreateProjectsAsync(org.Id); @@ -363,7 +383,7 @@ public async Task Delete_SmNotEnabled_NotFound(bool useSecrets, bool accessSecre [Fact] public async Task Delete_MissingAccessPolicy_AccessDenied() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -417,7 +437,7 @@ private async Task> CreateProjectsAsync(Guid orgId, int numberToCreat private async Task<(List, Organization)> SetupProjectsWithAccessAsync(PermissionType permissionType, int projectsToCreate = 3) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var projectIds = await CreateProjectsAsync(org.Id, projectsToCreate); @@ -446,7 +466,7 @@ private async Task> CreateProjectsAsync(Guid orgId, int numberToCreat private async Task SetupProjectWithAccessAsync(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var initialProject = await _projectRepository.CreateAsync(new Project diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs index 0d937d3433f9..8cea05c5cb7f 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs @@ -56,12 +56,16 @@ private async Task LoginAsync(string email) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task ListByOrganization_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task ListByOrganization_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var response = await _client.GetAsync($"/organizations/{org.Id}/secrets"); @@ -73,7 +77,7 @@ public async Task ListByOrganization_SmNotEnabled_NotFound(bool useSecrets, bool [InlineData(PermissionType.RunAsUserWithPermission)] public async Task ListByOrganization_Success(PermissionType permissionType) { - var (org, orgUserOwner) = await _organizationHelper.Initialize(true, true); + var (org, orgUserOwner) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var project = await _projectRepository.CreateAsync(new Project @@ -123,12 +127,16 @@ public async Task ListByOrganization_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Create_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Create_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var request = new SecretCreateRequestModel @@ -145,7 +153,7 @@ public async Task Create_SmNotEnabled_NotFound(bool useSecrets, bool accessSecre [Fact] public async Task CreateWithoutProject_RunAsAdmin_Success() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var request = new SecretCreateRequestModel @@ -179,7 +187,7 @@ public async Task CreateWithoutProject_RunAsAdmin_Success() [Fact] public async Task CreateWithDifferentProjectOrgId_RunAsAdmin_NotFound() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var project = await _projectRepository.CreateAsync(new Project { Name = "123" }); @@ -199,7 +207,7 @@ public async Task CreateWithDifferentProjectOrgId_RunAsAdmin_NotFound() [Fact] public async Task CreateWithMultipleProjects_RunAsAdmin_BadRequest() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var projectA = await _projectRepository.CreateAsync(new Project { OrganizationId = org.Id, Name = "123A" }); @@ -220,7 +228,7 @@ public async Task CreateWithMultipleProjects_RunAsAdmin_BadRequest() [Fact] public async Task CreateWithoutProject_RunAsUser_NotFound() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -240,7 +248,7 @@ public async Task CreateWithoutProject_RunAsUser_NotFound() [InlineData(PermissionType.RunAsUserWithPermission)] public async Task CreateWithProject_Success(PermissionType permissionType) { - var (org, orgAdminUser) = await _organizationHelper.Initialize(true, true); + var (org, orgAdminUser) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); AccessClientType accessType = AccessClientType.NoAccessCheck; @@ -296,12 +304,16 @@ public async Task CreateWithProject_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Get_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Get_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var secret = await _secretRepository.CreateAsync(new Secret @@ -321,7 +333,7 @@ public async Task Get_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) [InlineData(PermissionType.RunAsUserWithPermission)] public async Task Get_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var project = await _projectRepository.CreateAsync(new Project() @@ -371,12 +383,16 @@ public async Task Get_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task GetSecretsByProject_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task GetSecretsByProject_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var project = await _projectRepository.CreateAsync(new Project @@ -392,7 +408,7 @@ public async Task GetSecretsByProject_SmNotEnabled_NotFound(bool useSecrets, boo [Fact] public async Task GetSecretsByProject_UserWithNoPermission_EmptyList() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -425,7 +441,7 @@ public async Task GetSecretsByProject_UserWithNoPermission_EmptyList() [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetSecretsByProject_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var project = await _projectRepository.CreateAsync(new Project() @@ -473,12 +489,16 @@ public async Task GetSecretsByProject_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Update_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Update_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var secret = await _secretRepository.CreateAsync(new Secret @@ -505,7 +525,7 @@ public async Task Update_SmNotEnabled_NotFound(bool useSecrets, bool accessSecre [InlineData(PermissionType.RunAsUserWithPermission)] public async Task Update_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var project = await _projectRepository.CreateAsync(new Project() @@ -572,7 +592,7 @@ public async Task Update_Success(PermissionType permissionType) [Fact] public async Task UpdateWithDifferentProjectOrgId_RunAsAdmin_NotFound() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var project = await _projectRepository.CreateAsync(new Project { Name = "123" }); @@ -600,7 +620,7 @@ public async Task UpdateWithDifferentProjectOrgId_RunAsAdmin_NotFound() [Fact] public async Task UpdateWithMultipleProjects_BadRequest() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var projectA = await _projectRepository.CreateAsync(new Project { OrganizationId = org.Id, Name = "123A" }); @@ -627,12 +647,16 @@ public async Task UpdateWithMultipleProjects_BadRequest() } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Delete_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Delete_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var secret = await _secretRepository.CreateAsync(new Secret @@ -651,7 +675,7 @@ public async Task Delete_SmNotEnabled_NotFound(bool useSecrets, bool accessSecre [Fact] public async Task Delete_MissingAccessPolicy_AccessDenied() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -671,7 +695,7 @@ public async Task Delete_MissingAccessPolicy_AccessDenied() [InlineData(PermissionType.RunAsUserWithPermission)] public async Task Delete_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var (project, secretIds) = await CreateSecretsAsync(org.Id, 3); @@ -710,12 +734,16 @@ public async Task Delete_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task GetSecretsByIds_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task GetSecretsByIds_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var secret = await _secretRepository.CreateAsync(new Secret @@ -737,7 +765,7 @@ public async Task GetSecretsByIds_SmNotEnabled_NotFound(bool useSecrets, bool ac [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetSecretsByIds_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var (project, secretIds) = await CreateSecretsAsync(org.Id); diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsManagerEventsControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsManagerEventsControllerTests.cs new file mode 100644 index 000000000000..4c053c3a2eb1 --- /dev/null +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsManagerEventsControllerTests.cs @@ -0,0 +1,71 @@ +using System.Net; +using System.Net.Http.Headers; +using Bit.Api.IntegrationTest.Factories; +using Bit.Core.SecretsManager.Entities; +using Bit.Core.SecretsManager.Repositories; +using Xunit; + +namespace Bit.Api.IntegrationTest.SecretsManager.Controllers; + +public class SecretsManagerEventsControllerTests : IClassFixture, IAsyncLifetime +{ + private const string _mockEncryptedString = + "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98sp4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; + + private readonly HttpClient _client; + private readonly ApiApplicationFactory _factory; + + private readonly IServiceAccountRepository _serviceAccountRepository; + + private string _email = null!; + private SecretsManagerOrganizationHelper _organizationHelper = null!; + + public SecretsManagerEventsControllerTests(ApiApplicationFactory factory) + { + _factory = factory; + _client = _factory.CreateClient(); + _serviceAccountRepository = _factory.GetService(); + } + + public async Task InitializeAsync() + { + _email = $"integration-test{Guid.NewGuid()}@bitwarden.com"; + await _factory.LoginWithNewAccount(_email); + _organizationHelper = new SecretsManagerOrganizationHelper(_factory, _email); + } + + public Task DisposeAsync() + { + _client.Dispose(); + return Task.CompletedTask; + } + + private async Task LoginAsync(string email) + { + var tokens = await _factory.LoginAsync(email); + _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token); + } + + [Theory] + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task GetServiceAccountEvents_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); + await LoginAsync(_email); + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString + }); + + var response = await _client.GetAsync($"/sm/events/service-accounts/{serviceAccount.Id}"); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } +} diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsManagerPortingControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsManagerPortingControllerTests.cs index 62d5554099e1..c57ceb20d9a7 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsManagerPortingControllerTests.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsManagerPortingControllerTests.cs @@ -45,12 +45,16 @@ private async Task LoginAsync(string email) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Import_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Import_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var projectsList = new List(); @@ -62,12 +66,16 @@ public async Task Import_SmNotEnabled_NotFound(bool useSecrets, bool accessSecre } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Export_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Export_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var response = await _client.GetAsync($"sm/{org.Id}/export"); diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsTrashControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsTrashControllerTests.cs index 9160213ba1f8..69837981f6c7 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsTrashControllerTests.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsTrashControllerTests.cs @@ -48,12 +48,16 @@ private async Task LoginAsync(string email) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task ListByOrganization_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task ListByOrganization_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var response = await _client.GetAsync($"/secrets/{org.Id}/trash"); @@ -63,7 +67,7 @@ public async Task ListByOrganization_SmNotEnabled_NotFound(bool useSecrets, bool [Fact] public async Task ListByOrganization_NotAdmin_Unauthorized() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -74,7 +78,7 @@ public async Task ListByOrganization_NotAdmin_Unauthorized() [Fact] public async Task ListByOrganization_Success() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); await _secretRepository.CreateAsync(new Secret @@ -100,12 +104,16 @@ await _secretRepository.CreateAsync(new Secret } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Empty_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Empty_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var ids = new List { Guid.NewGuid() }; @@ -116,7 +124,7 @@ public async Task Empty_SmNotEnabled_NotFound(bool useSecrets, bool accessSecret [Fact] public async Task Empty_NotAdmin_Unauthorized() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -128,7 +136,7 @@ public async Task Empty_NotAdmin_Unauthorized() [Fact] public async Task Empty_Invalid_NotFound() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var secret = await _secretRepository.CreateAsync(new Secret @@ -146,7 +154,7 @@ public async Task Empty_Invalid_NotFound() [Fact] public async Task Empty_Success() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var secret = await _secretRepository.CreateAsync(new Secret @@ -163,12 +171,16 @@ public async Task Empty_Success() } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Restore_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Restore_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var ids = new List { Guid.NewGuid() }; @@ -179,7 +191,7 @@ public async Task Restore_SmNotEnabled_NotFound(bool useSecrets, bool accessSecr [Fact] public async Task Restore_NotAdmin_Unauthorized() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -191,7 +203,7 @@ public async Task Restore_NotAdmin_Unauthorized() [Fact] public async Task Restore_Invalid_NotFound() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var secret = await _secretRepository.CreateAsync(new Secret @@ -209,7 +221,7 @@ public async Task Restore_Invalid_NotFound() [Fact] public async Task Restore_Success() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var secret = await _secretRepository.CreateAsync(new Secret diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs index 814778d1b855..8150dced5229 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs @@ -61,12 +61,16 @@ private async Task LoginAsync(string email) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task ListByOrganization_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) - { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task ListByOrganization_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var response = await _client.GetAsync($"/organizations/{org.Id}/service-accounts"); @@ -76,7 +80,7 @@ public async Task ListByOrganization_SmNotEnabled_NotFound(bool useSecrets, bool [Fact] public async Task ListByOrganization_Admin_Success() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var serviceAccountIds = await SetupGetServiceAccountsByOrganizationAsync(org); @@ -93,7 +97,7 @@ public async Task ListByOrganization_Admin_Success() [Fact] public async Task ListByOrganization_User_Success() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -121,12 +125,16 @@ public async Task ListByOrganization_User_Success() } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task GetByServiceAccountId_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) - { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task GetByServiceAccountId_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -142,7 +150,7 @@ public async Task GetByServiceAccountId_SmNotEnabled_NotFound(bool useSecrets, b [Fact] public async Task GetByServiceAccountId_ServiceAccountDoesNotExist_NotFound() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var response = await _client.GetAsync($"/service-accounts/{new Guid()}"); @@ -152,7 +160,7 @@ public async Task GetByServiceAccountId_ServiceAccountDoesNotExist_NotFound() [Fact] public async Task GetByServiceAccountId_UserWithoutPermission_NotFound() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -185,12 +193,16 @@ public async Task GetByServiceAccountId_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Create_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) - { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Create_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var request = new ServiceAccountCreateRequestModel { Name = _mockEncryptedString }; @@ -204,7 +216,7 @@ public async Task Create_SmNotEnabled_NotFound(bool useSecrets, bool accessSecre [InlineData(PermissionType.RunAsUserWithPermission)] public async Task Create_Success(PermissionType permissionType) { - var (org, adminOrgUser) = await _organizationHelper.Initialize(true, true); + var (org, adminOrgUser) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var orgUserId = adminOrgUser.Id; @@ -248,12 +260,16 @@ public async Task Create_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Update_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) - { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Update_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -271,7 +287,7 @@ public async Task Update_SmNotEnabled_NotFound(bool useSecrets, bool accessSecre [Fact] public async Task Update_User_NoPermissions() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -290,7 +306,7 @@ public async Task Update_User_NoPermissions() [Fact] public async Task Update_NonExistingServiceAccount_NotFound() { - await _organizationHelper.Initialize(true, true); + await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var request = new ServiceAccountUpdateRequestModel { Name = _mockNewName }; @@ -327,12 +343,16 @@ public async Task Update_Success(PermissionType permissionType) } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task Delete_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) - { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task Delete_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -350,7 +370,7 @@ public async Task Delete_SmNotEnabled_NotFound(bool useSecrets, bool accessSecre [Fact] public async Task Delete_MissingAccessPolicy_AccessDenied() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -373,7 +393,7 @@ public async Task Delete_MissingAccessPolicy_AccessDenied() [InlineData(PermissionType.RunAsUserWithPermission)] public async Task Delete_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { @@ -413,12 +433,16 @@ await _accessPolicyRepository.CreateManyAsync(new List { } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task GetAccessTokens_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) - { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task GetAccessTokens_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -434,7 +458,7 @@ public async Task GetAccessTokens_SmNotEnabled_NotFound(bool useSecrets, bool ac [Fact] public async Task GetAccessTokens_UserNoPermission_NotFound() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -460,7 +484,7 @@ await _apiKeyRepository.CreateAsync(new ApiKey [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetAccessTokens_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -506,12 +530,16 @@ await _accessPolicyRepository.CreateManyAsync(new List { } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task CreateAccessToken_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) - { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task CreateAccessToken_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -536,7 +564,7 @@ public async Task CreateAccessToken_SmNotEnabled_NotFound(bool useSecrets, bool [Fact] public async Task CreateAccessToken_Admin() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -569,7 +597,7 @@ public async Task CreateAccessToken_Admin() [Fact] public async Task CreateAccessToken_User_WithPermission() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -605,7 +633,7 @@ public async Task CreateAccessToken_User_WithPermission() [Fact] public async Task CreateAccessToken_User_NoPermission() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -631,7 +659,7 @@ public async Task CreateAccessToken_User_NoPermission() [Fact] public async Task CreateAccessToken_ExpireAtNull_Admin() { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -661,12 +689,16 @@ public async Task CreateAccessToken_ExpireAtNull_Admin() } [Theory] - [InlineData(false, false)] - [InlineData(true, false)] - [InlineData(false, true)] - public async Task RevokeAccessToken_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) - { - var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + [InlineData(false, false, false)] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, true, true)] + [InlineData(true, false, false)] + [InlineData(true, false, true)] + [InlineData(true, true, false)] + public async Task RevokeAccessToken_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount @@ -696,7 +728,7 @@ public async Task RevokeAccessToken_SmNotEnabled_NotFound(bool useSecrets, bool [InlineData(true)] public async Task RevokeAccessToken_User_NoPermission(bool hasReadAccess) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await LoginAsync(email); @@ -740,7 +772,7 @@ await _accessPolicyRepository.CreateManyAsync(new List { [InlineData(PermissionType.RunAsUserWithPermission)] public async Task RevokeAccessToken_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { @@ -814,7 +846,7 @@ private async Task> SetupGetServiceAccountsByOrganizationAsync(Organi private async Task SetupServiceAccountWithAccessAsync(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount diff --git a/test/Api.IntegrationTest/SecretsManager/SecretsManagerOrganizationHelper.cs b/test/Api.IntegrationTest/SecretsManager/SecretsManagerOrganizationHelper.cs index 7e86386d27bd..6430f7199a97 100644 --- a/test/Api.IntegrationTest/SecretsManager/SecretsManagerOrganizationHelper.cs +++ b/test/Api.IntegrationTest/SecretsManager/SecretsManagerOrganizationHelper.cs @@ -25,13 +25,22 @@ public SecretsManagerOrganizationHelper(ApiApplicationFactory factory, string ow _ownerEmail = ownerEmail; } - public async Task<(Organization organization, OrganizationUser owner)> Initialize(bool useSecrets, bool ownerAccessSecrets) + public async Task<(Organization organization, OrganizationUser owner)> Initialize(bool useSecrets, bool ownerAccessSecrets, bool organizationEnabled) { (_organization, _owner) = await OrganizationTestHelpers.SignUpAsync(_factory, ownerEmail: _ownerEmail, billingEmail: _ownerEmail); - if (useSecrets) + if (useSecrets || !organizationEnabled) { - _organization.UseSecretsManager = true; + if (useSecrets) + { + _organization.UseSecretsManager = true; + } + + if (!organizationEnabled) + { + _organization.Enabled = false; + } + await _organizationRepository.ReplaceAsync(_organization); } diff --git a/test/Api.IntegrationTest/packages.lock.json b/test/Api.IntegrationTest/packages.lock.json index 7d5f3373e9b2..4dacbaa00735 100644 --- a/test/Api.IntegrationTest/packages.lock.json +++ b/test/Api.IntegrationTest/packages.lock.json @@ -321,10 +321,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -3145,25 +3146,25 @@ "AspNetCore.HealthChecks.SqlServer": "[6.0.2, )", "AspNetCore.HealthChecks.Uris": "[6.0.3, )", "Azure.Messaging.EventGrid": "[4.10.0, )", - "Commercial.Core": "[2023.8.3, )", - "Commercial.Infrastructure.EntityFramework": "[2023.8.3, )", - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )", + "Commercial.Core": "[2023.9.0, )", + "Commercial.Infrastructure.EntityFramework": "[2023.9.0, )", + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )", "Swashbuckle.AspNetCore": "[6.5.0, )" } }, "commercial.core": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )" + "Core": "[2023.9.0, )" } }, "commercial.infrastructure.entityframework": { "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } }, "common": { @@ -3171,7 +3172,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -3190,7 +3191,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -3223,15 +3224,15 @@ "identity": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )", + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )", "Swashbuckle.AspNetCore.SwaggerGen": "[6.5.0, )" } }, "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -3239,7 +3240,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -3251,8 +3252,8 @@ "integrationtestcommon": { "type": "Project", "dependencies": { - "Common": "[2023.8.3, )", - "Identity": "[2023.8.3, )", + "Common": "[2023.9.0, )", + "Identity": "[2023.9.0, )", "Microsoft.AspNetCore.Mvc.Testing": "[6.0.5, )", "Microsoft.EntityFrameworkCore.InMemory": "[7.0.5, )", "Microsoft.Extensions.Configuration": "[6.0.1, )" @@ -3261,9 +3262,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/test/Api.Test/Controllers/GroupsControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/GroupsControllerTests.cs similarity index 91% rename from test/Api.Test/Controllers/GroupsControllerTests.cs rename to test/Api.Test/AdminConsole/Controllers/GroupsControllerTests.cs index 8a8b65546df8..9777cb9e66e3 100644 --- a/test/Api.Test/Controllers/GroupsControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/GroupsControllerTests.cs @@ -1,16 +1,18 @@ -using Bit.Api.Controllers; -using Bit.Api.Models.Request; +using Bit.Api.AdminConsole.Controllers; +using Bit.Api.AdminConsole.Models.Request; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Models.Data; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; using Bit.Core.Repositories; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; using Xunit; -namespace Bit.Api.Test.Controllers; +namespace Bit.Api.Test.AdminConsole.Controllers; [ControllerCustomize(typeof(GroupsController))] [SutProviderCustomize] diff --git a/test/Api.Test/Controllers/OrganizationSponsorshipsControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationSponsorshipsControllerTests.cs similarity index 93% rename from test/Api.Test/Controllers/OrganizationSponsorshipsControllerTests.cs rename to test/Api.Test/AdminConsole/Controllers/OrganizationSponsorshipsControllerTests.cs index 99bc1df636cf..e54a89688564 100644 --- a/test/Api.Test/Controllers/OrganizationSponsorshipsControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationSponsorshipsControllerTests.cs @@ -13,18 +13,18 @@ using NSubstitute; using Xunit; -namespace Bit.Api.Test.Controllers; +namespace Bit.Api.Test.AdminConsole.Controllers; [ControllerCustomize(typeof(OrganizationSponsorshipsController))] [SutProviderCustomize] public class OrganizationSponsorshipsControllerTests { public static IEnumerable EnterprisePlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p }); public static IEnumerable NonEnterprisePlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p }); public static IEnumerable NonFamiliesPlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Families).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p }); public static IEnumerable NonConfirmedOrganizationUsersStatuses => Enum.GetValues() diff --git a/test/Api.Test/Controllers/OrganizationUsersControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs similarity index 96% rename from test/Api.Test/Controllers/OrganizationUsersControllerTests.cs rename to test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs index 6053d17ddba7..4b5c633cbeed 100644 --- a/test/Api.Test/Controllers/OrganizationUsersControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs @@ -1,5 +1,5 @@ -using Bit.Api.Controllers; -using Bit.Api.Models.Request.Organizations; +using Bit.Api.AdminConsole.Controllers; +using Bit.Api.AdminConsole.Models.Request.Organizations; using Bit.Core.Entities; using Bit.Core.Models.Data.Organizations.Policies; using Bit.Core.Repositories; @@ -10,7 +10,7 @@ using NSubstitute; using Xunit; -namespace Bit.Api.Test.Controllers; +namespace Bit.Api.Test.AdminConsole.Controllers; [ControllerCustomize(typeof(OrganizationUsersController))] [SutProviderCustomize] diff --git a/test/Api.Test/Controllers/OrganizationsControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs similarity index 98% rename from test/Api.Test/Controllers/OrganizationsControllerTests.cs rename to test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs index 8f8f9b20d935..1ba9ed60d94e 100644 --- a/test/Api.Test/Controllers/OrganizationsControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs @@ -1,6 +1,7 @@ using System.Security.Claims; using AutoFixture.Xunit2; -using Bit.Api.Controllers; +using Bit.Api.AdminConsole.Controllers; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Auth.Entities; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models.Data; @@ -18,7 +19,7 @@ using NSubstitute; using Xunit; -namespace Bit.Api.Test.Controllers; +namespace Bit.Api.Test.AdminConsole.Controllers; public class OrganizationsControllerTests : IDisposable { diff --git a/test/Api.Test/Public/Controllers/GroupsControllerTests.cs b/test/Api.Test/AdminConsole/Public/Controllers/GroupsControllerTests.cs similarity index 89% rename from test/Api.Test/Public/Controllers/GroupsControllerTests.cs rename to test/Api.Test/AdminConsole/Public/Controllers/GroupsControllerTests.cs index fe268ff1e0e9..37351b3255d1 100644 --- a/test/Api.Test/Public/Controllers/GroupsControllerTests.cs +++ b/test/Api.Test/AdminConsole/Public/Controllers/GroupsControllerTests.cs @@ -1,10 +1,12 @@ -using Bit.Api.Models.Public.Request; -using Bit.Api.Models.Public.Response; -using Bit.Api.Public.Controllers; +using Bit.Api.AdminConsole.Public.Controllers; +using Bit.Api.AdminConsole.Public.Models.Request; +using Bit.Api.AdminConsole.Public.Models.Response; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Models.Data; -using Bit.Core.OrganizationFeatures.Groups.Interfaces; using Bit.Core.Repositories; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -12,7 +14,7 @@ using NSubstitute; using Xunit; -namespace Bit.Api.Test.Public.Controllers; +namespace Bit.Api.Test.AdminConsole.Public.Controllers; [ControllerCustomize(typeof(GroupsController))] [SutProviderCustomize] diff --git a/test/Api.Test/Controllers/AccountsControllerTests.cs b/test/Api.Test/Controllers/AccountsControllerTests.cs index 0dff6db93d7a..0bd368790e67 100644 --- a/test/Api.Test/Controllers/AccountsControllerTests.cs +++ b/test/Api.Test/Controllers/AccountsControllerTests.cs @@ -1,6 +1,7 @@ using System.Security.Claims; using Bit.Api.Auth.Models.Request.Accounts; using Bit.Api.Controllers; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Auth.Models.Api.Request.Accounts; using Bit.Core.Auth.Services; using Bit.Core.Entities; diff --git a/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs b/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs index c95d6861f6c0..bbeb50b18223 100644 --- a/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs +++ b/test/Api.Test/Controllers/OrganizationConnectionsControllerTests.cs @@ -143,10 +143,10 @@ await sutProvider.GetDependency().Received public async Task UpdateConnection_RequiresOwnerPermissions(SutProvider sutProvider) { sutProvider.GetDependency() - .GetByIdAsync(Arg.Any()) + .GetByIdOrganizationIdAsync(Arg.Any(), Arg.Any()) .Returns(new OrganizationConnection()); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateConnection(default, null)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateConnection(default, new OrganizationConnectionRequestModel())); Assert.Contains("You do not have permission to update this connection.", exception.Message); } @@ -164,8 +164,8 @@ public async Task UpdateConnection_BillingSync_OnlyOneConnectionOfEachType(Organ sutProvider.GetDependency().OrganizationOwner(typedModel.OrganizationId).Returns(true); var orgConnectionRepository = sutProvider.GetDependency(); - orgConnectionRepository.GetByIdAsync(existing1.Id).Returns(existing1); - orgConnectionRepository.GetByIdAsync(existing2.Id).Returns(existing2); + orgConnectionRepository.GetByIdOrganizationIdAsync(existing1.Id, existing1.OrganizationId).Returns(existing1); + orgConnectionRepository.GetByIdOrganizationIdAsync(existing2.Id, existing2.OrganizationId).Returns(existing2); orgConnectionRepository.GetByOrganizationIdTypeAsync(typedModel.OrganizationId, type).Returns(new[] { existing1, existing2 }); var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateConnection(existing1.Id, typedModel)); @@ -186,7 +186,7 @@ public async Task UpdateConnection_Scim_OnlyOneConnectionOfEachType(Organization sutProvider.GetDependency().OrganizationOwner(typedModel.OrganizationId).Returns(true); sutProvider.GetDependency() - .GetByIdAsync(existing1.Id) + .GetByIdOrganizationIdAsync(existing1.Id, existing1.OrganizationId) .Returns(existing1); sutProvider.GetDependency().ManageScim(typedModel.OrganizationId).Returns(true); @@ -212,6 +212,7 @@ public async Task UpdateConnection_Success(OrganizationConnection existing, Bill }); updated.Config = JsonSerializer.Serialize(config); updated.Id = existing.Id; + updated.OrganizationId = existing.OrganizationId; updated.Type = OrganizationConnectionType.CloudBillingSync; var model = RequestModelFromEntity(updated); @@ -224,7 +225,7 @@ public async Task UpdateConnection_Success(OrganizationConnection existing, Bill .UpdateAsync(default) .ReturnsForAnyArgs(updated); sutProvider.GetDependency() - .GetByIdAsync(existing.Id) + .GetByIdOrganizationIdAsync(existing.Id, existing.OrganizationId) .Returns(existing); OrganizationLicense organizationLicense = new OrganizationLicense(); @@ -264,6 +265,7 @@ public async Task UpdateConnection_BillingSyncType_InvalidLicense_ErrorThrows(Or }); updated.Config = JsonSerializer.Serialize(config); updated.Id = existing.Id; + updated.OrganizationId = existing.OrganizationId; updated.Type = OrganizationConnectionType.CloudBillingSync; var model = RequestModelFromEntity(updated); sutProvider.GetDependency().SelfHosted.Returns(true); @@ -275,7 +277,7 @@ public async Task UpdateConnection_BillingSyncType_InvalidLicense_ErrorThrows(Or .UpdateAsync(default) .ReturnsForAnyArgs(updated); sutProvider.GetDependency() - .GetByIdAsync(existing.Id) + .GetByIdOrganizationIdAsync(existing.Id, existing.OrganizationId) .Returns(existing); OrganizationLicense organizationLicense = new OrganizationLicense(); diff --git a/test/Api.Test/Controllers/OrganizationDomainControllerTests.cs b/test/Api.Test/Controllers/OrganizationDomainControllerTests.cs index bd97aa20bf5e..019dbdcba0bb 100644 --- a/test/Api.Test/Controllers/OrganizationDomainControllerTests.cs +++ b/test/Api.Test/Controllers/OrganizationDomainControllerTests.cs @@ -4,6 +4,7 @@ using Bit.Api.Models.Response; using Bit.Api.Models.Response.Organizations; using Bit.Core.Context; +using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.Models.Data.Organizations; using Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; @@ -13,8 +14,6 @@ using NSubstitute; using NSubstitute.ReturnsExtensions; using Xunit; -using Organization = Bit.Core.Entities.Organization; -using OrganizationDomain = Bit.Core.Entities.OrganizationDomain; namespace Bit.Api.Test.Controllers; @@ -28,7 +27,7 @@ public async Task Get_ShouldThrowUnauthorized_WhenOrgIdCannotManageSso(Guid orgI { sutProvider.GetDependency().ManageSso(orgId).Returns(false); - var requestAction = async () => await sutProvider.Sut.Get(orgId.ToString()); + var requestAction = async () => await sutProvider.Sut.Get(orgId); await Assert.ThrowsAsync(requestAction); } @@ -40,7 +39,7 @@ public async Task Get_ShouldNotFound_WhenOrganizationDoesNotExist(Guid orgId, sutProvider.GetDependency().ManageSso(orgId).Returns(true); sutProvider.GetDependency().GetByIdAsync(orgId).ReturnsNull(); - var requestAction = async () => await sutProvider.Sut.Get(orgId.ToString()); + var requestAction = async () => await sutProvider.Sut.Get(orgId); await Assert.ThrowsAsync(requestAction); } @@ -52,7 +51,7 @@ public async Task Get_ShouldReturnOrganizationDomainList_WhenOrgIdIsValid(Guid o sutProvider.GetDependency().ManageSso(orgId).Returns(true); sutProvider.GetDependency().GetByIdAsync(orgId).Returns(new Organization()); sutProvider.GetDependency() - .GetDomainsByOrganizationId(orgId).Returns(new List + .GetDomainsByOrganizationIdAsync(orgId).Returns(new List { new() { @@ -64,7 +63,7 @@ public async Task Get_ShouldReturnOrganizationDomainList_WhenOrgIdIsValid(Guid o } }); - var result = await sutProvider.Sut.Get(orgId.ToString()); + var result = await sutProvider.Sut.Get(orgId); Assert.IsType>(result); Assert.Equal(orgId, result.Data.Select(x => x.OrganizationId).FirstOrDefault()); @@ -76,7 +75,7 @@ public async Task GetByOrgIdAndId_ShouldThrowUnauthorized_WhenOrgIdCannotManageS { sutProvider.GetDependency().ManageSso(orgId).Returns(false); - var requestAction = async () => await sutProvider.Sut.Get(orgId.ToString(), id.ToString()); + var requestAction = async () => await sutProvider.Sut.Get(orgId, id); await Assert.ThrowsAsync(requestAction); } @@ -88,7 +87,7 @@ public async Task GetByOrgIdAndId_ShouldThrowNotFound_WhenOrganizationDoesNotExi sutProvider.GetDependency().ManageSso(orgId).Returns(true); sutProvider.GetDependency().GetByIdAsync(orgId).ReturnsNull(); - var requestAction = async () => await sutProvider.Sut.Get(orgId.ToString(), id.ToString()); + var requestAction = async () => await sutProvider.Sut.Get(orgId, id); await Assert.ThrowsAsync(requestAction); } @@ -99,9 +98,24 @@ public async Task GetByOrgIdAndId_ShouldThrowNotFound_WhenOrganizationDomainEntr { sutProvider.GetDependency().ManageSso(orgId).Returns(true); sutProvider.GetDependency().GetByIdAsync(orgId).Returns(new Organization()); - sutProvider.GetDependency().GetOrganizationDomainById(id).ReturnsNull(); + sutProvider.GetDependency().GetOrganizationDomainByIdOrganizationIdAsync(id, orgId).ReturnsNull(); - var requestAction = async () => await sutProvider.Sut.Get(orgId.ToString(), id.ToString()); + var requestAction = async () => await sutProvider.Sut.Get(orgId, id); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task GetByOrgIdAndId_ShouldThrowNotFound_WhenOrgIdDoesNotMatch(OrganizationDomain organizationDomain, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(organizationDomain.OrganizationId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(organizationDomain.OrganizationId).Returns(new Organization()); + sutProvider.GetDependency() + .GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId) + .ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.Get(organizationDomain.OrganizationId, organizationDomain.Id); await Assert.ThrowsAsync(requestAction); } @@ -112,7 +126,7 @@ public async Task Get_ShouldReturnOrganizationDomain_WhenOrgIdAndIdAreValid(Guid { sutProvider.GetDependency().ManageSso(orgId).Returns(true); sutProvider.GetDependency().GetByIdAsync(orgId).Returns(new Organization()); - sutProvider.GetDependency().GetOrganizationDomainById(id) + sutProvider.GetDependency().GetOrganizationDomainByIdOrganizationIdAsync(id, orgId) .Returns(new OrganizationDomain { Id = Guid.NewGuid(), @@ -122,7 +136,7 @@ public async Task Get_ShouldReturnOrganizationDomain_WhenOrgIdAndIdAreValid(Guid Txt = "btw+12342" }); - var result = await sutProvider.Sut.Get(orgId.ToString(), id.ToString()); + var result = await sutProvider.Sut.Get(orgId, id); Assert.IsType(result); Assert.Equal(orgId, result.OrganizationId); @@ -134,7 +148,7 @@ public async Task Post_ShouldThrowUnauthorized_OrgIdCannotManageSso(Guid orgId, { sutProvider.GetDependency().ManageSso(orgId).Returns(false); - var requestAction = async () => await sutProvider.Sut.Post(orgId.ToString(), model); + var requestAction = async () => await sutProvider.Sut.Post(orgId, model); await Assert.ThrowsAsync(requestAction); } @@ -146,7 +160,7 @@ public async Task Post_ShouldThrowNotFound_WhenOrganizationDoesNotExist(Guid org sutProvider.GetDependency().ManageSso(orgId).Returns(true); sutProvider.GetDependency().GetByIdAsync(orgId).ReturnsNull(); - var requestAction = async () => await sutProvider.Sut.Post(orgId.ToString(), model); + var requestAction = async () => await sutProvider.Sut.Post(orgId, model); await Assert.ThrowsAsync(requestAction); } @@ -160,7 +174,7 @@ public async Task Post_ShouldCreateEntry_WhenRequestIsValid(Guid orgId, Organiza sutProvider.GetDependency().CreateAsync(Arg.Any()) .Returns(new OrganizationDomain()); - var result = await sutProvider.Sut.Post(orgId.ToString(), model); + var result = await sutProvider.Sut.Post(orgId, model); await sutProvider.GetDependency().ReceivedWithAnyArgs(1) .CreateAsync(Arg.Any()); @@ -173,7 +187,7 @@ public async Task Verify_ShouldThrowUnauthorized_WhenOrgIdCannotManageSso(Guid o { sutProvider.GetDependency().ManageSso(orgId).Returns(false); - var requestAction = async () => await sutProvider.Sut.Verify(orgId.ToString(), id.ToString()); + var requestAction = async () => await sutProvider.Sut.Verify(orgId, id); await Assert.ThrowsAsync(requestAction); } @@ -185,24 +199,42 @@ public async Task Verify_ShouldThrowNotFound_WhenOrganizationDoesNotExist(Guid o sutProvider.GetDependency().ManageSso(orgId).Returns(true); sutProvider.GetDependency().GetByIdAsync(orgId).ReturnsNull(); - var requestAction = async () => await sutProvider.Sut.Verify(orgId.ToString(), id.ToString()); + var requestAction = async () => await sutProvider.Sut.Verify(orgId, id); await Assert.ThrowsAsync(requestAction); } [Theory, BitAutoData] - public async Task Verify_WhenRequestIsValid(Guid orgId, Guid id, + public async Task VerifyOrganizationDomain_ShouldThrowNotFound_WhenOrgIdDoesNotMatch(OrganizationDomain organizationDomain, SutProvider sutProvider) { - sutProvider.GetDependency().ManageSso(orgId).Returns(true); - sutProvider.GetDependency().GetByIdAsync(orgId).Returns(new Organization()); - sutProvider.GetDependency().VerifyOrganizationDomain(id) + sutProvider.GetDependency().ManageSso(organizationDomain.OrganizationId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(organizationDomain.OrganizationId).Returns(new Organization()); + sutProvider.GetDependency() + .GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId) + .ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.Verify(organizationDomain.OrganizationId, organizationDomain.Id); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task Verify_WhenRequestIsValid(OrganizationDomain organizationDomain, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(organizationDomain.OrganizationId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(organizationDomain.OrganizationId).Returns(new Organization()); + sutProvider.GetDependency() + .GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId) + .Returns(organizationDomain); + sutProvider.GetDependency().VerifyOrganizationDomainAsync(organizationDomain) .Returns(new OrganizationDomain()); - var result = await sutProvider.Sut.Verify(orgId.ToString(), id.ToString()); + var result = await sutProvider.Sut.Verify(organizationDomain.OrganizationId, organizationDomain.Id); await sutProvider.GetDependency().Received(1) - .VerifyOrganizationDomain(id); + .VerifyOrganizationDomainAsync(organizationDomain); Assert.IsType(result); } @@ -212,7 +244,7 @@ public async Task RemoveDomain_ShouldThrowUnauthorized_OrgIdCannotManageSso(Guid { sutProvider.GetDependency().ManageSso(orgId).Returns(false); - var requestAction = async () => await sutProvider.Sut.RemoveDomain(orgId.ToString(), id.ToString()); + var requestAction = async () => await sutProvider.Sut.RemoveDomain(orgId, id); await Assert.ThrowsAsync(requestAction); } @@ -224,22 +256,40 @@ public async Task RemoveDomain_ShouldThrowNotFound_WhenOrganizationDoesNotExist( sutProvider.GetDependency().ManageSso(orgId).Returns(true); sutProvider.GetDependency().GetByIdAsync(orgId).ReturnsNull(); - var requestAction = async () => await sutProvider.Sut.RemoveDomain(orgId.ToString(), id.ToString()); + var requestAction = async () => await sutProvider.Sut.RemoveDomain(orgId, id); await Assert.ThrowsAsync(requestAction); } [Theory, BitAutoData] - public async Task RemoveDomain_WhenRequestIsValid(Guid orgId, Guid id, + public async Task RemoveDomain_ShouldThrowNotFound_WhenOrgIdDoesNotMatch(OrganizationDomain organizationDomain, SutProvider sutProvider) { - sutProvider.GetDependency().ManageSso(orgId).Returns(true); - sutProvider.GetDependency().GetByIdAsync(orgId).Returns(new Organization()); + sutProvider.GetDependency().ManageSso(organizationDomain.OrganizationId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(organizationDomain.OrganizationId).Returns(new Organization()); + sutProvider.GetDependency() + .GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId) + .ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.RemoveDomain(organizationDomain.OrganizationId, organizationDomain.Id); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task RemoveDomain_WhenRequestIsValid(OrganizationDomain organizationDomain, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(organizationDomain.OrganizationId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(organizationDomain.OrganizationId).Returns(new Organization()); + sutProvider.GetDependency() + .GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId) + .Returns(organizationDomain); - await sutProvider.Sut.RemoveDomain(orgId.ToString(), id.ToString()); + await sutProvider.Sut.RemoveDomain(organizationDomain.OrganizationId, organizationDomain.Id); await sutProvider.GetDependency().Received(1) - .DeleteAsync(id); + .DeleteAsync(organizationDomain); } [Theory, BitAutoData] diff --git a/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs b/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs index e369120a9541..6c3d85e425df 100644 --- a/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs +++ b/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs @@ -2,8 +2,9 @@ using Bit.Api.SecretsManager.Controllers; using Bit.Api.SecretsManager.Models.Request; using Bit.Api.Test.SecretsManager.Enums; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; -using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Repositories; diff --git a/test/Api.Test/SecretsManager/Controllers/ProjectsControllerTests.cs b/test/Api.Test/SecretsManager/Controllers/ProjectsControllerTests.cs index 27aa7ea713e2..aba196ecf2be 100644 --- a/test/Api.Test/SecretsManager/Controllers/ProjectsControllerTests.cs +++ b/test/Api.Test/SecretsManager/Controllers/ProjectsControllerTests.cs @@ -44,7 +44,7 @@ private static void SetupUserWithPermission(SutProvider sutP [Theory] [BitAutoData] - public async void ListByOrganization_SmNotEnabled_Throws(SutProvider sutProvider, Guid data) + public async void ListByOrganization_SmAccessDenied_Throws(SutProvider sutProvider, Guid data) { sutProvider.GetDependency().AccessSecretsManager(data).Returns(false); @@ -205,7 +205,7 @@ await sutProvider.GetDependency().Received(1) [Theory] [BitAutoData] - public async void Get_SmNotEnabled_Throws(SutProvider sutProvider, Guid data, Guid orgId) + public async void Get_SmAccessDenied_Throws(SutProvider sutProvider, Guid data, Guid orgId) { SetupAdmin(sutProvider, orgId); sutProvider.GetDependency().AccessSecretsManager(orgId).Returns(false); diff --git a/test/Api.Test/SecretsManager/Controllers/SecretsManagerEventsControllerTests.cs b/test/Api.Test/SecretsManager/Controllers/SecretsManagerEventsControllerTests.cs new file mode 100644 index 000000000000..63eecf14825d --- /dev/null +++ b/test/Api.Test/SecretsManager/Controllers/SecretsManagerEventsControllerTests.cs @@ -0,0 +1,79 @@ +using System.Security.Claims; +using Bit.Api.SecretsManager.Controllers; +using Bit.Core.Exceptions; +using Bit.Core.Models.Data; +using Bit.Core.Repositories; +using Bit.Core.SecretsManager.Entities; +using Bit.Core.SecretsManager.Repositories; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using Microsoft.AspNetCore.Authorization; +using NSubstitute; +using Xunit; + +namespace Bit.Api.Test.SecretsManager.Controllers; + +[ControllerCustomize(typeof(SecretsManagerEventsController))] +[SutProviderCustomize] +[JsonDocumentCustomize] +public class SecretsManagerEventsControllerTests +{ + [Theory] + [BitAutoData] + public async void GetServiceAccountEvents_NoAccess_Throws(SutProvider sutProvider, + ServiceAccount data) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(data); + sutProvider.GetDependency() + .AuthorizeAsync(Arg.Any(), data, + Arg.Any>()).ReturnsForAnyArgs(AuthorizationResult.Failed()); + + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetServiceAccountEventsAsync(data.Id)); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetManyByOrganizationServiceAccountAsync(Arg.Any(), Arg.Any(), Arg.Any(), + Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData] + public async void GetServiceAccountEvents_DateRangeOver_Throws( + SutProvider sutProvider, + ServiceAccount data) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(data); + sutProvider.GetDependency() + .AuthorizeAsync(Arg.Any(), data, + Arg.Any>()).ReturnsForAnyArgs(AuthorizationResult.Success()); + + var start = DateTime.UtcNow.AddYears(-1); + var end = DateTime.UtcNow.AddYears(1); + + await Assert.ThrowsAsync(() => + sutProvider.Sut.GetServiceAccountEventsAsync(data.Id, start, end)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetManyByOrganizationServiceAccountAsync(Arg.Any(), Arg.Any(), Arg.Any(), + Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData] + public async void GetServiceAccountEvents_Success(SutProvider sutProvider, + ServiceAccount data) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(data); + sutProvider.GetDependency() + .AuthorizeAsync(Arg.Any(), data, + Arg.Any>()).ReturnsForAnyArgs(AuthorizationResult.Success()); + sutProvider.GetDependency() + .GetManyByOrganizationServiceAccountAsync(default, default, default, default, default) + .ReturnsForAnyArgs(new PagedResult()); + + await sutProvider.Sut.GetServiceAccountEventsAsync(data.Id); + + await sutProvider.GetDependency().Received(1) + .GetManyByOrganizationServiceAccountAsync(data.OrganizationId, data.Id, Arg.Any(), + Arg.Any(), Arg.Any()); + } +} diff --git a/test/Api.Test/Vault/Controllers/SyncControllerTests.cs b/test/Api.Test/Vault/Controllers/SyncControllerTests.cs index b6e12eabced9..e1a9c1562978 100644 --- a/test/Api.Test/Vault/Controllers/SyncControllerTests.cs +++ b/test/Api.Test/Vault/Controllers/SyncControllerTests.cs @@ -3,9 +3,11 @@ using AutoFixture; using Bit.Api.Vault.Controllers; using Bit.Api.Vault.Models.Response; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Exceptions; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.OrganizationUsers; @@ -73,7 +75,7 @@ public async Task Get_Success_AtLeastOneEnabledOrg(User user, user.EquivalentDomains = JsonSerializer.Serialize(userEquivalentDomains); user.ExcludedGlobalEquivalentDomains = JsonSerializer.Serialize(userExcludedGlobalEquivalentDomains); - // At least 1 org needs to be enabled to fully test + // At least 1 org needs to be enabled to fully test if (!organizationUserDetails.Any(o => o.Enabled)) { // We need at least 1 enabled org @@ -165,7 +167,7 @@ public async Task Get_Success_AllDisabledOrgs(User user, user.EquivalentDomains = JsonSerializer.Serialize(userEquivalentDomains); user.ExcludedGlobalEquivalentDomains = JsonSerializer.Serialize(userExcludedGlobalEquivalentDomains); - // All orgs disabled + // All orgs disabled if (organizationUserDetails.Count > 0) { foreach (var orgUserDetails in organizationUserDetails) @@ -218,7 +220,7 @@ public async Task Get_Success_AllDisabledOrgs(User user, Assert.IsType(result); - // Collections should be empty when all standard orgs are disabled. + // Collections should be empty when all standard orgs are disabled. Assert.Empty(result.Collections); } @@ -297,7 +299,7 @@ public async Task Get_ProviderPlanTypeProperlyPopulated(User user, Assert.IsType(result); // Look up ProviderOrg output and compare to ProviderOrg method inputs to ensure - // product type is set correctly. + // product type is set correctly. foreach (var profProviderOrg in result.Profile.ProviderOrganizations) { var matchedProviderUserOrgDetails = @@ -305,7 +307,7 @@ public async Task Get_ProviderPlanTypeProperlyPopulated(User user, if (matchedProviderUserOrgDetails != null) { - var providerOrgProductType = StaticStore.GetPasswordManagerPlan(matchedProviderUserOrgDetails.PlanType).Product; + var providerOrgProductType = StaticStore.GetPlan(matchedProviderUserOrgDetails.PlanType).Product; Assert.Equal(providerOrgProductType, profProviderOrg.PlanProductType); } } @@ -337,7 +339,7 @@ await cipherRepository.ReceivedWithAnyArgs(1) await sendRepository.ReceivedWithAnyArgs(1) .GetManyByUserIdAsync(default); - // These two are only called when at least 1 enabled org. + // These two are only called when at least 1 enabled org. if (hasEnabledOrgs) { await collectionRepository.ReceivedWithAnyArgs(1) @@ -347,7 +349,7 @@ await collectionCipherRepository.ReceivedWithAnyArgs(1) } else { - // all disabled orgs + // all disabled orgs await collectionRepository.ReceivedWithAnyArgs(0) .GetManyByUserIdAsync(default); await collectionCipherRepository.ReceivedWithAnyArgs(0) diff --git a/test/Api.Test/packages.lock.json b/test/Api.Test/packages.lock.json index e8d23dc9ba8a..f778ea613b98 100644 --- a/test/Api.Test/packages.lock.json +++ b/test/Api.Test/packages.lock.json @@ -331,10 +331,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -3014,25 +3015,25 @@ "AspNetCore.HealthChecks.SqlServer": "[6.0.2, )", "AspNetCore.HealthChecks.Uris": "[6.0.3, )", "Azure.Messaging.EventGrid": "[4.10.0, )", - "Commercial.Core": "[2023.8.3, )", - "Commercial.Infrastructure.EntityFramework": "[2023.8.3, )", - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )", + "Commercial.Core": "[2023.9.0, )", + "Commercial.Infrastructure.EntityFramework": "[2023.9.0, )", + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )", "Swashbuckle.AspNetCore": "[6.5.0, )" } }, "commercial.core": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )" + "Core": "[2023.9.0, )" } }, "commercial.infrastructure.entityframework": { "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } }, "common": { @@ -3040,7 +3041,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -3059,7 +3060,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -3094,8 +3095,8 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Common": "[2023.8.3, )", - "Core": "[2023.8.3, )", + "Common": "[2023.9.0, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -3105,7 +3106,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -3113,7 +3114,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -3125,9 +3126,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/test/Billing.Test/Billing.Test.csproj b/test/Billing.Test/Billing.Test.csproj index 09252a70e1ae..302f590ad133 100644 --- a/test/Billing.Test/Billing.Test.csproj +++ b/test/Billing.Test/Billing.Test.csproj @@ -5,6 +5,7 @@ + @@ -24,4 +25,25 @@ + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + diff --git a/test/Billing.Test/Resources/Events/charge.succeeded.json b/test/Billing.Test/Resources/Events/charge.succeeded.json new file mode 100644 index 000000000000..e88efa40797a --- /dev/null +++ b/test/Billing.Test/Resources/Events/charge.succeeded.json @@ -0,0 +1,130 @@ +{ + "id": "evt_3NvKgBIGBnsLynRr0pJJqudS", + "object": "event", + "api_version": "2022-08-01", + "created": 1695909300, + "data": { + "object": { + "id": "ch_3NvKgBIGBnsLynRr0ZyvP9AN", + "object": "charge", + "amount": 7200, + "amount_captured": 7200, + "amount_refunded": 0, + "application": null, + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_3NvKgBIGBnsLynRr0KbYEz76", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "BITWARDEN", + "captured": true, + "created": 1695909299, + "currency": "usd", + "customer": "cus_OimAwOzQmThNXx", + "description": "Subscription update", + "destination": null, + "dispute": null, + "disputed": false, + "failure_balance_transaction": null, + "failure_code": null, + "failure_message": null, + "fraud_details": { + }, + "invoice": "in_1NvKgBIGBnsLynRrmRFHAcoV", + "livemode": false, + "metadata": { + }, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 37, + "seller_message": "Payment complete.", + "type": "authorized" + }, + "paid": true, + "payment_intent": "pi_3NvKgBIGBnsLynRr09Ny3Heu", + "payment_method": "pm_1NvKbpIGBnsLynRrcOwez4A1", + "payment_method_details": { + "card": { + "amount_authorized": 7200, + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "exp_month": 6, + "exp_year": 2033, + "extended_authorization": { + "status": "disabled" + }, + "fingerprint": "0VgUBpvqcUUnuSmK", + "funding": "credit", + "incremental_authorization": { + "status": "unavailable" + }, + "installments": null, + "last4": "4242", + "mandate": null, + "multicapture": { + "status": "unavailable" + }, + "network": "visa", + "network_token": { + "used": false + }, + "overcapture": { + "maximum_amount_capturable": 7200, + "status": "unavailable" + }, + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "receipt_email": "cturnbull@bitwarden.com", + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/invoices/CAcaFwoVYWNjdF8xOXNtSVhJR0Juc0x5blJyKLSL1qgGMgYTnk_JOUA6LBY_SDEZNtuae1guQ6Dlcuev1TUHwn712t-UNnZdIc383zS15bXv_1dby8e4?s=ap", + "refunded": false, + "refunds": { + "object": "list", + "data": [ + ], + "has_more": false, + "total_count": 0, + "url": "/v1/charges/ch_3NvKgBIGBnsLynRr0ZyvP9AN/refunds" + }, + "review": null, + "shipping": null, + "source": null, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + }, + "livemode": false, + "pending_webhooks": 9, + "request": { + "id": "req_rig8N5Ca8EXYRy", + "idempotency_key": "db75068d-5d90-4c65-a410-4e2ed8347509" + }, + "type": "charge.succeeded" +} diff --git a/test/Billing.Test/Resources/Events/customer.subscription.updated.json b/test/Billing.Test/Resources/Events/customer.subscription.updated.json new file mode 100644 index 000000000000..1a128c1508e3 --- /dev/null +++ b/test/Billing.Test/Resources/Events/customer.subscription.updated.json @@ -0,0 +1,177 @@ +{ + "id": "evt_1NvLMDIGBnsLynRr6oBxebrE", + "object": "event", + "api_version": "2022-08-01", + "created": 1695911902, + "data": { + "object": { + "id": "sub_1NvKoKIGBnsLynRrcLIAUWGf", + "object": "subscription", + "application": null, + "application_fee_percent": null, + "automatic_tax": { + "enabled": false + }, + "billing_cycle_anchor": 1695911900, + "billing_thresholds": null, + "cancel_at": null, + "cancel_at_period_end": false, + "canceled_at": null, + "cancellation_details": { + "comment": null, + "feedback": null, + "reason": null + }, + "collection_method": "charge_automatically", + "created": 1695909804, + "currency": "usd", + "current_period_end": 1727534300, + "current_period_start": 1695911900, + "customer": "cus_OimNNCC3RiI2HQ", + "days_until_due": null, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + ], + "description": null, + "discount": null, + "ended_at": null, + "items": { + "object": "list", + "data": [ + { + "id": "si_OimNgVtrESpqus", + "object": "subscription_item", + "billing_thresholds": null, + "created": 1695909805, + "metadata": { + }, + "plan": { + "id": "enterprise-org-seat-annually", + "object": "plan", + "active": true, + "aggregate_usage": null, + "amount": 3600, + "amount_decimal": "3600", + "billing_scheme": "per_unit", + "created": 1494268677, + "currency": "usd", + "interval": "year", + "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": "2019 Enterprise Seat (Annually)", + "product": "prod_BUtogGemxnTi9z", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" + }, + "price": { + "id": "enterprise-org-seat-annually", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1494268677, + "currency": "usd", + "custom_unit_amount": null, + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": "2019 Enterprise Seat (Annually)", + "product": "prod_BUtogGemxnTi9z", + "recurring": { + "aggregate_usage": null, + "interval": "year", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 3600, + "unit_amount_decimal": "3600" + }, + "quantity": 1, + "subscription": "sub_1NvKoKIGBnsLynRrcLIAUWGf", + "tax_rates": [ + ] + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/subscription_items?subscription=sub_1NvKoKIGBnsLynRrcLIAUWGf" + }, + "latest_invoice": "in_1NvLM9IGBnsLynRrOysII07d", + "livemode": false, + "metadata": { + "organizationId": "84a569ea-4643-474a-83a9-b08b00e7a20d" + }, + "next_pending_invoice_item_invoice": null, + "on_behalf_of": null, + "pause_collection": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null, + "save_default_payment_method": "off" + }, + "pending_invoice_item_interval": null, + "pending_setup_intent": null, + "pending_update": null, + "plan": { + "id": "enterprise-org-seat-annually", + "object": "plan", + "active": true, + "aggregate_usage": null, + "amount": 3600, + "amount_decimal": "3600", + "billing_scheme": "per_unit", + "created": 1494268677, + "currency": "usd", + "interval": "year", + "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": "2019 Enterprise Seat (Annually)", + "product": "prod_BUtogGemxnTi9z", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" + }, + "quantity": 1, + "schedule": null, + "start_date": 1695909804, + "status": "active", + "test_clock": null, + "transfer_data": null, + "trial_end": 1695911899, + "trial_settings": { + "end_behavior": { + "missing_payment_method": "create_invoice" + } + }, + "trial_start": 1695909804 + }, + "previous_attributes": { + "billing_cycle_anchor": 1696514604, + "current_period_end": 1696514604, + "current_period_start": 1695909804, + "latest_invoice": "in_1NvKoKIGBnsLynRrSNRC6oYI", + "status": "trialing", + "trial_end": 1696514604 + } + }, + "livemode": false, + "pending_webhooks": 8, + "request": { + "id": "req_DMZPUU3BI66zAx", + "idempotency_key": "3fd8b4a5-6a20-46ab-9f45-b37b02a8017f" + }, + "type": "customer.subscription.updated" +} diff --git a/test/Billing.Test/Resources/Events/customer.updated.json b/test/Billing.Test/Resources/Events/customer.updated.json new file mode 100644 index 000000000000..323a9b9ba5fe --- /dev/null +++ b/test/Billing.Test/Resources/Events/customer.updated.json @@ -0,0 +1,311 @@ +{ + "id": "evt_1NvKjSIGBnsLynRrS3MTK4DZ", + "object": "event", + "account": "acct_19smIXIGBnsLynRr", + "api_version": "2022-08-01", + "created": 1695909502, + "data": { + "object": { + "id": "cus_Of54kUr3gV88lM", + "object": "customer", + "address": { + "city": null, + "country": "US", + "line1": "", + "line2": null, + "postal_code": "33701", + "state": null + }, + "balance": 0, + "created": 1695056798, + "currency": "usd", + "default_source": "src_1NtAfeIGBnsLynRrYDrceax7", + "delinquent": false, + "description": "Premium User", + "discount": null, + "email": "premium@bitwarden.com", + "invoice_prefix": "C506E8CE", + "invoice_settings": { + "custom_fields": [ + { + "name": "Subscriber", + "value": "Premium User" + } + ], + "default_payment_method": "pm_1Nrku9IGBnsLynRrcsQ3hy6C", + "footer": null, + "rendering_options": null + }, + "livemode": false, + "metadata": { + "region": "US" + }, + "name": null, + "next_invoice_sequence": 2, + "phone": null, + "preferred_locales": [ + ], + "shipping": null, + "tax_exempt": "none", + "test_clock": null, + "account_balance": 0, + "cards": { + "object": "list", + "data": [ + ], + "has_more": false, + "total_count": 0, + "url": "/v1/customers/cus_Of54kUr3gV88lM/cards" + }, + "default_card": null, + "default_currency": "usd", + "sources": { + "object": "list", + "data": [ + { + "id": "src_1NtAfeIGBnsLynRrYDrceax7", + "object": "source", + "ach_credit_transfer": { + "account_number": "test_b2d1c6415f6f", + "routing_number": "110000000", + "fingerprint": "ePO4hBQanSft3gvU", + "swift_code": "TSTEZ122", + "bank_name": "TEST BANK", + "refund_routing_number": null, + "refund_account_holder_type": null, + "refund_account_holder_name": null + }, + "amount": null, + "client_secret": "src_client_secret_bUAP2uDRw6Pwj0xYk32LmJ3K", + "created": 1695394170, + "currency": "usd", + "customer": "cus_Of54kUr3gV88lM", + "flow": "receiver", + "livemode": false, + "metadata": { + }, + "owner": { + "address": null, + "email": "amount_0@stripe.com", + "name": null, + "phone": null, + "verified_address": null, + "verified_email": null, + "verified_name": null, + "verified_phone": null + }, + "receiver": { + "address": "110000000-test_b2d1c6415f6f", + "amount_charged": 0, + "amount_received": 0, + "amount_returned": 0, + "refund_attributes_method": "email", + "refund_attributes_status": "missing" + }, + "statement_descriptor": null, + "status": "pending", + "type": "ach_credit_transfer", + "usage": "reusable" + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/customers/cus_Of54kUr3gV88lM/sources" + }, + "subscriptions": { + "object": "list", + "data": [ + { + "id": "sub_1NrkuBIGBnsLynRrzjFGIjEw", + "object": "subscription", + "application": null, + "application_fee_percent": null, + "automatic_tax": { + "enabled": false + }, + "billing": "charge_automatically", + "billing_cycle_anchor": 1695056799, + "billing_thresholds": null, + "cancel_at": null, + "cancel_at_period_end": false, + "canceled_at": null, + "cancellation_details": { + "comment": null, + "feedback": null, + "reason": null + }, + "collection_method": "charge_automatically", + "created": 1695056799, + "currency": "usd", + "current_period_end": 1726679199, + "current_period_start": 1695056799, + "customer": "cus_Of54kUr3gV88lM", + "days_until_due": null, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + ], + "description": null, + "discount": null, + "ended_at": null, + "invoice_customer_balance_settings": { + "consume_applied_balance_on_void": true + }, + "items": { + "object": "list", + "data": [ + { + "id": "si_Of54i3aK9I5Wro", + "object": "subscription_item", + "billing_thresholds": null, + "created": 1695056800, + "metadata": { + }, + "plan": { + "id": "premium-annually", + "object": "plan", + "active": true, + "aggregate_usage": null, + "amount": 1000, + "amount_decimal": "1000", + "billing_scheme": "per_unit", + "created": 1499289328, + "currency": "usd", + "interval": "year", + "interval_count": 1, + "livemode": false, + "metadata": { + }, + "name": "Premium (Annually)", + "nickname": "Premium (Annually)", + "product": "prod_BUqgYr48VzDuCg", + "statement_description": null, + "statement_descriptor": null, + "tiers": null, + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" + }, + "price": { + "id": "premium-annually", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1499289328, + "currency": "usd", + "custom_unit_amount": null, + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": "Premium (Annually)", + "product": "prod_BUqgYr48VzDuCg", + "recurring": { + "aggregate_usage": null, + "interval": "year", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 1000, + "unit_amount_decimal": "1000" + }, + "quantity": 1, + "subscription": "sub_1NrkuBIGBnsLynRrzjFGIjEw", + "tax_rates": [ + ] + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/subscription_items?subscription=sub_1NrkuBIGBnsLynRrzjFGIjEw" + }, + "latest_invoice": "in_1NrkuBIGBnsLynRr40gyJTVU", + "livemode": false, + "metadata": { + "userId": "91f40b6d-ac3b-4348-804b-b0810119ac6a" + }, + "next_pending_invoice_item_invoice": null, + "on_behalf_of": null, + "pause_collection": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null, + "save_default_payment_method": "off" + }, + "pending_invoice_item_interval": null, + "pending_setup_intent": null, + "pending_update": null, + "plan": { + "id": "premium-annually", + "object": "plan", + "active": true, + "aggregate_usage": null, + "amount": 1000, + "amount_decimal": "1000", + "billing_scheme": "per_unit", + "created": 1499289328, + "currency": "usd", + "interval": "year", + "interval_count": 1, + "livemode": false, + "metadata": { + }, + "name": "Premium (Annually)", + "nickname": "Premium (Annually)", + "product": "prod_BUqgYr48VzDuCg", + "statement_description": null, + "statement_descriptor": null, + "tiers": null, + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" + }, + "quantity": 1, + "schedule": null, + "start": 1695056799, + "start_date": 1695056799, + "status": "active", + "tax_percent": null, + "test_clock": null, + "transfer_data": null, + "trial_end": null, + "trial_settings": { + "end_behavior": { + "missing_payment_method": "create_invoice" + } + }, + "trial_start": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/customers/cus_Of54kUr3gV88lM/subscriptions" + }, + "tax_ids": { + "object": "list", + "data": [ + ], + "has_more": false, + "total_count": 0, + "url": "/v1/customers/cus_Of54kUr3gV88lM/tax_ids" + }, + "tax_info": null, + "tax_info_verification": null + }, + "previous_attributes": { + "email": "premium-new@bitwarden.com" + } + }, + "livemode": false, + "pending_webhooks": 5, + "request": "req_2RtGdXCfiicFLx", + "type": "customer.updated", + "user_id": "acct_19smIXIGBnsLynRr" +} diff --git a/test/Billing.Test/Resources/Events/invoice.created.json b/test/Billing.Test/Resources/Events/invoice.created.json new file mode 100644 index 000000000000..b70442ed3691 --- /dev/null +++ b/test/Billing.Test/Resources/Events/invoice.created.json @@ -0,0 +1,222 @@ +{ + "id": "evt_1NvKzfIGBnsLynRr0SkwrlkE", + "object": "event", + "api_version": "2022-08-01", + "created": 1695910506, + "data": { + "object": { + "id": "in_1NvKzdIGBnsLynRr8fE8cpbg", + "object": "invoice", + "account_country": "US", + "account_name": "Bitwarden Inc.", + "account_tax_ids": null, + "amount_due": 0, + "amount_paid": 0, + "amount_remaining": 0, + "amount_shipping": 0, + "application": null, + "application_fee_amount": null, + "attempt_count": 0, + "attempted": true, + "auto_advance": false, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing_reason": "subscription_create", + "charge": null, + "collection_method": "charge_automatically", + "created": 1695910505, + "currency": "usd", + "custom_fields": [ + { + "name": "Organization", + "value": "teams 2023 monthly - 2" + } + ], + "customer": "cus_OimYrxnMTMMK1E", + "customer_address": { + "city": null, + "country": "US", + "line1": "", + "line2": null, + "postal_code": "12345", + "state": null + }, + "customer_email": "cturnbull@bitwarden.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [ + ], + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + ], + "description": null, + "discount": null, + "discounts": [ + ], + "due_date": null, + "effective_at": 1695910505, + "ending_balance": 0, + "footer": null, + "from_invoice": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_19smIXIGBnsLynRr/test_YWNjdF8xOXNtSVhJR0Juc0x5blJyLF9PaW1ZVlo4dFRtbkNQQVY5aHNpckQxN1QzRHBPcVBOLDg2NDUxMzA30200etYRHca2?s=ap", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_19smIXIGBnsLynRr/test_YWNjdF8xOXNtSVhJR0Juc0x5blJyLF9PaW1ZVlo4dFRtbkNQQVY5aHNpckQxN1QzRHBPcVBOLDg2NDUxMzA30200etYRHca2/pdf?s=ap", + "last_finalization_error": null, + "latest_revision": null, + "lines": { + "object": "list", + "data": [ + { + "id": "il_1NvKzdIGBnsLynRr2pS4ZA8e", + "object": "line_item", + "amount": 0, + "amount_excluding_tax": 0, + "currency": "usd", + "description": "Trial period for Teams Organization Seat", + "discount_amounts": [ + ], + "discountable": true, + "discounts": [ + ], + "livemode": false, + "metadata": { + "organizationId": "3fbc84ce-102d-4919-b89b-b08b00ead71a" + }, + "period": { + "end": 1696515305, + "start": 1695910505 + }, + "plan": { + "id": "2020-teams-org-seat-monthly", + "object": "plan", + "active": true, + "aggregate_usage": null, + "amount": 400, + "amount_decimal": "400", + "billing_scheme": "per_unit", + "created": 1595263113, + "currency": "usd", + "interval": "month", + "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": "Teams Organization Seat (Monthly) 2023", + "product": "prod_HgOooYXDr2DDAA", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" + }, + "price": { + "id": "2020-teams-org-seat-monthly", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1595263113, + "currency": "usd", + "custom_unit_amount": null, + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": "Teams Organization Seat (Monthly) 2023", + "product": "prod_HgOooYXDr2DDAA", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 400, + "unit_amount_decimal": "400" + }, + "proration": false, + "proration_details": { + "credited_items": null + }, + "quantity": 1, + "subscription": "sub_1NvKzdIGBnsLynRrKIHQamZc", + "subscription_item": "si_OimYNSbvuqdtTr", + "tax_amounts": [ + ], + "tax_rates": [ + ], + "type": "subscription", + "unit_amount_excluding_tax": "0" + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/invoices/in_1NvKzdIGBnsLynRr8fE8cpbg/lines" + }, + "livemode": false, + "metadata": { + }, + "next_payment_attempt": null, + "number": "3E96D078-0001", + "on_behalf_of": null, + "paid": true, + "paid_out_of_band": false, + "payment_intent": null, + "payment_settings": { + "default_mandate": null, + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1695910505, + "period_start": 1695910505, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "quote": null, + "receipt_number": null, + "rendering": null, + "rendering_options": null, + "shipping_cost": null, + "shipping_details": null, + "starting_balance": 0, + "statement_descriptor": null, + "status": "paid", + "status_transitions": { + "finalized_at": 1695910505, + "marked_uncollectible_at": null, + "paid_at": 1695910505, + "voided_at": null + }, + "subscription": "sub_1NvKzdIGBnsLynRrKIHQamZc", + "subscription_details": { + "metadata": { + "organizationId": "3fbc84ce-102d-4919-b89b-b08b00ead71a" + } + }, + "subtotal": 0, + "subtotal_excluding_tax": 0, + "tax": null, + "test_clock": null, + "total": 0, + "total_discount_amounts": [ + ], + "total_excluding_tax": 0, + "total_tax_amounts": [ + ], + "transfer_data": null, + "webhooks_delivered_at": null + } + }, + "livemode": false, + "pending_webhooks": 8, + "request": { + "id": "req_roIwONfgyfZdr4", + "idempotency_key": "dd2a171b-b9c7-4d2d-89d5-1ceae3c0595d" + }, + "type": "invoice.created" +} diff --git a/test/Billing.Test/Resources/Events/invoice.upcoming.json b/test/Billing.Test/Resources/Events/invoice.upcoming.json new file mode 100644 index 000000000000..7b9055d497bd --- /dev/null +++ b/test/Billing.Test/Resources/Events/invoice.upcoming.json @@ -0,0 +1,225 @@ +{ + "id": "evt_1Nv0w8IGBnsLynRrZoDVI44u", + "object": "event", + "api_version": "2022-08-01", + "created": 1695833408, + "data": { + "object": { + "object": "invoice", + "account_country": "US", + "account_name": "Bitwarden Inc.", + "account_tax_ids": null, + "amount_due": 0, + "amount_paid": 0, + "amount_remaining": 0, + "amount_shipping": 0, + "application": null, + "application_fee_amount": null, + "attempt_count": 0, + "attempted": false, + "automatic_tax": { + "enabled": true, + "status": "complete" + }, + "billing_reason": "upcoming", + "charge": null, + "collection_method": "charge_automatically", + "created": 1697128681, + "currency": "usd", + "custom_fields": null, + "customer": "cus_M8DV9wiyNa2JxQ", + "customer_address": { + "city": null, + "country": "US", + "line1": "", + "line2": null, + "postal_code": "90019", + "state": null + }, + "customer_email": "vphan@bitwarden.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [ + ], + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [ + ], + "description": null, + "discount": null, + "discounts": [ + ], + "due_date": null, + "effective_at": null, + "ending_balance": -6779, + "footer": null, + "from_invoice": null, + "last_finalization_error": null, + "latest_revision": null, + "lines": { + "object": "list", + "data": [ + { + "id": "il_tmp_12b5e8IGBnsLynRr1996ac3a", + "object": "line_item", + "amount": 2000, + "amount_excluding_tax": 2000, + "currency": "usd", + "description": "5 × 2019 Enterprise Seat (Monthly) (at $4.00 / month)", + "discount_amounts": [ + ], + "discountable": true, + "discounts": [ + ], + "livemode": false, + "metadata": { + }, + "period": { + "end": 1699807081, + "start": 1697128681 + }, + "plan": { + "id": "enterprise-org-seat-monthly", + "object": "plan", + "active": true, + "aggregate_usage": null, + "amount": 400, + "amount_decimal": "400", + "billing_scheme": "per_unit", + "created": 1494268635, + "currency": "usd", + "interval": "month", + "interval_count": 1, + "livemode": false, + "metadata": { + }, + "nickname": "2019 Enterprise Seat (Monthly)", + "product": "prod_BVButYytPSlgs6", + "tiers_mode": null, + "transform_usage": null, + "trial_period_days": null, + "usage_type": "licensed" + }, + "price": { + "id": "enterprise-org-seat-monthly", + "object": "price", + "active": true, + "billing_scheme": "per_unit", + "created": 1494268635, + "currency": "usd", + "custom_unit_amount": null, + "livemode": false, + "lookup_key": null, + "metadata": { + }, + "nickname": "2019 Enterprise Seat (Monthly)", + "product": "prod_BVButYytPSlgs6", + "recurring": { + "aggregate_usage": null, + "interval": "month", + "interval_count": 1, + "trial_period_days": null, + "usage_type": "licensed" + }, + "tax_behavior": "unspecified", + "tiers_mode": null, + "transform_quantity": null, + "type": "recurring", + "unit_amount": 400, + "unit_amount_decimal": "400" + }, + "proration": false, + "proration_details": { + "credited_items": null + }, + "quantity": 5, + "subscription": "sub_1NQxz4IGBnsLynRr1KbitG7v", + "subscription_item": "si_ODOmLnPDHBuMxX", + "tax_amounts": [ + { + "amount": 0, + "inclusive": false, + "tax_rate": "txr_1N6XCyIGBnsLynRr0LHs4AUD", + "taxability_reason": "product_exempt", + "taxable_amount": 0 + } + ], + "tax_rates": [ + ], + "type": "subscription", + "unit_amount_excluding_tax": "400" + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/invoices/upcoming/lines?customer=cus_M8DV9wiyNa2JxQ&subscription=sub_1NQxz4IGBnsLynRr1KbitG7v" + }, + "livemode": false, + "metadata": { + }, + "next_payment_attempt": 1697132281, + "number": null, + "on_behalf_of": null, + "paid": false, + "paid_out_of_band": false, + "payment_intent": null, + "payment_settings": { + "default_mandate": null, + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1697128681, + "period_start": 1694536681, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "quote": null, + "receipt_number": null, + "rendering": null, + "rendering_options": null, + "shipping_cost": null, + "shipping_details": null, + "starting_balance": -8779, + "statement_descriptor": null, + "status": "draft", + "status_transitions": { + "finalized_at": null, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": "sub_1NQxz4IGBnsLynRr1KbitG7v", + "subscription_details": { + "metadata": { + } + }, + "subtotal": 2000, + "subtotal_excluding_tax": 2000, + "tax": 0, + "test_clock": null, + "total": 2000, + "total_discount_amounts": [ + ], + "total_excluding_tax": 2000, + "total_tax_amounts": [ + { + "amount": 0, + "inclusive": false, + "tax_rate": "txr_1N6XCyIGBnsLynRr0LHs4AUD", + "taxability_reason": "product_exempt", + "taxable_amount": 0 + } + ], + "transfer_data": null, + "webhooks_delivered_at": null + } + }, + "livemode": false, + "pending_webhooks": 5, + "request": { + "id": null, + "idempotency_key": null + }, + "type": "invoice.upcoming" +} diff --git a/test/Billing.Test/Resources/Events/payment_method.attached.json b/test/Billing.Test/Resources/Events/payment_method.attached.json new file mode 100644 index 000000000000..40e6972bdd52 --- /dev/null +++ b/test/Billing.Test/Resources/Events/payment_method.attached.json @@ -0,0 +1,63 @@ +{ + "id": "evt_1NvKzcIGBnsLynRrPJ3hybkd", + "object": "event", + "api_version": "2022-08-01", + "created": 1695910504, + "data": { + "object": { + "id": "pm_1NvKzbIGBnsLynRry6x7Buvc", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "exp_month": 6, + "exp_year": 2033, + "fingerprint": "0VgUBpvqcUUnuSmK", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1695910503, + "customer": "cus_OimYrxnMTMMK1E", + "livemode": false, + "metadata": { + }, + "type": "card" + } + }, + "livemode": false, + "pending_webhooks": 7, + "request": { + "id": "req_2WslNSBD9wAV5v", + "idempotency_key": "db1a648a-3445-47b3-a403-9f3d1303a880" + }, + "type": "payment_method.attached" +} diff --git a/test/Billing.Test/Services/StripeEventServiceTests.cs b/test/Billing.Test/Services/StripeEventServiceTests.cs new file mode 100644 index 000000000000..1e4d6c26417b --- /dev/null +++ b/test/Billing.Test/Services/StripeEventServiceTests.cs @@ -0,0 +1,690 @@ +using Bit.Billing.Services; +using Bit.Billing.Services.Implementations; +using Bit.Billing.Test.Utilities; +using Bit.Core.Settings; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using NSubstitute; +using Stripe; +using Xunit; + +namespace Bit.Billing.Test.Services; + +public class StripeEventServiceTests +{ + private readonly IStripeFacade _stripeFacade; + private readonly IStripeEventService _stripeEventService; + + public StripeEventServiceTests() + { + var globalSettings = new GlobalSettings(); + var baseServiceUriSettings = new GlobalSettings.BaseServiceUriSettings(globalSettings) { CloudRegion = "US" }; + globalSettings.BaseServiceUri = baseServiceUriSettings; + + _stripeFacade = Substitute.For(); + _stripeEventService = new StripeEventService(globalSettings, Substitute.For>(), _stripeFacade); + } + + #region GetCharge + [Fact] + public async Task GetCharge_EventNotChargeRelated_ThrowsException() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.InvoiceCreated); + + // Act + var function = async () => await _stripeEventService.GetCharge(stripeEvent); + + // Assert + await function + .Should() + .ThrowAsync() + .WithMessage($"Stripe event with ID '{stripeEvent.Id}' does not have object matching type '{nameof(Charge)}'"); + + await _stripeFacade.DidNotReceiveWithAnyArgs().GetCharge( + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task GetCharge_NotFresh_ReturnsEventCharge() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.ChargeSucceeded); + + // Act + var charge = await _stripeEventService.GetCharge(stripeEvent); + + // Assert + charge.Should().BeEquivalentTo(stripeEvent.Data.Object as Charge); + + await _stripeFacade.DidNotReceiveWithAnyArgs().GetCharge( + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task GetCharge_Fresh_Expand_ReturnsAPICharge() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.ChargeSucceeded); + + var eventCharge = stripeEvent.Data.Object as Charge; + + var apiCharge = Copy(eventCharge); + + var expand = new List { "customer" }; + + _stripeFacade.GetCharge( + apiCharge.Id, + Arg.Is(options => options.Expand == expand)) + .Returns(apiCharge); + + // Act + var charge = await _stripeEventService.GetCharge(stripeEvent, true, expand); + + // Assert + charge.Should().Be(apiCharge); + charge.Should().NotBeSameAs(eventCharge); + + await _stripeFacade.Received().GetCharge( + apiCharge.Id, + Arg.Is(options => options.Expand == expand), + Arg.Any(), + Arg.Any()); + } + #endregion + + #region GetCustomer + [Fact] + public async Task GetCustomer_EventNotCustomerRelated_ThrowsException() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.InvoiceCreated); + + // Act + var function = async () => await _stripeEventService.GetCustomer(stripeEvent); + + // Assert + await function + .Should() + .ThrowAsync() + .WithMessage($"Stripe event with ID '{stripeEvent.Id}' does not have object matching type '{nameof(Customer)}'"); + + await _stripeFacade.DidNotReceiveWithAnyArgs().GetCustomer( + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task GetCustomer_NotFresh_ReturnsEventCustomer() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerUpdated); + + // Act + var customer = await _stripeEventService.GetCustomer(stripeEvent); + + // Assert + customer.Should().BeEquivalentTo(stripeEvent.Data.Object as Customer); + + await _stripeFacade.DidNotReceiveWithAnyArgs().GetCustomer( + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task GetCustomer_Fresh_Expand_ReturnsAPICustomer() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerUpdated); + + var eventCustomer = stripeEvent.Data.Object as Customer; + + var apiCustomer = Copy(eventCustomer); + + var expand = new List { "subscriptions" }; + + _stripeFacade.GetCustomer( + apiCustomer.Id, + Arg.Is(options => options.Expand == expand)) + .Returns(apiCustomer); + + // Act + var customer = await _stripeEventService.GetCustomer(stripeEvent, true, expand); + + // Assert + customer.Should().Be(apiCustomer); + customer.Should().NotBeSameAs(eventCustomer); + + await _stripeFacade.Received().GetCustomer( + apiCustomer.Id, + Arg.Is(options => options.Expand == expand), + Arg.Any(), + Arg.Any()); + } + #endregion + + #region GetInvoice + [Fact] + public async Task GetInvoice_EventNotInvoiceRelated_ThrowsException() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerUpdated); + + // Act + var function = async () => await _stripeEventService.GetInvoice(stripeEvent); + + // Assert + await function + .Should() + .ThrowAsync() + .WithMessage($"Stripe event with ID '{stripeEvent.Id}' does not have object matching type '{nameof(Invoice)}'"); + + await _stripeFacade.DidNotReceiveWithAnyArgs().GetInvoice( + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task GetInvoice_NotFresh_ReturnsEventInvoice() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.InvoiceCreated); + + // Act + var invoice = await _stripeEventService.GetInvoice(stripeEvent); + + // Assert + invoice.Should().BeEquivalentTo(stripeEvent.Data.Object as Invoice); + + await _stripeFacade.DidNotReceiveWithAnyArgs().GetInvoice( + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task GetInvoice_Fresh_Expand_ReturnsAPIInvoice() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.InvoiceCreated); + + var eventInvoice = stripeEvent.Data.Object as Invoice; + + var apiInvoice = Copy(eventInvoice); + + var expand = new List { "customer" }; + + _stripeFacade.GetInvoice( + apiInvoice.Id, + Arg.Is(options => options.Expand == expand)) + .Returns(apiInvoice); + + // Act + var invoice = await _stripeEventService.GetInvoice(stripeEvent, true, expand); + + // Assert + invoice.Should().Be(apiInvoice); + invoice.Should().NotBeSameAs(eventInvoice); + + await _stripeFacade.Received().GetInvoice( + apiInvoice.Id, + Arg.Is(options => options.Expand == expand), + Arg.Any(), + Arg.Any()); + } + #endregion + + #region GetPaymentMethod + [Fact] + public async Task GetPaymentMethod_EventNotPaymentMethodRelated_ThrowsException() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerUpdated); + + // Act + var function = async () => await _stripeEventService.GetPaymentMethod(stripeEvent); + + // Assert + await function + .Should() + .ThrowAsync() + .WithMessage($"Stripe event with ID '{stripeEvent.Id}' does not have object matching type '{nameof(PaymentMethod)}'"); + + await _stripeFacade.DidNotReceiveWithAnyArgs().GetPaymentMethod( + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task GetPaymentMethod_NotFresh_ReturnsEventPaymentMethod() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.PaymentMethodAttached); + + // Act + var paymentMethod = await _stripeEventService.GetPaymentMethod(stripeEvent); + + // Assert + paymentMethod.Should().BeEquivalentTo(stripeEvent.Data.Object as PaymentMethod); + + await _stripeFacade.DidNotReceiveWithAnyArgs().GetPaymentMethod( + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task GetPaymentMethod_Fresh_Expand_ReturnsAPIPaymentMethod() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.PaymentMethodAttached); + + var eventPaymentMethod = stripeEvent.Data.Object as PaymentMethod; + + var apiPaymentMethod = Copy(eventPaymentMethod); + + var expand = new List { "customer" }; + + _stripeFacade.GetPaymentMethod( + apiPaymentMethod.Id, + Arg.Is(options => options.Expand == expand)) + .Returns(apiPaymentMethod); + + // Act + var paymentMethod = await _stripeEventService.GetPaymentMethod(stripeEvent, true, expand); + + // Assert + paymentMethod.Should().Be(apiPaymentMethod); + paymentMethod.Should().NotBeSameAs(eventPaymentMethod); + + await _stripeFacade.Received().GetPaymentMethod( + apiPaymentMethod.Id, + Arg.Is(options => options.Expand == expand), + Arg.Any(), + Arg.Any()); + } + #endregion + + #region GetSubscription + [Fact] + public async Task GetSubscription_EventNotSubscriptionRelated_ThrowsException() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerUpdated); + + // Act + var function = async () => await _stripeEventService.GetSubscription(stripeEvent); + + // Assert + await function + .Should() + .ThrowAsync() + .WithMessage($"Stripe event with ID '{stripeEvent.Id}' does not have object matching type '{nameof(Subscription)}'"); + + await _stripeFacade.DidNotReceiveWithAnyArgs().GetSubscription( + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task GetSubscription_NotFresh_ReturnsEventSubscription() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerSubscriptionUpdated); + + // Act + var subscription = await _stripeEventService.GetSubscription(stripeEvent); + + // Assert + subscription.Should().BeEquivalentTo(stripeEvent.Data.Object as Subscription); + + await _stripeFacade.DidNotReceiveWithAnyArgs().GetSubscription( + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task GetSubscription_Fresh_Expand_ReturnsAPISubscription() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerSubscriptionUpdated); + + var eventSubscription = stripeEvent.Data.Object as Subscription; + + var apiSubscription = Copy(eventSubscription); + + var expand = new List { "customer" }; + + _stripeFacade.GetSubscription( + apiSubscription.Id, + Arg.Is(options => options.Expand == expand)) + .Returns(apiSubscription); + + // Act + var subscription = await _stripeEventService.GetSubscription(stripeEvent, true, expand); + + // Assert + subscription.Should().Be(apiSubscription); + subscription.Should().NotBeSameAs(eventSubscription); + + await _stripeFacade.Received().GetSubscription( + apiSubscription.Id, + Arg.Is(options => options.Expand == expand), + Arg.Any(), + Arg.Any()); + } + #endregion + + #region ValidateCloudRegion + [Fact] + public async Task ValidateCloudRegion_SubscriptionUpdated_Success() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerSubscriptionUpdated); + + var subscription = Copy(stripeEvent.Data.Object as Subscription); + + var customer = await GetCustomerAsync(); + + subscription.Customer = customer; + + _stripeFacade.GetSubscription( + subscription.Id, + Arg.Any()) + .Returns(subscription); + + // Act + var cloudRegionValid = await _stripeEventService.ValidateCloudRegion(stripeEvent); + + // Assert + cloudRegionValid.Should().BeTrue(); + + await _stripeFacade.Received(1).GetSubscription( + subscription.Id, + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task ValidateCloudRegion_ChargeSucceeded_Success() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.ChargeSucceeded); + + var charge = Copy(stripeEvent.Data.Object as Charge); + + var customer = await GetCustomerAsync(); + + charge.Customer = customer; + + _stripeFacade.GetCharge( + charge.Id, + Arg.Any()) + .Returns(charge); + + // Act + var cloudRegionValid = await _stripeEventService.ValidateCloudRegion(stripeEvent); + + // Assert + cloudRegionValid.Should().BeTrue(); + + await _stripeFacade.Received(1).GetCharge( + charge.Id, + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task ValidateCloudRegion_UpcomingInvoice_Success() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.InvoiceUpcoming); + + var invoice = Copy(stripeEvent.Data.Object as Invoice); + + var customer = await GetCustomerAsync(); + + _stripeFacade.GetCustomer( + invoice.CustomerId, + Arg.Any()) + .Returns(customer); + + // Act + var cloudRegionValid = await _stripeEventService.ValidateCloudRegion(stripeEvent); + + // Assert + cloudRegionValid.Should().BeTrue(); + + await _stripeFacade.Received(1).GetCustomer( + invoice.CustomerId, + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task ValidateCloudRegion_InvoiceCreated_Success() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.InvoiceCreated); + + var invoice = Copy(stripeEvent.Data.Object as Invoice); + + var customer = await GetCustomerAsync(); + + invoice.Customer = customer; + + _stripeFacade.GetInvoice( + invoice.Id, + Arg.Any()) + .Returns(invoice); + + // Act + var cloudRegionValid = await _stripeEventService.ValidateCloudRegion(stripeEvent); + + // Assert + cloudRegionValid.Should().BeTrue(); + + await _stripeFacade.Received(1).GetInvoice( + invoice.Id, + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task ValidateCloudRegion_PaymentMethodAttached_Success() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.PaymentMethodAttached); + + var paymentMethod = Copy(stripeEvent.Data.Object as PaymentMethod); + + var customer = await GetCustomerAsync(); + + paymentMethod.Customer = customer; + + _stripeFacade.GetPaymentMethod( + paymentMethod.Id, + Arg.Any()) + .Returns(paymentMethod); + + // Act + var cloudRegionValid = await _stripeEventService.ValidateCloudRegion(stripeEvent); + + // Assert + cloudRegionValid.Should().BeTrue(); + + await _stripeFacade.Received(1).GetPaymentMethod( + paymentMethod.Id, + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task ValidateCloudRegion_CustomerUpdated_Success() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerUpdated); + + var customer = Copy(stripeEvent.Data.Object as Customer); + + _stripeFacade.GetCustomer( + customer.Id, + Arg.Any()) + .Returns(customer); + + // Act + var cloudRegionValid = await _stripeEventService.ValidateCloudRegion(stripeEvent); + + // Assert + cloudRegionValid.Should().BeTrue(); + + await _stripeFacade.Received(1).GetCustomer( + customer.Id, + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task ValidateCloudRegion_MetadataNull_ReturnsFalse() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerSubscriptionUpdated); + + var subscription = Copy(stripeEvent.Data.Object as Subscription); + + var customer = await GetCustomerAsync(); + customer.Metadata = null; + + subscription.Customer = customer; + + _stripeFacade.GetSubscription( + subscription.Id, + Arg.Any()) + .Returns(subscription); + + // Act + var cloudRegionValid = await _stripeEventService.ValidateCloudRegion(stripeEvent); + + // Assert + cloudRegionValid.Should().BeFalse(); + + await _stripeFacade.Received(1).GetSubscription( + subscription.Id, + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task ValidateCloudRegion_MetadataNoRegion_DefaultUS_ReturnsTrue() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerSubscriptionUpdated); + + var subscription = Copy(stripeEvent.Data.Object as Subscription); + + var customer = await GetCustomerAsync(); + customer.Metadata = new Dictionary(); + + subscription.Customer = customer; + + _stripeFacade.GetSubscription( + subscription.Id, + Arg.Any()) + .Returns(subscription); + + // Act + var cloudRegionValid = await _stripeEventService.ValidateCloudRegion(stripeEvent); + + // Assert + cloudRegionValid.Should().BeTrue(); + + await _stripeFacade.Received(1).GetSubscription( + subscription.Id, + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + + [Fact] + public async Task ValidateCloudRegion_MetadataMiscasedRegion_ReturnsTrue() + { + // Arrange + var stripeEvent = await StripeTestEvents.GetAsync(StripeEventType.CustomerSubscriptionUpdated); + + var subscription = Copy(stripeEvent.Data.Object as Subscription); + + var customer = await GetCustomerAsync(); + customer.Metadata = new Dictionary + { + { "Region", "US" } + }; + + subscription.Customer = customer; + + _stripeFacade.GetSubscription( + subscription.Id, + Arg.Any()) + .Returns(subscription); + + // Act + var cloudRegionValid = await _stripeEventService.ValidateCloudRegion(stripeEvent); + + // Assert + cloudRegionValid.Should().BeTrue(); + + await _stripeFacade.Received(1).GetSubscription( + subscription.Id, + Arg.Any(), + Arg.Any(), + Arg.Any()); + } + #endregion + + private static T Copy(T input) + { + var copy = (T)Activator.CreateInstance(typeof(T)); + + var properties = input.GetType().GetProperties(); + + foreach (var property in properties) + { + var value = property.GetValue(input); + copy! + .GetType() + .GetProperty(property.Name)! + .SetValue(copy, value); + } + + return copy; + } + + private static async Task GetCustomerAsync() + => (await StripeTestEvents.GetAsync(StripeEventType.CustomerUpdated)).Data.Object as Customer; +} diff --git a/test/Billing.Test/Utilities/EmbeddedResourceReader.cs b/test/Billing.Test/Utilities/EmbeddedResourceReader.cs new file mode 100644 index 000000000000..a9a5612ea6bb --- /dev/null +++ b/test/Billing.Test/Utilities/EmbeddedResourceReader.cs @@ -0,0 +1,22 @@ +using System.Reflection; + +namespace Bit.Billing.Test.Utilities; + +public static class EmbeddedResourceReader +{ + public static async Task ReadAsync(string resourceType, string fileName) + { + var assembly = Assembly.GetExecutingAssembly(); + + await using var stream = assembly.GetManifestResourceStream($"Bit.Billing.Test.Resources.{resourceType}.{fileName}"); + + if (stream == null) + { + throw new Exception($"Failed to retrieve manifest resource stream for file: {fileName}."); + } + + using var reader = new StreamReader(stream); + + return await reader.ReadToEndAsync(); + } +} diff --git a/test/Billing.Test/Utilities/StripeTestEvents.cs b/test/Billing.Test/Utilities/StripeTestEvents.cs new file mode 100644 index 000000000000..eb1095bc2330 --- /dev/null +++ b/test/Billing.Test/Utilities/StripeTestEvents.cs @@ -0,0 +1,33 @@ +using Stripe; + +namespace Bit.Billing.Test.Utilities; + +public enum StripeEventType +{ + ChargeSucceeded, + CustomerSubscriptionUpdated, + CustomerUpdated, + InvoiceCreated, + InvoiceUpcoming, + PaymentMethodAttached +} + +public static class StripeTestEvents +{ + public static async Task GetAsync(StripeEventType eventType) + { + var fileName = eventType switch + { + StripeEventType.ChargeSucceeded => "charge.succeeded.json", + StripeEventType.CustomerSubscriptionUpdated => "customer.subscription.updated.json", + StripeEventType.CustomerUpdated => "customer.updated.json", + StripeEventType.InvoiceCreated => "invoice.created.json", + StripeEventType.InvoiceUpcoming => "invoice.upcoming.json", + StripeEventType.PaymentMethodAttached => "payment_method.attached.json" + }; + + var resource = await EmbeddedResourceReader.ReadAsync("Events", fileName); + + return EventUtility.ParseEvent(resource); + } +} diff --git a/test/Billing.Test/packages.lock.json b/test/Billing.Test/packages.lock.json index c1c83fc1d795..32f0b9630119 100644 --- a/test/Billing.Test/packages.lock.json +++ b/test/Billing.Test/packages.lock.json @@ -18,6 +18,15 @@ "resolved": "3.1.2", "contentHash": "wuLDIDKD5XMt0A7lE31JPenT7QQwZPFkP5rRpdJeblyXZ9MGLI8rYjvm5fvAKln+2/X+4IxxQDxBtwdrqKNLZw==" }, + "FluentAssertions": { + "type": "Direct", + "requested": "[6.12.0, )", + "resolved": "6.12.0", + "contentHash": "ZXhHT2YwP9lajrwSKbLlFqsmCCvFJMoRSK9t7sImfnCyd0OB3MhgxdoMcVqxbq1iyxD6mD2fiackWmBb7ayiXQ==", + "dependencies": { + "System.Configuration.ConfigurationManager": "4.4.0" + } + }, "Microsoft.NET.Test.Sdk": { "type": "Direct", "requested": "[17.1.0, )", @@ -228,10 +237,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2832,8 +2842,8 @@ "billing": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )" } }, "common": { @@ -2841,7 +2851,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -2860,7 +2870,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2893,7 +2903,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2901,7 +2911,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2913,9 +2923,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/test/Common/packages.lock.json b/test/Common/packages.lock.json index ee03c4cdaa3a..498b2f46135a 100644 --- a/test/Common/packages.lock.json +++ b/test/Common/packages.lock.json @@ -216,10 +216,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2673,7 +2674,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", diff --git a/test/Core.Test/OrganizationFeatures/Groups/CreateGroupCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/Groups/CreateGroupCommandTests.cs similarity index 95% rename from test/Core.Test/OrganizationFeatures/Groups/CreateGroupCommandTests.cs rename to test/Core.Test/AdminConsole/OrganizationFeatures/Groups/CreateGroupCommandTests.cs index 92011e49f23a..6d521887a097 100644 --- a/test/Core.Test/OrganizationFeatures/Groups/CreateGroupCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/Groups/CreateGroupCommandTests.cs @@ -1,9 +1,10 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Data; -using Bit.Core.OrganizationFeatures.Groups; -using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Test.AutoFixture.OrganizationFixtures; using Bit.Core.Tools.Enums; @@ -15,7 +16,7 @@ using NSubstitute; using Xunit; -namespace Bit.Core.Test.OrganizationFeatures.Groups; +namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Groups; [SutProviderCustomize] public class CreateGroupCommandTests diff --git a/test/Core.Test/OrganizationFeatures/Groups/DeleteGroupCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/Groups/DeleteGroupCommandTests.cs similarity index 93% rename from test/Core.Test/OrganizationFeatures/Groups/DeleteGroupCommandTests.cs rename to test/Core.Test/AdminConsole/OrganizationFeatures/Groups/DeleteGroupCommandTests.cs index 27b070d2133e..fcd63203f864 100644 --- a/test/Core.Test/OrganizationFeatures/Groups/DeleteGroupCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/Groups/DeleteGroupCommandTests.cs @@ -1,8 +1,8 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Enums; using Bit.Core.Exceptions; -using Bit.Core.OrganizationFeatures.Groups; -using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Test.AutoFixture.OrganizationFixtures; using Bit.Test.Common.AutoFixture; @@ -10,7 +10,7 @@ using NSubstitute; using Xunit; -namespace Bit.Core.Test.OrganizationFeatures.Groups; +namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Groups; [SutProviderCustomize] public class DeleteGroupCommandTests @@ -42,7 +42,7 @@ public async Task DeleteGroup_MismatchingOrganizationId_Throws(SutProvider() .GetByIdAsync(groupId) - .Returns(new Core.Entities.Group + .Returns(new Group { Id = groupId, OrganizationId = Guid.NewGuid() diff --git a/test/Core.Test/OrganizationFeatures/Groups/UpdateGroupCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/Groups/UpdateGroupCommandTests.cs similarity index 94% rename from test/Core.Test/OrganizationFeatures/Groups/UpdateGroupCommandTests.cs rename to test/Core.Test/AdminConsole/OrganizationFeatures/Groups/UpdateGroupCommandTests.cs index dde281b785c6..67ca9ffada2d 100644 --- a/test/Core.Test/OrganizationFeatures/Groups/UpdateGroupCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/Groups/UpdateGroupCommandTests.cs @@ -1,9 +1,10 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Data; -using Bit.Core.OrganizationFeatures.Groups; -using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Test.AutoFixture.OrganizationFixtures; using Bit.Test.Common.AutoFixture; @@ -12,7 +13,7 @@ using NSubstitute; using Xunit; -namespace Bit.Core.Test.OrganizationFeatures.Groups; +namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Groups; [SutProviderCustomize] public class UpdateGroupCommandTests diff --git a/test/Core.Test/Services/GroupServiceTests.cs b/test/Core.Test/AdminConsole/Services/GroupServiceTests.cs similarity index 95% rename from test/Core.Test/Services/GroupServiceTests.cs rename to test/Core.Test/AdminConsole/Services/GroupServiceTests.cs index e904ea7a75f8..46d6dc4793c5 100644 --- a/test/Core.Test/Services/GroupServiceTests.cs +++ b/test/Core.Test/AdminConsole/Services/GroupServiceTests.cs @@ -1,4 +1,7 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.AdminConsole.Services.Implementations; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Repositories; @@ -9,7 +12,7 @@ using NSubstitute; using Xunit; -namespace Bit.Core.Test.Services; +namespace Bit.Core.Test.AdminConsole.Services; [SutProviderCustomize] [OrganizationCustomize(UseGroups = true)] diff --git a/test/Core.Test/AutoFixture/OrganizationFixtures.cs b/test/Core.Test/AutoFixture/OrganizationFixtures.cs index 4ef0e43fa7f0..658964b73d1e 100644 --- a/test/Core.Test/AutoFixture/OrganizationFixtures.cs +++ b/test/Core.Test/AutoFixture/OrganizationFixtures.cs @@ -1,6 +1,7 @@ using System.Text.Json; using AutoFixture; using AutoFixture.Kernel; +using Bit.Core.AdminConsole.Entities; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models; using Bit.Core.Entities; @@ -66,7 +67,7 @@ internal class PaidOrganization : ICustomization public PlanType CheckedPlanType { get; set; } public void Customize(IFixture fixture) { - var validUpgradePlans = StaticStore.PasswordManagerPlans.Where(p => p.Type != PlanType.Free && p.LegacyYear == null).OrderBy(p => p.UpgradeSortOrder).Select(p => p.Type).ToList(); + var validUpgradePlans = StaticStore.Plans.Where(p => p.Type != PlanType.Free && p.LegacyYear == null).OrderBy(p => p.UpgradeSortOrder).Select(p => p.Type).ToList(); var lowestActivePaidPlan = validUpgradePlans.First(); CheckedPlanType = CheckedPlanType.Equals(PlanType.Free) ? lowestActivePaidPlan : CheckedPlanType; validUpgradePlans.Remove(lowestActivePaidPlan); @@ -94,11 +95,11 @@ public void Customize(IFixture fixture) .With(o => o.PlanType, PlanType.Free)); var plansToIgnore = new List { PlanType.Free, PlanType.Custom }; - var selectedPlan = StaticStore.PasswordManagerPlans.Last(p => !plansToIgnore.Contains(p.Type) && !p.Disabled); + var selectedPlan = StaticStore.Plans.Last(p => !plansToIgnore.Contains(p.Type) && !p.Disabled); fixture.Customize(composer => composer .With(ou => ou.Plan, selectedPlan.Type) - .With(ou => ou.PremiumAccessAddon, selectedPlan.HasPremiumAccessOption)); + .With(ou => ou.PremiumAccessAddon, selectedPlan.PasswordManager.HasPremiumAccessOption)); fixture.Customize(composer => composer .Without(o => o.GatewaySubscriptionId)); } @@ -140,7 +141,7 @@ public void Customize(IFixture fixture) .With(o => o.UseSecretsManager, true) .With(o => o.SecretsManagerBeta, false) .With(o => o.PlanType, planType) - .With(o => o.Plan, StaticStore.GetPasswordManagerPlan(planType).Name) + .With(o => o.Plan, StaticStore.GetPlan(planType).Name) .With(o => o.MaxAutoscaleSmSeats, (int?)null) .With(o => o.MaxAutoscaleSmServiceAccounts, (int?)null) ); diff --git a/test/Core.Test/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommandTests.cs index b9201d35afd7..3a559d5c3d41 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommandTests.cs @@ -1,13 +1,11 @@ using Bit.Core.Entities; using Bit.Core.Enums; -using Bit.Core.Exceptions; using Bit.Core.OrganizationFeatures.OrganizationDomains; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; -using NSubstitute.ReturnsExtensions; using Xunit; namespace Bit.Core.Test.OrganizationFeatures.OrganizationDomains; @@ -15,17 +13,6 @@ namespace Bit.Core.Test.OrganizationFeatures.OrganizationDomains; [SutProviderCustomize] public class DeleteOrganizationDomainCommandTests { - [Theory, BitAutoData] - public async Task DeleteAsync_ShouldThrowNotFoundException_WhenIdDoesNotExist(Guid id, - SutProvider sutProvider) - { - sutProvider.GetDependency().GetByIdAsync(id).ReturnsNull(); - - var requestAction = async () => await sutProvider.Sut.DeleteAsync(id); - - await Assert.ThrowsAsync(requestAction); - } - [Theory, BitAutoData] public async Task DeleteAsync_Success(Guid id, SutProvider sutProvider) { @@ -36,9 +23,8 @@ public async Task DeleteAsync_Success(Guid id, SutProvider().GetByIdAsync(id).Returns(expected); - await sutProvider.Sut.DeleteAsync(id); + await sutProvider.Sut.DeleteAsync(expected); await sutProvider.GetDependency().Received(1).DeleteAsync(expected); await sutProvider.GetDependency().Received(1) diff --git a/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdOrganizationIdQueryTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdOrganizationIdQueryTests.cs new file mode 100644 index 000000000000..6480d8be0fad --- /dev/null +++ b/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdOrganizationIdQueryTests.cs @@ -0,0 +1,80 @@ +using Bit.Core.Entities; +using Bit.Core.OrganizationFeatures.OrganizationDomains; +using Bit.Core.Repositories; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.OrganizationFeatures.OrganizationDomains; + +[SutProviderCustomize] +public class GetOrganizationDomainByIdOrganizationIdQueryTests +{ + [Theory, BitAutoData] + public async Task GetOrganizationDomainByIdAndOrganizationIdAsync_WithExistingParameters_ReturnsExpectedEntity( + OrganizationDomain organizationDomain, SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId) + .Returns(organizationDomain); + + var result = await sutProvider.Sut.GetOrganizationDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId); + + await sutProvider.GetDependency().Received(1) + .GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId); + + Assert.Equal(organizationDomain, result); + } + + [Theory, BitAutoData] + public async Task GetOrganizationDomainByIdAndOrganizationIdAsync_WithNonExistingParameters_ReturnsNull( + Guid id, Guid organizationId, OrganizationDomain organizationDomain, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId) + .Returns(organizationDomain); + + var result = await sutProvider.Sut.GetOrganizationDomainByIdOrganizationIdAsync(id, organizationId); + + await sutProvider.GetDependency().Received(1) + .GetDomainByIdOrganizationIdAsync(id, organizationId); + + Assert.Null(result); + } + + [Theory, BitAutoData] + public async Task GetOrganizationDomainByIdAndOrganizationIdAsync_WithNonExistingId_ReturnsNull( + Guid id, OrganizationDomain organizationDomain, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId) + .Returns(organizationDomain); + + var result = await sutProvider.Sut.GetOrganizationDomainByIdOrganizationIdAsync(id, organizationDomain.OrganizationId); + + await sutProvider.GetDependency().Received(1) + .GetDomainByIdOrganizationIdAsync(id, organizationDomain.OrganizationId); + + Assert.Null(result); + } + + [Theory, BitAutoData] + public async Task GetOrganizationDomainByIdAndOrganizationIdAsync_WithNonExistingOrgId_ReturnsNull( + Guid organizationId, OrganizationDomain organizationDomain, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationDomain.OrganizationId) + .Returns(organizationDomain); + + var result = await sutProvider.Sut.GetOrganizationDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationId); + + await sutProvider.GetDependency().Received(1) + .GetDomainByIdOrganizationIdAsync(organizationDomain.Id, organizationId); + + Assert.Null(result); + } +} diff --git a/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQueryTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQueryTests.cs deleted file mode 100644 index 0cb77d243abd..000000000000 --- a/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQueryTests.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Bit.Core.OrganizationFeatures.OrganizationDomains; -using Bit.Core.Repositories; -using Bit.Test.Common.AutoFixture; -using Bit.Test.Common.AutoFixture.Attributes; -using NSubstitute; -using Xunit; - -namespace Bit.Core.Test.OrganizationFeatures.OrganizationDomains; - -[SutProviderCustomize] -public class GetOrganizationDomainByIdQueryTests -{ - [Theory, BitAutoData] - public async Task GetOrganizationDomainById_CallsGetByIdAsync(Guid id, - SutProvider sutProvider) - { - await sutProvider.Sut.GetOrganizationDomainById(id); - - await sutProvider.GetDependency().Received(1) - .GetByIdAsync(id); - } -} diff --git a/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQueryTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQueryTests.cs index 964a3a76d4ff..d87667600487 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQueryTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQueryTests.cs @@ -14,7 +14,7 @@ public class GetOrganizationDomainByOrganizationIdQueryTests public async Task GetDomainsByOrganizationId_CallsGetDomainsByOrganizationIdAsync(Guid orgId, SutProvider sutProvider) { - await sutProvider.Sut.GetDomainsByOrganizationId(orgId); + await sutProvider.Sut.GetDomainsByOrganizationIdAsync(orgId); await sutProvider.GetDependency().Received(1) .GetDomainsByOrganizationIdAsync(orgId); diff --git a/test/Core.Test/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs index 54783982ada1..07bc2b14e84f 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs @@ -7,8 +7,6 @@ using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; -using NSubstitute.ReceivedExtensions; -using NSubstitute.ReturnsExtensions; using Xunit; namespace Bit.Core.Test.OrganizationFeatures.OrganizationDomains; @@ -16,19 +14,6 @@ namespace Bit.Core.Test.OrganizationFeatures.OrganizationDomains; [SutProviderCustomize] public class VerifyOrganizationDomainCommandTests { - [Theory, BitAutoData] - public async Task VerifyOrganizationDomain_ShouldThrowNotFound_WhenDomainDoesNotExist(Guid id, - SutProvider sutProvider) - { - sutProvider.GetDependency() - .GetByIdAsync(id) - .ReturnsNull(); - - var requestAction = async () => await sutProvider.Sut.VerifyOrganizationDomain(id); - - await Assert.ThrowsAsync(requestAction); - } - [Theory, BitAutoData] public async Task VerifyOrganizationDomain_ShouldThrowConflict_WhenDomainHasBeenClaimed(Guid id, SutProvider sutProvider) @@ -45,7 +30,7 @@ public async Task VerifyOrganizationDomain_ShouldThrowConflict_WhenDomainHasBeen .GetByIdAsync(id) .Returns(expected); - var requestAction = async () => await sutProvider.Sut.VerifyOrganizationDomain(id); + var requestAction = async () => await sutProvider.Sut.VerifyOrganizationDomainAsync(expected); var exception = await Assert.ThrowsAsync(requestAction); Assert.Contains("Domain has already been verified.", exception.Message); @@ -69,7 +54,7 @@ public async Task VerifyOrganizationDomain_ShouldThrowConflict_WhenDomainHasBeen .GetClaimedDomainsByDomainNameAsync(expected.DomainName) .Returns(new List { expected }); - var requestAction = async () => await sutProvider.Sut.VerifyOrganizationDomain(id); + var requestAction = async () => await sutProvider.Sut.VerifyOrganizationDomainAsync(expected); var exception = await Assert.ThrowsAsync(requestAction); Assert.Contains("The domain is not available to be claimed.", exception.Message); @@ -96,7 +81,7 @@ public async Task VerifyOrganizationDomain_ShouldVerifyDomainUpdateAndLogEvent_W .ResolveAsync(expected.DomainName, Arg.Any()) .Returns(true); - var result = await sutProvider.Sut.VerifyOrganizationDomain(id); + var result = await sutProvider.Sut.VerifyOrganizationDomainAsync(expected); Assert.NotNull(result.VerifiedDate); await sutProvider.GetDependency().Received(1) @@ -126,7 +111,7 @@ public async Task VerifyOrganizationDomain_ShouldNotSetVerifiedDate_WhenTxtRecor .ResolveAsync(expected.DomainName, Arg.Any()) .Returns(false); - var result = await sutProvider.Sut.VerifyOrganizationDomain(id); + var result = await sutProvider.Sut.VerifyOrganizationDomainAsync(expected); Assert.Null(result.VerifiedDate); await sutProvider.GetDependency().Received(1) diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/FamiliesForEnterpriseTestsBase.cs b/test/Core.Test/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/FamiliesForEnterpriseTestsBase.cs index a3fce8b3de2b..e49b095d76fa 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/FamiliesForEnterpriseTestsBase.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/FamiliesForEnterpriseTestsBase.cs @@ -6,16 +6,16 @@ namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesFo public abstract class FamiliesForEnterpriseTestsBase { public static IEnumerable EnterprisePlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p }); public static IEnumerable NonEnterprisePlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p }); public static IEnumerable FamiliesPlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product == ProductType.Families).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product == ProductType.Families).Select(p => new object[] { p }); public static IEnumerable NonFamiliesPlanTypes => - Enum.GetValues().Where(p => StaticStore.GetPasswordManagerPlan(p).Product != ProductType.Families).Select(p => new object[] { p }); + Enum.GetValues().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p }); public static IEnumerable NonConfirmedOrganizationUsersStatuses => Enum.GetValues() diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs index a09500cf67c7..e3b948719734 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs @@ -1,4 +1,7 @@ -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Business; @@ -29,7 +32,7 @@ public async Task SignUpAsync_ReturnsSuccessAndClientSecret_WhenOrganizationAndP { organization.PlanType = planType; - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == organization.PlanType); + var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType); await sutProvider.Sut.SignUpAsync(organization, additionalSmSeats, additionalServiceAccounts); @@ -46,8 +49,8 @@ await sutProvider.GetDependency().Received() // 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.SmSeats == plan.SecretsManager.BaseSeats + additionalSmSeats && + c.SmServiceAccounts == plan.SecretsManager.BaseServiceAccount + additionalServiceAccounts && c.UseSecretsManager == true)); } @@ -127,6 +130,25 @@ public async Task SignUpAsync_ThrowsException_WhenOrganizationAlreadyHasSecretsM await VerifyDependencyNotCalledAsync(sutProvider); } + [Theory] + [BitAutoData] + public async Task SignUpAsync_ThrowsException_WhenOrganizationIsManagedByMSP( + SutProvider sutProvider, + Organization organization, + Provider provider) + { + organization.UseSecretsManager = false; + organization.SecretsManagerBeta = false; + provider.Type = ProviderType.Msp; + sutProvider.GetDependency().GetByOrganizationIdAsync(organization.Id).Returns(provider); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.SignUpAsync(organization, 10, 10)); + + Assert.Contains("Organizations with a Managed Service Provider do not support Secrets Manager.", exception.Message); + await VerifyDependencyNotCalledAsync(sutProvider); + } + private static async Task VerifyDependencyNotCalledAsync(SutProvider sutProvider) { await sutProvider.GetDependency().DidNotReceive() diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs index 2834db335f1d..16508bd25a53 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpdateSecretsManagerSubscriptionCommandTests.cs @@ -52,9 +52,9 @@ public async Task UpdateSubscriptionAsync_UpdateEverything_ValidInput_Passes( await sutProvider.Sut.UpdateSubscriptionAsync(update); - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); + var plan = StaticStore.GetPlan(organization.PlanType); await sutProvider.GetDependency().Received(1) - .AdjustSeatsAsync(organization, plan, update.SmSeatsExcludingBase); + .AdjustSmSeatsAsync(organization, plan, update.SmSeatsExcludingBase); await sutProvider.GetDependency().Received(1) .AdjustServiceAccountsAsync(organization, plan, update.SmServiceAccountsExcludingBase); @@ -96,9 +96,9 @@ public async Task UpdateSubscriptionAsync_ValidInput_WithNullMaxAutoscale_Passes await sutProvider.Sut.UpdateSubscriptionAsync(update); - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == organization.PlanType); + var plan = StaticStore.GetPlan(organization.PlanType); await sutProvider.GetDependency().Received(1) - .AdjustSeatsAsync(organization, plan, update.SmSeatsExcludingBase); + .AdjustSmSeatsAsync(organization, plan, update.SmSeatsExcludingBase); await sutProvider.GetDependency().Received(1) .AdjustServiceAccountsAsync(organization, plan, update.SmServiceAccountsExcludingBase); @@ -213,11 +213,11 @@ public async Task UpdateSubscriptionAsync_PaidPlan_NullGatewaySubscriptionId_Thr public async Task AdjustServiceAccountsAsync_WithEnterpriseOrTeamsPlans_Success(PlanType planType, Guid organizationId, SutProvider sutProvider) { - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(p => p.Type == planType); + var plan = StaticStore.GetPlan(planType); - var organizationSeats = plan.BaseSeats + 10; + var organizationSeats = plan.SecretsManager.BaseSeats + 10; var organizationMaxAutoscaleSeats = 20; - var organizationServiceAccounts = plan.BaseServiceAccount.GetValueOrDefault() + 10; + var organizationServiceAccounts = plan.SecretsManager.BaseServiceAccount + 10; var organizationMaxAutoscaleServiceAccounts = 300; var organization = new Organization @@ -235,7 +235,7 @@ public async Task AdjustServiceAccountsAsync_WithEnterpriseOrTeamsPlans_Success( var smServiceAccountsAdjustment = 10; var expectedSmServiceAccounts = organizationServiceAccounts + smServiceAccountsAdjustment; - var expectedSmServiceAccountsExcludingBase = expectedSmServiceAccounts - plan.BaseServiceAccount.GetValueOrDefault(); + var expectedSmServiceAccountsExcludingBase = expectedSmServiceAccounts - plan.SecretsManager.BaseServiceAccount; var update = new SecretsManagerSubscriptionUpdate(organization, false).AdjustServiceAccounts(10); @@ -375,6 +375,7 @@ public async Task UpdateSubscriptionAsync_ThrowsBadRequestException_WhenOccupied Organization organization, SutProvider sutProvider) { + organization.SmSeats = 8; var update = new SecretsManagerSubscriptionUpdate(organization, false) { SmSeats = 7, @@ -598,7 +599,7 @@ public async Task UpdateMaxAutoscaleSmServiceAccounts_ThrowsBadRequestException_ private static async Task VerifyDependencyNotCalledAsync(SutProvider sutProvider) { await sutProvider.GetDependency().DidNotReceive() - .AdjustSeatsAsync(Arg.Any(), Arg.Any(), Arg.Any()); + .AdjustSmSeatsAsync(Arg.Any(), Arg.Any(), Arg.Any()); await sutProvider.GetDependency().DidNotReceive() .AdjustServiceAccountsAsync(Arg.Any(), Arg.Any(), Arg.Any()); // TODO: call ReferenceEventService - see AC-1481 diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs index f18a8b5de95c..f79001639e74 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/UpgradeOrganizationPlanCommandTests.cs @@ -94,6 +94,7 @@ public async Task UpgradePlan_Passes(Organization organization, OrganizationUpgr sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); upgrade.AdditionalSmSeats = 10; upgrade.AdditionalSeats = 10; + upgrade.Plan = PlanType.TeamsAnnually; await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade); await sutProvider.GetDependency().Received(1).ReplaceAndUpdateCacheAsync(organization); } @@ -108,8 +109,7 @@ public async Task UpgradePlan_SM_Passes(PlanType planType, Organization organiza { upgrade.Plan = planType; - var passwordManagerPlan = StaticStore.GetPasswordManagerPlan(upgrade.Plan); - var secretsManagerPlan = StaticStore.GetSecretsManagerPlan(upgrade.Plan); + var plan = StaticStore.GetPlan(upgrade.Plan); sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); @@ -121,9 +121,9 @@ public async Task UpgradePlan_SM_Passes(PlanType planType, Organization organiza 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)); + o.Seats == plan.PasswordManager.BaseSeats + upgrade.AdditionalSeats + && o.SmSeats == plan.SecretsManager.BaseSeats + upgrade.AdditionalSmSeats + && o.SmServiceAccounts == plan.SecretsManager.BaseServiceAccount + upgrade.AdditionalServiceAccounts)); Assert.True(result.Item1); Assert.NotNull(result.Item2); diff --git a/test/Core.Test/Services/EventServiceTests.cs b/test/Core.Test/Services/EventServiceTests.cs index 2cda759b150f..18f5371b4943 100644 --- a/test/Core.Test/Services/EventServiceTests.cs +++ b/test/Core.Test/Services/EventServiceTests.cs @@ -1,6 +1,8 @@ -using Bit.Core.Context; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Models.Data.Provider; +using Bit.Core.Context; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; using Bit.Core.Enums; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations; diff --git a/test/Core.Test/Services/HandlebarsMailServiceTests.cs b/test/Core.Test/Services/HandlebarsMailServiceTests.cs index a29e884367f9..266fbc0159e0 100644 --- a/test/Core.Test/Services/HandlebarsMailServiceTests.cs +++ b/test/Core.Test/Services/HandlebarsMailServiceTests.cs @@ -1,8 +1,8 @@ using System.Reflection; +using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.Auth.Entities; using Bit.Core.Auth.Models.Business; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; using Bit.Core.Services; using Bit.Core.Settings; using Microsoft.Extensions.Logging; diff --git a/test/Core.Test/Services/InMemoryApplicationCacheServiceTests.cs b/test/Core.Test/Services/InMemoryApplicationCacheServiceTests.cs index ff8e734b3224..37e91c001cc6 100644 --- a/test/Core.Test/Services/InMemoryApplicationCacheServiceTests.cs +++ b/test/Core.Test/Services/InMemoryApplicationCacheServiceTests.cs @@ -1,4 +1,5 @@ -using Bit.Core.Repositories; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Repositories; using Bit.Core.Services; using NSubstitute; using Xunit; diff --git a/test/Core.Test/Services/InMemoryServiceBusApplicationCacheServiceTests.cs b/test/Core.Test/Services/InMemoryServiceBusApplicationCacheServiceTests.cs index f74aa6f505c4..fcc6a63e501b 100644 --- a/test/Core.Test/Services/InMemoryServiceBusApplicationCacheServiceTests.cs +++ b/test/Core.Test/Services/InMemoryServiceBusApplicationCacheServiceTests.cs @@ -1,4 +1,5 @@ -using Bit.Core.Repositories; +using Bit.Core.AdminConsole.Repositories; +using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; using NSubstitute; diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index 4907efafcb75..c3c5219cddd6 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -1,4 +1,7 @@ using System.Text.Json; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Enums.Provider; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Auth.Entities; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models.Business; @@ -6,9 +9,7 @@ using Bit.Core.Auth.Repositories; using Bit.Core.Context; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Exceptions; using Bit.Core.Models.Business; using Bit.Core.Models.Data; @@ -155,20 +156,20 @@ public async Task SignUp_PM_Family_Passes(PlanType planType, OrganizationSignup { signup.Plan = planType; - var passwordManagerPlan = StaticStore.GetPasswordManagerPlan(signup.Plan); + var plan = StaticStore.GetPlan(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 purchaseOrganizationPlan = StaticStore.GetPlan(signup.Plan); var result = await sutProvider.Sut.SignUpAsync(signup); await sutProvider.GetDependency().Received(1).CreateAsync( Arg.Is(o => - o.Seats == passwordManagerPlan.BaseSeats + signup.AdditionalSeats + o.Seats == plan.PasswordManager.BaseSeats + signup.AdditionalSeats && o.SmSeats == null && o.SmServiceAccounts == null)); await sutProvider.GetDependency().Received(1).CreateAsync( @@ -177,8 +178,8 @@ await sutProvider.GetDependency().Received(1).Creat await sutProvider.GetDependency().Received(1) .RaiseEventAsync(Arg.Is(referenceEvent => referenceEvent.Type == ReferenceEventType.Signup && - referenceEvent.PlanName == passwordManagerPlan.Name && - referenceEvent.PlanType == passwordManagerPlan.Type && + referenceEvent.PlanName == plan.Name && + referenceEvent.PlanType == plan.Type && referenceEvent.Seats == result.Item1.Seats && referenceEvent.Storage == result.Item1.MaxStorageGb)); // TODO: add reference events for SmSeats and Service Accounts - see AC-1481 @@ -192,7 +193,7 @@ await sutProvider.GetDependency().Received(1).PurchaseOrganizat Arg.Any(), signup.PaymentMethodType.Value, signup.PaymentToken, - Arg.Is>(plan => plan.Single() == passwordManagerPlan), + plan, signup.AdditionalStorageGb, signup.AdditionalSeats, signup.PremiumAccessAddon, @@ -212,8 +213,7 @@ public async Task SignUp_SM_Passes(PlanType planType, OrganizationSignup signup, { signup.Plan = planType; - var passwordManagerPlan = StaticStore.GetPasswordManagerPlan(signup.Plan); - var secretsManagerPlan = StaticStore.GetSecretsManagerPlan(signup.Plan); + var plan = StaticStore.GetPlan(signup.Plan); signup.UseSecretsManager = true; signup.AdditionalSeats = 15; @@ -222,23 +222,21 @@ public async Task SignUp_SM_Passes(PlanType planType, OrganizationSignup signup, 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).CreateAsync( Arg.Is(o => - o.Seats == passwordManagerPlan.BaseSeats + signup.AdditionalSeats - && o.SmSeats == secretsManagerPlan.BaseSeats + signup.AdditionalSmSeats - && o.SmServiceAccounts == secretsManagerPlan.BaseServiceAccount + signup.AdditionalServiceAccounts)); + o.Seats == plan.PasswordManager.BaseSeats + signup.AdditionalSeats + && o.SmSeats == plan.SecretsManager.BaseSeats + signup.AdditionalSmSeats + && o.SmServiceAccounts == plan.SecretsManager.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 => referenceEvent.Type == ReferenceEventType.Signup && - referenceEvent.PlanName == purchaseOrganizationPlan[0].Name && - referenceEvent.PlanType == purchaseOrganizationPlan[0].Type && + referenceEvent.PlanName == plan.Name && + referenceEvent.PlanType == plan.Type && referenceEvent.Seats == result.Item1.Seats && referenceEvent.Storage == result.Item1.MaxStorageGb)); // TODO: add reference events for SmSeats and Service Accounts - see AC-1481 @@ -252,7 +250,7 @@ await sutProvider.GetDependency().Received(1).PurchaseOrganizat Arg.Any(), signup.PaymentMethodType.Value, signup.PaymentToken, - Arg.Is>(plan => plan.All(p => purchaseOrganizationPlan.Contains(p))), + Arg.Is(plan), signup.AdditionalStorageGb, signup.AdditionalSeats, signup.PremiumAccessAddon, @@ -263,6 +261,22 @@ await sutProvider.GetDependency().Received(1).PurchaseOrganizat ); } + [Theory] + [BitAutoData(PlanType.EnterpriseAnnually)] + public async Task SignUp_SM_Throws_WhenManagedByMSP(PlanType planType, OrganizationSignup signup, SutProvider sutProvider) + { + signup.Plan = planType; + signup.UseSecretsManager = true; + signup.AdditionalSeats = 15; + signup.AdditionalSmSeats = 10; + signup.AdditionalServiceAccounts = 20; + signup.PaymentMethodType = PaymentMethodType.Card; + signup.PremiumAccessAddon = false; + + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.SignUpAsync(signup, true)); + Assert.Contains("Organizations with a Managed Service Provider do not support Secrets Manager.", exception.Message); + } + [Theory] [BitAutoData] public async Task SignUpAsync_SecretManager_AdditionalServiceAccounts_NotAllowedByPlan_ShouldThrowException(OrganizationSignup signup, SutProvider sutProvider) @@ -1690,7 +1704,7 @@ public async Task HasConfirmedOwnersExcept_WithConfirmedProviderUser_IncludeProv public void ValidateSecretsManagerPlan_ThrowsException_WhenInvalidPlanSelected( PlanType planType, SutProvider sutProvider) { - var plan = StaticStore.Plans.FirstOrDefault(x => x.Type == planType); + var plan = StaticStore.GetPlan(planType); var signup = new OrganizationUpgrade { @@ -1711,7 +1725,7 @@ public void ValidateSecretsManagerPlan_ThrowsException_WhenInvalidPlanSelected( [BitAutoData(PlanType.EnterpriseMonthly)] public void ValidateSecretsManagerPlan_ThrowsException_WhenNoSecretsManagerSeats(PlanType planType, SutProvider sutProvider) { - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var plan = StaticStore.GetPlan(planType); var signup = new OrganizationUpgrade { UseSecretsManager = true, @@ -1728,7 +1742,7 @@ public void ValidateSecretsManagerPlan_ThrowsException_WhenNoSecretsManagerSeats [BitAutoData(PlanType.Free)] public void ValidateSecretsManagerPlan_ThrowsException_WhenSubtractingSeats(PlanType planType, SutProvider sutProvider) { - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var plan = StaticStore.GetPlan(planType); var signup = new OrganizationUpgrade { UseSecretsManager = true, @@ -1745,7 +1759,7 @@ public void ValidateSecretsManagerPlan_ThrowsException_WhenPlanDoesNotAllowAddit PlanType planType, SutProvider sutProvider) { - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var plan = StaticStore.GetPlan(planType); var signup = new OrganizationUpgrade { UseSecretsManager = true, @@ -1764,7 +1778,7 @@ public void ValidateSecretsManagerPlan_ThrowsException_WhenPlanDoesNotAllowAddit [BitAutoData(PlanType.EnterpriseMonthly)] public void ValidateSecretsManagerPlan_ThrowsException_WhenMoreSeatsThanPasswordManagerSeats(PlanType planType, SutProvider sutProvider) { - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var plan = StaticStore.GetPlan(planType); var signup = new OrganizationUpgrade { UseSecretsManager = true, @@ -1785,7 +1799,7 @@ public void ValidateSecretsManagerPlan_ThrowsException_WhenSubtractingServiceAcc PlanType planType, SutProvider sutProvider) { - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var plan = StaticStore.GetPlan(planType); var signup = new OrganizationUpgrade { UseSecretsManager = true, @@ -1803,7 +1817,7 @@ public void ValidateSecretsManagerPlan_ThrowsException_WhenPlanDoesNotAllowAddit PlanType planType, SutProvider sutProvider) { - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var plan = StaticStore.GetPlan(planType); var signup = new OrganizationUpgrade { UseSecretsManager = true, @@ -1824,7 +1838,7 @@ public void ValidateSecretsManagerPlan_ValidPlan_NoExceptionThrown( PlanType planType, SutProvider sutProvider) { - var plan = StaticStore.SecretManagerPlans.FirstOrDefault(x => x.Type == planType); + var plan = StaticStore.GetPlan(planType); var signup = new OrganizationUpgrade { UseSecretsManager = true, diff --git a/test/Core.Test/Services/StripePaymentServiceTests.cs b/test/Core.Test/Services/StripePaymentServiceTests.cs index cc32a93b800a..2133a14a9735 100644 --- a/test/Core.Test/Services/StripePaymentServiceTests.cs +++ b/test/Core.Test/Services/StripePaymentServiceTests.cs @@ -40,7 +40,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 plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -56,7 +56,7 @@ public async void PurchaseOrganizationAsync_Stripe_ProviderOrg_Coupon_Add(SutPro .BaseServiceUri.CloudRegion .Returns("US"); - var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo, provider); + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo, provider); Assert.Null(result); Assert.Equal(GatewayType.Stripe, organization.Gateway); @@ -95,8 +95,8 @@ await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo, bool provider = true) { - var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); - + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); + organization.UseSecretsManager = true; var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer { @@ -112,7 +112,7 @@ public async void PurchaseOrganizationAsync_SM_Stripe_ProviderOrg_Coupon_Add(Sut .BaseServiceUri.CloudRegion .Returns("US"); - var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 1, 1, + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 1, 1, false, taxInfo, provider, 1, 1); Assert.Null(result); @@ -151,8 +151,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 plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); + organization.UseSecretsManager = true; var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer { @@ -167,7 +167,7 @@ public async void PurchaseOrganizationAsync_Stripe(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plans = StaticStore.PasswordManagerPlans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); paymentToken = "pm_" + paymentToken; var stripeAdapter = sutProvider.GetDependency(); @@ -224,7 +224,7 @@ public async void PurchaseOrganizationAsync_Stripe_PM(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -280,7 +280,7 @@ public async void PurchaseOrganizationAsync_Stripe_TaxRate(SutProvider { new() { Id = "T-1" } }); - var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo); + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo); Assert.Null(result); @@ -293,7 +293,7 @@ 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 plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -309,7 +309,7 @@ public async void PurchaseOrganizationAsync_Stripe_TaxRate_SM(SutProvider { new() { Id = "T-1" } }); - var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 2, 2, + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 2, 2, false, taxInfo, false, 2, 2); Assert.Null(result); @@ -323,7 +323,7 @@ await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plan = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); paymentToken = "pm_" + paymentToken; var stripeAdapter = sutProvider.GetDependency(); @@ -356,7 +356,7 @@ 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(); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); paymentToken = "pm_" + paymentToken; var stripeAdapter = sutProvider.GetDependency(); @@ -390,7 +390,7 @@ public async void PurchaseOrganizationAsync_SM_Stripe_Declined(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -412,7 +412,7 @@ public async void PurchaseOrganizationAsync_Stripe_RequiresAction(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -443,7 +443,7 @@ public async void PurchaseOrganizationAsync_SM_Stripe_RequiresAction(SutProvider }, }); - var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 10, 10, false, taxInfo, false, 10, 10); Assert.Equal("clientSecret", result); @@ -453,7 +453,7 @@ public async void PurchaseOrganizationAsync_SM_Stripe_RequiresAction(SutProvider [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Paypal(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer @@ -480,7 +480,7 @@ public async void PurchaseOrganizationAsync_Paypal(SutProvider(); braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult); - var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, 0, 0, false, taxInfo); + var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo); Assert.Null(result); Assert.Equal(GatewayType.Stripe, organization.Gateway); @@ -517,10 +517,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 plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); + organization.UseSecretsManager = true; var stripeAdapter = sutProvider.GetDependency(); stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer { @@ -550,7 +548,7 @@ public async void PurchaseOrganizationAsync_SM_Paypal(SutProvider 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 + s.Items.Count(i => i.Plan == plan.PasswordManager.StripeSeatPlanId && i.Quantity == additionalSeats) == 1 && + s.Items.Count(i => i.Plan == plan.PasswordManager.StripeStoragePlanId && i.Quantity == additionalStorage) == 1 && + s.Items.Count(i => i.Plan == plan.SecretsManager.StripeSeatPlanId && i.Quantity == additionalSmSeats) == 1 && + s.Items.Count(i => i.Plan == plan.SecretsManager.StripeServiceAccountPlanId && i.Quantity == additionalServiceAccounts) == 1 )); } [Theory, BitAutoData] public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); var customerResult = Substitute.For>(); customerResult.IsSuccess().Returns(false); @@ -601,7 +599,7 @@ public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider( - () => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, 0, 0, false, taxInfo)); + () => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo)); Assert.Equal("Failed to create PayPal customer record.", exception.Message); } @@ -609,7 +607,7 @@ public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo) { - var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList(); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); var customerResult = Substitute.For>(); customerResult.IsSuccess().Returns(false); @@ -618,7 +616,7 @@ public async void PurchaseOrganizationAsync_SM_Paypal_FailedCreate(SutProvider( - () => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, + () => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 1, 1, false, taxInfo, false, 8, 8)); Assert.Equal("Failed to create PayPal customer record.", exception.Message); @@ -627,7 +625,7 @@ 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 plans = StaticStore.GetPlan(PlanType.EnterpriseAnnually); paymentToken = "pm_" + paymentToken; var stripeAdapter = sutProvider.GetDependency(); @@ -689,7 +687,7 @@ public async void UpgradeFreeOrganizationAsync_Success(SutProvider p.Type == PlanType.EnterpriseAnnually).ToList(); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); var upgrade = new OrganizationUpgrade() { @@ -700,7 +698,7 @@ public async void UpgradeFreeOrganizationAsync_Success(SutProvider p.Type == PlanType.EnterpriseAnnually).ToList(); - var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plans, upgrade); + var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually); + var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plan, upgrade); Assert.Null(result); } diff --git a/test/Core.Test/Services/UserServiceTests.cs b/test/Core.Test/Services/UserServiceTests.cs index 2dccea2c8751..54dccd661d99 100644 --- a/test/Core.Test/Services/UserServiceTests.cs +++ b/test/Core.Test/Services/UserServiceTests.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using Bit.Core.AdminConsole.Repositories; using AutoFixture; using Bit.Core.Auth.Entities; using Bit.Core.Auth.Enums; diff --git a/test/Core.Test/Utilities/CoreHelpersTests.cs b/test/Core.Test/Utilities/CoreHelpersTests.cs index 4def69404259..6534feef5519 100644 --- a/test/Core.Test/Utilities/CoreHelpersTests.cs +++ b/test/Core.Test/Utilities/CoreHelpersTests.cs @@ -1,9 +1,10 @@ using System.Text; using AutoFixture; +using Bit.Core.AdminConsole.Context; +using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; -using Bit.Core.Enums.Provider; using Bit.Core.Test.AutoFixture.UserFixtures; using Bit.Core.Utilities; using Bit.Test.Common.AutoFixture; diff --git a/test/Core.Test/Utilities/StaticStoreTests.cs b/test/Core.Test/Utilities/StaticStoreTests.cs index d30a0e6c733b..4e85928cc7d9 100644 --- a/test/Core.Test/Utilities/StaticStoreTests.cs +++ b/test/Core.Test/Utilities/StaticStoreTests.cs @@ -1,5 +1,4 @@ using Bit.Core.Enums; -using Bit.Core.Models.StaticStore; using Bit.Core.Utilities; using Xunit; @@ -14,57 +13,18 @@ public void StaticStore_Initialization_Success() var plans = StaticStore.Plans; Assert.NotNull(plans); Assert.NotEmpty(plans); - Assert.Equal(17, plans.Count()); + Assert.Equal(12, plans.Count()); } [Theory] [InlineData(PlanType.EnterpriseAnnually)] - public void StaticStore_GetPasswordManagerPlanByPlanType_Success(PlanType planType) + [InlineData(PlanType.EnterpriseMonthly)] + [InlineData(PlanType.TeamsMonthly)] + [InlineData(PlanType.TeamsAnnually)] + public void StaticStore_GetPlan_Success(PlanType planType) { - var plan = StaticStore.GetPasswordManagerPlan(planType); - + var plan = StaticStore.GetPlan(planType); Assert.NotNull(plan); Assert.Equal(planType, plan.Type); } - - [Theory] - [InlineData(PlanType.EnterpriseAnnually)] - public void StaticStore_GetSecretsManagerPlanByPlanType_Success(PlanType planType) - { - var plan = StaticStore.GetSecretsManagerPlan(planType); - - Assert.NotNull(plan); - Assert.Equal(planType, plan.Type); - } - - [Theory] - [InlineData(PlanType.EnterpriseAnnually)] - public void StaticStore_GetPasswordManagerPlan_ReturnsPasswordManagerPlans(PlanType planType) - { - var plan = StaticStore.GetPasswordManagerPlan(planType); - Assert.NotNull(plan); - Assert.Equal(BitwardenProductType.PasswordManager, plan.BitwardenProduct); - } - - [Theory] - [InlineData(PlanType.EnterpriseAnnually)] - public void StaticStore_GetSecretsManagerPlan_ReturnsSecretManagerPlans(PlanType planType) - { - var plan = StaticStore.GetSecretsManagerPlan(planType); - Assert.NotNull(plan); - Assert.Equal(BitwardenProductType.SecretsManager, plan.BitwardenProduct); - } - - [Theory] - [InlineData(PlanType.EnterpriseAnnually, BitwardenProductType.PasswordManager)] - public void StaticStore_AddDuplicatePlans_SingleOrDefaultThrowsException(PlanType planType, BitwardenProductType bitwardenProductType) - { - var plansStore = new List - { - new Plan { Type = PlanType.EnterpriseAnnually, BitwardenProduct = BitwardenProductType.PasswordManager }, - new Plan { Type = PlanType.EnterpriseAnnually, BitwardenProduct = BitwardenProductType.PasswordManager } - }; - - Assert.Throws(() => plansStore.SingleOrDefault(p => p.Type == planType && p.BitwardenProduct == bitwardenProductType)); - } } diff --git a/test/Core.Test/packages.lock.json b/test/Core.Test/packages.lock.json index f4111c44d5a8..c814703a9188 100644 --- a/test/Core.Test/packages.lock.json +++ b/test/Core.Test/packages.lock.json @@ -222,10 +222,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2672,7 +2673,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -2691,7 +2692,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", diff --git a/test/Icons.Test/packages.lock.json b/test/Icons.Test/packages.lock.json index 69cfc6d888db..58a29e615a4c 100644 --- a/test/Icons.Test/packages.lock.json +++ b/test/Icons.Test/packages.lock.json @@ -235,10 +235,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2841,7 +2842,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -2860,7 +2861,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2894,14 +2895,14 @@ "type": "Project", "dependencies": { "AngleSharp": "[1.0.4, )", - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )" } }, "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2909,7 +2910,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2921,9 +2922,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/test/Identity.IntegrationTest/packages.lock.json b/test/Identity.IntegrationTest/packages.lock.json index 9400fd738a9a..49e914b2d0f0 100644 --- a/test/Identity.IntegrationTest/packages.lock.json +++ b/test/Identity.IntegrationTest/packages.lock.json @@ -239,10 +239,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2988,7 +2989,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -3007,7 +3008,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -3040,15 +3041,15 @@ "identity": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )", + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )", "Swashbuckle.AspNetCore.SwaggerGen": "[6.5.0, )" } }, "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -3056,7 +3057,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -3068,8 +3069,8 @@ "integrationtestcommon": { "type": "Project", "dependencies": { - "Common": "[2023.8.3, )", - "Identity": "[2023.8.3, )", + "Common": "[2023.9.0, )", + "Identity": "[2023.9.0, )", "Microsoft.AspNetCore.Mvc.Testing": "[6.0.5, )", "Microsoft.EntityFrameworkCore.InMemory": "[7.0.5, )", "Microsoft.Extensions.Configuration": "[6.0.1, )" @@ -3078,9 +3079,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/test/Identity.Test/packages.lock.json b/test/Identity.Test/packages.lock.json index 243e7117248b..4d501fa090ab 100644 --- a/test/Identity.Test/packages.lock.json +++ b/test/Identity.Test/packages.lock.json @@ -228,10 +228,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2855,7 +2856,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -2874,7 +2875,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2907,15 +2908,15 @@ "identity": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )", + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )", "Swashbuckle.AspNetCore.SwaggerGen": "[6.5.0, )" } }, "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2923,7 +2924,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2935,9 +2936,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/GroupCompare.cs b/test/Infrastructure.EFIntegration.Test/AdminConsole/Repositories/EqualityComparers/GroupCompare.cs similarity index 73% rename from test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/GroupCompare.cs rename to test/Infrastructure.EFIntegration.Test/AdminConsole/Repositories/EqualityComparers/GroupCompare.cs index dcb0be2ff1b4..eb52446af0fd 100644 --- a/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/GroupCompare.cs +++ b/test/Infrastructure.EFIntegration.Test/AdminConsole/Repositories/EqualityComparers/GroupCompare.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; -namespace Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; +namespace Bit.Infrastructure.EFIntegration.Test.AdminConsole.Repositories.EqualityComparers; public class GroupCompare : IEqualityComparer { diff --git a/test/Infrastructure.EFIntegration.Test/AutoFixture/EntityFrameworkRepositoryFixtures.cs b/test/Infrastructure.EFIntegration.Test/AutoFixture/EntityFrameworkRepositoryFixtures.cs index fd14cde65bea..b610f51da2e6 100644 --- a/test/Infrastructure.EFIntegration.Test/AutoFixture/EntityFrameworkRepositoryFixtures.cs +++ b/test/Infrastructure.EFIntegration.Test/AutoFixture/EntityFrameworkRepositoryFixtures.cs @@ -4,6 +4,7 @@ using AutoMapper; using Bit.Core.Settings; using Bit.Infrastructure.EFIntegration.Test.Helpers; +using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider; using Bit.Infrastructure.EntityFramework.Auth.Models; using Bit.Infrastructure.EntityFramework.Models; using Bit.Infrastructure.EntityFramework.Repositories; diff --git a/test/Infrastructure.EFIntegration.Test/AutoFixture/GroupFixtures.cs b/test/Infrastructure.EFIntegration.Test/AutoFixture/GroupFixtures.cs index c6cca49015b5..67d15c1774a7 100644 --- a/test/Infrastructure.EFIntegration.Test/AutoFixture/GroupFixtures.cs +++ b/test/Infrastructure.EFIntegration.Test/AutoFixture/GroupFixtures.cs @@ -1,7 +1,8 @@ using AutoFixture; using AutoFixture.Kernel; -using Bit.Core.Entities; +using Bit.Core.AdminConsole.Entities; using Bit.Infrastructure.EFIntegration.Test.AutoFixture.Relays; +using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; diff --git a/test/Infrastructure.EFIntegration.Test/AutoFixture/GroupUserFixtures.cs b/test/Infrastructure.EFIntegration.Test/AutoFixture/GroupUserFixtures.cs index 2b68cde32269..f127383041dc 100644 --- a/test/Infrastructure.EFIntegration.Test/AutoFixture/GroupUserFixtures.cs +++ b/test/Infrastructure.EFIntegration.Test/AutoFixture/GroupUserFixtures.cs @@ -1,7 +1,7 @@ using AutoFixture; using AutoFixture.Kernel; -using Bit.Core.Entities; -using Bit.Infrastructure.EntityFramework.Repositories; +using Bit.Core.AdminConsole.Entities; +using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; diff --git a/test/Infrastructure.EFIntegration.Test/AutoFixture/PolicyFixtures.cs b/test/Infrastructure.EFIntegration.Test/AutoFixture/PolicyFixtures.cs index 70cea3e01189..5ecd31f9c68c 100644 --- a/test/Infrastructure.EFIntegration.Test/AutoFixture/PolicyFixtures.cs +++ b/test/Infrastructure.EFIntegration.Test/AutoFixture/PolicyFixtures.cs @@ -1,6 +1,7 @@ using AutoFixture; using AutoFixture.Kernel; using Bit.Core.Entities; +using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationUserRepositoryTests.cs b/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationUserRepositoryTests.cs index 4f3b912b6ec0..67b199b35ae3 100644 --- a/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationUserRepositoryTests.cs +++ b/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationUserRepositoryTests.cs @@ -1,6 +1,7 @@ using System.Text.Json; +using Bit.Core.AdminConsole.Entities.Provider; +using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; -using Bit.Core.Entities.Provider; using Bit.Core.Enums; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.OrganizationUsers; @@ -9,6 +10,7 @@ using Bit.Infrastructure.EFIntegration.Test.AutoFixture; using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; using Xunit; +using EfAdminConsoleRepo = Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; using EfRepo = Bit.Infrastructure.EntityFramework.Repositories; using OrganizationUser = Bit.Core.Entities.OrganizationUser; using SqlRepo = Bit.Infrastructure.Dapper.Repositories; @@ -184,18 +186,18 @@ public async void GetByUserIdWithPolicyDetailsAsync_Works_DataMatches( List efUserRepository, List efOrganizationRepository, List suts, - List efProviderRepository, - List efProviderOrganizationRepository, - List efProviderUserRepository, + List efProviderRepository, + List efProviderOrganizationRepository, + List efProviderUserRepository, // Auto data - SQL repos SqlRepo.PolicyRepository sqlPolicyRepo, SqlRepo.UserRepository sqlUserRepo, SqlRepo.OrganizationRepository sqlOrganizationRepo, - SqlRepo.ProviderRepository sqlProviderRepo, + EfAdminConsoleRepo.ProviderRepository sqlProviderRepo, SqlRepo.OrganizationUserRepository sqlOrganizationUserRepo, - SqlRepo.ProviderOrganizationRepository sqlProviderOrganizationRepo, - SqlRepo.ProviderUserRepository sqlProviderUserRepo + EfAdminConsoleRepo.ProviderOrganizationRepository sqlProviderOrganizationRepo, + EfAdminConsoleRepo.ProviderUserRepository sqlProviderUserRepo ) { // Combine EF and SQL repos into one list per type diff --git a/test/Infrastructure.EFIntegration.Test/packages.lock.json b/test/Infrastructure.EFIntegration.Test/packages.lock.json index f8c67ce44e14..ef30b1cb1a55 100644 --- a/test/Infrastructure.EFIntegration.Test/packages.lock.json +++ b/test/Infrastructure.EFIntegration.Test/packages.lock.json @@ -229,10 +229,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2835,7 +2836,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -2854,7 +2855,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2889,8 +2890,8 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Common": "[2023.8.3, )", - "Core": "[2023.8.3, )", + "Common": "[2023.9.0, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -2900,7 +2901,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2908,7 +2909,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", diff --git a/test/Infrastructure.IntegrationTest/Auth/Repositories/AuthRequestRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Auth/Repositories/AuthRequestRepositoryTests.cs index b23b8ce4b877..6c5bf135e144 100644 --- a/test/Infrastructure.IntegrationTest/Auth/Repositories/AuthRequestRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/Auth/Repositories/AuthRequestRepositoryTests.cs @@ -15,8 +15,7 @@ public class AuthRequestRepositoryTests [DatabaseTheory, DatabaseData] public async Task DeleteExpiredAsync_Works( IAuthRequestRepository authRequestRepository, - IUserRepository userRepository, - ITestDatabaseHelper helper) + IUserRepository userRepository) { var user = await userRepository.CreateAsync(new User { @@ -54,8 +53,6 @@ public async Task DeleteExpiredAsync_Works( var notExpiredApprovedAdminApprovalRequest = await authRequestRepository.CreateAsync( CreateAuthRequest(user.Id, AuthRequestType.AdminApproval, DateTime.UtcNow.AddDays(7), true, DateTime.UtcNow.AddHours(11))); - helper.ClearTracker(); - var numberOfDeleted = await authRequestRepository.DeleteExpiredAsync(_userRequestExpiration, _adminRequestExpiration, _afterAdminApprovalExpiration); // Ensure all the AuthRequests that should have been deleted, have been deleted. diff --git a/test/Infrastructure.IntegrationTest/Auth/Repositories/EmergencyAccessRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Auth/Repositories/EmergencyAccessRepositoryTests.cs index 6454c01ea9ec..def3dcb1e76b 100644 --- a/test/Infrastructure.IntegrationTest/Auth/Repositories/EmergencyAccessRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/Auth/Repositories/EmergencyAccessRepositoryTests.cs @@ -10,8 +10,7 @@ public class EmergencyAccessRepositoriesTests { [DatabaseTheory, DatabaseData] public async Task DeleteAsync_UpdatesRevisionDate(IUserRepository userRepository, - IEmergencyAccessRepository emergencyAccessRepository, - ITestDatabaseHelper helper) + IEmergencyAccessRepository emergencyAccessRepository) { var grantorUser = await userRepository.CreateAsync(new User { @@ -36,8 +35,6 @@ public async Task DeleteAsync_UpdatesRevisionDate(IUserRepository userRepository Status = EmergencyAccessStatusType.Confirmed, }); - helper.ClearTracker(); - await emergencyAccessRepository.DeleteAsync(emergencyAccess); var updatedGrantee = await userRepository.GetByIdAsync(granteeUser.Id); diff --git a/test/Infrastructure.IntegrationTest/DatabaseDataAttribute.cs b/test/Infrastructure.IntegrationTest/DatabaseDataAttribute.cs index 3bf2211020b8..6433fcb207c1 100644 --- a/test/Infrastructure.IntegrationTest/DatabaseDataAttribute.cs +++ b/test/Infrastructure.IntegrationTest/DatabaseDataAttribute.cs @@ -3,7 +3,6 @@ using Bit.Core.Settings; using Bit.Infrastructure.Dapper; using Bit.Infrastructure.EntityFramework; -using Bit.Infrastructure.EntityFramework.Repositories; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -65,7 +64,7 @@ private IEnumerable GetDatabaseProviders(IConfiguration config }; dapperSqlServerCollection.AddSingleton(globalSettings); dapperSqlServerCollection.AddSingleton(globalSettings); - dapperSqlServerCollection.AddSingleton(_ => new DapperSqlServerTestDatabaseHelper(database)); + dapperSqlServerCollection.AddSingleton(database); dapperSqlServerCollection.AddDataProtection(); yield return dapperSqlServerCollection.BuildServiceProvider(); } @@ -75,7 +74,7 @@ private IEnumerable GetDatabaseProviders(IConfiguration config efCollection.AddLogging(configureLogging); efCollection.SetupEntityFramework(database.ConnectionString, database.Type); efCollection.AddPasswordManagerEFRepositories(SelfHosted); - efCollection.AddTransient(sp => new EfTestDatabaseHelper(sp.GetRequiredService(), database)); + efCollection.AddSingleton(database); efCollection.AddDataProtection(); yield return efCollection.BuildServiceProvider(); } diff --git a/test/Infrastructure.IntegrationTest/Repositories/OrganizationUserRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Repositories/OrganizationUserRepositoryTests.cs index 1b780f114dd3..e07b3ba72069 100644 --- a/test/Infrastructure.IntegrationTest/Repositories/OrganizationUserRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/Repositories/OrganizationUserRepositoryTests.cs @@ -10,8 +10,7 @@ public class OrganizationUserRepositoryTests [DatabaseTheory, DatabaseData] public async Task DeleteAsync_Works(IUserRepository userRepository, IOrganizationRepository organizationRepository, - IOrganizationUserRepository organizationUserRepository, - ITestDatabaseHelper helper) + IOrganizationUserRepository organizationUserRepository) { var user = await userRepository.CreateAsync(new User { @@ -35,8 +34,6 @@ public async Task DeleteAsync_Works(IUserRepository userRepository, Status = OrganizationUserStatusType.Confirmed, }); - helper.ClearTracker(); - await organizationUserRepository.DeleteAsync(orgUser); var newUser = await userRepository.GetByIdAsync(user.Id); @@ -46,8 +43,7 @@ public async Task DeleteAsync_Works(IUserRepository userRepository, [DatabaseTheory, DatabaseData] public async Task DeleteManyAsync_Works(IUserRepository userRepository, IOrganizationRepository organizationRepository, - IOrganizationUserRepository organizationUserRepository, - ITestDatabaseHelper helper) + IOrganizationUserRepository organizationUserRepository) { var user1 = await userRepository.CreateAsync(new User { @@ -86,8 +82,6 @@ public async Task DeleteManyAsync_Works(IUserRepository userRepository, Status = OrganizationUserStatusType.Confirmed, }); - helper.ClearTracker(); - await organizationUserRepository.DeleteManyAsync(new List { orgUser1.Id, diff --git a/test/Infrastructure.IntegrationTest/TestDatabaseHelpers.cs b/test/Infrastructure.IntegrationTest/TestDatabaseHelpers.cs deleted file mode 100644 index 5cf8c73a8a7b..000000000000 --- a/test/Infrastructure.IntegrationTest/TestDatabaseHelpers.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Bit.Infrastructure.EntityFramework.Repositories; - -namespace Bit.Infrastructure.IntegrationTest; - -public interface ITestDatabaseHelper -{ - Database Info { get; } - void ClearTracker(); -} - -public class EfTestDatabaseHelper : ITestDatabaseHelper -{ - private readonly DatabaseContext _databaseContext; - - public EfTestDatabaseHelper(DatabaseContext databaseContext, Database database) - { - _databaseContext = databaseContext; - Info = database; - } - - public Database Info { get; } - - public void ClearTracker() - { - _databaseContext.ChangeTracker.Clear(); - } -} - -public class DapperSqlServerTestDatabaseHelper : ITestDatabaseHelper -{ - public DapperSqlServerTestDatabaseHelper(Database database) - { - Info = database; - } - - public Database Info { get; } - - public void ClearTracker() - { - // There are no tracked entities in Dapper SQL Server - } -} diff --git a/test/Infrastructure.IntegrationTest/Vault/Repositories/CipherRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Vault/Repositories/CipherRepositoryTests.cs index d9cc228c203b..1712313d3765 100644 --- a/test/Infrastructure.IntegrationTest/Vault/Repositories/CipherRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/Vault/Repositories/CipherRepositoryTests.cs @@ -16,8 +16,7 @@ public class CipherRepositoryTests [DatabaseTheory, DatabaseData] public async Task DeleteAsync_UpdatesUserRevisionDate( IUserRepository userRepository, - ICipherRepository cipherRepository, - ITestDatabaseHelper helper) + ICipherRepository cipherRepository) { var user = await userRepository.CreateAsync(new User { @@ -34,8 +33,6 @@ public async Task DeleteAsync_UpdatesUserRevisionDate( Data = "", // TODO: EF does not enforce this as NOT NULL }); - helper.ClearTracker(); - await cipherRepository.DeleteAsync(cipher); var deletedCipher = await cipherRepository.GetByIdAsync(cipher.Id); @@ -52,8 +49,7 @@ public async Task CreateAsync_UpdateWithCollections_Works( IOrganizationUserRepository organizationUserRepository, ICollectionRepository collectionRepository, ICipherRepository cipherRepository, - ICollectionCipherRepository collectionCipherRepository, - ITestDatabaseHelper helper) + ICollectionCipherRepository collectionCipherRepository) { var user = await userRepository.CreateAsync(new User { @@ -63,8 +59,6 @@ public async Task CreateAsync_UpdateWithCollections_Works( SecurityStamp = "stamp", }); - helper.ClearTracker(); - user = await userRepository.GetByIdAsync(user.Id); var organization = await organizationRepository.CreateAsync(new Organization @@ -100,8 +94,6 @@ await collectionRepository.UpdateUsersAsync(collection.Id, new[] }, }); - helper.ClearTracker(); - await Task.Delay(100); await cipherRepository.CreateAsync(new CipherDetails @@ -128,8 +120,7 @@ public async Task ReplaceAsync_SuccessfullyMovesCipherToOrganization(IUserReposi ICipherRepository cipherRepository, IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, - IFolderRepository folderRepository, - ITestDatabaseHelper helper) + IFolderRepository folderRepository) { // This tests what happens when a cipher is moved into an organizations var user = await userRepository.CreateAsync(new User @@ -171,8 +162,6 @@ public async Task ReplaceAsync_SuccessfullyMovesCipherToOrganization(IUserReposi UserId = user.Id, }); - helper.ClearTracker(); - // Move cipher to organization vault await cipherRepository.ReplaceAsync(new CipherDetails { diff --git a/test/Infrastructure.IntegrationTest/packages.lock.json b/test/Infrastructure.IntegrationTest/packages.lock.json index 9d11e6873ddb..9c69a541c8a3 100644 --- a/test/Infrastructure.IntegrationTest/packages.lock.json +++ b/test/Infrastructure.IntegrationTest/packages.lock.json @@ -228,10 +228,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2712,7 +2713,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2745,7 +2746,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2753,7 +2754,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", diff --git a/test/IntegrationTestCommon/packages.lock.json b/test/IntegrationTestCommon/packages.lock.json index 91e6b1f0a043..6e1296d5c627 100644 --- a/test/IntegrationTestCommon/packages.lock.json +++ b/test/IntegrationTestCommon/packages.lock.json @@ -215,10 +215,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2974,7 +2975,7 @@ "dependencies": { "AutoFixture.AutoNSubstitute": "[4.17.0, )", "AutoFixture.Xunit2": "[4.17.0, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", "Microsoft.NET.Test.Sdk": "[17.1.0, )", "NSubstitute": "[4.3.0, )", @@ -2993,7 +2994,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -3026,15 +3027,15 @@ "identity": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )", + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )", "Swashbuckle.AspNetCore.SwaggerGen": "[6.5.0, )" } }, "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -3042,7 +3043,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -3054,9 +3055,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/test/bitwarden.tests.sln b/test/bitwarden.tests.sln index 5f60637e13a5..a5d8b86ddeab 100644 --- a/test/bitwarden.tests.sln +++ b/test/bitwarden.tests.sln @@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.IntegrationTest", "Api. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.IntegrationTest", "Infrastructure.IntegrationTest\Infrastructure.IntegrationTest.csproj", "{5827E256-D1C5-4BBE-BB74-ED28A83578FA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity.Test", "Identity.Test\Identity.Test.csproj", "{CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -156,5 +158,17 @@ Global {5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Release|x64.Build.0 = Release|Any CPU {5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Release|x86.ActiveCfg = Release|Any CPU {5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Release|x86.Build.0 = Release|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Debug|x64.ActiveCfg = Debug|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Debug|x64.Build.0 = Debug|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Debug|x86.ActiveCfg = Debug|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Debug|x86.Build.0 = Debug|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Release|Any CPU.Build.0 = Release|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Release|x64.ActiveCfg = Release|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Release|x64.Build.0 = Release|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Release|x86.ActiveCfg = Release|Any CPU + {CE6A0F24-4193-4CCC-9BE1-6D1D85782CA9}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/util/Migrator/DbMigrator.cs b/util/Migrator/DbMigrator.cs index 2dca5006ae41..24e78aaee641 100644 --- a/util/Migrator/DbMigrator.cs +++ b/util/Migrator/DbMigrator.cs @@ -2,6 +2,7 @@ using System.Reflection; using Bit.Core; using DbUp; +using DbUp.Helpers; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; @@ -24,6 +25,8 @@ public DbMigrator(string connectionString, ILogger logger) } public bool MigrateMsSqlDatabaseWithRetries(bool enableLogging = true, + bool repeatable = false, + string folderName = MigratorConstants.DefaultMigrationsFolderName, CancellationToken cancellationToken = default(CancellationToken)) { var attempt = 1; @@ -32,7 +35,7 @@ public bool MigrateMsSqlDatabaseWithRetries(bool enableLogging = true, { try { - var success = MigrateDatabase(enableLogging, cancellationToken); + var success = MigrateDatabase(enableLogging, repeatable, folderName, cancellationToken); return success; } catch (SqlException ex) @@ -54,6 +57,8 @@ public bool MigrateMsSqlDatabaseWithRetries(bool enableLogging = true, } public bool MigrateDatabase(bool enableLogging = true, + bool repeatable = false, + string folderName = MigratorConstants.DefaultMigrationsFolderName, CancellationToken cancellationToken = default(CancellationToken)) { if (_logger != null) @@ -98,12 +103,20 @@ public bool MigrateDatabase(bool enableLogging = true, cancellationToken.ThrowIfCancellationRequested(); var builder = DeployChanges.To .SqlDatabase(_connectionString) - .JournalToSqlTable("dbo", "Migration") .WithScriptsAndCodeEmbeddedInAssembly(Assembly.GetExecutingAssembly(), - s => s.Contains($".DbScripts.") && !s.Contains(".Archive.")) + s => s.Contains($".{folderName}.") && !s.Contains(".Archive.")) .WithTransaction() .WithExecutionTimeout(new TimeSpan(0, 5, 0)); + if (repeatable) + { + builder.JournalTo(new NullJournal()); + } + else + { + builder.JournalToSqlTable("dbo", MigratorConstants.SqlTableJournalName); + } + if (enableLogging) { if (_logger != null) diff --git a/util/Migrator/DbScripts_future/2023-01-FutureMigration.sql b/util/Migrator/DbScripts/2023-09-11_00_2023-01-FutureMigration.sql similarity index 82% rename from util/Migrator/DbScripts_future/2023-01-FutureMigration.sql rename to util/Migrator/DbScripts/2023-09-11_00_2023-01-FutureMigration.sql index 9f725c7be23d..1f975f42dab6 100644 --- a/util/Migrator/DbScripts_future/2023-01-FutureMigration.sql +++ b/util/Migrator/DbScripts/2023-09-11_00_2023-01-FutureMigration.sql @@ -51,6 +51,8 @@ CREATE OR ALTER PROCEDURE [dbo].[User_Create] @LicenseKey VARCHAR(100), @Kdf TINYINT, @KdfIterations INT, + @KdfMemory INT = NULL, + @KdfParallelism INT = NULL, @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7), @ApiKey VARCHAR(30), @@ -58,7 +60,11 @@ CREATE OR ALTER PROCEDURE [dbo].[User_Create] @UsesKeyConnector BIT = 0, @FailedLoginCount INT = 0, @LastFailedLoginDate DATETIME2(7), - @AvatarColor VARCHAR(7) = NULL + @AvatarColor VARCHAR(7) = NULL, + @LastPasswordChangeDate DATETIME2(7) = NULL, + @LastKdfChangeDate DATETIME2(7) = NULL, + @LastKeyRotationDate DATETIME2(7) = NULL, + @LastEmailChangeDate DATETIME2(7) = NULL AS BEGIN SET NOCOUNT ON @@ -100,7 +106,13 @@ BEGIN [UsesKeyConnector], [FailedLoginCount], [LastFailedLoginDate], - [AvatarColor] + [AvatarColor], + [KdfMemory], + [KdfParallelism], + [LastPasswordChangeDate], + [LastKdfChangeDate], + [LastKeyRotationDate], + [LastEmailChangeDate] ) VALUES ( @@ -139,7 +151,13 @@ BEGIN @UsesKeyConnector, @FailedLoginCount, @LastFailedLoginDate, - @AvatarColor + @AvatarColor, + @KdfMemory, + @KdfParallelism, + @LastPasswordChangeDate, + @LastKdfChangeDate, + @LastKeyRotationDate, + @LastEmailChangeDate ) END GO @@ -174,6 +192,8 @@ CREATE OR ALTER PROCEDURE [dbo].[User_Update] @LicenseKey VARCHAR(100), @Kdf TINYINT, @KdfIterations INT, + @KdfMemory INT = NULL, + @KdfParallelism INT = NULL, @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7), @ApiKey VARCHAR(30), @@ -181,7 +201,11 @@ CREATE OR ALTER PROCEDURE [dbo].[User_Update] @UsesKeyConnector BIT = 0, @FailedLoginCount INT, @LastFailedLoginDate DATETIME2(7), - @AvatarColor VARCHAR(7) + @AvatarColor VARCHAR(7), + @LastPasswordChangeDate DATETIME2(7) = NULL, + @LastKdfChangeDate DATETIME2(7) = NULL, + @LastKeyRotationDate DATETIME2(7) = NULL, + @LastEmailChangeDate DATETIME2(7) = NULL AS BEGIN SET NOCOUNT ON @@ -216,6 +240,8 @@ BEGIN [LicenseKey] = @LicenseKey, [Kdf] = @Kdf, [KdfIterations] = @KdfIterations, + [KdfMemory] = @KdfMemory, + [KdfParallelism] = @KdfParallelism, [CreationDate] = @CreationDate, [RevisionDate] = @RevisionDate, [ApiKey] = @ApiKey, @@ -223,7 +249,11 @@ BEGIN [UsesKeyConnector] = @UsesKeyConnector, [FailedLoginCount] = @FailedLoginCount, [LastFailedLoginDate] = @LastFailedLoginDate, - [AvatarColor] = @AvatarColor + [AvatarColor] = @AvatarColor, + [LastPasswordChangeDate] = @LastPasswordChangeDate, + [LastKdfChangeDate] = @LastKdfChangeDate, + [LastKeyRotationDate] = @LastKeyRotationDate, + [LastEmailChangeDate] = @LastEmailChangeDate WHERE [Id] = @Id END diff --git a/util/Migrator/DbScripts_future/2023-02-FutureMigration.sql b/util/Migrator/DbScripts/2023-09-11_01_2023-02-FutureMigration.sql similarity index 100% rename from util/Migrator/DbScripts_future/2023-02-FutureMigration.sql rename to util/Migrator/DbScripts/2023-09-11_01_2023-02-FutureMigration.sql diff --git a/util/Migrator/DbScripts/2023-09-27_00_CipherKeyUpdate.sql b/util/Migrator/DbScripts/2023-09-27_00_CipherKeyUpdate.sql new file mode 100644 index 000000000000..93fcd34656d2 --- /dev/null +++ b/util/Migrator/DbScripts/2023-09-27_00_CipherKeyUpdate.sql @@ -0,0 +1,428 @@ +-- Add Key Column +IF COL_LENGTH('[dbo].[Cipher]', 'Key') IS NULL +BEGIN + ALTER TABLE [dbo].[Cipher] ADD [Key] VARCHAR (MAX) NULL; +END +GO + +-- Remove ForceKeyRotation Column from the Cipher table +-- (this is to help with the dev work as the column was no longer needed) +IF COL_LENGTH('[dbo].[Cipher]', 'ForceKeyRotation') IS NOT NULL +BEGIN + ALTER TABLE [dbo].[Cipher] DROP CONSTRAINT [D_Cipher_ForceKeyRotation]; + ALTER TABLE [dbo].[Cipher] DROP COLUMN [ForceKeyRotation]; +END +GO + +CREATE OR ALTER FUNCTION [dbo].[CipherDetails](@UserId UNIQUEIDENTIFIER) +RETURNS TABLE +AS RETURN +SELECT + C.[Id], + C.[UserId], + C.[OrganizationId], + C.[Type], + C.[Data], + C.[Attachments], + C.[CreationDate], + C.[RevisionDate], + CASE + WHEN + @UserId IS NULL + OR C.[Favorites] IS NULL + OR JSON_VALUE(C.[Favorites], CONCAT('$."', @UserId, '"')) IS NULL + THEN 0 + ELSE 1 + END [Favorite], + CASE + WHEN + @UserId IS NULL + OR C.[Folders] IS NULL + THEN NULL + ELSE TRY_CONVERT(UNIQUEIDENTIFIER, JSON_VALUE(C.[Folders], CONCAT('$."', @UserId, '"'))) +END [FolderId], + C.[DeletedDate], + C.[Reprompt], + C.[Key] +FROM + [dbo].[Cipher] C +GO + +IF OBJECT_ID('[dbo].[UserCipherDetails]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[UserCipherDetails]'; +END +GO + +IF OBJECT_ID('[dbo].[CipherView]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[CipherView]'; +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Create] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), -- not used + @Folders NVARCHAR(MAX), -- not used + @Attachments NVARCHAR(MAX), -- not used + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @FolderId UNIQUEIDENTIFIER, + @Favorite BIT, + @Edit BIT, -- not used + @ViewPassword BIT, -- not used + @OrganizationUseTotp BIT, -- not used + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL +AS +BEGIN + SET NOCOUNT ON + + DECLARE @UserIdKey VARCHAR(50) = CONCAT('"', @UserId, '"') + DECLARE @UserIdPath VARCHAR(50) = CONCAT('$.', @UserIdKey) + + INSERT INTO [dbo].[Cipher] + ( + [Id], + [UserId], + [OrganizationId], + [Type], + [Data], + [Favorites], + [Folders], + [CreationDate], + [RevisionDate], + [DeletedDate], + [Reprompt], + [Key] + ) + VALUES + ( + @Id, + CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END, + @OrganizationId, + @Type, + @Data, + CASE WHEN @Favorite = 1 THEN CONCAT('{', @UserIdKey, ':true}') ELSE NULL END, + CASE WHEN @FolderId IS NOT NULL THEN CONCAT('{', @UserIdKey, ':"', @FolderId, '"', '}') ELSE NULL END, + @CreationDate, + @RevisionDate, + @DeletedDate, + @Reprompt, + @Key + ) + + IF @OrganizationId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + END + ELSE IF @UserId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + END +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Update] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), -- not used + @Folders NVARCHAR(MAX), -- not used + @Attachments NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @FolderId UNIQUEIDENTIFIER, + @Favorite BIT, + @Edit BIT, -- not used + @ViewPassword BIT, -- not used + @OrganizationUseTotp BIT, -- not used + @DeletedDate DATETIME2(2), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL +AS +BEGIN + SET NOCOUNT ON + + DECLARE @UserIdKey VARCHAR(50) = CONCAT('"', @UserId, '"') + DECLARE @UserIdPath VARCHAR(50) = CONCAT('$.', @UserIdKey) + + UPDATE + [dbo].[Cipher] + SET + [UserId] = CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END, + [OrganizationId] = @OrganizationId, + [Type] = @Type, + [Data] = @Data, + [Folders] = + CASE + WHEN @FolderId IS NOT NULL AND [Folders] IS NULL THEN + CONCAT('{', @UserIdKey, ':"', @FolderId, '"', '}') + WHEN @FolderId IS NOT NULL THEN + JSON_MODIFY([Folders], @UserIdPath, CAST(@FolderId AS VARCHAR(50))) + ELSE + JSON_MODIFY([Folders], @UserIdPath, NULL) + END, + [Favorites] = + CASE + WHEN @Favorite = 1 AND [Favorites] IS NULL THEN + CONCAT('{', @UserIdKey, ':true}') + WHEN @Favorite = 1 THEN + JSON_MODIFY([Favorites], @UserIdPath, CAST(1 AS BIT)) + ELSE + JSON_MODIFY([Favorites], @UserIdPath, NULL) + END, + [Attachments] = @Attachments, + [Reprompt] = @Reprompt, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate, + [DeletedDate] = @DeletedDate, + [Key] = @Key + WHERE + [Id] = @Id + + IF @OrganizationId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + END + ELSE IF @UserId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + END +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[Cipher_Update] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), + @Folders NVARCHAR(MAX), + @Attachments NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[Cipher] + SET + [UserId] = CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END, + [OrganizationId] = @OrganizationId, + [Type] = @Type, + [Data] = @Data, + [Favorites] = @Favorites, + [Folders] = @Folders, + [Attachments] = @Attachments, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate, + [DeletedDate] = @DeletedDate, + [Reprompt] = @Reprompt, + [Key] = @Key + WHERE + [Id] = @Id + + IF @OrganizationId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + END + ELSE IF @UserId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + END +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[Cipher_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), + @Folders NVARCHAR(MAX), + @Attachments NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[Cipher] + ( + [Id], + [UserId], + [OrganizationId], + [Type], + [Data], + [Favorites], + [Folders], + [Attachments], + [CreationDate], + [RevisionDate], + [DeletedDate], + [Reprompt], + [Key] + ) + VALUES + ( + @Id, + CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END, + @OrganizationId, + @Type, + @Data, + @Favorites, + @Folders, + @Attachments, + @CreationDate, + @RevisionDate, + @DeletedDate, + @Reprompt, + @Key + ) + + IF @OrganizationId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + END + ELSE IF @UserId IS NOT NULL + BEGIN + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId + END +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[Cipher_CreateWithCollections] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), + @Folders NVARCHAR(MAX), + @Attachments NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, + @CollectionIds AS [dbo].[GuidIdArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[Cipher_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders, + @Attachments, @CreationDate, @RevisionDate, @DeletedDate, @Reprompt, @Key + + DECLARE @UpdateCollectionsSuccess INT + EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[Cipher_UpdateWithCollections] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), + @Folders NVARCHAR(MAX), + @Attachments NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, + @CollectionIds AS [dbo].[GuidIdArray] READONLY +AS +BEGIN + SET NOCOUNT ON + +BEGIN TRANSACTION Cipher_UpdateWithCollections + + DECLARE @UpdateCollectionsSuccess INT + EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds + + IF @UpdateCollectionsSuccess < 0 + BEGIN + COMMIT TRANSACTION Cipher_UpdateWithCollections + SELECT -1 -- -1 = Failure + RETURN + END + + UPDATE + [dbo].[Cipher] + SET + [UserId] = NULL, + [OrganizationId] = @OrganizationId, + [Data] = @Data, + [Attachments] = @Attachments, + [RevisionDate] = @RevisionDate, + [DeletedDate] = @DeletedDate, + [Key] = @Key + -- No need to update CreationDate, Favorites, Folders, or Type since that data will not change + WHERE + [Id] = @Id + + COMMIT TRANSACTION Cipher_UpdateWithCollections + + IF @Attachments IS NOT NULL + BEGIN + EXEC [dbo].[Organization_UpdateStorage] @OrganizationId + EXEC [dbo].[User_UpdateStorage] @UserId + END + + EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId + + SELECT 0 -- 0 = Success +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_CreateWithCollections] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Data NVARCHAR(MAX), + @Favorites NVARCHAR(MAX), -- not used + @Folders NVARCHAR(MAX), -- not used + @Attachments NVARCHAR(MAX), -- not used + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @FolderId UNIQUEIDENTIFIER, + @Favorite BIT, + @Edit BIT, -- not used + @ViewPassword BIT, -- not used + @OrganizationUseTotp BIT, -- not used + @DeletedDate DATETIME2(7), + @Reprompt TINYINT, + @Key VARCHAR(MAX) = NULL, + @CollectionIds AS [dbo].[GuidIdArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[CipherDetails_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders, + @Attachments, @CreationDate, @RevisionDate, @FolderId, @Favorite, @Edit, @ViewPassword, + @OrganizationUseTotp, @DeletedDate, @Reprompt, @Key + + DECLARE @UpdateCollectionsSuccess INT + EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds +END +GO diff --git a/util/Migrator/DbScripts/2023-09-29_00_OrgDomainReadByIdOrgId.sql b/util/Migrator/DbScripts/2023-09-29_00_OrgDomainReadByIdOrgId.sql new file mode 100644 index 000000000000..501bf7010379 --- /dev/null +++ b/util/Migrator/DbScripts/2023-09-29_00_OrgDomainReadByIdOrgId.sql @@ -0,0 +1,17 @@ +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_ReadByIdOrganizationId] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationDomain] +WHERE + [Id] = @Id + AND + [OrganizationId] = @OrganizationId +END +GO diff --git a/util/Migrator/DbScripts/2023-10-03_00_OrganizationReadOwnerEmailAddresses.sql b/util/Migrator/DbScripts/2023-10-03_00_OrganizationReadOwnerEmailAddresses.sql new file mode 100644 index 000000000000..c88b12af0348 --- /dev/null +++ b/util/Migrator/DbScripts/2023-10-03_00_OrganizationReadOwnerEmailAddresses.sql @@ -0,0 +1,17 @@ +CREATE OR ALTER PROCEDURE [dbo].[Organization_ReadOwnerEmailAddressesById] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + [U].[Email] + FROM [User] AS [U] + INNER JOIN [OrganizationUser] AS [OU] ON [U].[Id] = [OU].[UserId] + WHERE + [OU].[OrganizationId] = @OrganizationId AND + [OU].[Type] = 0 AND -- Owner + [OU].[Status] = 2 -- Confirmed + GROUP BY [U].[Email] +END +GO diff --git a/util/Migrator/DbScripts/2023-10-05_00_OrgConnectionsReadByIdOrgId.sql b/util/Migrator/DbScripts/2023-10-05_00_OrgConnectionsReadByIdOrgId.sql new file mode 100644 index 000000000000..34c9503086e8 --- /dev/null +++ b/util/Migrator/DbScripts/2023-10-05_00_OrgConnectionsReadByIdOrgId.sql @@ -0,0 +1,16 @@ +CREATE OR ALTER PROCEDURE [dbo].[OrganizationConnection_ReadByIdOrganizationId] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationConnectionView] +WHERE + [Id] = @Id AND + [OrganizationId] = @OrganizationId +END +GO diff --git a/util/Migrator/DbScripts/2023-10-09_00_Event_ReadPageByOrganizationIdServiceAccountId.sql b/util/Migrator/DbScripts/2023-10-09_00_Event_ReadPageByOrganizationIdServiceAccountId.sql new file mode 100644 index 000000000000..334ebc6cad6f --- /dev/null +++ b/util/Migrator/DbScripts/2023-10-09_00_Event_ReadPageByOrganizationIdServiceAccountId.sql @@ -0,0 +1,25 @@ +CREATE OR ALTER PROCEDURE [dbo].[Event_ReadPageByOrganizationIdServiceAccountId] + @OrganizationId UNIQUEIDENTIFIER, + @ServiceAccountId UNIQUEIDENTIFIER, + @StartDate DATETIME2(7), + @EndDate DATETIME2(7), + @BeforeDate DATETIME2(7), + @PageSize INT +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[EventView] + WHERE + [Date] >= @StartDate + AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate) + AND (@BeforeDate IS NULL OR [Date] < @BeforeDate) + AND [OrganizationId] = @OrganizationId + AND [ServiceAccountId] = @ServiceAccountId + ORDER BY [Date] DESC + OFFSET 0 ROWS + FETCH NEXT @PageSize ROWS ONLY +END diff --git a/util/Migrator/DbScripts_finalization/.gitkeep b/util/Migrator/DbScripts_finalization/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/util/Migrator/DbScripts_transition/.gitkeep b/util/Migrator/DbScripts_transition/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/util/Migrator/Migrator.csproj b/util/Migrator/Migrator.csproj index c7f00465dd52..017e339acbaa 100644 --- a/util/Migrator/Migrator.csproj +++ b/util/Migrator/Migrator.csproj @@ -2,6 +2,7 @@ + diff --git a/util/Migrator/MigratorConstants.cs b/util/Migrator/MigratorConstants.cs new file mode 100644 index 000000000000..1ffcfcfe223d --- /dev/null +++ b/util/Migrator/MigratorConstants.cs @@ -0,0 +1,8 @@ +namespace Bit.Migrator; + +public static class MigratorConstants +{ + public const string SqlTableJournalName = "Migration"; + public const string DefaultMigrationsFolderName = "DbScripts"; + public const string TransitionMigrationsFolderName = "DbScripts_data_migration"; +} diff --git a/util/Migrator/SqlServerDbMigrator.cs b/util/Migrator/SqlServerDbMigrator.cs index 219a3ff39946..3885a6f6ca17 100644 --- a/util/Migrator/SqlServerDbMigrator.cs +++ b/util/Migrator/SqlServerDbMigrator.cs @@ -70,7 +70,7 @@ public bool MigrateDatabase(bool enableLogging = true, cancellationToken.ThrowIfCancellationRequested(); var builder = DeployChanges.To .SqlDatabase(_connectionString) - .JournalToSqlTable("dbo", "Migration") + .JournalToSqlTable("dbo", MigratorConstants.SqlTableJournalName) .WithScriptsAndCodeEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains($".DbScripts.") && !s.Contains(".Archive.")) .WithTransaction() diff --git a/util/Migrator/packages.lock.json b/util/Migrator/packages.lock.json index 407014b6973e..b6286102d753 100644 --- a/util/Migrator/packages.lock.json +++ b/util/Migrator/packages.lock.json @@ -165,10 +165,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2637,7 +2638,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", diff --git a/util/MsSqlMigratorUtility/Dockerfile b/util/MsSqlMigratorUtility/Dockerfile index 314fa664ef71..3678d429e3b2 100644 --- a/util/MsSqlMigratorUtility/Dockerfile +++ b/util/MsSqlMigratorUtility/Dockerfile @@ -5,4 +5,6 @@ LABEL com.bitwarden.product="bitwarden" WORKDIR /app COPY obj/build-output/publish . -CMD ["sh", "-c", "dotnet /app/MsSqlMigratorUtility.dll \"${MSSQL_CONN_STRING}\" -v"] +ENTRYPOINT ["sh", "-c", "dotnet /app/MsSqlMigratorUtility.dll \"${MSSQL_CONN_STRING}\""] + +CMD [ "-v" ] diff --git a/util/MsSqlMigratorUtility/MsSqlMigratorUtility.csproj b/util/MsSqlMigratorUtility/MsSqlMigratorUtility.csproj index 0e423b150d19..7f19d674590e 100644 --- a/util/MsSqlMigratorUtility/MsSqlMigratorUtility.csproj +++ b/util/MsSqlMigratorUtility/MsSqlMigratorUtility.csproj @@ -10,6 +10,7 @@ + diff --git a/util/MsSqlMigratorUtility/Program.cs b/util/MsSqlMigratorUtility/Program.cs index 517b9ecd98ce..681225ca3029 100644 --- a/util/MsSqlMigratorUtility/Program.cs +++ b/util/MsSqlMigratorUtility/Program.cs @@ -1,55 +1,51 @@ using Bit.Migrator; +using CommandDotNet; using Microsoft.Extensions.Logging; internal class Program { + private static IDictionary Parameters { get; set; } + private static int Main(string[] args) { - if (args.Length == 0) - { - Console.WriteLine("Please enter a database connection string argument."); - WriteUsageToConsole(); - return 1; - } - - if (args.Length == 1 && (args[0] == "--verbose" || args[0] == "-v")) - { - Console.WriteLine($"Please enter a database connection string argument before {args[0]} option."); - WriteUsageToConsole(); - return 1; - } - - var databaseConnectionString = args[0]; - - var verbose = false; - - if (args.Length == 2 && (args[1] == "--verbose" || args[1] == "-v")) - { - verbose = true; - } - - var success = MigrateDatabase(databaseConnectionString, verbose); - - if (!success) - { - return -1; - } - - return 0; + return new AppRunner().Run(args); } + [DefaultCommand] + public void Execute( + [Operand(Description = "Database connection string")] + string databaseConnectionString, + [Option('v', "verbose", Description = "Enable verbose output of migrator logs")] + bool verbose = false, + [Option('r', "repeatable", Description = "Mark scripts as repeatable")] + bool repeatable = false, + [Option('f', "folder", Description = "Folder name of database scripts")] + string folderName = MigratorConstants.DefaultMigrationsFolderName) => MigrateDatabase(databaseConnectionString, verbose, repeatable, folderName); + private static void WriteUsageToConsole() { Console.WriteLine("Usage: MsSqlMigratorUtility "); Console.WriteLine("Usage: MsSqlMigratorUtility -v|--verbose (for verbose output of migrator logs)"); + Console.WriteLine("Usage: MsSqlMigratorUtility -r|--repeatable (for marking scripts as repeatable) -f|--folder (for specifying folder name of scripts)"); + Console.WriteLine("Usage: MsSqlMigratorUtility -v|--verbose (for verbose output of migrator logs) -r|--repeatable (for marking scripts as repeatable) -f|--folder (for specifying folder name of scripts)"); } - private static bool MigrateDatabase(string databaseConnectionString, bool verbose = false, int attempt = 1) + private static bool MigrateDatabase(string databaseConnectionString, bool verbose = false, bool repeatable = false, string folderName = "") { var logger = CreateLogger(verbose); + logger.LogInformation($"Migrating database with repeatable: {repeatable} and folderName: {folderName}."); + var migrator = new DbMigrator(databaseConnectionString, logger); - var success = migrator.MigrateMsSqlDatabaseWithRetries(verbose); + bool success = false; + if (!string.IsNullOrWhiteSpace(folderName)) + { + success = migrator.MigrateMsSqlDatabaseWithRetries(verbose, repeatable, folderName); + } + else + { + success = migrator.MigrateMsSqlDatabaseWithRetries(verbose, repeatable); + } return success; } diff --git a/util/MsSqlMigratorUtility/packages.lock.json b/util/MsSqlMigratorUtility/packages.lock.json index ef06a62640be..7a1316c0f7fd 100644 --- a/util/MsSqlMigratorUtility/packages.lock.json +++ b/util/MsSqlMigratorUtility/packages.lock.json @@ -2,6 +2,15 @@ "version": 1, "dependencies": { "net6.0": { + "CommandDotNet": { + "type": "Direct", + "requested": "[7.0.2, )", + "resolved": "7.0.2", + "contentHash": "sFfqn4T6Ux4AbGnhS+BZvf9iVeP6b9p9bwaMT8nsefVcYH2tC9MHj3AoY+GG0nnBPmFawRqdgud08cBjBdPByQ==", + "dependencies": { + "Microsoft.CSharp": "4.7.0" + } + }, "Microsoft.Extensions.Logging": { "type": "Direct", "requested": "[6.0.0, )", @@ -168,10 +177,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2666,7 +2676,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2699,7 +2709,7 @@ "migrator": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.Extensions.Logging": "[6.0.0, )", "dbup-sqlserver": "[5.0.8, )" } diff --git a/util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.Designer.cs b/util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.Designer.cs new file mode 100644 index 000000000000..643d98835780 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.Designer.cs @@ -0,0 +1,2135 @@ +// +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("20230208212115_CipherKeyUpdate")] + partial class CipherKeyUpdate + { + 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.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("PublicKey") + .HasColumnType("longtext"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("RequestFingerprint") + .HasColumnType("longtext"); + + 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("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.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("Key") + .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.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("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.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.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("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("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.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("Bit.Infrastructure.EntityFramework.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.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("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("SelfHost") + .HasColumnType("tinyint(1)"); + + 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("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.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.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.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.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("UnknownDeviceVerificationEnabled") + .HasColumnType("tinyint(1)"); + + 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("ClientSecret") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + 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("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.Models.AuthRequest", b => + { + 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("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.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.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.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.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.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .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.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.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.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.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("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.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + 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("Connections"); + + 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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.cs b/util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.cs new file mode 100644 index 000000000000..52b6b8be5ec9 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +public partial class CipherKeyUpdate : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Key", + table: "Cipher", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Key", + table: "Cipher"); + } +} diff --git a/util/MySqlMigrations/Migrations/20230824202452_ACCESSPOLICY_ADD_DELETE_CASCADE.Designer.cs b/util/MySqlMigrations/Migrations/20230824202452_ACCESSPOLICY_ADD_DELETE_CASCADE.Designer.cs new file mode 100644 index 000000000000..8fc9edb7c430 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230824202452_ACCESSPOLICY_ADD_DELETE_CASCADE.Designer.cs @@ -0,0 +1,2236 @@ +// +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("20230824202452_ACCESSPOLICY_ADD_DELETE_CASCADE")] + partial class ACCESSPOLICY_ADD_DELETE_CASCADE + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.5") + .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"); + + b.UseTphMappingStrategy(); + }); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + 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/20230824202452_ACCESSPOLICY_ADD_DELETE_CASCADE.cs b/util/MySqlMigrations/Migrations/20230824202452_ACCESSPOLICY_ADD_DELETE_CASCADE.cs new file mode 100644 index 000000000000..711cfe735939 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230824202452_ACCESSPOLICY_ADD_DELETE_CASCADE.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +/// +public partial class ACCESSPOLICY_ADD_DELETE_CASCADE : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy"); + + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy"); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy", + column: "GroupId", + principalTable: "Group", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy", + column: "GrantedProjectId", + principalTable: "Project", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy"); + + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy"); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy", + column: "GroupId", + principalTable: "Group", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy", + column: "GrantedProjectId", + principalTable: "Project", + principalColumn: "Id"); + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index 8ed8369dea87..593df417fe6f 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -1498,6 +1498,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Folders") .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); b.Property("OrganizationId") .HasColumnType("char(36)"); @@ -2072,11 +2075,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedProjectId"); + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") .WithMany() - .HasForeignKey("GroupId"); + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); b.Navigation("GrantedProject"); @@ -2091,7 +2096,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") .WithMany() - .HasForeignKey("GroupId"); + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); b.Navigation("GrantedServiceAccount"); @@ -2102,7 +2108,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") .WithMany("ServiceAccountAccessPolicies") - .HasForeignKey("GrantedProjectId"); + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") .WithMany() @@ -2117,7 +2124,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedProjectId"); + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") .WithMany() diff --git a/util/MySqlMigrations/packages.lock.json b/util/MySqlMigrations/packages.lock.json index 97c998508dac..8a6ee7dbd015 100644 --- a/util/MySqlMigrations/packages.lock.json +++ b/util/MySqlMigrations/packages.lock.json @@ -170,10 +170,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2622,7 +2623,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2656,7 +2657,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", diff --git a/util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.Designer.cs b/util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.Designer.cs new file mode 100644 index 000000000000..87b065a0c8e6 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.Designer.cs @@ -0,0 +1,2146 @@ +// +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("20230208210621_CipherKeyUpdate")] + partial class CipherKeyUpdate + { + 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.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("PublicKey") + .HasColumnType("text"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestFingerprint") + .HasColumnType("text"); + + 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("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.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("Key") + .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.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("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.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.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("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("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.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("Bit.Infrastructure.EntityFramework.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.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("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("SelfHost") + .HasColumnType("boolean"); + + 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("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.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.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.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.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("UnknownDeviceVerificationEnabled") + .HasColumnType("boolean"); + + 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("ClientSecret") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + 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("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.Models.AuthRequest", b => + { + 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("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.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.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.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.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.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .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.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.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.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.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("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.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + 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("Connections"); + + 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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.cs b/util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.cs new file mode 100644 index 000000000000..ebc30961ff16 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +public partial class CipherKeyUpdate : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Key", + table: "Cipher", + type: "text", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Key", + table: "Cipher"); + } +} diff --git a/util/PostgresMigrations/Migrations/20230824202447_ACCESSPOLICY_ADD_DELETE_CASCADE.Designer.cs b/util/PostgresMigrations/Migrations/20230824202447_ACCESSPOLICY_ADD_DELETE_CASCADE.Designer.cs new file mode 100644 index 000000000000..504faedcbff7 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230824202447_ACCESSPOLICY_ADD_DELETE_CASCADE.Designer.cs @@ -0,0 +1,2247 @@ +// +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("20230824202447_ACCESSPOLICY_ADD_DELETE_CASCADE")] + partial class ACCESSPOLICY_ADD_DELETE_CASCADE + { + /// + 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", "7.0.5") + .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"); + + b.UseTphMappingStrategy(); + }); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + 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/20230824202447_ACCESSPOLICY_ADD_DELETE_CASCADE.cs b/util/PostgresMigrations/Migrations/20230824202447_ACCESSPOLICY_ADD_DELETE_CASCADE.cs new file mode 100644 index 000000000000..36e07cd38e49 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230824202447_ACCESSPOLICY_ADD_DELETE_CASCADE.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +/// +public partial class ACCESSPOLICY_ADD_DELETE_CASCADE : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy"); + + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy"); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy", + column: "GroupId", + principalTable: "Group", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy", + column: "GrantedProjectId", + principalTable: "Project", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy"); + + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy"); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy", + column: "GroupId", + principalTable: "Group", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy", + column: "GrantedProjectId", + principalTable: "Project", + principalColumn: "Id"); + } +} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index 4caa416f2277..f1d405872f8b 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -1509,6 +1509,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Folders") .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); b.Property("OrganizationId") .HasColumnType("uuid"); @@ -2083,11 +2086,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedProjectId"); + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") .WithMany() - .HasForeignKey("GroupId"); + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); b.Navigation("GrantedProject"); @@ -2102,7 +2107,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") .WithMany() - .HasForeignKey("GroupId"); + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); b.Navigation("GrantedServiceAccount"); @@ -2113,7 +2119,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") .WithMany("ServiceAccountAccessPolicies") - .HasForeignKey("GrantedProjectId"); + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") .WithMany() @@ -2128,7 +2135,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedProjectId"); + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") .WithMany() diff --git a/util/PostgresMigrations/packages.lock.json b/util/PostgresMigrations/packages.lock.json index 97c998508dac..8a6ee7dbd015 100644 --- a/util/PostgresMigrations/packages.lock.json +++ b/util/PostgresMigrations/packages.lock.json @@ -170,10 +170,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2622,7 +2623,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2656,7 +2657,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", diff --git a/util/Setup/Program.cs b/util/Setup/Program.cs index e6eae07494b5..93304c6bb55e 100644 --- a/util/Setup/Program.cs +++ b/util/Setup/Program.cs @@ -190,7 +190,12 @@ private static void MigrateDatabase(int attempt = 1) var vaultConnectionString = Helpers.GetValueFromEnvFile("global", "globalSettings__sqlServer__connectionString"); var migrator = new DbMigrator(vaultConnectionString, null); - migrator.MigrateMsSqlDatabaseWithRetries(false); + + var log = false; + + migrator.MigrateMsSqlDatabaseWithRetries(log); + + migrator.MigrateMsSqlDatabaseWithRetries(log, true, MigratorConstants.TransitionMigrationsFolderName); } private static bool ValidateInstallation() diff --git a/util/Setup/packages.lock.json b/util/Setup/packages.lock.json index c6a07417e64e..75193a0ccfc4 100644 --- a/util/Setup/packages.lock.json +++ b/util/Setup/packages.lock.json @@ -169,10 +169,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2643,7 +2644,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2676,7 +2677,7 @@ "migrator": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.Extensions.Logging": "[6.0.0, )", "dbup-sqlserver": "[5.0.8, )" } diff --git a/util/SqlServerEFScaffold/packages.lock.json b/util/SqlServerEFScaffold/packages.lock.json index b34b9d233899..b3c52196cc4d 100644 --- a/util/SqlServerEFScaffold/packages.lock.json +++ b/util/SqlServerEFScaffold/packages.lock.json @@ -273,10 +273,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2800,25 +2801,25 @@ "AspNetCore.HealthChecks.SqlServer": "[6.0.2, )", "AspNetCore.HealthChecks.Uris": "[6.0.3, )", "Azure.Messaging.EventGrid": "[4.10.0, )", - "Commercial.Core": "[2023.8.3, )", - "Commercial.Infrastructure.EntityFramework": "[2023.8.3, )", - "Core": "[2023.8.3, )", - "SharedWeb": "[2023.8.3, )", + "Commercial.Core": "[2023.9.0, )", + "Commercial.Infrastructure.EntityFramework": "[2023.9.0, )", + "Core": "[2023.9.0, )", + "SharedWeb": "[2023.9.0, )", "Swashbuckle.AspNetCore": "[6.5.0, )" } }, "commercial.core": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )" + "Core": "[2023.9.0, )" } }, "commercial.infrastructure.entityframework": { "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } }, "core": { @@ -2833,7 +2834,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2866,7 +2867,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Dapper": "[2.0.123, )" } }, @@ -2874,7 +2875,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )", @@ -2886,9 +2887,9 @@ "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.8.3, )", - "Infrastructure.Dapper": "[2023.8.3, )", - "Infrastructure.EntityFramework": "[2023.8.3, )" + "Core": "[2023.9.0, )", + "Infrastructure.Dapper": "[2023.9.0, )", + "Infrastructure.EntityFramework": "[2023.9.0, )" } } } diff --git a/util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.Designer.cs b/util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.Designer.cs new file mode 100644 index 000000000000..52e9a82808ea --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.Designer.cs @@ -0,0 +1,2133 @@ +// +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("20230208210629_CipherKeyUpdate")] + partial class CipherKeyUpdate + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.12"); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.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("PublicKey") + .HasColumnType("TEXT"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("RequestDeviceType") + .HasColumnType("INTEGER"); + + b.Property("RequestFingerprint") + .HasColumnType("TEXT"); + + 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("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.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("Key") + .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.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("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.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.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("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("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.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("Bit.Infrastructure.EntityFramework.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.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("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("SelfHost") + .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("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.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.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.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.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("UnknownDeviceVerificationEnabled") + .HasColumnType("INTEGER"); + + 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("ClientSecret") + .HasMaxLength(30) + .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("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.Models.AuthRequest", b => + { + 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("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.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.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.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.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.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .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.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.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.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.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("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.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + 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("Connections"); + + 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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.cs b/util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.cs new file mode 100644 index 000000000000..60afba8757b0 --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations; + +public partial class CipherKeyUpdate : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Key", + table: "Cipher", + type: "TEXT", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Key", + table: "Cipher"); + } +} diff --git a/util/SqliteMigrations/Migrations/20230824202443_ACCESSPOLICY_ADD_DELETE_CASCADE.Designer.cs b/util/SqliteMigrations/Migrations/20230824202443_ACCESSPOLICY_ADD_DELETE_CASCADE.Designer.cs new file mode 100644 index 000000000000..b4fae3362b11 --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230824202443_ACCESSPOLICY_ADD_DELETE_CASCADE.Designer.cs @@ -0,0 +1,2234 @@ +// +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("20230824202443_ACCESSPOLICY_ADD_DELETE_CASCADE")] + partial class ACCESSPOLICY_ADD_DELETE_CASCADE + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.5"); + + 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"); + + b.UseTphMappingStrategy(); + }); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + 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/20230824202443_ACCESSPOLICY_ADD_DELETE_CASCADE.cs b/util/SqliteMigrations/Migrations/20230824202443_ACCESSPOLICY_ADD_DELETE_CASCADE.cs new file mode 100644 index 000000000000..9b30c7fe2d4c --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230824202443_ACCESSPOLICY_ADD_DELETE_CASCADE.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations; + +/// +public partial class ACCESSPOLICY_ADD_DELETE_CASCADE : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy"); + + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy"); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy", + column: "GroupId", + principalTable: "Group", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy", + column: "GrantedProjectId", + principalTable: "Project", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy"); + + migrationBuilder.DropForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy"); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Group_GroupId", + table: "AccessPolicy", + column: "GroupId", + principalTable: "Group", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_AccessPolicy_Project_GrantedProjectId", + table: "AccessPolicy", + column: "GrantedProjectId", + principalTable: "Project", + principalColumn: "Id"); + } +} diff --git a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs index 36ad15b05124..7849438d028e 100644 --- a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -1496,6 +1496,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Folders") .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); b.Property("OrganizationId") .HasColumnType("TEXT"); @@ -2070,11 +2073,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") .WithMany("GroupAccessPolicies") - .HasForeignKey("GrantedProjectId"); + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") .WithMany() - .HasForeignKey("GroupId"); + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); b.Navigation("GrantedProject"); @@ -2089,7 +2094,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") .WithMany() - .HasForeignKey("GroupId"); + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); b.Navigation("GrantedServiceAccount"); @@ -2100,7 +2106,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") .WithMany("ServiceAccountAccessPolicies") - .HasForeignKey("GrantedProjectId"); + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") .WithMany() @@ -2115,7 +2122,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") .WithMany("UserAccessPolicies") - .HasForeignKey("GrantedProjectId"); + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") .WithMany() diff --git a/util/SqliteMigrations/packages.lock.json b/util/SqliteMigrations/packages.lock.json index 97c998508dac..8a6ee7dbd015 100644 --- a/util/SqliteMigrations/packages.lock.json +++ b/util/SqliteMigrations/packages.lock.json @@ -170,10 +170,11 @@ }, "Braintree": { "type": "Transitive", - "resolved": "5.12.0", - "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "resolved": "5.19.0", + "contentHash": "B60wIX54g78nMsy5cJkvSfqs1VasYDXWFZQW0cUQ4QeW8Y5jPyBSaoxHwKC806lXUDaKC8kr5Y7Q6EdsBkPANQ==", "dependencies": { - "Newtonsoft.Json": "9.0.1", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", "System.Xml.XPath.XmlDocument": "4.3.0" } }, @@ -2622,7 +2623,7 @@ "Azure.Storage.Blobs": "[12.14.1, )", "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", - "Braintree": "[5.12.0, )", + "Braintree": "[5.19.0, )", "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", @@ -2656,7 +2657,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )", - "Core": "[2023.8.3, )", + "Core": "[2023.9.0, )", "Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )", "Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )", "Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",