From a20e127c9c8ba2563949a80218b3f787f0260a4b Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Fri, 7 Oct 2022 09:53:33 -0400 Subject: [PATCH] merge branch 'master' into 'encrypted-string-perf' --- .github/workflows/build.yml | 36 +- .github/workflows/qa-deploy.yml | 2 +- .github/workflows/release.yml | 56 +- Directory.Build.props | 2 +- bitwarden-server.sln | 7 + .../Scim/Controllers/v2/UsersController.cs | 20 +- bitwarden_license/src/Scim/Program.cs | 5 +- .../src/Scim/Queries/Users/GetUserQuery.cs | 27 + .../Queries/Users/Interfaces/IGetUserQuery.cs | 8 + bitwarden_license/src/Scim/Startup.cs | 2 + .../ExceptionHandlerFilterAttribute.cs | 35 + .../ScimServiceCollectionExtensions.cs | 12 + .../src/Sso/Controllers/AccountController.cs | 4 +- .../Sso}/IdentityServer/OidcIdentityClient.cs | 6 +- bitwarden_license/src/Sso/Program.cs | 5 +- .../Utilities/ServiceCollectionExtensions.cs | 2 +- .../Queries/Users/GetUserQueryTests.cs | 66 + .../test/Scim.Test/Scim.Test.csproj | 25 + .../test/Scim.Test/packages.lock.json | 3249 +++++++++++++++++ dev/docker-compose.yml | 3 - dev/helpers/mssql/run_migrations.sh | 16 +- renovate.json | 25 + src/Admin/Controllers/LoginController.cs | 4 +- .../PasswordlessSignInManager.cs | 4 +- .../ReadOnlyEnvIdentityUserStore.cs | 7 +- .../ReadOnlyIdentityUserStore.cs | 37 +- .../ServiceCollectionExtensions.cs | 44 + src/Admin/Jobs/DeleteAuthRequestsJob.cs | 27 + src/Admin/Jobs/JobsHostedService.cs | 9 +- src/Admin/Models/OrganizationViewModel.cs | 4 +- src/Admin/Program.cs | 5 +- src/Admin/Startup.cs | 2 +- .../Organizations/_ViewInformation.cshtml | 2 +- src/Api/Controllers/AccountsController.cs | 17 +- src/Api/Controllers/AuthRequestsController.cs | 146 + src/Api/Controllers/DevicesController.cs | 24 +- .../Controllers/OrganizationsController.cs | 2 +- src/Api/Controllers/TwoFactorController.cs | 1 + .../Models/Request/AuthRequestRequestModel.cs | 32 + .../Response/AuthRequestResponseModel.cs | 43 + src/Api/Program.cs | 11 +- .../Utilities/ServiceCollectionExtensions.cs | 4 +- src/Billing/Program.cs | 10 +- src/Core/Entities/AuthRequest.cs | 41 + src/Core/Enums/AuthRequestType.cs | 7 + src/Core/Enums/PushType.cs | 3 + src/Core/Exceptions/BadRequestException.cs | 3 + src/Core/Exceptions/ConflictException.cs | 3 + src/Core/Exceptions/NotFoundException.cs | 10 +- .../ReadOnlyDatabaseIdentityUserStore.cs | 38 - .../ICaptchaProtectedResponseModel.cs | 6 + .../Accounts/RegisterResponseModel.cs | 12 + .../OrganizationUserUserDetails.cs | 8 + src/Core/Models/PushNotification.cs | 6 + ...OrganizationServiceCollectionExtensions.cs | 4 +- .../SelfHostedSyncSponsorshipsCommand.cs | 3 +- .../Repositories/IAuthRequestRepository.cs | 9 + src/Core/Services/IOrganizationService.cs | 1 + src/Core/Services/IPushNotificationService.cs | 2 + .../AzureQueuePushNotificationService.cs | 21 + .../Services/Implementations/CipherService.cs | 8 +- .../MultiServicePushNotificationService.cs | 12 + .../NotificationHubPushNotificationService.cs | 21 + ...NotificationsApiPushNotificationService.cs | 21 + .../Implementations/OrganizationService.cs | 49 +- .../RelayPushNotificationService.cs | 21 + .../NoopPushNotificationService.cs | 10 + src/Core/Settings/GlobalSettings.cs | 12 +- src/Core/Settings/IGlobalSettings.cs | 2 + src/Core/Settings/ILogLevelSettings.cs | 74 + .../Settings/IPasswordlessAuthSettings.cs | 6 + .../LoggingSettings/AdminLogLevelSettings.cs | 8 + .../LoggingSettings/ApiLogLevelSettings.cs | 10 + .../BillingLogLevelSettings.cs | 9 + .../LoggingSettings/EventsLogLevelSettings.cs | 9 + .../EventsProcessorLogLevelSettings.cs | 8 + .../LoggingSettings/IconsLogLevelSettings.cs | 8 + .../IdentityLogLevelSettings.cs | 10 + .../LoggingSettings/LogLevelSettings.cs | 16 + .../NotificationsLogLevelSettings.cs | 9 + .../LoggingSettings/ScimLogLevelSettings.cs | 8 + .../LoggingSettings/SsoLogLevelSettings.cs | 8 + src/Core/Tokens/DataProtectorTokenFactory.cs | 12 +- src/Core/Tokens/Token.cs | 26 +- src/Core/Utilities/CoreHelpers.cs | 26 - src/Core/Utilities/LoggerFactoryExtensions.cs | 15 +- src/Events/Controllers/CollectController.cs | 3 +- src/Events/Program.cs | 7 +- src/EventsProcessor/Program.cs | 3 +- src/Icons/Program.cs | 3 +- .../Controllers/AccountsController.cs | 17 +- .../IdentityServer/ApiClient.cs | 2 +- .../IdentityServer/ApiResources.cs | 2 +- .../IdentityServer/ApiScopes.cs | 2 +- .../IdentityServer/AuthorizationCodeStore.cs | 3 +- .../IdentityServer/BaseRequestValidator.cs | 4 +- .../IdentityServer/ClientStore.cs | 2 +- .../CustomTokenRequestValidator.cs | 3 +- .../CustomValidatorRequestContext.cs | 2 +- .../IdentityServer/PersistedGrantStore.cs | 2 +- .../IdentityServer/ProfileService.cs | 2 +- .../ResourceOwnerPasswordValidator.cs | 31 +- .../IdentityServer/StaticClientStore.cs | 2 +- .../IdentityServer/VaultCorsPolicyService.cs | 2 +- src/Identity/Program.cs | 12 +- .../Utilities/ServiceCollectionExtensions.cs | 4 +- .../DapperServiceCollectionExtensions.cs | 1 + .../Repositories/AuthRequestRepository.cs | 43 + ...ityFrameworkServiceCollectionExtensions.cs | 1 + .../Models/AuthRequest.cs | 17 + .../Repositories/AuthRequestRepository.cs | 35 + .../Repositories/DatabaseContext.cs | 4 + .../AnonymousNotificationsHub.cs | 19 + src/Notifications/AzureQueueHostedService.cs | 5 +- .../Controllers/SendController.cs | 2 +- src/Notifications/HubHelpers.cs | 22 +- src/Notifications/INotificationHub.cs | 7 + src/Notifications/Program.cs | 6 +- src/Notifications/Startup.cs | 7 +- .../Utilities/ServiceCollectionExtensions.cs | 43 +- src/Sql/Sql.sqlproj | 8 + .../Stored Procedures/AuthRequest_Create.sql | 57 + .../AuthRequest_DeleteById.sql | 12 + .../AuthRequest_DeleteIfExpired.sql | 6 + .../AuthRequest_ReadById.sql | 13 + .../AuthRequest_ReadByUserId.sql | 13 + .../Stored Procedures/AuthRequest_Update.sql | 40 + src/Sql/dbo/Tables/AuthRequest.sql | 23 + src/Sql/dbo/Views/AuthRequestView.sql | 6 + .../Controllers/AccountsControllerTests.cs | 5 +- .../Services/OrganizationServiceTests.cs | 2 +- test/Core.Test/Utilities/CoreHelpersTests.cs | 25 - .../Endpoints/IdentityServerTests.cs | 3 +- .../Controllers/AccountsControllerTests.cs | 5 +- .../AutoFixture/AuthRequestFixtures.cs | 61 + .../EntityFrameworkRepositoryFixtures.cs | 1 + .../AuthRequestRepositoryTests.cs | 50 + .../EqualityComparers/AuthRequestCompare.cs | 23 + .../2022-09-12_00_AuthRequestInit.sql | 218 ++ .../2022-09-12_01_AuthRequestUpdate.sql | 47 + util/MsSql/Dockerfile | 4 +- ...44222_PasswordlessAuthRequests.Designer.cs | 1672 +++++++++ ...20220912144222_PasswordlessAuthRequests.cs | 71 + .../DatabaseContextModelSnapshot.cs | 76 + .../2022-09-12_00_PasswordlessAuth.sql | 31 + ...63921_PasswordlessAuthRequests.Designer.cs | 1680 +++++++++ ...20220830163921_PasswordlessAuthRequests.cs | 905 +++++ .../DatabaseContextModelSnapshot.cs | 175 +- ...022-09-12_00_PasswordlessAuthRequests.psql | 133 + 149 files changed, 9934 insertions(+), 394 deletions(-) create mode 100644 bitwarden_license/src/Scim/Queries/Users/GetUserQuery.cs create mode 100644 bitwarden_license/src/Scim/Queries/Users/Interfaces/IGetUserQuery.cs create mode 100644 bitwarden_license/src/Scim/Utilities/ExceptionHandlerFilterAttribute.cs create mode 100644 bitwarden_license/src/Scim/Utilities/ScimServiceCollectionExtensions.cs rename {src/Core => bitwarden_license/src/Sso}/IdentityServer/OidcIdentityClient.cs (78%) create mode 100644 bitwarden_license/test/Scim.Test/Queries/Users/GetUserQueryTests.cs create mode 100644 bitwarden_license/test/Scim.Test/Scim.Test.csproj create mode 100644 bitwarden_license/test/Scim.Test/packages.lock.json create mode 100644 renovate.json rename src/{Core/Identity => Admin/IdentityServer}/PasswordlessSignInManager.cs (96%) rename src/{Core/Identity => Admin/IdentityServer}/ReadOnlyEnvIdentityUserStore.cs (89%) rename src/{Core/Identity => Admin/IdentityServer}/ReadOnlyIdentityUserStore.cs (67%) create mode 100644 src/Admin/IdentityServer/ServiceCollectionExtensions.cs create mode 100644 src/Admin/Jobs/DeleteAuthRequestsJob.cs create mode 100644 src/Api/Controllers/AuthRequestsController.cs create mode 100644 src/Api/Models/Request/AuthRequestRequestModel.cs create mode 100644 src/Api/Models/Response/AuthRequestResponseModel.cs create mode 100644 src/Core/Entities/AuthRequest.cs create mode 100644 src/Core/Enums/AuthRequestType.cs create mode 100644 src/Core/Exceptions/ConflictException.cs delete mode 100644 src/Core/Identity/ReadOnlyDatabaseIdentityUserStore.cs create mode 100644 src/Core/Models/Api/Response/Accounts/ICaptchaProtectedResponseModel.cs create mode 100644 src/Core/Models/Api/Response/Accounts/RegisterResponseModel.cs create mode 100644 src/Core/Repositories/IAuthRequestRepository.cs create mode 100644 src/Core/Settings/ILogLevelSettings.cs create mode 100644 src/Core/Settings/IPasswordlessAuthSettings.cs create mode 100644 src/Core/Settings/LoggingSettings/AdminLogLevelSettings.cs create mode 100644 src/Core/Settings/LoggingSettings/ApiLogLevelSettings.cs create mode 100644 src/Core/Settings/LoggingSettings/BillingLogLevelSettings.cs create mode 100644 src/Core/Settings/LoggingSettings/EventsLogLevelSettings.cs create mode 100644 src/Core/Settings/LoggingSettings/EventsProcessorLogLevelSettings.cs create mode 100644 src/Core/Settings/LoggingSettings/IconsLogLevelSettings.cs create mode 100644 src/Core/Settings/LoggingSettings/IdentityLogLevelSettings.cs create mode 100644 src/Core/Settings/LoggingSettings/LogLevelSettings.cs create mode 100644 src/Core/Settings/LoggingSettings/NotificationsLogLevelSettings.cs create mode 100644 src/Core/Settings/LoggingSettings/ScimLogLevelSettings.cs create mode 100644 src/Core/Settings/LoggingSettings/SsoLogLevelSettings.cs rename src/{Core => Identity}/IdentityServer/ApiClient.cs (98%) rename src/{Core => Identity}/IdentityServer/ApiResources.cs (96%) rename src/{Core => Identity}/IdentityServer/ApiScopes.cs (93%) rename src/{Core => Identity}/IdentityServer/AuthorizationCodeStore.cs (95%) rename src/{Core => Identity}/IdentityServer/BaseRequestValidator.cs (99%) rename src/{Core => Identity}/IdentityServer/ClientStore.cs (99%) rename src/{Core => Identity}/IdentityServer/CustomTokenRequestValidator.cs (98%) rename src/{Core => Identity}/IdentityServer/CustomValidatorRequestContext.cs (86%) rename src/{Core => Identity}/IdentityServer/PersistedGrantStore.cs (98%) rename src/{Core => Identity}/IdentityServer/ProfileService.cs (98%) rename src/{Core => Identity}/IdentityServer/ResourceOwnerPasswordValidator.cs (83%) rename src/{Core => Identity}/IdentityServer/StaticClientStore.cs (95%) rename src/{Core => Identity}/IdentityServer/VaultCorsPolicyService.cs (92%) create mode 100644 src/Infrastructure.Dapper/Repositories/AuthRequestRepository.cs create mode 100644 src/Infrastructure.EntityFramework/Models/AuthRequest.cs create mode 100644 src/Infrastructure.EntityFramework/Repositories/AuthRequestRepository.cs create mode 100644 src/Notifications/AnonymousNotificationsHub.cs create mode 100644 src/Notifications/INotificationHub.cs create mode 100644 src/Sql/dbo/Stored Procedures/AuthRequest_Create.sql create mode 100644 src/Sql/dbo/Stored Procedures/AuthRequest_DeleteById.sql create mode 100644 src/Sql/dbo/Stored Procedures/AuthRequest_DeleteIfExpired.sql create mode 100644 src/Sql/dbo/Stored Procedures/AuthRequest_ReadById.sql create mode 100644 src/Sql/dbo/Stored Procedures/AuthRequest_ReadByUserId.sql create mode 100644 src/Sql/dbo/Stored Procedures/AuthRequest_Update.sql create mode 100644 src/Sql/dbo/Tables/AuthRequest.sql create mode 100644 src/Sql/dbo/Views/AuthRequestView.sql create mode 100644 test/Infrastructure.EFIntegration.Test/AutoFixture/AuthRequestFixtures.cs create mode 100644 test/Infrastructure.EFIntegration.Test/Repositories/AuthRequestRepositoryTests.cs create mode 100644 test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/AuthRequestCompare.cs create mode 100644 util/Migrator/DbScripts/2022-09-12_00_AuthRequestInit.sql create mode 100644 util/Migrator/DbScripts/2022-09-12_01_AuthRequestUpdate.sql create mode 100644 util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.Designer.cs create mode 100644 util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.cs create mode 100644 util/MySqlMigrations/Scripts/2022-09-12_00_PasswordlessAuth.sql create mode 100644 util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.Designer.cs create mode 100644 util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.cs create mode 100644 util/PostgresMigrations/Scripts/2022-09-12_00_PasswordlessAuthRequests.psql diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47eef1845..a7fff50e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,10 +4,10 @@ name: Build on: push: branches-ignore: - - 'l10n_master' - - 'gh-pages' + - "l10n_master" + - "gh-pages" paths-ignore: - - '.github/workflows/**' + - ".github/workflows/**" workflow_dispatch: inputs: {} @@ -27,7 +27,6 @@ jobs: - name: Print lines of code run: cloc --include-lang C#,SQL,Razor,"Bourne Shell",PowerShell,HTML,CSS,Sass,JavaScript,TypeScript --vcs git - lint: name: Lint runs-on: ubuntu-20.04 @@ -38,7 +37,6 @@ jobs: - name: Verify Format run: dotnet format --verify-no-changes - testing: name: Testing runs-on: windows-2022 @@ -48,7 +46,7 @@ jobs: - name: Set up dotnet uses: actions/setup-dotnet@9211491ffb35dd6a6657ca4f45d43dfe6e97c829 with: - dotnet-version: '6.0.x' + dotnet-version: "6.0.x" - name: Set up MSBuild uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab @@ -83,7 +81,6 @@ jobs: run: dotnet test ./bitwarden_license/test/Commercial.Core.Test --configuration Debug --no-build shell: pwsh - build-artifacts: name: Build artifacts runs-on: ubuntu-20.04 @@ -126,11 +123,11 @@ jobs: uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0 + uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a with: - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - node-version: '16' + cache: "npm" + cache-dependency-path: "**/package-lock.json" + node-version: "16" - name: Print environment run: | @@ -176,7 +173,6 @@ jobs: path: ${{ matrix.base_path }}/${{ matrix.service_name }}/${{ matrix.service_name }}.zip if-no-files-found: error - build-docker: name: Build Docker images runs-on: ubuntu-20.04 @@ -321,13 +317,13 @@ jobs: github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc') id: retrieve-secrets - uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f + uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af with: keyvault: "bitwarden-prod-kv" secrets: "docker-password, - docker-username, - dct-delegate-2-repo-passphrase, - dct-delegate-2-key" + docker-username, + dct-delegate-2-repo-passphrase, + dct-delegate-2-key" - name: Log into Docker if: | @@ -385,7 +381,6 @@ jobs: docker logout echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV - upload: name: Upload runs-on: ubuntu-20.04 @@ -454,7 +449,7 @@ jobs: cd ../.. env: ASPNETCORE_ENVIRONMENT: Production - swaggerGen: 'True' + swaggerGen: "True" DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX: 2 - name: Upload Swagger artifact @@ -464,7 +459,6 @@ jobs: path: swagger.json if-no-files-found: error - check-failures: name: Check for failures if: always() @@ -512,14 +506,14 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f + uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af if: failure() with: keyvault: "bitwarden-prod-kv" secrets: "devops-alerts-slack-webhook-url" - name: Notify Slack on failure - uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33 # v1.2.2 + uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33 if: failure() env: SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} diff --git a/.github/workflows/qa-deploy.yml b/.github/workflows/qa-deploy.yml index 40d656e52..47e356941 100644 --- a/.github/workflows/qa-deploy.yml +++ b/.github/workflows/qa-deploy.yml @@ -101,7 +101,7 @@ jobs: description: 'Deploy from ${{ env.branch_name }} branch' - name: Download latest ${{ matrix.name }} asset from ${{ env.branch_name }} - uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8 + uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a env: branch_name: ${{ steps.setup.outputs.branch_name }} with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 028ab0284..98bb40c8f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -87,7 +87,7 @@ jobs: - name: Download latest Release ${{ matrix.name }} asset if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8 + uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a with: workflow: build.yml workflow_conclusion: success @@ -96,7 +96,7 @@ jobs: - name: Download latest Release ${{ matrix.name }} asset if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8 + uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a with: workflow: build.yml workflow_conclusion: success @@ -179,21 +179,38 @@ jobs: matrix: include: - service_name: Admin + origin_docker_repo: bitwarden - service_name: Api + origin_docker_repo: bitwarden - service_name: Attachments + origin_docker_repo: bitwarden - service_name: Events prod_acr: true + origin_docker_repo: bitwarden + - service_name: EventsProcessor + prod_acr: true + origin_docker_repo: bitwardenqa.azurecr.io - service_name: Icons + origin_docker_repo: bitwarden prod_acr: true - service_name: Identity + origin_docker_repo: bitwarden - service_name: K8S-Proxy + origin_docker_repo: bitwarden - service_name: MsSql + origin_docker_repo: bitwarden - service_name: Nginx + origin_docker_repo: bitwarden - service_name: Notifications + origin_docker_repo: bitwarden - service_name: Server + origin_docker_repo: bitwarden - service_name: Setup + origin_docker_repo: bitwarden - service_name: Sso + origin_docker_repo: bitwarden - service_name: Scim + origin_docker_repo: bitwarden skip_dct: true steps: - name: Print environment @@ -220,6 +237,7 @@ jobs: ########## DockerHub ########## - name: Setup DCT id: setup-dct + if: matrix.origin_docker_repo == 'bitwarden' uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff with: azure-creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} @@ -227,6 +245,7 @@ jobs: - name: Check for DCT value id: check-matrix-dct + if: matrix.origin_docker_repo == 'bitwarden' run: | if [[ "${{ matrix.skip_dct }}" == "true" ]]; then echo "::set-output name=dct_enabled::0" @@ -235,6 +254,7 @@ jobs: fi - name: Pull latest selfhost image + if: matrix.origin_docker_repo == 'bitwarden' env: SERVICE_NAME: ${{ steps.setup.outputs.service_name }} run: | @@ -245,6 +265,7 @@ jobs: fi - name: Tag version and latest + if: matrix.origin_docker_repo == 'bitwarden' env: SERVICE_NAME: ${{ steps.setup.outputs.service_name }} run: | @@ -255,7 +276,7 @@ jobs: fi - name: Push version and latest image - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ github.event.inputs.release_type != 'Dry Run' && matrix.origin_docker_repo == 'bitwarden' }} env: DOCKER_CONTENT_TRUST: ${{ steps.check-matrix-dct.outputs.dct_enabled }} DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }} @@ -264,6 +285,7 @@ jobs: docker push bitwarden/$SERVICE_NAME:$_RELEASE_VERSION - name: Log out of Docker and disable Docker Notary + if: matrix.origin_docker_repo == 'bitwarden' run: | docker logout echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV @@ -277,15 +299,28 @@ jobs: - name: Login to Azure ACR run: az acr login -n bitwardenqa - - name: Tag version and latest + - name: Pull latest selfhost image + if: matrix.origin_docker_repo == 'bitwardenqa.azurecr.io' env: SERVICE_NAME: ${{ steps.setup.outputs.service_name }} REGISTRY: bitwardenqa.azurecr.io run: | if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then - docker tag bitwarden/$SERVICE_NAME:latest $REGISTRY/$SERVICE_NAME:dryrun + docker pull $REGISTRY/$SERVICE_NAME:latest else - docker tag bitwarden/$SERVICE_NAME:$_BRANCH_NAME $REGISTRY/$SERVICE_NAME:$_RELEASE_VERSION + docker pull $REGISTRY/$SERVICE_NAME:$_BRANCH_NAME + fi + + - name: Tag version and latest + env: + SERVICE_NAME: ${{ steps.setup.outputs.service_name }} + REGISTRY: bitwardenqa.azurecr.io + ORIGIN_REGISTY: ${{ matrix.origin_docker_repo }} + run: | + if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then + docker tag $ORIGIN_REGISTY/$SERVICE_NAME:latest $REGISTRY/$SERVICE_NAME:dryrun + else + docker tag $ORIGIN_REGISTY/$SERVICE_NAME:$_BRANCH_NAME $REGISTRY/$SERVICE_NAME:$_RELEASE_VERSION fi - name: Push version and latest image @@ -315,11 +350,12 @@ jobs: env: SERVICE_NAME: ${{ steps.setup.outputs.service_name }} REGISTRY: bitwardenprod.azurecr.io + ORIGIN_REGISTY: ${{ matrix.origin_docker_repo }} run: | if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then - docker tag bitwarden/$SERVICE_NAME:latest $REGISTRY/$SERVICE_NAME:dryrun + docker tag $ORIGIN_REGISTY/$SERVICE_NAME:latest $REGISTRY/$SERVICE_NAME:dryrun else - docker tag bitwarden/$SERVICE_NAME:$_BRANCH_NAME $REGISTRY/$SERVICE_NAME:$_RELEASE_VERSION + docker tag $ORIGIN_REGISTY/$SERVICE_NAME:$_BRANCH_NAME $REGISTRY/$SERVICE_NAME:$_RELEASE_VERSION fi - name: Push version and latest image @@ -344,7 +380,7 @@ jobs: steps: - name: Download latest Release docker-stub if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8 + uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a with: workflow: build.yml workflow_conclusion: success @@ -355,7 +391,7 @@ jobs: - name: Download latest Release docker-stub if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8 + uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a with: workflow: build.yml workflow_conclusion: success diff --git a/Directory.Build.props b/Directory.Build.props index fe7fdfc3d..d7b420e51 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ net6.0 - 2022.9.0 + 2022.9.5 Bit.$(MSBuildProjectName) true enable diff --git a/bitwarden-server.sln b/bitwarden-server.sln index 70f035f26..a89476655 100644 --- a/bitwarden-server.sln +++ b/bitwarden-server.sln @@ -98,6 +98,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.IntegrationTest", "test EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scim.IntegrationTest", "bitwarden_license\test\Scim.IntegrationTest\Scim.IntegrationTest.csproj", "{FE998849-5FC8-41A2-B7C9-9227901471A0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scim.Test", "bitwarden_license\test\Scim.Test\Scim.Test.csproj", "{B1595DA3-4C60-41AA-8BF0-499A5F75A885}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perf", "perf", "{EC2D422A-6060-48E2-AAD2-37220D759F03}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicroBenchmarks", "perf\MicroBenchmarks\MicroBenchmarks.csproj", "{9C8F8255-5F74-4085-AB9C-9075CF6DDC61}" @@ -246,6 +248,10 @@ Global {9C8F8255-5F74-4085-AB9C-9075CF6DDC61}.Debug|Any CPU.Build.0 = Debug|Any CPU {9C8F8255-5F74-4085-AB9C-9075CF6DDC61}.Release|Any CPU.ActiveCfg = Release|Any CPU {9C8F8255-5F74-4085-AB9C-9075CF6DDC61}.Release|Any CPU.Build.0 = Release|Any CPU + {B1595DA3-4C60-41AA-8BF0-499A5F75A885}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1595DA3-4C60-41AA-8BF0-499A5F75A885}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1595DA3-4C60-41AA-8BF0-499A5F75A885}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1595DA3-4C60-41AA-8BF0-499A5F75A885}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -285,6 +291,7 @@ Global {CBE96C6D-A4D6-46E1-94C5-42D6CAD8531C} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F} {FE998849-5FC8-41A2-B7C9-9227901471A0} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B} {9C8F8255-5F74-4085-AB9C-9075CF6DDC61} = {EC2D422A-6060-48E2-AAD2-37220D759F03} + {B1595DA3-4C60-41AA-8BF0-499A5F75A885} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F} diff --git a/bitwarden_license/src/Scim/Controllers/v2/UsersController.cs b/bitwarden_license/src/Scim/Controllers/v2/UsersController.cs index 7291be7f6..c0c6ed1e7 100644 --- a/bitwarden_license/src/Scim/Controllers/v2/UsersController.cs +++ b/bitwarden_license/src/Scim/Controllers/v2/UsersController.cs @@ -5,6 +5,8 @@ using Bit.Core.Services; using Bit.Core.Utilities; using Bit.Scim.Context; using Bit.Scim.Models; +using Bit.Scim.Queries.Users.Interfaces; +using Bit.Scim.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -13,6 +15,7 @@ namespace Bit.Scim.Controllers.v2; [Authorize("Scim")] [Route("v2/{organizationId}/users")] +[ExceptionHandlerFilter] public class UsersController : Controller { private readonly IUserService _userService; @@ -21,6 +24,7 @@ public class UsersController : Controller private readonly IOrganizationService _organizationService; private readonly IScimContext _scimContext; private readonly ScimSettings _scimSettings; + private readonly IGetUserQuery _getUserQuery; private readonly ILogger _logger; public UsersController( @@ -30,6 +34,7 @@ public class UsersController : Controller IOrganizationService organizationService, IScimContext scimContext, IOptions scimSettings, + IGetUserQuery getUserQuery, ILogger logger) { _userService = userService; @@ -38,22 +43,15 @@ public class UsersController : Controller _organizationService = organizationService; _scimContext = scimContext; _scimSettings = scimSettings?.Value; + _getUserQuery = getUserQuery; _logger = logger; } [HttpGet("{id}")] public async Task Get(Guid organizationId, Guid id) { - var orgUser = await _organizationUserRepository.GetDetailsByIdAsync(id); - if (orgUser == null || orgUser.OrganizationId != organizationId) - { - return new NotFoundObjectResult(new ScimErrorResponseModel - { - Status = 404, - Detail = "User not found." - }); - } - return new ObjectResult(new ScimUserResponseModel(orgUser)); + var scimUserResponseModel = await _getUserQuery.GetUserAsync(organizationId, id); + return Ok(scimUserResponseModel); } [HttpGet("")] @@ -262,7 +260,7 @@ public class UsersController : Controller } [HttpDelete("{id}")] - public async Task Delete(Guid organizationId, Guid id, [FromBody] ScimUserRequestModel model) + public async Task Delete(Guid organizationId, Guid id) { var orgUser = await _organizationUserRepository.GetByIdAsync(id); if (orgUser == null || orgUser.OrganizationId != organizationId) diff --git a/bitwarden_license/src/Scim/Program.cs b/bitwarden_license/src/Scim/Program.cs index 48d5711e1..5d7d505aa 100644 --- a/bitwarden_license/src/Scim/Program.cs +++ b/bitwarden_license/src/Scim/Program.cs @@ -1,5 +1,4 @@ using Bit.Core.Utilities; -using Serilog.Events; namespace Bit.Scim; @@ -13,7 +12,7 @@ public class Program { webBuilder.UseStartup(); webBuilder.ConfigureLogging((hostingContext, logging) => - logging.AddSerilog(hostingContext, e => + logging.AddSerilog(hostingContext, (e, globalSettings) => { var context = e.Properties["SourceContext"].ToString(); @@ -24,7 +23,7 @@ public class Program return false; } - return e.Level >= LogEventLevel.Warning; + return e.Level >= globalSettings.MinLogLevel.ScimSettings.Default; })); }) .Build() diff --git a/bitwarden_license/src/Scim/Queries/Users/GetUserQuery.cs b/bitwarden_license/src/Scim/Queries/Users/GetUserQuery.cs new file mode 100644 index 000000000..ed434078e --- /dev/null +++ b/bitwarden_license/src/Scim/Queries/Users/GetUserQuery.cs @@ -0,0 +1,27 @@ +using Bit.Core.Exceptions; +using Bit.Core.Repositories; +using Bit.Scim.Models; +using Bit.Scim.Queries.Users.Interfaces; + +namespace Bit.Scim.Queries.Users; + +public class GetUserQuery : IGetUserQuery +{ + private readonly IOrganizationUserRepository _organizationUserRepository; + + public GetUserQuery(IOrganizationUserRepository organizationUserRepository) + { + _organizationUserRepository = organizationUserRepository; + } + + public async Task GetUserAsync(Guid organizationId, Guid id) + { + var orgUser = await _organizationUserRepository.GetDetailsByIdAsync(id); + if (orgUser == null || orgUser.OrganizationId != organizationId) + { + throw new NotFoundException("User not found."); + } + + return new ScimUserResponseModel(orgUser); + } +} diff --git a/bitwarden_license/src/Scim/Queries/Users/Interfaces/IGetUserQuery.cs b/bitwarden_license/src/Scim/Queries/Users/Interfaces/IGetUserQuery.cs new file mode 100644 index 000000000..7de37f30b --- /dev/null +++ b/bitwarden_license/src/Scim/Queries/Users/Interfaces/IGetUserQuery.cs @@ -0,0 +1,8 @@ +using Bit.Scim.Models; + +namespace Bit.Scim.Queries.Users.Interfaces; + +public interface IGetUserQuery +{ + Task GetUserAsync(Guid organizationId, Guid id); +} diff --git a/bitwarden_license/src/Scim/Startup.cs b/bitwarden_license/src/Scim/Startup.cs index 65e9220a7..bd926bf10 100644 --- a/bitwarden_license/src/Scim/Startup.cs +++ b/bitwarden_license/src/Scim/Startup.cs @@ -75,6 +75,8 @@ public class Startup config.Filters.Add(new LoggingExceptionHandlerFilterAttribute()); }); services.Configure(options => options.LowercaseUrls = true); + + services.AddScimUserQueries(); } public void Configure( diff --git a/bitwarden_license/src/Scim/Utilities/ExceptionHandlerFilterAttribute.cs b/bitwarden_license/src/Scim/Utilities/ExceptionHandlerFilterAttribute.cs new file mode 100644 index 000000000..a7e932a0e --- /dev/null +++ b/bitwarden_license/src/Scim/Utilities/ExceptionHandlerFilterAttribute.cs @@ -0,0 +1,35 @@ +using Bit.Core.Exceptions; +using Bit.Scim.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Bit.Scim.Utilities; + +public class ExceptionHandlerFilterAttribute : ExceptionFilterAttribute +{ + public override void OnException(ExceptionContext context) + { + var exception = context.Exception; + if (exception == null) + { + // Should never happen. + return; + } + + int statusCode = StatusCodes.Status500InternalServerError; + var scimErrorResponseModel = new ScimErrorResponseModel + { + Detail = exception.Message + }; + + if (exception is NotFoundException) + { + statusCode = StatusCodes.Status404NotFound; + } + + scimErrorResponseModel.Status = statusCode; + + context.HttpContext.Response.StatusCode = statusCode; + context.Result = new ObjectResult(scimErrorResponseModel); + } +} diff --git a/bitwarden_license/src/Scim/Utilities/ScimServiceCollectionExtensions.cs b/bitwarden_license/src/Scim/Utilities/ScimServiceCollectionExtensions.cs new file mode 100644 index 000000000..1356bb79f --- /dev/null +++ b/bitwarden_license/src/Scim/Utilities/ScimServiceCollectionExtensions.cs @@ -0,0 +1,12 @@ +using Bit.Scim.Queries.Users; +using Bit.Scim.Queries.Users.Interfaces; + +namespace Bit.Scim.Utilities; + +public static class ScimServiceCollectionExtensions +{ + public static void AddScimUserQueries(this IServiceCollection services) + { + services.AddScoped(); + } +} diff --git a/bitwarden_license/src/Sso/Controllers/AccountController.cs b/bitwarden_license/src/Sso/Controllers/AccountController.cs index 4ab9d7ef0..fdbcf0f0d 100644 --- a/bitwarden_license/src/Sso/Controllers/AccountController.cs +++ b/bitwarden_license/src/Sso/Controllers/AccountController.cs @@ -483,9 +483,9 @@ public class AccountController : Controller // Before any user creation - if Org User doesn't exist at this point - make sure there are enough seats to add one if (orgUser == null && organization.Seats.HasValue) { - var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(orgId); + var occupiedSeats = await _organizationService.GetOccupiedSeatCount(organization); var initialSeatCount = organization.Seats.Value; - var availableSeats = initialSeatCount - userCount; + var availableSeats = initialSeatCount - occupiedSeats; var prorationDate = DateTime.UtcNow; if (availableSeats < 1) { diff --git a/src/Core/IdentityServer/OidcIdentityClient.cs b/bitwarden_license/src/Sso/IdentityServer/OidcIdentityClient.cs similarity index 78% rename from src/Core/IdentityServer/OidcIdentityClient.cs rename to bitwarden_license/src/Sso/IdentityServer/OidcIdentityClient.cs index 822ac56cd..8629da07e 100644 --- a/src/Core/IdentityServer/OidcIdentityClient.cs +++ b/bitwarden_license/src/Sso/IdentityServer/OidcIdentityClient.cs @@ -2,7 +2,7 @@ using IdentityServer4; using IdentityServer4.Models; -namespace Bit.Core.IdentityServer; +namespace Bit.Sso.IdentityServer; public class OidcIdentityClient : Client { @@ -11,8 +11,8 @@ public class OidcIdentityClient : Client ClientId = "oidc-identity"; RequireClientSecret = true; RequirePkce = true; - ClientSecrets = new List { new Secret(globalSettings.OidcIdentityClientKey.Sha256()) }; - AllowedScopes = new string[] + ClientSecrets = new List { new(globalSettings.OidcIdentityClientKey.Sha256()) }; + AllowedScopes = new[] { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile diff --git a/bitwarden_license/src/Sso/Program.cs b/bitwarden_license/src/Sso/Program.cs index 672c73bfb..051caca9c 100644 --- a/bitwarden_license/src/Sso/Program.cs +++ b/bitwarden_license/src/Sso/Program.cs @@ -1,6 +1,5 @@ using Bit.Core.Utilities; using Serilog; -using Serilog.Events; namespace Bit.Sso; @@ -15,7 +14,7 @@ public class Program { webBuilder.UseStartup(); webBuilder.ConfigureLogging((hostingContext, logging) => - logging.AddSerilog(hostingContext, e => + logging.AddSerilog(hostingContext, (e, globalSettings) => { var context = e.Properties["SourceContext"].ToString(); if (e.Properties.ContainsKey("RequestPath") && @@ -24,7 +23,7 @@ public class Program { return false; } - return e.Level >= LogEventLevel.Error; + return e.Level >= globalSettings.MinLogLevel.SsoSettings.Default; })); }) .Build() diff --git a/bitwarden_license/src/Sso/Utilities/ServiceCollectionExtensions.cs b/bitwarden_license/src/Sso/Utilities/ServiceCollectionExtensions.cs index d7a5e3b1b..cd4aa707d 100644 --- a/bitwarden_license/src/Sso/Utilities/ServiceCollectionExtensions.cs +++ b/bitwarden_license/src/Sso/Utilities/ServiceCollectionExtensions.cs @@ -1,8 +1,8 @@ using Bit.Core.Business.Sso; -using Bit.Core.IdentityServer; using Bit.Core.Settings; using Bit.Core.Utilities; using Bit.SharedWeb.Utilities; +using Bit.Sso.IdentityServer; using Bit.Sso.Models; using IdentityServer4.Models; using IdentityServer4.ResponseHandling; diff --git a/bitwarden_license/test/Scim.Test/Queries/Users/GetUserQueryTests.cs b/bitwarden_license/test/Scim.Test/Queries/Users/GetUserQueryTests.cs new file mode 100644 index 000000000..2ff42c22d --- /dev/null +++ b/bitwarden_license/test/Scim.Test/Queries/Users/GetUserQueryTests.cs @@ -0,0 +1,66 @@ +using Bit.Core.Entities; +using Bit.Core.Exceptions; +using Bit.Core.Models.Data.Organizations.OrganizationUsers; +using Bit.Core.Repositories; +using Bit.Scim.Queries.Users; +using Bit.Scim.Utilities; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using Bit.Test.Common.Helpers; +using NSubstitute; +using Xunit; + +namespace Bit.Scim.Test.Queries.Users; + +[SutProviderCustomize] +public class GetUserQueryTests +{ + [Theory] + [BitAutoData] + public async Task GetUser_Success(SutProvider sutProvider, OrganizationUserUserDetails organizationUserUserDetails) + { + var expectedResult = new Models.ScimUserResponseModel + { + Id = organizationUserUserDetails.Id.ToString(), + UserName = organizationUserUserDetails.Email, + Name = new Models.BaseScimUserModel.NameModel(organizationUserUserDetails.Name), + Emails = new List { new Models.BaseScimUserModel.EmailModel(organizationUserUserDetails.Email) }, + DisplayName = organizationUserUserDetails.Name, + Active = organizationUserUserDetails.Status != Core.Enums.OrganizationUserStatusType.Revoked ? true : false, + Groups = new List(), + ExternalId = organizationUserUserDetails.ExternalId, + Schemas = new List { ScimConstants.Scim2SchemaUser } + }; + + sutProvider.GetDependency() + .GetDetailsByIdAsync(organizationUserUserDetails.Id) + .Returns(organizationUserUserDetails); + + var result = await sutProvider.Sut.GetUserAsync(organizationUserUserDetails.OrganizationId, organizationUserUserDetails.Id); + + await sutProvider.GetDependency().Received(1).GetDetailsByIdAsync(organizationUserUserDetails.Id); + AssertHelper.AssertPropertyEqual(expectedResult, result); + } + + [Theory] + [BitAutoData] + public async Task GetUser_NotFound_Throws(SutProvider sutProvider, Guid organizationId, Guid organizationUserId) + { + await Assert.ThrowsAsync(async () => await sutProvider.Sut.GetUserAsync(organizationId, organizationUserId)); + } + + [Theory] + [BitAutoData] + public async Task GetUser_MismatchingOrganizationId_Throws(SutProvider sutProvider, Guid organizationId, Guid organizationUserId) + { + sutProvider.GetDependency() + .GetByIdAsync(organizationUserId) + .Returns(new OrganizationUser + { + Id = organizationUserId, + OrganizationId = Guid.NewGuid() + }); + + await Assert.ThrowsAsync(async () => await sutProvider.Sut.GetUserAsync(organizationId, organizationUserId)); + } +} diff --git a/bitwarden_license/test/Scim.Test/Scim.Test.csproj b/bitwarden_license/test/Scim.Test/Scim.Test.csproj new file mode 100644 index 000000000..35e4df83a --- /dev/null +++ b/bitwarden_license/test/Scim.Test/Scim.Test.csproj @@ -0,0 +1,25 @@ + + + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + \ No newline at end of file diff --git a/bitwarden_license/test/Scim.Test/packages.lock.json b/bitwarden_license/test/Scim.Test/packages.lock.json new file mode 100644 index 000000000..10a511d86 --- /dev/null +++ b/bitwarden_license/test/Scim.Test/packages.lock.json @@ -0,0 +1,3249 @@ +{ + "version": 1, + "dependencies": { + "net6.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[3.1.2, )", + "resolved": "3.1.2", + "contentHash": "wuLDIDKD5XMt0A7lE31JPenT7QQwZPFkP5rRpdJeblyXZ9MGLI8rYjvm5fvAKln+2/X+4IxxQDxBtwdrqKNLZw==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.1.0, )", + "resolved": "17.1.0", + "contentHash": "MVKvOsHIfrZrvg+8aqOF5dknO/qWrR1sWZjMPQ1N42MKMlL/zQL30FQFZxPeWfmVKWUWAOmAHYsqB5OerTKziw==", + "dependencies": { + "Microsoft.CodeCoverage": "17.1.0", + "Microsoft.TestPlatform.TestHost": "17.1.0" + } + }, + "NSubstitute": { + "type": "Direct", + "requested": "[4.3.0, )", + "resolved": "4.3.0", + "contentHash": "c0nY4GGSe5KidQemTk+CTuDLdv7hLvHHftH6vRbKoYb6bw07wzJ6DdgA0NWrwbW3xjmp/ByEskCsUEWAaMC20g==", + "dependencies": { + "Castle.Core": "4.4.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.4.1, )", + "resolved": "2.4.1", + "contentHash": "XNR3Yz9QTtec16O0aKcO6+baVNpXmOnPUxDkCY97J+8krUYxPvXT1szYYEUdKk4sB8GOI2YbAjRIOm8ZnXRfzQ==", + "dependencies": { + "xunit.analyzers": "0.10.0", + "xunit.assert": "[2.4.1]", + "xunit.core": "[2.4.1]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.4.3, )", + "resolved": "2.4.3", + "contentHash": "kZZSmOmKA8OBlAJaquPXnJJLM9RwQ27H7BMVqfMLUcTi9xHinWGJiWksa3D4NEtz0wZ/nxd2mogObvBgJKCRhQ==" + }, + "AspNetCoreRateLimit": { + "type": "Transitive", + "resolved": "4.0.2", + "contentHash": "FzXAJFgaRjKfnKAVwjEEC7OAGQM5v/I3sQw2tpzmR0yHTCGhUAxZzDuwZiXTk8XLrI6vovzkqKkfKmiDl3nYMg==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.1", + "Microsoft.Extensions.Options": "6.0.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "AspNetCoreRateLimit.Redis": { + "type": "Transitive", + "resolved": "1.0.1", + "contentHash": "CsSGy/7SXt6iBOKg0xCvsRjb/ZHshbtr2Of1MHc912L2sLnZqadUrTboyXZC+ZlgEBeJ14GyjPTu8ZyfEhGUnw==", + "dependencies": { + "AspNetCoreRateLimit": "4.0.2", + "StackExchange.Redis": "2.5.43" + } + }, + "AutoFixture": { + "type": "Transitive", + "resolved": "4.17.0", + "contentHash": "efMRCG3Epc4QDELwdmQGf6/caQUleRXPRCnLAq5gLMpTuOTcOQWV12vEJ8qo678Rj97/TjjxHYu/34rGkXdVAA==", + "dependencies": { + "Fare": "[2.1.1, 3.0.0)", + "System.ComponentModel.Annotations": "4.3.0" + } + }, + "AutoFixture.AutoNSubstitute": { + "type": "Transitive", + "resolved": "4.17.0", + "contentHash": "iWsRiDQ7T8s6F4mvYbSvPTq0GDtxJD6D+E1Fu9gVbHUvJiNikC1yIDNTH+3tQF7RK864HH/3R8ETj9m2X8UXvg==", + "dependencies": { + "AutoFixture": "4.17.0", + "NSubstitute": "[2.0.3, 5.0.0)" + } + }, + "AutoFixture.Xunit2": { + "type": "Transitive", + "resolved": "4.17.0", + "contentHash": "lrURL/LhJLPkn2tSPUEW8Wscr5LoV2Mr8A+ikn5gwkofex3o7qWUsBswlLw+KCA7EOpeqwZOldp3k91zDF+48Q==", + "dependencies": { + "AutoFixture": "4.17.0", + "xunit.extensibility.core": "[2.2.0, 3.0.0)" + } + }, + "AutoMapper": { + "type": "Transitive", + "resolved": "11.0.0", + "contentHash": "+596AnKykYCk9RxXCEF4GYuapSebQtFVvIA1oVG1rrRkCLAC7AkWehJ0brCfYUbdDW3v1H/p0W3hob7JoXGjMw==", + "dependencies": { + "Microsoft.CSharp": "4.7.0" + } + }, + "AutoMapper.Extensions.Microsoft.DependencyInjection": { + "type": "Transitive", + "resolved": "11.0.0", + "contentHash": "0asw5WxdCFh2OTi9Gv+oKyH9SzxwYQSnO8TV5Dd0GggovILzJW4UimP26JAcxc3yB5NnC5urooZ1BBs8ElpiBw==", + "dependencies": { + "AutoMapper": "11.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.10.11", + "contentHash": "B+M7ggPC0FogATRPQxDXL0eTusCQtXulW4zCuX39yiHV8+u9MEXRytcAw0ZA3zFBYYx6ovl9lklho6OQo1DRRQ==" + }, + "AWSSDK.SimpleEmail": { + "type": "Transitive", + "resolved": "3.7.0.150", + "contentHash": "rc/4ZnISfbgTfqz5/BWqMHBAzk4R09qfe1xkdJf2jXo44Zn2X72W8IiLLweBtmNhL7d8Tcf6UCtOHYkFwxHvug==", + "dependencies": { + "AWSSDK.Core": "[3.7.10.11, 4.0.0)" + } + }, + "AWSSDK.SQS": { + "type": "Transitive", + "resolved": "3.7.2.47", + "contentHash": "RPTVBsY333n+aIEqw148Envx9OQkE1/jhjlioNXDP6BrA3fAPN9A+2HoA02c0KSp/sazXYWg8w/kDL8FchH8Dw==", + "dependencies": { + "AWSSDK.Core": "[3.7.10.11, 4.0.0)" + } + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.22.0", + "contentHash": "ze/xRCHSSDe5TIk5vBDbVrauW1EN7UIbnBvIBfMH8KSt/I9+/7yPAjTBDgNBk0IwG6WBV+BBHp4IUtS/PGAQwQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.1", + "System.Diagnostics.DiagnosticSource": "4.6.0", + "System.Memory.Data": "1.0.2", + "System.Numerics.Vectors": "4.5.0", + "System.Text.Encodings.Web": "4.7.2", + "System.Text.Json": "4.7.2", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Azure.Extensions.AspNetCore.DataProtection.Blobs": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "wxvkC6DeWThBtaPbsWdicp5Ltya4J8JuhxmZJDQkhnXG7oihfu8RqBV6w/X1nMieuIOq1qQaGTvjx7nEHHfxSQ==", + "dependencies": { + "Azure.Core": "1.14.0", + "Azure.Storage.Blobs": "12.8.0", + "Microsoft.AspNetCore.DataProtection": "2.1.0" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "l1SYfZKOFBuUFG7C2SWHmJcrQQaiXgBdVCycx4vcZQkC6efDVt7mzZ5pfJAFEJDBUq7mjRQ0RPq9ZDGdSswqMg==", + "dependencies": { + "Azure.Core": "1.6.0", + "Microsoft.Identity.Client": "4.22.0", + "Microsoft.Identity.Client.Extensions.Msal": "2.16.5", + "System.Memory": "4.5.3", + "System.Security.Cryptography.ProtectedData": "4.5.0", + "System.Text.Json": "4.6.0", + "System.Threading.Tasks.Extensions": "4.5.2" + } + }, + "Azure.Storage.Blobs": { + "type": "Transitive", + "resolved": "12.11.0", + "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "dependencies": { + "Azure.Storage.Common": "12.10.0", + "System.Text.Json": "4.7.2" + } + }, + "Azure.Storage.Common": { + "type": "Transitive", + "resolved": "12.10.0", + "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "dependencies": { + "Azure.Core": "1.22.0", + "System.IO.Hashing": "6.0.0" + } + }, + "Azure.Storage.Queues": { + "type": "Transitive", + "resolved": "12.9.0", + "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "dependencies": { + "Azure.Storage.Common": "12.10.0", + "System.Memory.Data": "1.0.2", + "System.Text.Json": "4.7.2" + } + }, + "BitPay.Light": { + "type": "Transitive", + "resolved": "1.0.1907", + "contentHash": "QTTIgXakHrRNQPxNyH7bZ7frm0bI8N6gRDtiqVyKG/QYQ+KfjN70xt0zQ0kO0zf8UBaKuwcV5B7vvpXtzR9ijg==", + "dependencies": { + "Newtonsoft.Json": "12.0.2" + } + }, + "Braintree": { + "type": "Transitive", + "resolved": "5.12.0", + "contentHash": "bV2tsVIvBQeKwULT4qPZUWhxSr8mFwyAAcvLDvDpCU0cMYPHzGSahha+ghUdgGMb317BqL34/Od59n2s3MkhOQ==", + "dependencies": { + "Newtonsoft.Json": "9.0.1", + "System.Xml.XPath.XmlDocument": "4.3.0" + } + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "4.4.1", + "contentHash": "zanbjWC0Y05gbx4eGXkzVycOQqVOFVeCjVsDSyuao9P4mtN1w3WxxTo193NGC7j3o2u3AJRswaoC6hEbnGACnQ==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "System.Collections.Specialized": "4.3.0", + "System.ComponentModel": "4.3.0", + "System.ComponentModel.TypeConverter": "4.3.0", + "System.Diagnostics.TraceSource": "4.3.0", + "System.Dynamic.Runtime": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Xml.XmlDocument": "4.3.0" + } + }, + "Dapper": { + "type": "Transitive", + "resolved": "2.0.123", + "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" + }, + "Fare": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "HaI8puqA66YU7/9cK4Sgbs1taUTP1Ssa4QT2PIzqJ7GvAbN1QgkjbRsjH+FSbMh1MJdvS0CIwQNLtFT+KF6KpA==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "Fido2": { + "type": "Transitive", + "resolved": "3.0.0-beta2", + "contentHash": "FnNMbK88dyPp0Ww/iMim5g89rSPdqkjQiDiTJJtvxDcEk8JK/eBdTzAl4myNaKS9e8PKrxxddOTrnNja3PHGtQ==", + "dependencies": { + "Fido2.Models": "3.0.0-beta2", + "NSec.Cryptography": "20.2.0", + "System.Formats.Cbor": "5.0.0", + "System.IdentityModel.Tokens.Jwt": "6.6.0" + } + }, + "Fido2.AspNet": { + "type": "Transitive", + "resolved": "3.0.0-beta2", + "contentHash": "qkowZS0WPS26gDG97rwjZObOa/xtFVjSpvWHl3OwWRQ9ZU5xNePXKk2XJWmO2MCQc40idxyEOfA34MMexCHc3w==", + "dependencies": { + "Fido2": "3.0.0-beta2", + "Fido2.Models": "3.0.0-beta2" + } + }, + "Fido2.Models": { + "type": "Transitive", + "resolved": "3.0.0-beta2", + "contentHash": "6ePSMUtqz6lAfDUjDvOONMLugcKiAyz8hzoLSAISk3iDIjBMLMPlZSV3TVZqiY+5SAzC8x61OHNoCODqorucNw==" + }, + "Handlebars.Net": { + "type": "Transitive", + "resolved": "2.1.2", + "contentHash": "p60QyeBYpZmcZdIXRMqs9XySIBaxJ0lj3+QD0EJVr4ybTigOTCumXMMin5dPwjo9At1UwkDZ3gGwa1lmGjG6DA==", + "dependencies": { + "Microsoft.CSharp": "4.7.0" + } + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "rsYXB7+iUPP8AHgQ8JP2UZI2xK2KhjcdGr9E6zX3CsZaTLCaw8M35vaAJRo1rfxeaZEVMuXeaquLVCkZ7JcZ5Q==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "IdentityModel": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==", + "dependencies": { + "Newtonsoft.Json": "11.0.2", + "System.Text.Encodings.Web": "4.7.0" + } + }, + "IdentityModel.AspNetCore.OAuth2Introspection": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==", + "dependencies": { + "IdentityModel": "4.0.0" + } + }, + "IdentityServer4": { + "type": "Transitive", + "resolved": "4.1.2", + "contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==", + "dependencies": { + "IdentityModel": "4.4.0", + "IdentityServer4.Storage": "4.1.2", + "Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0", + "Newtonsoft.Json": "12.0.2" + } + }, + "IdentityServer4.AccessTokenValidation": { + "type": "Transitive", + "resolved": "3.0.1", + "contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==", + "dependencies": { + "IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1", + "Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0" + } + }, + "IdentityServer4.Storage": { + "type": "Transitive", + "resolved": "4.1.2", + "contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==", + "dependencies": { + "IdentityModel": "4.4.0" + } + }, + "Kralizek.AutoFixture.Extensions.MockHttp": { + "type": "Transitive", + "resolved": "1.2.0", + "contentHash": "6zmks7/5mVczazv910N7V2EdiU6B+rY61lwdgVO0o2iZuTI6KI3T+Hgkrjv0eGOKYucq2OMC+gnAc5Ej2ajoTQ==", + "dependencies": { + "AutoFixture": "4.11.0", + "RichardSzalay.MockHttp": "6.0.0" + } + }, + "libsodium": { + "type": "Transitive", + "resolved": "1.0.18", + "contentHash": "Ajv3AR9Qg/C4SQcE2ONx/UieeKnn5lSvVNc6egC3p6NP6qjZzWJ+Xg2vJURNYjkpHui/KctBwQjMPqpZK8/CHA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1" + } + }, + "linq2db": { + "type": "Transitive", + "resolved": "3.7.0", + "contentHash": "iDous2TbSchtALnTLNXQnprmNZF4GrXas0MBz6ZHWkSdilSJjcf26qFM7Qf98Mny0OXHEmNXG/jtIDhoVJ5KmQ==", + "dependencies": { + "System.ComponentModel.Annotations": "4.7.0" + } + }, + "linq2db.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "6.7.1", + "contentHash": "Bb25vUDyFw3nKnf7KY+bauwKGD0hdM7/syodS+IgHdWlcbH9g7tHxYmMa9+DNuL0yy6DFvP6Q3BkClm7zbQdAw==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Relational": "6.0.0", + "linq2db": "3.7.0" + } + }, + "MailKit": { + "type": "Transitive", + "resolved": "3.2.0", + "contentHash": "5MTpTqmjqT7HPvYbP3HozRZMth5vSaT0ReN0iM3rAM4CgLI/R1qqtLDDNWGnFFIlcNzeJkZQRJJMkv8cgzWBbA==", + "dependencies": { + "MimeKit": "3.2.0" + } + }, + "Microsoft.AspNetCore.Authentication.JwtBearer": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "joDS3+lD1i9qcdFLWP4D316t3bHpezmTNOzbMIf9ZcRPX4QTuiUutZcQn/kZplf3BiLHqwUChZXxPjCAMKaKAQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0" + } + }, + "Microsoft.AspNetCore.Authentication.OpenIdConnect": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==", + "dependencies": { + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0" + } + }, + "Microsoft.AspNetCore.Cryptography.Internal": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "/0FX1OqckMmXAAlsHgBFNymTZuq4nuAOMhiwm6e8CEMi2aOjnMYwiMc7mtvpGTAO0O4C0zwx+iaChxDgvqit2A==" + }, + "Microsoft.AspNetCore.Cryptography.KeyDerivation": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "1Lbwrxg/HRY/nbrkcrB3EUXUYQN8Tkw7Ktgb6/2on2P7ybT5aM59H05gk+OBC8ZTBxwdle9e1tyT3wxEYKw5xw==", + "dependencies": { + "Microsoft.AspNetCore.Cryptography.Internal": "6.0.4" + } + }, + "Microsoft.AspNetCore.DataProtection": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "G+UoMHL0xiyFh30wkL7Bv/XL6eugTAKYhLPS53k1/Me1bYRwOOw+8VL/q0ppq3/yMzpHX+MkExaCTDlYl48FgA==", + "dependencies": { + "Microsoft.AspNetCore.Cryptography.Internal": "2.1.0", + "Microsoft.AspNetCore.DataProtection.Abstractions": "2.1.0", + "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0", + "Microsoft.Extensions.Logging.Abstractions": "2.1.0", + "Microsoft.Extensions.Options": "2.1.0", + "Microsoft.Win32.Registry": "4.5.0", + "System.Security.Cryptography.Xml": "4.5.0", + "System.Security.Principal.Windows": "4.5.0" + } + }, + "Microsoft.AspNetCore.DataProtection.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "2+HVDhUqrnV9+EJNEewSy+Gk4hOVPzLPMpFDZI7kuH7NWxtbNkI6A6gT5lO2/kEPMyM8/iLWtohbOwjpC9rHVw==" + }, + "Microsoft.AspNetCore.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "1TQgBfd/NPZLR2o/h6l5Cml2ZCF5hsyV4h9WEwWwAIavrbdTnaNozGGcTOd4AOgQvogMM9UM1ajflm9Cwd0jLQ==", + "dependencies": { + "Microsoft.AspNetCore.Hosting.Server.Abstractions": "2.1.0", + "Microsoft.AspNetCore.Http.Abstractions": "2.1.0", + "Microsoft.Extensions.Hosting.Abstractions": "2.1.0" + } + }, + "Microsoft.AspNetCore.Hosting.Server.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "YTKMi2vHX6P+WHEVpW/DS+eFHnwivCSMklkyamcK1ETtc/4j8H3VR0kgW8XIBqukNxhD8k5wYt22P7PhrWSXjQ==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.1.0", + "Microsoft.Extensions.Configuration.Abstractions": "2.1.0" + } + }, + "Microsoft.AspNetCore.Html.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "Y4rs5aMEXY8G7wJo5S3EEt6ltqyOTr/qOeZzfn+hw/fuQj5GppGckMY5psGLETo1U9hcT5MmAhaT5xtusM1b5g==", + "dependencies": { + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "vbFDyKsSYBnxl3+RABtN79b0vsTcG66fDY8vD6Nqvu9uLtSej70Q5NcbGlnN6bJpZci5orSdgFTHMhBywivDPg==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.1.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "UmkUePxRjsQW0j5euFFscBwjvTu25b8+qIK/2fI3GvcqQ+mkwgbWNAT8b/Gkoei1m2bTWC07lSdutuRDPPLcJA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.0" + } + }, + "Microsoft.AspNetCore.Razor": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "V54PIyDCFl8COnTp9gezNHpUNHk7F9UnerGeZy3UfbnwYvfzbo+ipqQmSgeoESH8e0JvKhRTyQyZquW2EPtCmg==", + "dependencies": { + "Microsoft.AspNetCore.Html.Abstractions": "2.2.0" + } + }, + "Microsoft.AspNetCore.Razor.Language": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "6yOBBASGfXMx1fY6hyjvG+oM3eR8vovIehDdEZW7jAV4gKlY4xuAvTm7Iw1fEq7KPunh2VrJwo7oRK1XxUn1OQ==" + }, + "Microsoft.AspNetCore.Razor.Runtime": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "7YqK+H61lN6yj9RiQUko7oaOhKtRR9Q/kBcoWNRemhJdTIWOh1OmdvJKzZrMWOlff3BAjejkPQm+0V0qXk+B1w==", + "dependencies": { + "Microsoft.AspNetCore.Html.Abstractions": "2.2.0", + "Microsoft.AspNetCore.Razor": "2.2.0" + } + }, + "Microsoft.Azure.Amqp": { + "type": "Transitive", + "resolved": "2.4.11", + "contentHash": "7x5fu2f6TLQDDJS0sY5qW8/daFwJaY9O75YvU8RcUfRzbug+9YGjXUBxoRrprgyi0jxdBAMQL05p1s783SOSFQ==", + "dependencies": { + "System.Net.WebSockets.Client": "4.0.2", + "System.Runtime.Serialization.Primitives": "4.1.1" + } + }, + "Microsoft.Azure.Cosmos": { + "type": "Transitive", + "resolved": "3.24.0", + "contentHash": "QpUe5ho6OzlXwgcJVgAmOR7t3XLC9RI4t8T96RZY61pSOIllPOJdp30L0LwA16tKcqi5r2KayEgWO/MS9fh/6A==", + "dependencies": { + "Azure.Core": "1.3.0", + "Microsoft.Bcl.AsyncInterfaces": "1.0.0", + "Microsoft.Bcl.HashCode": "1.1.0", + "Newtonsoft.Json": "10.0.2", + "System.Buffers": "4.5.1", + "System.Collections.Immutable": "1.7.0", + "System.Configuration.ConfigurationManager": "4.7.0", + "System.Memory": "4.5.4", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.0" + } + }, + "Microsoft.Azure.Cosmos.Table": { + "type": "Transitive", + "resolved": "1.0.8", + "contentHash": "ToeEd1yijM7nQfLYvdFLG//RjKPmfqm45eOm86UAKrxtyGI/CXqP8iL74mzBp6mZ9A/K/ZYA2fVdpH0xHR5Keg==", + "dependencies": { + "Microsoft.Azure.DocumentDB.Core": "2.11.2", + "Microsoft.OData.Core": "7.6.4", + "Newtonsoft.Json": "10.0.2" + } + }, + "Microsoft.Azure.DocumentDB.Core": { + "type": "Transitive", + "resolved": "2.11.2", + "contentHash": "cA8eWrTFbYrkHrz095x4CUGb7wqQgA1slzFZCYexhNwz6Zcn3v+S1yvWMGwGRmRjT0MKU9tYdFWgLfT0OjSycw==", + "dependencies": { + "NETStandard.Library": "1.6.0", + "Newtonsoft.Json": "9.0.1", + "System.Collections.Immutable": "1.3.0", + "System.Collections.NonGeneric": "4.0.1", + "System.Collections.Specialized": "4.0.1", + "System.Diagnostics.TraceSource": "4.0.0", + "System.Dynamic.Runtime": "4.0.11", + "System.Linq.Queryable": "4.0.1", + "System.Net.Http": "4.3.4", + "System.Net.NameResolution": "4.0.0", + "System.Net.NetworkInformation": "4.1.0", + "System.Net.Requests": "4.0.11", + "System.Net.Security": "4.3.2", + "System.Net.WebHeaderCollection": "4.0.1", + "System.Runtime.Serialization.Primitives": "4.1.1", + "System.Security.SecureString": "4.0.0" + } + }, + "Microsoft.Azure.NotificationHubs": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "C2SssjX3e6/HIo1OCImQDDVOn64d1+gkgEmgxJryzkwixyivJHWH2YIgxZs33pyzVQcZWx5PR2tqLkQ7riSq8Q==", + "dependencies": { + "Microsoft.Extensions.Caching.Memory": "3.1.8", + "Newtonsoft.Json": "12.0.3" + } + }, + "Microsoft.Azure.ServiceBus": { + "type": "Transitive", + "resolved": "5.2.0", + "contentHash": "wyZNJggyFNtKxd+HgvcTiuRYuTjDGi+pgE4RcBvFbfvNiarKr5AOlE4Ne7on1eUJZuMuEa19wN5dj694HlP60A==", + "dependencies": { + "Microsoft.Azure.Amqp": "2.4.11", + "Microsoft.Azure.Services.AppAuthentication": "[1.0.3, 2.0.0)", + "Newtonsoft.Json": "10.0.3", + "System.Diagnostics.DiagnosticSource": "4.5.1", + "System.IdentityModel.Tokens.Jwt": "5.4.0" + } + }, + "Microsoft.Azure.Services.AppAuthentication": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "ywpQaK1klu1IoX4VUf+TBmU4kR71aWNI6O5rEIJU8z28L2xhJhnIm7k2Nf1Zu/PygeuOtt5g0QPCk5+lLltbeQ==", + "dependencies": { + "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.14.2", + "NETStandard.Library": "1.6.1", + "System.Diagnostics.Process": "4.3.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + }, + "Microsoft.Bcl.HashCode": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "J2G1k+u5unBV+aYcwxo94ip16Rkp65pgWFb0R6zwJipzWNMgvqlWeuI7/+R+e8bob66LnSG+llLJ+z8wI94cHg==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "ojG5pGAhTPmjxRGTNvuszO3H8XPZqksDwr9xLd4Ae/JBjZZdl6GuoLk7uLMf+o7yl5wO0TAqoWcEKkEWqrZE5g==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "3.8.0", + "contentHash": "8YTZ7GpsbTdC08DITx7/kwV0k4SC6cbBAFqc13cOm5vKJZcEIAh51tNSyGSkWisMgYCr96B2wb5Zri1bsla3+g==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.0.0", + "System.Collections.Immutable": "5.0.0", + "System.Memory": "4.5.4", + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.CompilerServices.Unsafe": "4.7.1", + "System.Text.Encoding.CodePages": "4.5.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "3.8.0", + "contentHash": "hKqFCUSk9TIMBDjiYMF8/ZfK9p9mzpU+slM73CaCHu4ctfkoqJGHLQhyT8wvrYsIg+ufrUWBF8hcJYmyr5rc5Q==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[3.8.0]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Workspaces": { + "type": "Transitive", + "resolved": "3.8.0", + "contentHash": "rdEBvPWqe/IIscsnp7OkZ4tQin8khxBcSLyV9tU+sHdw9uW9U0GKL+Dv2rD4voC1bZBaO18Hp+m4Vkyfmaz0OA==", + "dependencies": { + "Humanizer.Core": "2.2.0", + "Microsoft.CodeAnalysis.CSharp": "[3.8.0]", + "Microsoft.CodeAnalysis.Common": "[3.8.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[3.8.0]" + } + }, + "Microsoft.CodeAnalysis.Razor": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "s4u/6z/MQ35y/egrXf4WgJlUZf5GGvuba9mZ700dH4XxLBrA9Fw9kFZ8uymoATry7hwz5owvFhBVo+2VnoiGRg==", + "dependencies": { + "Microsoft.AspNetCore.Razor.Language": "5.0.0", + "Microsoft.CodeAnalysis.CSharp": "3.7.0", + "Microsoft.CodeAnalysis.Common": "3.7.0" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "3.8.0", + "contentHash": "GPYVydsmOmScOWDJA1LFky7/MkoXpx1JI3lZJShxC+bvVUvL9zVKE8WDZMLsYJ5MAbry2xkZftdfeMpZ+kvLDQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.1", + "Microsoft.CodeAnalysis.Common": "[3.8.0]", + "System.Composition": "1.0.31" + } + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.1.0", + "contentHash": "0N/ZJ71ncCxQWhgtkEYKOgu2oMHa8h1tsOUbhmIKXF8UwtSUCe4vHAsJ3DVcNWRwNfQzSTy263ZE+QF6MdIhhQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.SqlClient": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "o/sIRlcKEcI9vg5z9USqJ/VCxtUUBYEOXYr4TrkMNu+gGBh0KfUi06Jqpe+xZgeoxcqYruV9dLOn046uFA4vHQ==", + "dependencies": { + "Azure.Identity": "1.3.0", + "Microsoft.Data.SqlClient.SNI.runtime": "4.0.0", + "Microsoft.Identity.Client": "4.22.0", + "Microsoft.IdentityModel.JsonWebTokens": "6.8.0", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.8.0", + "Microsoft.Win32.Registry": "5.0.0", + "System.Buffers": "4.5.1", + "System.Configuration.ConfigurationManager": "5.0.0", + "System.Diagnostics.DiagnosticSource": "5.0.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime.Caching": "5.0.0", + "System.Security.Cryptography.Cng": "5.0.0", + "System.Security.Principal.Windows": "5.0.0", + "System.Text.Encoding.CodePages": "5.0.0", + "System.Text.Encodings.Web": "4.7.2" + } + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "4.0.0", + "contentHash": "wtLlRwQX7YoBUYm25xBjJ3UsuLgycme1xXqDn8t3S5kPCWiZrx8uOkyZHLKzH4kkCiQ9m2/J5JeCKNRbZNn3Qg==" + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "gTh3SJsF5WNjEmG32kYc3U4tjeTIv55QOrwHAJcF/xtrIVMteDHMArGC35N0dw86WFY0v8yFkKYKOIOln4jkfQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "6.0.4", + "Microsoft.EntityFrameworkCore.Analyzers": "6.0.4", + "Microsoft.Extensions.Caching.Memory": "6.0.1", + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "System.Collections.Immutable": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "jycTQF0FUJp10cGWBmtsyFhQNeISU9CltDRKCaNiX4QRSEFzgRgaFN4vAFK0T+G5etmXugyddijE4NWCGtgznQ==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "t12WodVyGGP2CuLo7R1qwcawHY5zlg+GiQzvkceZpsjcFJVyTFFBFDPg1isBtzurLzWsl+G3z5fVXeic90mPxg==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "E867NbEXYRTElBF5ff+1AN5Awa1jkORy/Rrm0ueibaTAV5uw89LsLoH6yTe+b9urZTWMHtLfGd1RDdNjk8+KzA==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "6.0.4", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "bcz5sSFJbganH0+YrfvIjJDIcKNW7TL07C4d1eTmXy/wOt52iz4LVogJb6pazs7W0+74j0YpXFErvp++Aq5Bsw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "B4y+Cev05eMcjf1na0v9gza6GUtahXbtY1JCypIgx3B4Ea/KAgsWyXEmW4q6zMbmTMtKzmPVk09rvFJirvMwTg==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Caching.StackExchangeRedis": { + "type": "Transitive", + "resolved": "6.0.6", + "contentHash": "bdVQpYm1hcHf0pyAypMjtDw3HjWQJ89UzloyyF1OBs56QlgA1naM498tP2Vjlho5vVRALMGPYzdRKCen8koubw==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "StackExchange.Redis": "2.2.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "tq2wXyh3fL17EMF2bXgRhU7JrbO3on93MRKYxzz4JzzvuGSA1l0W3GI9/tl8EO89TH+KWEymP7bcFway6z9fXg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "IznHHzGUtrdpuQqIUdmzF6TYPcsYHONhHh3o9dGp39sX/9Zfmt476UnhvU0UhXgJnXXAikt/MpN6AuSLCCMdEQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.0.0" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "pnyXV1LFOsYjGveuC07xp0YHIyGq7jRq5Ncb5zrrIieMLWVwgMyYxcOH0jTnBedDT4Gh1QinSqsjqzcieHk1og==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "Fy8yr4V6obi7ZxvKYI1i85jqtwMq8tqyxQVZpRSkgeA8enqy/KvBIMdcuNdznlxQMZa72mvbHqb7vbg4Pyx95w==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "Iaectmzg9Dc4ZbKX/FurrRjgO/I8rTumL5UU+Uube6vZuGetcnXoIgTA94RthFWePhdMVm8MMhVFJZdbzMsdyQ==", + "dependencies": { + "System.Text.Json": "4.6.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "3.1.8", + "contentHash": "7ZJUKwPipkDvuv2KJPZ3r01wp2AWNMiYH+61i0dL89F7QICknjKpWgLKLpTSUYFgl77S3b4264I6i4HzDdrb2A==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "3.1.8", + "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.8", + "Microsoft.Extensions.FileProviders.Abstractions": "3.1.8", + "Microsoft.Extensions.Logging.Abstractions": "3.1.8" + } + }, + "Microsoft.Extensions.Identity.Core": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "8vBsyGkA8ZI3lZvm1nf+9ynRC/TzPD+UtbdgTlKk+cz+AW5I41LrK8f/adGej5uXgprOA2DMjZw33vZG6vyXxA==", + "dependencies": { + "Microsoft.AspNetCore.Cryptography.KeyDerivation": "6.0.4", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Identity.Stores": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "linRCnWBfnqg8qjrd9u/KMISy8O4a6X/GRhpHXU0ar654YQw9LJ/Ht+psx8QLqSX5EsCBbBCZzuamatH2FWIyQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "6.0.0", + "Microsoft.Extensions.Identity.Core": "6.0.4", + "Microsoft.Extensions.Logging": "6.0.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "dzB2Cgg+JmrouhjkcQGzSFjjvpwlq353i8oBQO2GWNjCXSzhbtBRUf28HSauWe7eib3wYOdb3tItdjRwAdwCSg==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "Y/lGICwO27fCkQRK3tZseVzFjZaxfGmui990E67sB4MuiPzdJHnJDS/BeYWrHShSSBgCl4KyKRx4ux686fftPg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.0.0", + "Microsoft.Extensions.Configuration.Binder": "2.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.0.0", + "Microsoft.Extensions.Options": "2.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.22.0", + "contentHash": "GlamU9rs8cSVIx9WSGv5QKpt66KkE+ImxNa/wNZZUJ3knt3PM98T9sOY8B7NcEfhw7NoxU2/0TSOcmnRSJQgqw==" + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "2.16.5", + "contentHash": "VlGUZEpF8KP/GCfFI59sdE0WA0o9quqwM1YQY0dSp6jpGy5EOBkureaybLfpwCuYUUjQbLkN2p7neUIcQCfbzA==", + "dependencies": { + "Microsoft.Identity.Client": "4.22.0", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.IdentityModel.Clients.ActiveDirectory": { + "type": "Transitive", + "resolved": "3.14.2", + "contentHash": "TNsJJMiRnkeby1ovThVoV9yFsPWjAdluwOA+Nf0LtSsBVVrKQv8Qp4kYOgyNwMVj+pDwbhXISySk+4HyHVWNZQ==", + "dependencies": { + "NETStandard.Library": "1.6.0", + "System.Runtime.Serialization.Json": "4.0.2", + "System.Runtime.Serialization.Primitives": "4.1.1" + } + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "6.10.0", + "contentHash": "0qjS31rN1MQTc46tAYbzmMTSRfdV5ndZxSjYxIGqKSidd4wpNJfNII/pdhU5Fx8olarQoKL9lqqYw4yNOIwT0Q==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "6.10.0" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "6.10.0", + "contentHash": "zbcwV6esnNzhZZ/VP87dji6VrUBLB5rxnZBkDMqNYpyG+nrBnBsbm4PUYLCBMUflHCM9EMLDG0rLnqqT+l0ldA==" + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "6.10.0", + "contentHash": "DFyXD0xylP+DknCT3hzJ7q/Q5qRNu0hO/gCU90O0ATdR0twZmlcuY9RNYaaDofXKVbzcShYNCFCGle2G/o8mkg==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "6.10.0", + "Microsoft.IdentityModel.Tokens": "6.10.0" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "6.10.0", + "contentHash": "LVvMXAWPbPeEWTylDrxunlHH2wFyE4Mv0L4gZrJHC4HTESbWHquKZb/y/S8jgiQEDycOP0PDQvbG4RR/tr2TVQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "6.10.0", + "System.IdentityModel.Tokens.Jwt": "6.10.0" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "6.10.0", + "contentHash": "qbf1NslutDB4oLrriYTJpy7oB1pbh2ej2lEHd2IPDQH9C74ysOdhU5wAC7KoXblldbo7YsNR2QYFOqQM/b0Rsg==", + "dependencies": { + "Microsoft.CSharp": "4.5.0", + "Microsoft.IdentityModel.Logging": "6.10.0", + "System.Security.Cryptography.Cng": "4.5.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.OData.Core": { + "type": "Transitive", + "resolved": "7.6.4", + "contentHash": "/EjnJezMBjXf8OjcShhGzPY7pOO0CopgoZGhS6xsP3t2uhC+O72IBHgtQ7F3v1rRXWVtJwLGhzE1GfJUlx3c4Q==", + "dependencies": { + "Microsoft.OData.Edm": "[7.6.4]", + "Microsoft.Spatial": "[7.6.4]" + } + }, + "Microsoft.OData.Edm": { + "type": "Transitive", + "resolved": "7.6.4", + "contentHash": "MSSmA6kIfpgFTtNpOnnayoSj/6KSzHC1U9KOjF7cTA1PG4tZ7rIMi1pvjFc8CmYEvP4cxGl/+vrCn+HpK26HTQ==" + }, + "Microsoft.Spatial": { + "type": "Transitive", + "resolved": "7.6.4", + "contentHash": "3mB+Frn4LU4yb5ie9R752QiRn0Hvp9PITkSRofV/Lzm9EyLM87Fy9ziqgz75O/c712dh6GxuypMSBUGmNFwMeA==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.1.0", + "contentHash": "OMo/FYnKGy3lZEK0gfitskRM3ga/YBt6MyCyFPq0xNLeybGOQ6HnYNAAvzyePo5WPuMiw3LX+HiuRWNjnas1fA==", + "dependencies": { + "NuGet.Frameworks": "5.11.0", + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.1.0", + "contentHash": "JS0JDLniDhIzkSPLHz7N/x1CG8ywJOtwInFDYA3KQvbz+ojGoT5MT2YDVReL1b86zmNRV8339vsTSm/zh0RcMg==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.1.0", + "Newtonsoft.Json": "9.0.1" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "YUah81QG5q/ViVbr1BZcTbDLNJ5/k84fr+xx3/IoDVJR8KEUm89HmPAGM+FMMyWOjit+CIVpyOq7yEmRBBWXxQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "5.0.0", + "Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore": "5.0.2" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration.Contracts": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "34v6AkkRJykgFq7rHwNbzXBsLFquevLuegM9XDQl2j+wyOfj+ql1++jUR1WdZoPkv04WoM09mD47S3lMzJmHrQ==", + "dependencies": { + "Newtonsoft.Json": "11.0.2", + "System.Collections.Immutable": "1.7.0" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration.Core": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "R7mrxvTtv/MiEH42OtHYi/3L0A/vaAH8mwg+3yAyQtVuy6v9CeeVyL30lfTQ7EYV4ezUmuQKFwfjcU6PP0/KSQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "5.0.0", + "Microsoft.VisualStudio.Web.CodeGeneration.Templating": "5.0.2", + "Newtonsoft.Json": "11.0.2" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration.Design": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "9eTZV7W+S2iO2AJD03xXyXJZ+Nf71Y25gMXhqyXb8bB63jPfn+VQhV8I1lb6J+NR3jW98m5EB9QBftBSrjgiYQ==", + "dependencies": { + "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": "5.0.2" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "f9XeBRS9ICosrCpbO9jnAVMd/ISLhaZgx388XNBjigiyBJuq577J6tQgQWZA8PQTiPj6MKe9HVIW2GnKXDiUrQ==", + "dependencies": { + "Microsoft.VisualStudio.Web.CodeGeneration.Core": "5.0.2" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration.Templating": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "P3z/JZTGP5DhSc8ik4xrimWuCZ2ZaEZ6q7WGgfgmSVibfXxwh2Oo+dtdkiXwq8MNlkrcP0AZAo3+1wowYUzluA==", + "dependencies": { + "Microsoft.AspNetCore.Razor.Language": "5.0.0", + "Microsoft.AspNetCore.Razor.Runtime": "2.2.0", + "Microsoft.CodeAnalysis.CSharp": "3.8.0", + "Microsoft.CodeAnalysis.Razor": "5.0.0", + "Microsoft.VisualStudio.Web.CodeGeneration.Utils": "5.0.2" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration.Utils": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "4zViWGIFeKsGxDmc5xpn2G8kWs2FSHiLOolw85ZPHihDXc2jiFKp7qjA3SRt8U23kR3zeb0vZiFlETxgTHwAUA==", + "dependencies": { + "Microsoft.CodeAnalysis.CSharp.Workspaces": "3.8.0", + "Microsoft.VisualStudio.Web.CodeGeneration.Contracts": "5.0.2", + "Newtonsoft.Json": "11.0.2" + } + }, + "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": { + "type": "Transitive", + "resolved": "5.0.2", + "contentHash": "W4Uk2y0oja+4E+XP5d5OFu+ViTEtlqm3a6nYuuC3tjA+lTK6dLaMf0G6WnO4BO18i0kM0l49XjTwwXd5XpjnAQ==", + "dependencies": { + "Microsoft.VisualStudio.Web.CodeGeneration": "5.0.2" + } + }, + "Microsoft.Win32.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "Microsoft.Win32.SystemEvents": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A==" + }, + "MimeKit": { + "type": "Transitive", + "resolved": "3.2.0", + "contentHash": "l9YHMBhBUwY7qQHUp8fw0EvjcbmhN4Iggz6MdjqIShBf42+0nJTa5gu0kuupCOPuiARc9ZaS9c9f0gKz4OnxKw==", + "dependencies": { + "Portable.BouncyCastle": "1.9.0", + "System.Security.Cryptography.Pkcs": "6.0.0" + } + }, + "MySqlConnector": { + "type": "Transitive", + "resolved": "2.1.2", + "contentHash": "JVokQTUNN3WHAu9Vw8ieeq1dXTFokJiig5P0VJ4f439UxRrsPo6SaVWC8Zdm6mkPeQFhZ0/9afdWa02EY/1j/w==" + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.Win32.Primitives": "4.3.0", + "System.AppContext": "4.3.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Console": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.Compression.ZipFile": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.Net.Http": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Net.Sockets": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Timer": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0", + "System.Xml.XDocument": "4.3.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "Npgsql": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "SJMlOmFHr32oOzVXeHmarGaBKkhi0wHVN/rzuu2tUSJ4Qx2AkHCpr9R/DhLWwDiklqgzFU++9wkFyGJxbx/zzg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Npgsql.EntityFrameworkCore.PostgreSQL": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "fzgRmBd3nAFvKt/L70sJfFWAdobtwDEeOzOzruJq9og97O8/5B96inQOAgOpYyaUjPYpS4ZS5/bxm3vnOJ0+pQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "6.0.4", + "Microsoft.EntityFrameworkCore.Abstractions": "6.0.4", + "Microsoft.EntityFrameworkCore.Relational": "6.0.4", + "Npgsql": "6.0.4" + } + }, + "NSec.Cryptography": { + "type": "Transitive", + "resolved": "20.2.0", + "contentHash": "NxzHaDQm3JfH+9VQdLI1bC4h/ZTKPo5o/4BEscBu4KK0Yv35sB87hSRuzpr09VahxY5ZpJfE2tHyK4u27jfiyQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.7.0", + "libsodium": "[1.0.18, 1.0.19)" + } + }, + "NuGet.Frameworks": { + "type": "Transitive", + "resolved": "5.11.0", + "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" + }, + "Otp.NET": { + "type": "Transitive", + "resolved": "1.2.2", + "contentHash": "2hrZfkbzeWJ3tNXXt/1beg4IY+nS4F3gIfh4NVFvW0f6Pj51hGpiJ4prBz7Dmrr4ZYrA96rTERVGieZ4xYm7jA==" + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "Bhk0FWxH1paI+18zr1g5cTL+ebeuDcBCR+rRFO+fKEhretgjs7MF2Mc1P64FGLecWp4zKCUOPzngBNrqVyY7Zg==", + "dependencies": { + "System.IO.Pipelines": "5.0.1" + } + }, + "Pomelo.EntityFrameworkCore.MySql": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "sFIo5e9RmQoCTEvH6EeSV8ptmX3dw/6XgyD8R93X/i7A9+XCeG9KTjSNjrszVjVOtCu/eyvYqqcv2uZ/BHhlYA==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Relational": "[6.0.1, 7.0.0)", + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "MySqlConnector": "2.1.2" + } + }, + "Portable.BouncyCastle": { + "type": "Transitive", + "resolved": "1.9.0", + "contentHash": "eZZBCABzVOek+id9Xy04HhmgykF0wZg9wpByzrWN7q8qEI0Qen9b7tfd7w8VA3dOeesumMG7C5ZPy0jk7PSRHw==" + }, + "Quartz": { + "type": "Transitive", + "resolved": "3.4.0", + "contentHash": "N8350OAlQhd8zKg0ARFikGjh3bfAW/CF/KVxu2fTIlAALB/oC1eg54n/QAPYR5ryHuYyDr5G8/Qa4k+D/7OFRQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.1.1", + "System.Configuration.ConfigurationManager": "4.7.0", + "System.Diagnostics.DiagnosticSource": "4.7.1" + } + }, + "RichardSzalay.MockHttp": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "bStGNqIX/MGYtML7K3EzdsE/k5HGVAcg7XgN23TQXGXqxNC9fvYFR94fA0sGM5hAT36R+BBGet6ZDQxXL/IPxg==" + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "7VSGO0URRKoMEAq0Sc9cRz8mb6zbyx/BZDEWhgPdzzpmFhkam3fJ1DAGWFXBI4nGlma+uPKpfuMQP5LXRnOH5g==" + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "0oAaTAm6e2oVH+/Zttt0cuhGaePQYKII1dY8iaqP7CvOpVKgLybKRFvQjXR2LtxXOXTVPNv14j0ot8uV+HrUmw==" + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "G24ibsCNi5Kbz0oXWynBoRgtGvsw5ZSVEWjv13/KiCAM8C6wz9zzcCniMeQFIkJ2tasjo2kXlvlBZhplL51kGg==" + }, + "runtime.native.System": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Data.SqlClient.sni": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "9kyFSIdN3T0qjDQ2R0HRXYIhS3l5psBzQi6qqhdLz+SzFyEy4sVxNOke+yyYv8Cu8rPER12c3RDjLT8wF3WBYQ==", + "dependencies": { + "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0", + "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0", + "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0" + } + }, + "runtime.native.System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Net.Security": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "M2nN92ePS8BgQ2oi6Jj3PlTUzadYSIWLdZrHY1n1ZcW9o4wAQQ6W+aQ2lfq1ysZQfVCgDwY58alUdowrzezztg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", + "dependencies": { + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" + } + }, + "runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "QR1OwtwehHxSeQvZKXe+iSd+d3XZNkEcuWMFYa2i0aG1l+lR739HPicKMlTbJst3spmeekDVBUS7SeS26s4U/g==", + "dependencies": { + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" + } + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "I+GNKGg2xCHueRd1m9PzeEW7WLbNNLznmTuEi8/vZX71HudUbx1UTwlGkiwMri7JLl8hGaIAWnA/GONhu+LOyQ==" + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "1Z3TAq1ytS1IBRtPXJvEUZdVsfWfeNEhBkbiOCGEl9wwAfsjP2lz3ZFDx5tq8p60/EqbS0HItG5piHuB71RjoA==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "6mU/cVmmHtQiDXhnzUImxIcDL48GbTk+TsptXyJA+MIOG9LRjPoAQC/qBFB7X+UNyK86bmvGwC8t+M66wsYC8w==" + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "vjwG0GGcTW/PPg6KVud8F9GLWYuAV1rrw1BKAqY0oh4jcUqg15oYF1+qkGR2x2ZHM4DQnWKQ7cJgYbfncz/lYg==" + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "7KMFpTkHC/zoExs+PwP8jDCWcrK9H6L7soowT80CUx3e+nxP/AFnq0AQAW5W76z2WYbLAYCRyPfwYFG6zkvQRw==" + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "xrlmRCnKZJLHxyyLIqkZjNXqgxnKdZxfItrPkjI+6pkRo5lHX8YvSZlWrSI5AVwLMi4HbNWP7064hcAWeZKp5w==" + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==" + }, + "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==" + }, + "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==" + }, + "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==" + }, + "SendGrid": { + "type": "Transitive", + "resolved": "9.27.0", + "contentHash": "kMyXRQ8hmN2bG3tYZ7T31Ufl1kXkpuP5+WBh1BJ32WY31DTnBTCVGURoIqfbTo/tRuQfAYLxra6C8cQGN6kk+A==", + "dependencies": { + "Newtonsoft.Json": "9.0.1", + "starkbank-ecdsa": "[1.3.3, 2.0.0)" + } + }, + "Sentry": { + "type": "Transitive", + "resolved": "3.16.0", + "contentHash": "Pkw4+51EDUQ0X02jdCZIpaM2Q4UO06VKGDE+dYYNxgvOirRXGKTKxRk4NPKJTLSTNl+2JyT9HoE7C6BTlYhLOw==" + }, + "Sentry.Serilog": { + "type": "Transitive", + "resolved": "3.16.0", + "contentHash": "GFTVfQdOFqZ9Vmo8EEZTx1EQMDRJjka/4v2CwxnAUh+sqHDICga4eOm4AyGzDBbE4s9iAHMgMUCceIqo+7z84w==", + "dependencies": { + "Sentry": "3.16.0", + "Serilog": "2.10.0" + } + }, + "Serilog": { + "type": "Transitive", + "resolved": "2.10.0", + "contentHash": "+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==" + }, + "Serilog.AspNetCore": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "/JO/txIxRR61x1UXQAgUzG2Sx05o1QHCkokVBWrKzmAoDu+p5EtCAj7L/TVVg7Ezhh3GPiZ0JI9OJCmRO9tSRw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "5.0.0", + "Microsoft.Extensions.Logging": "5.0.0", + "Serilog": "2.10.0", + "Serilog.Extensions.Hosting": "4.2.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Settings.Configuration": "3.3.0", + "Serilog.Sinks.Console": "4.0.1", + "Serilog.Sinks.Debug": "2.0.0", + "Serilog.Sinks.File": "5.0.0" + } + }, + "Serilog.Extensions.Hosting": { + "type": "Transitive", + "resolved": "4.2.0", + "contentHash": "gT2keceCmPQR9EX0VpXQZvUgELdfE7yqJ7MOxBhm3WLCblcvRgswEOOTgok/DHObbM15A3V/DtF3VdVDQPIZzQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.8", + "Microsoft.Extensions.Hosting.Abstractions": "3.1.8", + "Microsoft.Extensions.Logging.Abstractions": "3.1.8", + "Serilog": "2.10.0", + "Serilog.Extensions.Logging": "3.1.0" + } + }, + "Serilog.Extensions.Logging": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "IWfem7wfrFbB3iw1OikqPFNPEzfayvDuN4WP7Ue1AVFskalMByeWk3QbtUXQR34SBkv1EbZ3AySHda/ErDgpcg==", + "dependencies": { + "Microsoft.Extensions.Logging": "2.0.0", + "Serilog": "2.9.0" + } + }, + "Serilog.Extensions.Logging.File": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "usO0qr4v9VCMBWiTJ1nQmAbPNCt40FrkDol6CpfCXbsxGZS/hH+YCueF7vvPQ32ATI0GWcMWiKRdjXEE7/HxTQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.0.0", + "Microsoft.Extensions.Configuration.Binder": "2.0.0", + "Serilog": "2.5.0", + "Serilog.Extensions.Logging": "2.0.2", + "Serilog.Formatting.Compact": "1.0.0", + "Serilog.Sinks.Async": "1.1.0", + "Serilog.Sinks.RollingFile": "3.3.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Settings.Configuration": { + "type": "Transitive", + "resolved": "3.3.0", + "contentHash": "7GNudISZwqaT902hqEL2OFGTZeUFWfnrNLupJkOqeF41AR3GjcxX+Hwb30xb8gG2/CDXsCMVfF8o0+8KY0fJNg==", + "dependencies": { + "Microsoft.Extensions.DependencyModel": "3.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "2.0.0", + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.Async": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "xll0Kanz2BkCxuv+F3p1WXr47jdsVM0GU1n1LZvK+18QiRZ/WGFNxSNw9EMKFV5ED5gr7MUpAe6PCMNL1HGUMA==", + "dependencies": { + "Serilog": "2.1.0", + "System.Collections.Concurrent": "4.0.12" + } + }, + "Serilog.Sinks.AzureCosmosDB": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "Im2/ZqjXQIpsd727qEo5Pq+br0MiNVuTvI40Yk7736tgjCpEx+omPHv4+c4fEAxnOP2kL9Ge6UoDFoDw3cjF2A==", + "dependencies": { + "Microsoft.Azure.Cosmos": "3.24.0", + "Microsoft.CSharp": "4.7.0", + "Newtonsoft.Json": "13.0.1", + "Serilog": "2.10.0", + "Serilog.Sinks.PeriodicBatching": "2.3.1" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.Debug": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "Y6g3OBJ4JzTyyw16fDqtFcQ41qQAydnEvEqmXjhwhgjsnG/FaJ8GUqF5ldsC/bVkK8KYmqrPhDO+tm4dF6xx4A==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "2.3.1", + "contentHash": "LVYvqpqjSTD8dhfxRnzpxTs8/ys3V2q01MvaY3r0eKsDgpKK1U1y/5N6gFHgiesbxG0V+O5IWdz4+c1DzoNyOQ==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "Serilog.Sinks.RollingFile": { + "type": "Transitive", + "resolved": "3.3.0", + "contentHash": "2lT5X1r3GH4P0bRWJfhA7etGl8Q2Ipw9AACvtAHWRUSpYZ42NGVyHoVs2ALBZ/cAkkS+tA4jl80Zie144eLQPg==", + "dependencies": { + "Serilog.Sinks.File": "3.2.0", + "System.IO": "4.1.0", + "System.IO.FileSystem.Primitives": "4.0.1", + "System.Runtime.InteropServices": "4.1.0", + "System.Text.Encoding.Extensions": "4.0.11" + } + }, + "Serilog.Sinks.SyslogMessages": { + "type": "Transitive", + "resolved": "2.0.6", + "contentHash": "V2Yq2GEbk7taEPbpBLFzLXhrHrUzKf4sQu/zLrANU8XIoUn/Mr08M2E8PrcrWVXCj0R4xLMWYe0Z1sxOrMF3IA==", + "dependencies": { + "Serilog": "2.5.0", + "Serilog.Sinks.PeriodicBatching": "2.3.0" + } + }, + "StackExchange.Redis": { + "type": "Transitive", + "resolved": "2.5.43", + "contentHash": "YQ38jVbX1b5mBi6lizESou+NpV6QZpeo6ofRR6qeuqJ8ePOmhcwhje3nDTNIGEkfPSK0sLuF6pR5rtFyq2F46g==", + "dependencies": { + "Pipelines.Sockets.Unofficial": "2.2.2", + "System.Diagnostics.PerformanceCounter": "5.0.0" + } + }, + "starkbank-ecdsa": { + "type": "Transitive", + "resolved": "1.3.3", + "contentHash": "OblOaKb1enXn+dSp7tsx9yjwV+/BEKM9jFhshIkZTwCk7LuTFTp+wSon6rFzuPiIiTGtvVWQNUw2slHjGktJog==" + }, + "Stripe.net": { + "type": "Transitive", + "resolved": "40.0.0", + "contentHash": "SD1bGiF+sVQG3p2LXNTZ5rEG2aCnXIHokcxYS9yyW3dR01J0ryf+iNFOwid148yePZ0gCBcRxj3wiW1mTmP7UQ==", + "dependencies": { + "Newtonsoft.Json": "12.0.3", + "System.Configuration.ConfigurationManager": "6.0.0" + } + }, + "System.AppContext": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Collections.Concurrent": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Collections.NonGeneric": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", + "dependencies": { + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Collections.Specialized": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==", + "dependencies": { + "System.Collections.NonGeneric": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.ComponentModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "0YFqjhp/mYkDGpU0Ye1GjE53HMp9UVfGN7seGpAMttAC0C40v5gw598jCgpbBLMmCo0E5YRLBv5Z2doypO49ZQ==" + }, + "System.ComponentModel.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==", + "dependencies": { + "System.ComponentModel": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.ComponentModel.TypeConverter": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Collections.NonGeneric": "4.3.0", + "System.Collections.Specialized": "4.3.0", + "System.ComponentModel": "4.3.0", + "System.ComponentModel.Primitives": "4.3.0", + "System.Globalization": "4.3.0", + "System.Linq": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Composition": { + "type": "Transitive", + "resolved": "1.0.31", + "contentHash": "I+D26qpYdoklyAVUdqwUBrEIckMNjAYnuPJy/h9dsQItpQwVREkDFs4b4tkBza0kT2Yk48Lcfsv2QQ9hWsh9Iw==", + "dependencies": { + "System.Composition.AttributedModel": "1.0.31", + "System.Composition.Convention": "1.0.31", + "System.Composition.Hosting": "1.0.31", + "System.Composition.Runtime": "1.0.31", + "System.Composition.TypedParts": "1.0.31" + } + }, + "System.Composition.AttributedModel": { + "type": "Transitive", + "resolved": "1.0.31", + "contentHash": "NHWhkM3ZkspmA0XJEsKdtTt1ViDYuojgSND3yHhTzwxepiwqZf+BCWuvCbjUt4fe0NxxQhUDGJ5km6sLjo9qnQ==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Composition.Convention": { + "type": "Transitive", + "resolved": "1.0.31", + "contentHash": "GLjh2Ju71k6C0qxMMtl4efHa68NmWeIUYh4fkUI8xbjQrEBvFmRwMDFcylT8/PR9SQbeeL48IkFxU/+gd0nYEQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Composition.AttributedModel": "1.0.31", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Globalization": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Composition.Hosting": { + "type": "Transitive", + "resolved": "1.0.31", + "contentHash": "fN1bT4RX4vUqjbgoyuJFVUizAl2mYF5VAb+bVIxIYZSSc0BdnX+yGAxcavxJuDDCQ1K+/mdpgyEFc8e9ikjvrg==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Composition.Runtime": "1.0.31", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Globalization": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Composition.Runtime": { + "type": "Transitive", + "resolved": "1.0.31", + "contentHash": "0LEJN+2NVM89CE4SekDrrk5tHV5LeATltkp+9WNYrR+Huiyt0vaCqHbbHtVAjPyeLWIc8dOz/3kthRBj32wGQg==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Globalization": "4.3.0", + "System.Linq": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Composition.TypedParts": { + "type": "Transitive", + "resolved": "1.0.31", + "contentHash": "0Zae/FtzeFgDBBuILeIbC/T9HMYbW4olAmi8XqqAGosSOWvXfiQLfARZEhiGd0LVXaYgXr0NhxiU1LldRP1fpQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Composition.AttributedModel": "1.0.31", + "System.Composition.Hosting": "1.0.31", + "System.Composition.Runtime": "1.0.31", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Globalization": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "7T+m0kDSlIPTHIkPMIu6m6tV6qsMqJpvQWW2jIc2qi7sn40qxFo0q+7mEQAhMPXZHMKnWrnv47ntGlM/ejvw3g==", + "dependencies": { + "System.Security.Cryptography.ProtectedData": "6.0.0", + "System.Security.Permissions": "6.0.0" + } + }, + "System.Console": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Data.SqlClient": { + "type": "Transitive", + "resolved": "4.8.3", + "contentHash": "yERfVLXAY0QbylAgaGLByYN0hFxX28aeEQ0hUgJO+Ntn1AfmWl5HHUoYJA0Yl9HhIUUJHVaS/Sw/RLZr5aaC+A==", + "dependencies": { + "Microsoft.Win32.Registry": "4.7.0", + "System.Security.Principal.Windows": "4.7.0", + "runtime.native.System.Data.SqlClient.sni": "4.7.0" + } + }, + "System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Diagnostics.PerformanceCounter": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "kcQWWtGVC3MWMNXdMDWfrmIlFZZ2OdoeT6pSNVRtk9+Sa7jwdPiMlNwb0ZQcS7NRlT92pCfmjRtkSWUW3RAKwg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "Microsoft.Win32.Registry": "5.0.0", + "System.Configuration.ConfigurationManager": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Diagnostics.Process": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "J0wOX07+QASQblsfxmIMFc9Iq7KTXYL3zs2G/Xc704Ylv3NpuVdo6gij6V3PGiptTxqsK0K7CdXenRvKUnkA2g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.Win32.Primitives": "4.3.0", + "Microsoft.Win32.Registry": "4.3.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Thread": "4.3.0", + "System.Threading.ThreadPool": "4.3.0", + "runtime.native.System": "4.3.0" + } + }, + "System.Diagnostics.Tools": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.TraceSource": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VnYp1NxGx8Ww731y2LJ1vpfb/DKVNKEZ8Jsh5SgQTZREL/YpWRArgh9pI8CDLmgHspZmLL697CaLvH85qQpRiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0" + } + }, + "System.Diagnostics.Tracing": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Drawing.Common": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==", + "dependencies": { + "Microsoft.Win32.SystemEvents": "6.0.0" + } + }, + "System.Dynamic.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Formats.Asn1": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "T6fD00dQ3NTbPDy31m4eQUwKW84s03z0N2C8HpOklyeaDgaJPa/TexP4/SkORMSOwc7WhKifnA6Ya33AkzmafA==" + }, + "System.Formats.Cbor": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "OJ8UXNyYIvu22ZrMHDBcnBvs3l6w2wEWUSwgPf2gimUrdoKJC4pcg963kiYAA9kvs8HYLQKQ+2Arr7pm19aZ4A==" + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Calendars": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0" + } + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "6.10.0", + "contentHash": "C+Q5ORsFycRkRuvy/Xd0Pv5xVpmWSAvQYZAGs7VQogmkqlLhvfZXTgBIlHqC3cxkstSoLJAYx6xZB7foQ2y5eg==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "6.10.0", + "Microsoft.IdentityModel.Tokens": "6.10.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Buffers": "4.3.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.IO.Compression": "4.3.0" + } + }, + "System.IO.Compression.ZipFile": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", + "dependencies": { + "System.Buffers": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.IO.FileSystem": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.FileSystem.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Rfm2jYCaUeGysFEZjDe7j1R4x6Z6BzumS/vUT5a1AA/AWJuGX71PoGB0RmpyX3VmrGqVnAwtfMn39OHR8Y/5+g==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==" + }, + "System.Linq": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Linq.Expressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Linq": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Emit.Lightweight": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Linq.Queryable": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "Yn/WfYe9RoRfmSLvUt2JerP0BTGGykCZkQPgojaxgzF2N0oPo+/AhB8TXOpdCcNlrG3VRtsamtK2uzsp3cqRVw==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Linq": "4.1.0", + "System.Linq.Expressions": "4.1.0", + "System.Reflection": "4.1.0", + "System.Reflection.Extensions": "4.0.1", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "JGkzeqgBsiZwKJZ1IxPNsDFZDhUvuEdX8L8BDC8N3KOj+6zMcNU28CNN59TpZE/VJYy9cP+5M+sbxtWJx3/xtw==", + "dependencies": { + "System.Text.Encodings.Web": "4.7.2", + "System.Text.Json": "4.6.0" + } + }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.4", + "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.1", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.DiagnosticSource": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" + } + }, + "System.Net.NameResolution": { + "type": "Transitive", + "resolved": "4.0.0", + "contentHash": "JdqRdM1Qym3YehqdKIi5LHrpypP4JMfxKQSNCJ2z4WawkG0il+N3XfNeJOxll2XrTnG7WgYYPoeiu/KOwg0DQw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "System.Collections": "4.0.11", + "System.Diagnostics.Tracing": "4.1.0", + "System.Globalization": "4.0.11", + "System.Net.Primitives": "4.0.11", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.Handles": "4.0.1", + "System.Runtime.InteropServices": "4.1.0", + "System.Security.Principal.Windows": "4.0.0", + "System.Threading": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "runtime.native.System": "4.0.0" + } + }, + "System.Net.NetworkInformation": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "Q0rfeiW6QsiZuicGjrFA7cRr2+kXex0JIljTTxzI09GIftB8k+aNL31VsQD1sI2g31cw7UGDTgozA/FgeNSzsQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "Microsoft.Win32.Primitives": "4.0.1", + "System.Collections": "4.0.11", + "System.Diagnostics.Tracing": "4.1.0", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.IO.FileSystem": "4.0.1", + "System.IO.FileSystem.Primitives": "4.0.1", + "System.Linq": "4.1.0", + "System.Net.Primitives": "4.0.11", + "System.Net.Sockets": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.Handles": "4.0.1", + "System.Runtime.InteropServices": "4.1.0", + "System.Security.Principal.Windows": "4.0.0", + "System.Threading": "4.0.11", + "System.Threading.Overlapped": "4.0.1", + "System.Threading.Tasks": "4.0.11", + "System.Threading.Thread": "4.0.0", + "System.Threading.ThreadPool": "4.0.10", + "runtime.native.System": "4.0.0" + } + }, + "System.Net.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Net.Requests": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "vxGt7C0cZixN+VqoSW4Yakc1Y9WknmxauDqzxgpw/FnBdz4kQNN51l4wxdXX5VY1xjqy//+G+4CvJWp1+f+y6Q==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Diagnostics.Tracing": "4.1.0", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Net.Http": "4.1.0", + "System.Net.Primitives": "4.0.11", + "System.Net.WebHeaderCollection": "4.0.1", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Threading": "4.0.11", + "System.Threading.Tasks": "4.0.11" + } + }, + "System.Net.Security": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "xT2jbYpbBo3ha87rViHoTA6WdvqOAW37drmqyx/6LD8p7HEPT2qgdxoimRzWtPg8Jh4X5G9BV2seeTv4x6FYlA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.Win32.Primitives": "4.3.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.IO": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Claims": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Security.Principal": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.ThreadPool": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Security": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" + } + }, + "System.Net.Sockets": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Net.WebHeaderCollection": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "XX2TIAN+wBSAIV51BU2FvvXMdstUa8b0FBSZmDWjZdwUMmggQSifpTOZ5fNH20z9ZCg2fkV1L5SsZnpO2RQDRQ==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0" + } + }, + "System.Net.WebSockets": { + "type": "Transitive", + "resolved": "4.0.0", + "contentHash": "2KJo8hir6Edi9jnMDAMhiJoI691xRBmKcbNpwjrvpIMOCTYOtBpSsSEGBxBDV7PKbasJNaFp1+PZz1D7xS41Hg==", + "dependencies": { + "Microsoft.Win32.Primitives": "4.0.1", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Threading.Tasks": "4.0.11" + } + }, + "System.Net.WebSockets.Client": { + "type": "Transitive", + "resolved": "4.0.2", + "contentHash": "NUCcDroX4lCQXgOrzlwIZ1u9YJ0krfyF0wk0ONnyLUmcQoEiYV2/OfUPRqUwQBbpH1BlGApkLgoQUwMqb5+c1g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.2", + "Microsoft.Win32.Primitives": "4.0.1", + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Diagnostics.Tracing": "4.1.0", + "System.Globalization": "4.0.11", + "System.Net.Primitives": "4.0.11", + "System.Net.WebHeaderCollection": "4.0.1", + "System.Net.WebSockets": "4.0.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.Handles": "4.0.1", + "System.Runtime.InteropServices": "4.1.0", + "System.Security.Cryptography.X509Certificates": "4.1.0", + "System.Text.Encoding": "4.0.11", + "System.Threading": "4.0.11", + "System.Threading.Tasks": "4.0.11" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.ObjectModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Private.DataContractSerialization": { + "type": "Transitive", + "resolved": "4.1.1", + "contentHash": "lcqFBUaCZxPiUkA4dlSOoPZGtZsAuuElH2XHgLwGLxd7ZozWetV5yiz0qGAV2AUYOqw97MtZBjbLMN16Xz4vXA==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Collections.Concurrent": "4.0.12", + "System.Diagnostics.Debug": "4.0.11", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Linq": "4.1.0", + "System.Reflection": "4.1.0", + "System.Reflection.Emit.ILGeneration": "4.0.1", + "System.Reflection.Emit.Lightweight": "4.0.1", + "System.Reflection.Extensions": "4.0.1", + "System.Reflection.Primitives": "4.0.1", + "System.Reflection.TypeExtensions": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.Serialization.Primitives": "4.1.1", + "System.Text.Encoding": "4.0.11", + "System.Text.Encoding.Extensions": "4.0.11", + "System.Text.RegularExpressions": "4.1.0", + "System.Threading": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "System.Xml.ReaderWriter": "4.0.11", + "System.Xml.XmlDocument": "4.0.1", + "System.Xml.XmlSerializer": "4.0.11" + } + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.ILGeneration": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.Lightweight": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==" + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "System.Runtime.Caching": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "30D6MkO8WF9jVGWZIP0hmCN8l9BTY4LCsAzLIe4xFSXzs+AjDotR7DpSmj27pFskDURzUvqYYY0ikModgBTxWw==", + "dependencies": { + "System.Configuration.ConfigurationManager": "5.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0" + } + }, + "System.Runtime.Numerics": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", + "dependencies": { + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Runtime.Serialization.Json": { + "type": "Transitive", + "resolved": "4.0.2", + "contentHash": "+7DIJhnKYgCzUgcLbVTtRQb2l1M0FP549XFlFkQM5lmNiUBl44AfNbx4bz61xA8PzLtlYwfmif4JJJW7MPPnjg==", + "dependencies": { + "System.IO": "4.1.0", + "System.Private.DataContractSerialization": "4.1.1", + "System.Runtime": "4.1.0" + } + }, + "System.Runtime.Serialization.Primitives": { + "type": "Transitive", + "resolved": "4.1.1", + "contentHash": "HZ6Du5QrTG8MNJbf4e4qMO3JRAkIboGT5Fk804uZtg3Gq516S7hAqTm2UZKUHa7/6HUGdVy3AqMQKbns06G/cg==", + "dependencies": { + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==" + }, + "System.Security.Claims": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "P/+BR/2lnc4PNDHt/TPBAWHVMLMRHsyYZbU1NphW4HIWzCggz8mJbTQQ3MKljFE7LS3WagmVFuBgoLcFzYXlkA==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Security.Principal": "4.3.0" + } + }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.Apple": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Cng": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==", + "dependencies": { + "System.Formats.Asn1": "5.0.0" + } + }, + "System.Security.Cryptography.Csp": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Linq": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", + "dependencies": { + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "elM3x+xSRhzQysiqo85SbidJJ2YbZlnvmh+53TuSZHsD7dNuuEWser+9EFtY+rYupBwkq2avc6ZCO3/6qACgmg==", + "dependencies": { + "System.Formats.Asn1": "6.0.0" + } + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", + "dependencies": { + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ==" + }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Cng": "4.3.0", + "System.Security.Cryptography.Csp": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Xml": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "i2Jn6rGXR63J0zIklImGRkDIJL4b1NfPSEbIVHBlqoIb12lfXIigCbDRpDmIEzwSo/v1U5y/rYJdzZYSyCWxvg==", + "dependencies": { + "System.Security.Cryptography.Pkcs": "4.5.0", + "System.Security.Permissions": "4.5.0" + } + }, + "System.Security.Permissions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==", + "dependencies": { + "System.Security.AccessControl": "6.0.0", + "System.Windows.Extensions": "6.0.0" + } + }, + "System.Security.Principal": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Security.SecureString": { + "type": "Transitive", + "resolved": "4.0.0", + "contentHash": "sqzq9GD6/b0yqPuMpgIKBuoLf4VKAj8oAfh4kXSzPaN6eoKY3hRi9C5L27uip25qlU+BGPfb0xh2Rmbwc4jFVA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Handles": "4.0.1", + "System.Runtime.InteropServices": "4.1.0", + "System.Security.Cryptography.Primitives": "4.0.0", + "System.Text.Encoding": "4.0.11", + "System.Threading": "4.0.11" + } + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0" + } + }, + "System.Text.Encoding.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "6.0.0" + } + }, + "System.Text.RegularExpressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Threading": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", + "dependencies": { + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Threading.Overlapped": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "f7aLuLkBoCQM2kng7zqLFBXz9Gk48gDK8lk1ih9rH/1arJJzZK9gJwNvPDhL6Ps/l6rwOr8jw+4FCHL0KKWiEg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.0.1", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Handles": "4.0.1" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" + }, + "System.Threading.Thread": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OHmbT+Zz065NKII/ZHcH9XO1dEuLGI1L2k7uYss+9C1jLxTC9kTZZuzUOyXHayRk+dft9CiDf3I/QZ0t8JKyBQ==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Threading.ThreadPool": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "k/+g4b7vjdd4aix83sTgC9VG6oXYKAktSfNIJUNGxPEj7ryEOfzHHhfnmsZvjxawwcD9HyWXKCXmPjX8U4zeSw==", + "dependencies": { + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Threading.Timer": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "System.Windows.Extensions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==", + "dependencies": { + "System.Drawing.Common": "6.0.0" + } + }, + "System.Xml.ReaderWriter": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Tasks.Extensions": "4.3.0" + } + }, + "System.Xml.XDocument": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0" + } + }, + "System.Xml.XmlDocument": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0" + } + }, + "System.Xml.XmlSerializer": { + "type": "Transitive", + "resolved": "4.0.11", + "contentHash": "FrazwwqfIXTfq23mfv4zH+BjqkSFNaNFBtjzu3I9NRmG8EELYyrv/fJnttCIwRMFRR/YKXF1hmsMmMEnl55HGw==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Linq": "4.1.0", + "System.Reflection": "4.1.0", + "System.Reflection.Emit": "4.0.1", + "System.Reflection.Emit.ILGeneration": "4.0.1", + "System.Reflection.Extensions": "4.0.1", + "System.Reflection.Primitives": "4.0.1", + "System.Reflection.TypeExtensions": "4.1.0", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Text.RegularExpressions": "4.1.0", + "System.Threading": "4.0.11", + "System.Xml.ReaderWriter": "4.0.11", + "System.Xml.XmlDocument": "4.0.1" + } + }, + "System.Xml.XPath": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "v1JQ5SETnQusqmS3RwStF7vwQ3L02imIzl++sewmt23VGygix04pEH+FCj1yWb+z4GDzKiljr1W7Wfvrx0YwgA==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0" + } + }, + "System.Xml.XPath.XmlDocument": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "A/uxsWi/Ifzkmd4ArTLISMbfFs6XpRPsXZonrIqyTY70xi8t+mDtvSM5Os0RqyRDobjMBwIDHDL4NOIbkDwf7A==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0", + "System.Xml.XPath": "4.3.0", + "System.Xml.XmlDocument": "4.3.0" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "0.10.0", + "contentHash": "4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.4.1", + "contentHash": "O/Oe0BS5RmSsM+LQOb041TzuPo5MdH2Rov+qXGS37X+KFG1Hxz7kopYklM5+1Y+tRGeXrOx5+Xne1RuqLFQoyQ==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.4.1", + "contentHash": "Zsj5OMU6JasNGERXZy8s72+pcheG6Q15atS5XpZXqAtULuyQiQ6XNnUsp1gyfC6WgqScqMvySiEHmHcOG6Eg0Q==", + "dependencies": { + "xunit.extensibility.core": "[2.4.1]", + "xunit.extensibility.execution": "[2.4.1]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.4.1", + "contentHash": "yKZKm/8QNZnBnGZFD9SewkllHBiK0DThybQD/G4PiAmQjKtEZyHi6ET70QPU9KtSMJGRYS6Syk7EyR2EVDU4Kg==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.4.1", + "contentHash": "7e/1jqBpcb7frLkB6XDrHCGXAbKN4Rtdb88epYxCSRQuZDRW8UtTfdTEVpdTl8s4T56e07hOBVd4G0OdCxIY2A==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.extensibility.core": "[2.4.1]" + } + }, + "YubicoDotNetClient": { + "type": "Transitive", + "resolved": "1.2.0", + "contentHash": "uP5F3Ko1gqZi3lwS2R/jAAwhBxXs/6PKDpS6FdQjsBA5qmF0hQmbtfxM6QHTXOMoWbUtfetG7+LtgmG8T5zDIg==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "common": { + "type": "Project", + "dependencies": { + "AutoFixture.AutoNSubstitute": "[4.17.0, )", + "AutoFixture.Xunit2": "[4.17.0, )", + "Core": "[2022.9.0, )", + "Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )", + "Microsoft.NET.Test.Sdk": "[17.1.0, )", + "NSubstitute": "[4.3.0, )", + "xunit": "[2.4.1, )" + } + }, + "core": { + "type": "Project", + "dependencies": { + "AWSSDK.SQS": "[3.7.2.47, )", + "AWSSDK.SimpleEmail": "[3.7.0.150, )", + "AspNetCoreRateLimit": "[4.0.2, )", + "AspNetCoreRateLimit.Redis": "[1.0.1, )", + "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", + "Azure.Storage.Blobs": "[12.11.0, )", + "Azure.Storage.Queues": "[12.9.0, )", + "BitPay.Light": "[1.0.1907, )", + "Braintree": "[5.12.0, )", + "Fido2.AspNet": "[3.0.0-beta2, )", + "Handlebars.Net": "[2.1.2, )", + "IdentityServer4": "[4.1.2, )", + "IdentityServer4.AccessTokenValidation": "[3.0.1, )", + "MailKit": "[3.2.0, )", + "Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )", + "Microsoft.Azure.Cosmos.Table": "[1.0.8, )", + "Microsoft.Azure.NotificationHubs": "[4.1.0, )", + "Microsoft.Azure.ServiceBus": "[5.2.0, )", + "Microsoft.Data.SqlClient": "[4.1.0, )", + "Microsoft.Extensions.Caching.StackExchangeRedis": "[6.0.6, )", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "[6.0.1, )", + "Microsoft.Extensions.Configuration.UserSecrets": "[6.0.1, )", + "Microsoft.Extensions.Identity.Stores": "[6.0.4, )", + "Newtonsoft.Json": "[13.0.1, )", + "Otp.NET": "[1.2.2, )", + "Quartz": "[3.4.0, )", + "SendGrid": "[9.27.0, )", + "Sentry.Serilog": "[3.16.0, )", + "Serilog.AspNetCore": "[5.0.0, )", + "Serilog.Extensions.Logging": "[3.1.0, )", + "Serilog.Extensions.Logging.File": "[2.0.0, )", + "Serilog.Sinks.AzureCosmosDB": "[2.0.0, )", + "Serilog.Sinks.SyslogMessages": "[2.0.6, )", + "Stripe.net": "[40.0.0, )", + "YubicoDotNetClient": "[1.2.0, )" + } + }, + "infrastructure.dapper": { + "type": "Project", + "dependencies": { + "Core": "[2022.9.0, )", + "Dapper": "[2.0.123, )", + "System.Data.SqlClient": "[4.8.3, )" + } + }, + "infrastructure.entityframework": { + "type": "Project", + "dependencies": { + "AutoMapper.Extensions.Microsoft.DependencyInjection": "[11.0.0, )", + "Core": "[2022.9.0, )", + "Microsoft.EntityFrameworkCore.Relational": "[6.0.4, )", + "Npgsql.EntityFrameworkCore.PostgreSQL": "[6.0.4, )", + "Pomelo.EntityFrameworkCore.MySql": "[6.0.1, )", + "linq2db.EntityFrameworkCore": "[6.7.1, )" + } + }, + "scim": { + "type": "Project", + "dependencies": { + "Core": "[2022.9.0, )", + "Microsoft.VisualStudio.Web.CodeGeneration.Design": "[5.0.2, )", + "SharedWeb": "[2022.9.0, )" + } + }, + "sharedweb": { + "type": "Project", + "dependencies": { + "Core": "[2022.9.0, )", + "Infrastructure.Dapper": "[2022.9.0, )", + "Infrastructure.EntityFramework": "[2022.9.0, )" + } + } + } + } +} \ No newline at end of file diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml index 201356914..66d07b56e 100644 --- a/dev/docker-compose.yml +++ b/dev/docker-compose.yml @@ -3,7 +3,6 @@ version: "3.9" services: mssql: image: mcr.microsoft.com/azure-sql-edge:latest - restart: always environment: ACCEPT_EULA: Y MSSQL_SA_PASSWORD: ${MSSQL_PASSWORD} @@ -41,7 +40,6 @@ services: postgres: image: postgres:14 - restart: always ports: - "5432:5432" environment: @@ -58,7 +56,6 @@ services: mysql: image: mysql:8 container_name: bw-mysql - restart: always ports: - "3306:3306" command: --default-authentication-plugin=mysql_native_password diff --git a/dev/helpers/mssql/run_migrations.sh b/dev/helpers/mssql/run_migrations.sh index 3da82b6d6..2ff75e7c5 100755 --- a/dev/helpers/mssql/run_migrations.sh +++ b/dev/helpers/mssql/run_migrations.sh @@ -33,22 +33,30 @@ BEGIN CREATE DATABASE $DATABASE; END; +GO IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = 'migrations_$DATABASE') BEGIN CREATE DATABASE migrations_$DATABASE; END; GO -IF OBJECT_ID('[migrations_$DATABASE].[dbo].[migrations]') IS NULL +" +/opt/mssql-tools/bin/sqlcmd -S $SERVER -d master -U $USER -P $PASSWD -I -Q "$QUERY" +echo "Return code: $?" + +# Create migrations table if it does not already exist +QUERY="IF OBJECT_ID('[migrations_$DATABASE].[dbo].[migrations]') IS NULL BEGIN CREATE TABLE [migrations_$DATABASE].[dbo].[migrations] ( [Id] INT IDENTITY(1,1) PRIMARY KEY, [Filename] NVARCHAR(MAX) NOT NULL, [CreationDate] DATETIME2 (7) NULL, ); -END;" - -/opt/mssql-tools/bin/sqlcmd -S $SERVER -d master -U $USER -P $PASSWD -I -Q "$QUERY" +END; +GO +" +/opt/mssql-tools/bin/sqlcmd -S $SERVER -d migrations_$DATABASE -U $USER -P $PASSWD -I -Q "$QUERY" +echo "Return code: $?" should_migrate () { local file=$(basename $1) diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000..61ebaed0d --- /dev/null +++ b/renovate.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base", + "schedule:monthly", + ":maintainLockFilesMonthly", + ":preserveSemverRanges", + ":rebaseStalePrs", + ":disableMajorUpdates" + ], + "enabledManagers": [ + "nuget" + ], + "packageRules": [ + { + "matchManagers": ["nuget"], + "groupName": "Nuget updates", + "groupSlug": "nuget", + "matchUpdateTypes": [ + "minor", + "patch" + ] + } + ] +} diff --git a/src/Admin/Controllers/LoginController.cs b/src/Admin/Controllers/LoginController.cs index 47f9d5b34..a6ee8e37f 100644 --- a/src/Admin/Controllers/LoginController.cs +++ b/src/Admin/Controllers/LoginController.cs @@ -1,5 +1,5 @@ -using Bit.Admin.Models; -using Bit.Core.Identity; +using Bit.Admin.IdentityServer; +using Bit.Admin.Models; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; diff --git a/src/Core/Identity/PasswordlessSignInManager.cs b/src/Admin/IdentityServer/PasswordlessSignInManager.cs similarity index 96% rename from src/Core/Identity/PasswordlessSignInManager.cs rename to src/Admin/IdentityServer/PasswordlessSignInManager.cs index 1ca010835..6c346158e 100644 --- a/src/Core/Identity/PasswordlessSignInManager.cs +++ b/src/Admin/IdentityServer/PasswordlessSignInManager.cs @@ -1,11 +1,9 @@ using Bit.Core.Services; using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace Bit.Core.Identity; +namespace Bit.Admin.IdentityServer; public class PasswordlessSignInManager : SignInManager where TUser : class { diff --git a/src/Core/Identity/ReadOnlyEnvIdentityUserStore.cs b/src/Admin/IdentityServer/ReadOnlyEnvIdentityUserStore.cs similarity index 89% rename from src/Core/Identity/ReadOnlyEnvIdentityUserStore.cs rename to src/Admin/IdentityServer/ReadOnlyEnvIdentityUserStore.cs index 341bcd38a..15b8d894b 100644 --- a/src/Core/Identity/ReadOnlyEnvIdentityUserStore.cs +++ b/src/Admin/IdentityServer/ReadOnlyEnvIdentityUserStore.cs @@ -1,8 +1,7 @@ using Bit.Core.Utilities; using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Configuration; -namespace Bit.Core.Identity; +namespace Bit.Admin.IdentityServer; public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore { @@ -14,7 +13,7 @@ public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore } public override Task FindByEmailAsync(string normalizedEmail, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { var usersCsv = _configuration["adminSettings:admins"]; if (!CoreHelpers.SettingHasValue(usersCsv)) @@ -59,7 +58,7 @@ public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore } public override Task FindByIdAsync(string userId, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { return FindByEmailAsync(userId, cancellationToken); } diff --git a/src/Core/Identity/ReadOnlyIdentityUserStore.cs b/src/Admin/IdentityServer/ReadOnlyIdentityUserStore.cs similarity index 67% rename from src/Core/Identity/ReadOnlyIdentityUserStore.cs rename to src/Admin/IdentityServer/ReadOnlyIdentityUserStore.cs index 50c42c819..88f3a40b1 100644 --- a/src/Core/Identity/ReadOnlyIdentityUserStore.cs +++ b/src/Admin/IdentityServer/ReadOnlyIdentityUserStore.cs @@ -1,108 +1,107 @@ using Microsoft.AspNetCore.Identity; -namespace Bit.Core.Identity; +namespace Bit.Admin.IdentityServer; public abstract class ReadOnlyIdentityUserStore : - IUserStore, IUserEmailStore, IUserSecurityStampStore { public void Dispose() { } public Task CreateAsync(IdentityUser user, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public Task DeleteAsync(IdentityUser user, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public abstract Task FindByEmailAsync(string normalizedEmail, - CancellationToken cancellationToken = default(CancellationToken)); + CancellationToken cancellationToken = default); public abstract Task FindByIdAsync(string userId, - CancellationToken cancellationToken = default(CancellationToken)); + CancellationToken cancellationToken = default); public async Task FindByNameAsync(string normalizedUserName, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { return await FindByEmailAsync(normalizedUserName, cancellationToken); } public Task GetEmailAsync(IdentityUser user, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { return Task.FromResult(user.Email); } public Task GetEmailConfirmedAsync(IdentityUser user, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { return Task.FromResult(user.EmailConfirmed); } public Task GetNormalizedEmailAsync(IdentityUser user, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { return Task.FromResult(user.Email); } public Task GetNormalizedUserNameAsync(IdentityUser user, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { return Task.FromResult(user.Email); } public Task GetUserIdAsync(IdentityUser user, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { return Task.FromResult(user.Id); } public Task GetUserNameAsync(IdentityUser user, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { return Task.FromResult(user.Email); } public Task SetEmailAsync(IdentityUser user, string email, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public Task SetEmailConfirmedAsync(IdentityUser user, bool confirmed, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public Task SetNormalizedEmailAsync(IdentityUser user, string normalizedEmail, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { user.NormalizedEmail = normalizedEmail; return Task.FromResult(0); } public Task SetNormalizedUserNameAsync(IdentityUser user, string normalizedName, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { user.NormalizedUserName = normalizedName; return Task.FromResult(0); } public Task SetUserNameAsync(IdentityUser user, string userName, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public Task UpdateAsync(IdentityUser user, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { return Task.FromResult(IdentityResult.Success); } diff --git a/src/Admin/IdentityServer/ServiceCollectionExtensions.cs b/src/Admin/IdentityServer/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..bf4ac66cc --- /dev/null +++ b/src/Admin/IdentityServer/ServiceCollectionExtensions.cs @@ -0,0 +1,44 @@ +using Bit.Core.Entities; +using Bit.Core.Identity; +using Bit.Core.Settings; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Bit.Admin.IdentityServer; + +public static class ServiceCollectionExtensions +{ + public static Tuple AddPasswordlessIdentityServices( + this IServiceCollection services, GlobalSettings globalSettings) where TUserStore : class + { + services.TryAddTransient(); + services.Configure(options => + { + options.TokenLifespan = TimeSpan.FromMinutes(15); + }); + + var passwordlessIdentityBuilder = services.AddIdentity() + .AddUserStore() + .AddRoleStore() + .AddDefaultTokenProviders(); + + var regularIdentityBuilder = services.AddIdentityCore() + .AddUserStore(); + + services.TryAddScoped, PasswordlessSignInManager>(); + + services.ConfigureApplicationCookie(options => + { + options.LoginPath = "/login"; + options.LogoutPath = "/"; + options.AccessDeniedPath = "/login?accessDenied=true"; + options.Cookie.Name = $"Bitwarden_{globalSettings.ProjectName}"; + options.Cookie.HttpOnly = true; + options.ExpireTimeSpan = TimeSpan.FromDays(2); + options.ReturnUrlParameter = "returnUrl"; + options.SlidingExpiration = true; + }); + + return new Tuple(passwordlessIdentityBuilder, regularIdentityBuilder); + } +} diff --git a/src/Admin/Jobs/DeleteAuthRequestsJob.cs b/src/Admin/Jobs/DeleteAuthRequestsJob.cs new file mode 100644 index 000000000..f0d26a33a --- /dev/null +++ b/src/Admin/Jobs/DeleteAuthRequestsJob.cs @@ -0,0 +1,27 @@ +using Bit.Core; +using Bit.Core.Jobs; +using Bit.Core.Repositories; +using Quartz; + +namespace Bit.Admin.Jobs; + +public class DeleteAuthRequestsJob : BaseJob +{ + private readonly IAuthRequestRepository _authRepo; + + public DeleteAuthRequestsJob( + IAuthRequestRepository authrepo, + ILogger logger) + : base(logger) + { + _authRepo = authrepo; + } + + protected async override Task ExecuteJobAsync(IJobExecutionContext context) + { + _logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteAuthRequestsJob: Start"); + var count = await _authRepo.DeleteExpiredAsync(); + _logger.LogInformation(Constants.BypassFiltersEventId, $"{count} records deleted from AuthRequests."); + _logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteAuthRequestsJob: End"); + } +} diff --git a/src/Admin/Jobs/JobsHostedService.cs b/src/Admin/Jobs/JobsHostedService.cs index 53b5c0566..fd83e07af 100644 --- a/src/Admin/Jobs/JobsHostedService.cs +++ b/src/Admin/Jobs/JobsHostedService.cs @@ -59,6 +59,11 @@ public class JobsHostedService : BaseJobsHostedService .StartNow() .WithCronSchedule("0 0 0 * * ?") .Build(); + var everyFifteenMinutesTrigger = TriggerBuilder.Create() + .WithIdentity("everyFifteenMinutesTrigger") + .StartNow() + .WithCronSchedule("0 */15 * ? * *") + .Build(); var jobs = new List> { @@ -67,7 +72,8 @@ public class JobsHostedService : BaseJobsHostedService new Tuple(typeof(DatabaseUpdateStatisticsJob), everySaturdayAtMidnightTrigger), new Tuple(typeof(DatabaseRebuildlIndexesJob), everySundayAtMidnightTrigger), new Tuple(typeof(DeleteCiphersJob), everyDayAtMidnightUtc), - new Tuple(typeof(DatabaseExpiredSponsorshipsJob), everyMondayAtMidnightTrigger) + new Tuple(typeof(DatabaseExpiredSponsorshipsJob), everyMondayAtMidnightTrigger), + new Tuple(typeof(DeleteAuthRequestsJob), everyFifteenMinutesTrigger), }; if (!_globalSettings.SelfHosted) @@ -91,5 +97,6 @@ public class JobsHostedService : BaseJobsHostedService services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } } diff --git a/src/Admin/Models/OrganizationViewModel.cs b/src/Admin/Models/OrganizationViewModel.cs index 5a487cd03..31504f937 100644 --- a/src/Admin/Models/OrganizationViewModel.cs +++ b/src/Admin/Models/OrganizationViewModel.cs @@ -18,7 +18,7 @@ public class OrganizationViewModel UserInvitedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Invited); UserAcceptedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Accepted); UserConfirmedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Confirmed); - UserCount = orgUsers.Count(); + OccupiedSeatCount = orgUsers.Count(u => u.OccupiesOrganizationSeat); CipherCount = ciphers.Count(); CollectionCount = collections.Count(); GroupCount = groups?.Count() ?? 0; @@ -40,7 +40,7 @@ public class OrganizationViewModel public int UserInvitedCount { get; set; } public int UserConfirmedCount { get; set; } public int UserAcceptedCount { get; set; } - public int UserCount { get; set; } + public int OccupiedSeatCount { get; set; } public int CipherCount { get; set; } public int CollectionCount { get; set; } public int GroupCount { get; set; } diff --git a/src/Admin/Program.cs b/src/Admin/Program.cs index f5bc877ab..fb5dc7e08 100644 --- a/src/Admin/Program.cs +++ b/src/Admin/Program.cs @@ -1,5 +1,4 @@ using Bit.Core.Utilities; -using Serilog.Events; namespace Bit.Admin; @@ -18,7 +17,7 @@ public class Program }); webBuilder.UseStartup(); webBuilder.ConfigureLogging((hostingContext, logging) => - logging.AddSerilog(hostingContext, e => + logging.AddSerilog(hostingContext, (e, globalSettings) => { var context = e.Properties["SourceContext"].ToString(); if (e.Properties.ContainsKey("RequestPath") && @@ -27,7 +26,7 @@ public class Program { return false; } - return e.Level >= LogEventLevel.Error; + return e.Level >= globalSettings.MinLogLevel.AdminSettings.Default; })); }) .Build() diff --git a/src/Admin/Startup.cs b/src/Admin/Startup.cs index 37645873e..d10a1d445 100644 --- a/src/Admin/Startup.cs +++ b/src/Admin/Startup.cs @@ -1,6 +1,6 @@ using System.Globalization; +using Bit.Admin.IdentityServer; using Bit.Core.Context; -using Bit.Core.Identity; using Bit.Core.Settings; using Bit.Core.Utilities; using Bit.SharedWeb.Utilities; diff --git a/src/Admin/Views/Organizations/_ViewInformation.cshtml b/src/Admin/Views/Organizations/_ViewInformation.cshtml index 3c5a5ca8a..24e69d298 100644 --- a/src/Admin/Views/Organizations/_ViewInformation.cshtml +++ b/src/Admin/Views/Organizations/_ViewInformation.cshtml @@ -11,7 +11,7 @@
Users
- @Model.UserCount / @(Model.Organization.Seats?.ToString() ?? "-") + @Model.OccupiedSeatCount / @(Model.Organization.Seats?.ToString() ?? "-") (@Model.UserInvitedCount / @Model.UserAcceptedCount / @Model.UserConfirmedCount) diff --git a/src/Api/Controllers/AccountsController.cs b/src/Api/Controllers/AccountsController.cs index 5d7e6511a..5f40cd96a 100644 --- a/src/Api/Controllers/AccountsController.cs +++ b/src/Api/Controllers/AccountsController.cs @@ -35,6 +35,7 @@ public class AccountsController : Controller private readonly IUserService _userService; private readonly ISendRepository _sendRepository; private readonly ISendService _sendService; + private readonly ICaptchaValidationService _captchaValidationService; public AccountsController( GlobalSettings globalSettings, @@ -47,7 +48,8 @@ public class AccountsController : Controller IUserRepository userRepository, IUserService userService, ISendRepository sendRepository, - ISendService sendService) + ISendService sendService, + ICaptchaValidationService captchaValidationService) { _cipherRepository = cipherRepository; _folderRepository = folderRepository; @@ -60,11 +62,12 @@ public class AccountsController : Controller _userService = userService; _sendRepository = sendRepository; _sendService = sendService; + _captchaValidationService = captchaValidationService; } #region DEPRECATED (Moved to Identity Service) - [Obsolete("2022-01-12 Moved to Identity, left for backwards compatability with older clients")] + [Obsolete("TDL-136 Moved to Identity (2022-01-12 cloud, 2022-09-19 self-hosted), left for backwards compatability with older clients.")] [HttpPost("prelogin")] [AllowAnonymous] public async Task PostPrelogin([FromBody] PreloginRequestModel model) @@ -81,17 +84,19 @@ public class AccountsController : Controller return new PreloginResponseModel(kdfInformation); } - [Obsolete("2022-01-12 Moved to Identity, left for backwards compatability with older clients")] + [Obsolete("TDL-136 Moved to Identity (2022-01-12 cloud, 2022-09-19 self-hosted), left for backwards compatability with older clients.")] [HttpPost("register")] [AllowAnonymous] [CaptchaProtected] - public async Task PostRegister([FromBody] RegisterRequestModel model) + public async Task PostRegister([FromBody] RegisterRequestModel model) { - var result = await _userService.RegisterUserAsync(model.ToUser(), model.MasterPasswordHash, + var user = model.ToUser(); + var result = await _userService.RegisterUserAsync(user, model.MasterPasswordHash, model.Token, model.OrganizationUserId); if (result.Succeeded) { - return; + var captchaBypassToken = _captchaValidationService.GenerateCaptchaBypassToken(user); + return new RegisterResponseModel(captchaBypassToken); } foreach (var error in result.Errors.Where(e => e.Code != "DuplicateUserName")) diff --git a/src/Api/Controllers/AuthRequestsController.cs b/src/Api/Controllers/AuthRequestsController.cs new file mode 100644 index 000000000..def2997ff --- /dev/null +++ b/src/Api/Controllers/AuthRequestsController.cs @@ -0,0 +1,146 @@ +using Bit.Api.Models.Request; +using Bit.Api.Models.Response; +using Bit.Core.Context; +using Bit.Core.Entities; +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; + +[Route("auth-requests")] +[Authorize("Application")] +public class AuthRequestsController : Controller +{ + private readonly IUserRepository _userRepository; + private readonly IDeviceRepository _deviceRepository; + private readonly IUserService _userService; + private readonly IAuthRequestRepository _authRequestRepository; + private readonly ICurrentContext _currentContext; + private readonly IPushNotificationService _pushNotificationService; + private readonly IGlobalSettings _globalSettings; + + public AuthRequestsController( + IUserRepository userRepository, + IDeviceRepository deviceRepository, + IUserService userService, + IAuthRequestRepository authRequestRepository, + ICurrentContext currentContext, + IPushNotificationService pushNotificationService, + IGlobalSettings globalSettings) + { + _userRepository = userRepository; + _deviceRepository = deviceRepository; + _userService = userService; + _authRequestRepository = authRequestRepository; + _currentContext = currentContext; + _pushNotificationService = pushNotificationService; + _globalSettings = globalSettings; + } + + [HttpGet("")] + public async Task> Get() + { + var userId = _userService.GetProperUserId(User).Value; + var authRequests = await _authRequestRepository.GetManyByUserIdAsync(userId); + var responses = authRequests.Select(a => new AuthRequestResponseModel(a, _globalSettings.BaseServiceUri.Vault)).ToList(); + return new ListResponseModel(responses); + } + + [HttpGet("{id}")] + public async Task Get(string id) + { + var userId = _userService.GetProperUserId(User).Value; + var authRequest = await _authRequestRepository.GetByIdAsync(new Guid(id)); + if (authRequest == null || authRequest.UserId != userId) + { + throw new NotFoundException(); + } + + return new AuthRequestResponseModel(authRequest, _globalSettings.BaseServiceUri.Vault); + } + + [HttpGet("{id}/response")] + [AllowAnonymous] + public async Task GetResponse(string id, [FromQuery] string code) + { + var authRequest = await _authRequestRepository.GetByIdAsync(new Guid(id)); + if (authRequest == null || code != authRequest.AccessCode || authRequest.GetExpirationDate() < DateTime.UtcNow) + { + throw new NotFoundException(); + } + + return new AuthRequestResponseModel(authRequest, _globalSettings.BaseServiceUri.Vault); + } + + [HttpPost("")] + [AllowAnonymous] + public async Task Post([FromBody] AuthRequestCreateRequestModel model) + { + var user = await _userRepository.GetByEmailAsync(model.Email); + if (user == null) + { + throw new NotFoundException(); + } + if (!_currentContext.DeviceType.HasValue) + { + throw new BadRequestException("Device type not provided."); + } + if (_globalSettings.PasswordlessAuth.KnownDevicesOnly) + { + var devices = await _deviceRepository.GetManyByUserIdAsync(user.Id); + if (devices == null || !devices.Any(d => d.Identifier == model.DeviceIdentifier)) + { + throw new BadRequestException("Login with device is only available on devices that have been previously logged in."); + } + } + + var authRequest = new AuthRequest + { + RequestDeviceIdentifier = model.DeviceIdentifier, + RequestDeviceType = _currentContext.DeviceType.Value, + RequestIpAddress = _currentContext.IpAddress, + AccessCode = model.AccessCode, + PublicKey = model.PublicKey, + UserId = user.Id, + Type = model.Type.Value, + RequestFingerprint = model.FingerprintPhrase + }; + authRequest = await _authRequestRepository.CreateAsync(authRequest); + await _pushNotificationService.PushAuthRequestAsync(authRequest); + var r = new AuthRequestResponseModel(authRequest, _globalSettings.BaseServiceUri.Vault); + return r; + } + + [HttpPut("{id}")] + public async Task Put(string id, [FromBody] AuthRequestUpdateRequestModel model) + { + var userId = _userService.GetProperUserId(User).Value; + var authRequest = await _authRequestRepository.GetByIdAsync(new Guid(id)); + if (authRequest == null || authRequest.UserId != userId || authRequest.GetExpirationDate() < DateTime.UtcNow) + { + throw new NotFoundException(); + } + + var device = await _deviceRepository.GetByIdentifierAsync(model.DeviceIdentifier); + if (device == null) + { + throw new BadRequestException("Invalid device."); + } + + if (model.RequestApproved) + { + authRequest.Key = model.Key; + authRequest.MasterPasswordHash = model.MasterPasswordHash; + authRequest.ResponseDeviceId = device.Id; + authRequest.ResponseDate = DateTime.UtcNow; + await _authRequestRepository.ReplaceAsync(authRequest); + await _pushNotificationService.PushAuthRequestResponseAsync(authRequest); + } + + return new AuthRequestResponseModel(authRequest, _globalSettings.BaseServiceUri.Vault); + } +} diff --git a/src/Api/Controllers/DevicesController.cs b/src/Api/Controllers/DevicesController.cs index 77fb34c64..cc4ae0f12 100644 --- a/src/Api/Controllers/DevicesController.cs +++ b/src/Api/Controllers/DevicesController.cs @@ -16,15 +16,18 @@ public class DevicesController : Controller private readonly IDeviceRepository _deviceRepository; private readonly IDeviceService _deviceService; private readonly IUserService _userService; + private readonly IUserRepository _userRepository; public DevicesController( IDeviceRepository deviceRepository, IDeviceService deviceService, - IUserService userService) + IUserService userService, + IUserRepository userRepository) { _deviceRepository = deviceRepository; _deviceService = deviceService; _userService = userService; + _userRepository = userRepository; } [HttpGet("{id}")] @@ -126,4 +129,23 @@ public class DevicesController : Controller await _deviceService.DeleteAsync(device); } + + [AllowAnonymous] + [HttpGet("knowndevice/{email}/{identifier}")] + public async Task GetByIdentifier(string email, string identifier) + { + if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(identifier)) + { + throw new BadRequestException("Please provide an email and device identifier"); + } + + var user = await _userRepository.GetByEmailAsync(email); + if (user == null) + { + return false; + } + + var device = await _deviceRepository.GetByIdentifierAsync(identifier, user.Id); + return device != null; + } } diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index f38b0dbc3..717fc2a26 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -537,7 +537,7 @@ public class OrganizationsController : Controller } [HttpGet("{id}/api-key-information/{type?}")] - public async Task> ApiKeyInformation(Guid id, OrganizationApiKeyType? type) + public async Task> ApiKeyInformation(Guid id, [FromRoute] OrganizationApiKeyType? type) { if (!await HasApiKeyAccessAsync(id, type)) { diff --git a/src/Api/Controllers/TwoFactorController.cs b/src/Api/Controllers/TwoFactorController.cs index 6ed2b8796..b07f65c4f 100644 --- a/src/Api/Controllers/TwoFactorController.cs +++ b/src/Api/Controllers/TwoFactorController.cs @@ -229,6 +229,7 @@ public class TwoFactorController : Controller } [HttpPost("get-webauthn-challenge")] + [ApiExplorerSettings(IgnoreApi = true)] // Disable Swagger due to CredentialCreateOptions not converting properly public async Task GetWebAuthnChallenge([FromBody] SecretVerificationRequestModel model) { var user = await CheckAsync(model, true); diff --git a/src/Api/Models/Request/AuthRequestRequestModel.cs b/src/Api/Models/Request/AuthRequestRequestModel.cs new file mode 100644 index 000000000..8dab8036b --- /dev/null +++ b/src/Api/Models/Request/AuthRequestRequestModel.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; +using Bit.Core.Enums; +using Newtonsoft.Json; + +namespace Bit.Api.Models.Request; + +public class AuthRequestCreateRequestModel +{ + [Required] + public string Email { get; set; } + [Required] + public string PublicKey { get; set; } + [Required] + public string DeviceIdentifier { get; set; } + [Required] + [StringLength(25)] + public string AccessCode { get; set; } + [Required] + public AuthRequestType? Type { get; set; } + [Required] + public string FingerprintPhrase { get; set; } +} + +public class AuthRequestUpdateRequestModel +{ + public string Key { get; set; } + public string MasterPasswordHash { get; set; } + [Required] + public string DeviceIdentifier { get; set; } + [Required] + public bool RequestApproved { get; set; } +} diff --git a/src/Api/Models/Response/AuthRequestResponseModel.cs b/src/Api/Models/Response/AuthRequestResponseModel.cs new file mode 100644 index 000000000..348355e95 --- /dev/null +++ b/src/Api/Models/Response/AuthRequestResponseModel.cs @@ -0,0 +1,43 @@ +using System.ComponentModel.DataAnnotations; +using System.Reflection; +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Models.Api; + +namespace Bit.Api.Models.Response; + +public class AuthRequestResponseModel : ResponseModel +{ + public AuthRequestResponseModel(AuthRequest authRequest, string vaultUri, string obj = "auth-request") + : base(obj) + { + if (authRequest == null) + { + throw new ArgumentNullException(nameof(authRequest)); + } + + Id = authRequest.Id.ToString(); + PublicKey = authRequest.PublicKey; + RequestDeviceType = authRequest.RequestDeviceType.GetType().GetMember(authRequest.RequestDeviceType.ToString()) + .FirstOrDefault()?.GetCustomAttribute()?.GetName(); + RequestIpAddress = authRequest.RequestIpAddress; + RequestFingerprint = authRequest.RequestFingerprint; + Key = authRequest.Key; + MasterPasswordHash = authRequest.MasterPasswordHash; + CreationDate = authRequest.CreationDate; + RequestApproved = !string.IsNullOrWhiteSpace(Key) && + (authRequest.Type == AuthRequestType.Unlock || !string.IsNullOrWhiteSpace(MasterPasswordHash)); + Origin = new Uri(vaultUri).Host; + } + + public string Id { get; set; } + public string PublicKey { get; set; } + public string RequestDeviceType { get; set; } + public string RequestIpAddress { get; set; } + public string RequestFingerprint { get; set; } + public string Key { get; set; } + public string MasterPasswordHash { get; set; } + public DateTime CreationDate { get; set; } + public bool RequestApproved { get; set; } + public string Origin { get; set; } +} diff --git a/src/Api/Program.cs b/src/Api/Program.cs index b7e80d6c2..b44ffa835 100644 --- a/src/Api/Program.cs +++ b/src/Api/Program.cs @@ -1,7 +1,6 @@ using AspNetCoreRateLimit; using Bit.Core.Utilities; using Microsoft.IdentityModel.Tokens; -using Serilog.Events; namespace Bit.Api; @@ -16,7 +15,7 @@ public class Program { webBuilder.UseStartup(); webBuilder.ConfigureLogging((hostingContext, logging) => - logging.AddSerilog(hostingContext, e => + logging.AddSerilog(hostingContext, (e, globalSettings) => { var context = e.Properties["SourceContext"].ToString(); if (e.Exception != null && @@ -26,19 +25,19 @@ public class Program return false; } - if (e.Level == LogEventLevel.Information && + if ( context.Contains(typeof(IpRateLimitMiddleware).FullName)) { - return true; + return e.Level >= globalSettings.MinLogLevel.ApiSettings.IpRateLimit; } if (context.Contains("IdentityServer4.Validation.TokenValidator") || context.Contains("IdentityServer4.Validation.TokenRequestValidator")) { - return e.Level > LogEventLevel.Error; + return e.Level >= globalSettings.MinLogLevel.ApiSettings.IdentityToken; } - return e.Level >= LogEventLevel.Error; + return e.Level >= globalSettings.MinLogLevel.ApiSettings.Default; })); }) .Build() diff --git a/src/Api/Utilities/ServiceCollectionExtensions.cs b/src/Api/Utilities/ServiceCollectionExtensions.cs index ff0ff0705..be3c98be7 100644 --- a/src/Api/Utilities/ServiceCollectionExtensions.cs +++ b/src/Api/Utilities/ServiceCollectionExtensions.cs @@ -28,7 +28,7 @@ public static class ServiceCollectionExtensions }); config.SwaggerDoc("internal", new OpenApiInfo { Title = "Bitwarden Internal API", Version = "latest" }); - config.AddSecurityDefinition("OAuth2 Client Credentials", new OpenApiSecurityScheme + config.AddSecurityDefinition("oauth2-client-credentials", new OpenApiSecurityScheme { Type = SecuritySchemeType.OAuth2, Flows = new OpenApiOAuthFlows @@ -52,7 +52,7 @@ public static class ServiceCollectionExtensions Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, - Id = "OAuth2 Client Credentials" + Id = "oauth2-client-credentials" }, }, new[] { "api.organization" } diff --git a/src/Billing/Program.cs b/src/Billing/Program.cs index d7ebadd92..33e266542 100644 --- a/src/Billing/Program.cs +++ b/src/Billing/Program.cs @@ -1,5 +1,4 @@ using Bit.Core.Utilities; -using Serilog.Events; namespace Bit.Billing; @@ -13,13 +12,12 @@ public class Program { webBuilder.UseStartup(); webBuilder.ConfigureLogging((hostingContext, logging) => - logging.AddSerilog(hostingContext, e => + logging.AddSerilog(hostingContext, (e, globalSettings) => { var context = e.Properties["SourceContext"].ToString(); - if (e.Level == LogEventLevel.Information && - (context.StartsWith("\"Bit.Billing.Jobs") || context.StartsWith("\"Bit.Core.Jobs"))) + if (context.StartsWith("\"Bit.Billing.Jobs") || context.StartsWith("\"Bit.Core.Jobs")) { - return true; + return e.Level >= globalSettings.MinLogLevel.BillingSettings.Jobs; } if (e.Properties.ContainsKey("RequestPath") && @@ -29,7 +27,7 @@ public class Program return false; } - return e.Level >= LogEventLevel.Warning; + return e.Level >= globalSettings.MinLogLevel.BillingSettings.Default; })); }) .Build() diff --git a/src/Core/Entities/AuthRequest.cs b/src/Core/Entities/AuthRequest.cs new file mode 100644 index 000000000..75646ccd5 --- /dev/null +++ b/src/Core/Entities/AuthRequest.cs @@ -0,0 +1,41 @@ +using System.ComponentModel.DataAnnotations; +using Bit.Core.Utilities; + +namespace Bit.Core.Entities; + +public class AuthRequest : ITableObject +{ + public Guid Id { get; set; } + public Guid UserId { get; set; } + public Enums.AuthRequestType Type { get; set; } + [MaxLength(50)] + public string RequestDeviceIdentifier { get; set; } + public Enums.DeviceType RequestDeviceType { get; set; } + [MaxLength(50)] + public string RequestIpAddress { get; set; } + public string RequestFingerprint { get; set; } + public Guid? ResponseDeviceId { get; set; } + [MaxLength(25)] + public string AccessCode { get; set; } + public string PublicKey { get; set; } + public string Key { get; set; } + public string MasterPasswordHash { get; set; } + public DateTime CreationDate { get; set; } = DateTime.UtcNow; + public DateTime? ResponseDate { get; set; } + public DateTime? AuthenticationDate { get; set; } + + public void SetNewId() + { + Id = CoreHelpers.GenerateComb(); + } + + public bool IsSpent() + { + return ResponseDate.HasValue || AuthenticationDate.HasValue || GetExpirationDate() < DateTime.UtcNow; + } + + public DateTime GetExpirationDate() + { + return CreationDate.AddMinutes(15); + } +} diff --git a/src/Core/Enums/AuthRequestType.cs b/src/Core/Enums/AuthRequestType.cs new file mode 100644 index 000000000..e8e88465c --- /dev/null +++ b/src/Core/Enums/AuthRequestType.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.Enums; + +public enum AuthRequestType : byte +{ + AuthenticateAndUnlock = 0, + Unlock = 1 +} diff --git a/src/Core/Enums/PushType.cs b/src/Core/Enums/PushType.cs index 9054d1d40..82029e921 100644 --- a/src/Core/Enums/PushType.cs +++ b/src/Core/Enums/PushType.cs @@ -20,4 +20,7 @@ public enum PushType : byte SyncSendCreate = 12, SyncSendUpdate = 13, SyncSendDelete = 14, + + AuthRequest = 15, + AuthRequestResponse = 16, } diff --git a/src/Core/Exceptions/BadRequestException.cs b/src/Core/Exceptions/BadRequestException.cs index d18bd041e..e7268b6c5 100644 --- a/src/Core/Exceptions/BadRequestException.cs +++ b/src/Core/Exceptions/BadRequestException.cs @@ -4,6 +4,9 @@ namespace Bit.Core.Exceptions; public class BadRequestException : Exception { + public BadRequestException() : base() + { } + public BadRequestException(string message) : base(message) { } diff --git a/src/Core/Exceptions/ConflictException.cs b/src/Core/Exceptions/ConflictException.cs new file mode 100644 index 000000000..8a3186ac8 --- /dev/null +++ b/src/Core/Exceptions/ConflictException.cs @@ -0,0 +1,3 @@ +namespace Bit.Core.Exceptions; + +public class ConflictException : Exception { } diff --git a/src/Core/Exceptions/NotFoundException.cs b/src/Core/Exceptions/NotFoundException.cs index 3f52f792c..70769d41e 100644 --- a/src/Core/Exceptions/NotFoundException.cs +++ b/src/Core/Exceptions/NotFoundException.cs @@ -1,3 +1,11 @@ namespace Bit.Core.Exceptions; -public class NotFoundException : Exception { } +public class NotFoundException : Exception +{ + public NotFoundException() : base() + { } + + public NotFoundException(string message) + : base(message) + { } +} diff --git a/src/Core/Identity/ReadOnlyDatabaseIdentityUserStore.cs b/src/Core/Identity/ReadOnlyDatabaseIdentityUserStore.cs deleted file mode 100644 index 70d3da007..000000000 --- a/src/Core/Identity/ReadOnlyDatabaseIdentityUserStore.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Bit.Core.Repositories; -using Bit.Core.Services; -using Microsoft.AspNetCore.Identity; - -namespace Bit.Core.Identity; - -public class ReadOnlyDatabaseIdentityUserStore : ReadOnlyIdentityUserStore -{ - private readonly IUserService _userService; - private readonly IUserRepository _userRepository; - - public ReadOnlyDatabaseIdentityUserStore( - IUserService userService, - IUserRepository userRepository) - { - _userService = userService; - _userRepository = userRepository; - } - - public override async Task FindByEmailAsync(string normalizedEmail, - CancellationToken cancellationToken = default(CancellationToken)) - { - var user = await _userRepository.GetByEmailAsync(normalizedEmail); - return user?.ToIdentityUser(await _userService.TwoFactorIsEnabledAsync(user)); - } - - public override async Task FindByIdAsync(string userId, - CancellationToken cancellationToken = default(CancellationToken)) - { - if (!Guid.TryParse(userId, out var userIdGuid)) - { - return null; - } - - var user = await _userRepository.GetByIdAsync(userIdGuid); - return user?.ToIdentityUser(await _userService.TwoFactorIsEnabledAsync(user)); - } -} diff --git a/src/Core/Models/Api/Response/Accounts/ICaptchaProtectedResponseModel.cs b/src/Core/Models/Api/Response/Accounts/ICaptchaProtectedResponseModel.cs new file mode 100644 index 000000000..863480b27 --- /dev/null +++ b/src/Core/Models/Api/Response/Accounts/ICaptchaProtectedResponseModel.cs @@ -0,0 +1,6 @@ +namespace Bit.Core.Models.Api.Response.Accounts; + +public interface ICaptchaProtectedResponseModel +{ + public string CaptchaBypassToken { get; set; } +} diff --git a/src/Core/Models/Api/Response/Accounts/RegisterResponseModel.cs b/src/Core/Models/Api/Response/Accounts/RegisterResponseModel.cs new file mode 100644 index 000000000..857fc8a87 --- /dev/null +++ b/src/Core/Models/Api/Response/Accounts/RegisterResponseModel.cs @@ -0,0 +1,12 @@ +namespace Bit.Core.Models.Api.Response.Accounts; + +public class RegisterResponseModel : ResponseModel, ICaptchaProtectedResponseModel +{ + public RegisterResponseModel(string captchaBypassToken) + : base("register") + { + CaptchaBypassToken = captchaBypassToken; + } + + public string CaptchaBypassToken { get; set; } +} diff --git a/src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserUserDetails.cs b/src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserUserDetails.cs index ff28d1f3c..ce9cf64a5 100644 --- a/src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserUserDetails.cs +++ b/src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserUserDetails.cs @@ -56,4 +56,12 @@ public class OrganizationUserUserDetails : IExternal, ITwoFactorProvidersUser { return Premium.GetValueOrDefault(false); } + + public bool OccupiesOrganizationSeat + { + get + { + return Status != OrganizationUserStatusType.Revoked; + } + } } diff --git a/src/Core/Models/PushNotification.cs b/src/Core/Models/PushNotification.cs index 4cbdae8b6..37b3b25c0 100644 --- a/src/Core/Models/PushNotification.cs +++ b/src/Core/Models/PushNotification.cs @@ -44,3 +44,9 @@ public class SyncSendPushNotification public Guid UserId { get; set; } public DateTime RevisionDate { get; set; } } + +public class AuthRequestPushNotification +{ + public Guid UserId { get; set; } + public Guid Id { get; set; } +} diff --git a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs index e428318c5..4a6fbf3f7 100644 --- a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs @@ -12,6 +12,7 @@ using Bit.Core.Settings; using Bit.Core.Tokens; using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace Bit.Core.OrganizationFeatures; @@ -70,7 +71,8 @@ public static class OrganizationServiceCollectionExtensions new DataProtectorTokenFactory( OrganizationSponsorshipOfferTokenable.ClearTextPrefix, OrganizationSponsorshipOfferTokenable.DataProtectorPurpose, - serviceProvider.GetDataProtectionProvider()) + serviceProvider.GetDataProtectionProvider(), + serviceProvider.GetRequiredService>>()) ); } } diff --git a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SelfHosted/SelfHostedSyncSponsorshipsCommand.cs b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SelfHosted/SelfHostedSyncSponsorshipsCommand.cs index df293c3a7..be588cd4e 100644 --- a/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SelfHosted/SelfHostedSyncSponsorshipsCommand.cs +++ b/src/Core/OrganizationFeatures/OrganizationSponsorships/FamiliesForEnterprise/SelfHosted/SelfHostedSyncSponsorshipsCommand.cs @@ -8,7 +8,6 @@ using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterpri using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; -using Bit.Core.Utilities; using Microsoft.Extensions.Logging; namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted; @@ -71,7 +70,7 @@ public class SelfHostedSyncSponsorshipsCommand : BaseIdentityClientService, ISel } var syncedSponsorships = new List(); - foreach (var orgSponsorshipsBatch in CoreHelpers.Batch(organizationSponsorshipsDict.Values, 1000)) + foreach (var orgSponsorshipsBatch in organizationSponsorshipsDict.Values.Chunk(1000)) { var response = await SendAsync(HttpMethod.Post, "organization/sponsorship/sync", new OrganizationSponsorshipSyncRequestModel { diff --git a/src/Core/Repositories/IAuthRequestRepository.cs b/src/Core/Repositories/IAuthRequestRepository.cs new file mode 100644 index 000000000..b8bf0625d --- /dev/null +++ b/src/Core/Repositories/IAuthRequestRepository.cs @@ -0,0 +1,9 @@ +using Bit.Core.Entities; + +namespace Bit.Core.Repositories; + +public interface IAuthRequestRepository : IRepository +{ + Task DeleteExpiredAsync(); + Task> GetManyByUserIdAsync(Guid userId); +} diff --git a/src/Core/Services/IOrganizationService.cs b/src/Core/Services/IOrganizationService.cs index 3bd3e1f6e..45f91c8bb 100644 --- a/src/Core/Services/IOrganizationService.cs +++ b/src/Core/Services/IOrganizationService.cs @@ -64,4 +64,5 @@ public interface IOrganizationService Task RestoreUserAsync(OrganizationUser organizationUser, Guid? restoringUserId, IUserService userService); Task>> RestoreUsersAsync(Guid organizationId, IEnumerable organizationUserIds, Guid? restoringUserId, IUserService userService); + Task GetOccupiedSeatCount(Organization organization); } diff --git a/src/Core/Services/IPushNotificationService.cs b/src/Core/Services/IPushNotificationService.cs index 34e98515f..0efddb4ca 100644 --- a/src/Core/Services/IPushNotificationService.cs +++ b/src/Core/Services/IPushNotificationService.cs @@ -19,6 +19,8 @@ public interface IPushNotificationService Task PushSyncSendCreateAsync(Send send); Task PushSyncSendUpdateAsync(Send send); Task PushSyncSendDeleteAsync(Send send); + Task PushAuthRequestAsync(AuthRequest authRequest); + Task PushAuthRequestResponseAsync(AuthRequest authRequest); Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, string deviceId = null); Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, string deviceId = null); diff --git a/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs b/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs index fb7bcafca..ab5bdfbe5 100644 --- a/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs +++ b/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs @@ -130,6 +130,27 @@ public class AzureQueuePushNotificationService : IPushNotificationService await SendMessageAsync(type, message, false); } + public async Task PushAuthRequestAsync(AuthRequest authRequest) + { + await PushAuthRequestAsync(authRequest, PushType.AuthRequest); + } + + public async Task PushAuthRequestResponseAsync(AuthRequest authRequest) + { + await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse); + } + + private async Task PushAuthRequestAsync(AuthRequest authRequest, PushType type) + { + var message = new AuthRequestPushNotification + { + Id = authRequest.Id, + UserId = authRequest.UserId + }; + + await SendMessageAsync(type, message, true); + } + public async Task PushSyncSendCreateAsync(Send send) { await PushSendAsync(send, PushType.SyncSendCreate); diff --git a/src/Core/Services/Implementations/CipherService.cs b/src/Core/Services/Implementations/CipherService.cs index d114ba416..21bb4a857 100644 --- a/src/Core/Services/Implementations/CipherService.cs +++ b/src/Core/Services/Implementations/CipherService.cs @@ -403,7 +403,7 @@ public class CipherService : ICipherService var events = deletingCiphers.Select(c => new Tuple(c, EventType.Cipher_Deleted, null)); - foreach (var eventsBatch in events.Batch(100)) + foreach (var eventsBatch in events.Chunk(100)) { await _eventService.LogCipherEventsAsync(eventsBatch); } @@ -574,7 +574,7 @@ public class CipherService : ICipherService var events = cipherInfos.Select(c => new Tuple(c.cipher, EventType.Cipher_Shared, null)); - foreach (var eventsBatch in events.Batch(100)) + foreach (var eventsBatch in events.Chunk(100)) { await _eventService.LogCipherEventsAsync(eventsBatch); } @@ -791,7 +791,7 @@ public class CipherService : ICipherService var events = deletingCiphers.Select(c => new Tuple(c, EventType.Cipher_SoftDeleted, null)); - foreach (var eventsBatch in events.Batch(100)) + foreach (var eventsBatch in events.Chunk(100)) { await _eventService.LogCipherEventsAsync(eventsBatch); } @@ -840,7 +840,7 @@ public class CipherService : ICipherService c.DeletedDate = null; return new Tuple(c, EventType.Cipher_Restored, null); }); - foreach (var eventsBatch in events.Batch(100)) + foreach (var eventsBatch in events.Chunk(100)) { await _eventService.LogCipherEventsAsync(eventsBatch); } diff --git a/src/Core/Services/Implementations/MultiServicePushNotificationService.cs b/src/Core/Services/Implementations/MultiServicePushNotificationService.cs index 4e1678da6..0e0b3d3a3 100644 --- a/src/Core/Services/Implementations/MultiServicePushNotificationService.cs +++ b/src/Core/Services/Implementations/MultiServicePushNotificationService.cs @@ -133,6 +133,18 @@ public class MultiServicePushNotificationService : IPushNotificationService return Task.FromResult(0); } + public Task PushAuthRequestAsync(AuthRequest authRequest) + { + PushToServices((s) => s.PushAuthRequestAsync(authRequest)); + return Task.FromResult(0); + } + + public Task PushAuthRequestResponseAsync(AuthRequest authRequest) + { + PushToServices((s) => s.PushAuthRequestResponseAsync(authRequest)); + return Task.FromResult(0); + } + public Task PushSyncSendDeleteAsync(Send send) { PushToServices((s) => s.PushSyncSendDeleteAsync(send)); diff --git a/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs b/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs index fbd7ab9ce..908dcb76d 100644 --- a/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs +++ b/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs @@ -167,6 +167,27 @@ public class NotificationHubPushNotificationService : IPushNotificationService } } + public async Task PushAuthRequestAsync(AuthRequest authRequest) + { + await PushAuthRequestAsync(authRequest, PushType.AuthRequest); + } + + public async Task PushAuthRequestResponseAsync(AuthRequest authRequest) + { + await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse); + } + + private async Task PushAuthRequestAsync(AuthRequest authRequest, PushType type) + { + var message = new AuthRequestPushNotification + { + Id = authRequest.Id, + UserId = authRequest.UserId + }; + + await SendPayloadToUserAsync(authRequest.UserId, type, message, true); + } + private async Task SendPayloadToUserAsync(Guid userId, PushType type, object payload, bool excludeCurrentContext) { await SendPayloadToUserAsync(userId.ToString(), type, payload, GetContextIdentifier(excludeCurrentContext)); diff --git a/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs b/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs index 144178f84..bff7f392f 100644 --- a/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs +++ b/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs @@ -137,6 +137,27 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService await SendMessageAsync(type, message, false); } + public async Task PushAuthRequestAsync(AuthRequest authRequest) + { + await PushAuthRequestAsync(authRequest, PushType.AuthRequest); + } + + public async Task PushAuthRequestResponseAsync(AuthRequest authRequest) + { + await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse); + } + + private async Task PushAuthRequestAsync(AuthRequest authRequest, PushType type) + { + var message = new AuthRequestPushNotification + { + Id = authRequest.Id, + UserId = authRequest.UserId + }; + + await SendMessageAsync(type, message, true); + } + public async Task PushSyncSendCreateAsync(Send send) { await PushSendAsync(send, PushType.SyncSendCreate); diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index d9551811b..ce2b33ec5 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -44,7 +44,6 @@ public class OrganizationService : IOrganizationService private readonly ICurrentContext _currentContext; private readonly ILogger _logger; - public OrganizationService( IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, @@ -199,10 +198,10 @@ public class OrganizationService : IOrganizationService (newPlan.HasAdditionalSeatsOption ? upgrade.AdditionalSeats : 0)); if (!organization.Seats.HasValue || organization.Seats.Value > newPlanSeats) { - var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id); - if (userCount > newPlanSeats) + var occupiedSeats = await GetOccupiedSeatCount(organization); + if (occupiedSeats > newPlanSeats) { - throw new BadRequestException($"Your organization currently has {userCount} seats filled. " + + throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " + $"Your new plan only has ({newPlanSeats}) seats. Remove some users."); } } @@ -494,10 +493,10 @@ public class OrganizationService : IOrganizationService if (!organization.Seats.HasValue || organization.Seats.Value > newSeatTotal) { - var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id); - if (userCount > newSeatTotal) + var occupiedSeats = await GetOccupiedSeatCount(organization); + if (occupiedSeats > newSeatTotal) { - throw new BadRequestException($"Your organization currently has {userCount} seats filled. " + + throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " + $"Your new plan only has ({newSeatTotal}) seats. Remove some users."); } } @@ -861,10 +860,10 @@ public class OrganizationService : IOrganizationService if (license.Seats.HasValue && (!organization.Seats.HasValue || organization.Seats.Value > license.Seats.Value)) { - var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id); - if (userCount > license.Seats.Value) + var occupiedSeats = await GetOccupiedSeatCount(organization); + if (occupiedSeats > license.Seats.Value) { - throw new BadRequestException($"Your organization currently has {userCount} seats filled. " + + throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " + $"Your new license only has ({license.Seats.Value}) seats. Remove some users."); } } @@ -1138,8 +1137,8 @@ public class OrganizationService : IOrganizationService organizationId, invites.SelectMany(i => i.invite.Emails), false), StringComparer.InvariantCultureIgnoreCase); if (organization.Seats.HasValue) { - var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organizationId); - var availableSeats = organization.Seats.Value - userCount; + var occupiedSeats = await GetOccupiedSeatCount(organization); + var availableSeats = organization.Seats.Value - occupiedSeats; newSeatsRequired = invites.Sum(i => i.invite.Emails.Count()) - existingEmails.Count() - availableSeats; } @@ -1559,7 +1558,7 @@ public class OrganizationService : IOrganizationService organization.MaxAutoscaleSeats.HasValue && organization.MaxAutoscaleSeats.Value < organization.Seats.Value + seatsToAdd) { - return (false, $"Cannot invite new users. Seat limit has been reached."); + return (false, $"Seat limit has been reached."); } return (true, failureReason); @@ -1951,8 +1950,8 @@ public class OrganizationService : IOrganizationService var enoughSeatsAvailable = true; if (organization.Seats.HasValue) { - var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organizationId); - seatsAvailable = organization.Seats.Value - userCount; + var occupiedSeats = await GetOccupiedSeatCount(organization); + seatsAvailable = organization.Seats.Value - occupiedSeats; enoughSeatsAvailable = seatsAvailable >= usersToAdd.Count; } @@ -2324,6 +2323,14 @@ public class OrganizationService : IOrganizationService throw new BadRequestException("Only owners can restore other owners."); } + var organization = await _organizationRepository.GetByIdAsync(organizationUser.OrganizationId); + var occupiedSeats = await GetOccupiedSeatCount(organization); + var availableSeats = organization.Seats.GetValueOrDefault(0) - occupiedSeats; + if (availableSeats < 1) + { + await AutoAddSeatsAsync(organization, 1, DateTime.UtcNow); + } + await CheckPoliciesBeforeRestoreAsync(organizationUser, userService); var status = GetPriorActiveOrganizationUserStatusType(organizationUser); @@ -2345,6 +2352,12 @@ public class OrganizationService : IOrganizationService throw new BadRequestException("Users invalid."); } + var organization = await _organizationRepository.GetByIdAsync(organizationId); + var occupiedSeats = await GetOccupiedSeatCount(organization); + var availableSeats = organization.Seats.GetValueOrDefault(0) - occupiedSeats; + var newSeatsRequired = organizationUserIds.Count() - availableSeats; + await AutoAddSeatsAsync(organization, newSeatsRequired, DateTime.UtcNow); + var deletingUserIsOwner = false; if (restoringUserId.HasValue) { @@ -2455,4 +2468,10 @@ public class OrganizationService : IOrganizationService return status; } + + public async Task GetOccupiedSeatCount(Organization organization) + { + var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organization.Id); + return orgUsers.Count(ou => ou.OccupiesOrganizationSeat); + } } diff --git a/src/Core/Services/Implementations/RelayPushNotificationService.cs b/src/Core/Services/Implementations/RelayPushNotificationService.cs index b3670ad7b..573102ac9 100644 --- a/src/Core/Services/Implementations/RelayPushNotificationService.cs +++ b/src/Core/Services/Implementations/RelayPushNotificationService.cs @@ -167,6 +167,27 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti } } + public async Task PushAuthRequestAsync(AuthRequest authRequest) + { + await PushAuthRequestAsync(authRequest, PushType.AuthRequest); + } + + public async Task PushAuthRequestResponseAsync(AuthRequest authRequest) + { + await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse); + } + + private async Task PushAuthRequestAsync(AuthRequest authRequest, PushType type) + { + var message = new AuthRequestPushNotification + { + Id = authRequest.Id, + UserId = authRequest.UserId + }; + + await SendPayloadToUserAsync(authRequest.UserId, type, message, true); + } + private async Task SendPayloadToUserAsync(Guid userId, PushType type, object payload, bool excludeCurrentContext) { var request = new PushSendRequestModel diff --git a/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs b/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs index ee2c6a498..a99ef96fb 100644 --- a/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs +++ b/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs @@ -81,6 +81,16 @@ public class NoopPushNotificationService : IPushNotificationService return Task.FromResult(0); } + public Task PushAuthRequestAsync(AuthRequest authRequest) + { + return Task.FromResult(0); + } + + public Task PushAuthRequestResponseAsync(AuthRequest authRequest) + { + return Task.FromResult(0); + } + public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, string deviceId = null) { diff --git a/src/Core/Settings/GlobalSettings.cs b/src/Core/Settings/GlobalSettings.cs index 7bb66377d..ef80935b3 100644 --- a/src/Core/Settings/GlobalSettings.cs +++ b/src/Core/Settings/GlobalSettings.cs @@ -1,4 +1,6 @@ -namespace Bit.Core.Settings; +using Bit.Core.Settings.LoggingSettings; + +namespace Bit.Core.Settings; public class GlobalSettings : IGlobalSettings { @@ -23,6 +25,7 @@ public class GlobalSettings : IGlobalSettings set => _logDirectory = value; } public virtual long? LogRollBySizeLimit { get; set; } + public virtual bool EnableDevLogging { get; set; } = false; public virtual string LicenseDirectory { get => BuildDirectory(_licenseDirectory, "/core/licenses"); @@ -58,6 +61,7 @@ public class GlobalSettings : IGlobalSettings public virtual DocumentDbSettings DocumentDb { get; set; } = new DocumentDbSettings(); public virtual SentrySettings Sentry { get; set; } = new SentrySettings(); public virtual SyslogSettings Syslog { get; set; } = new SyslogSettings(); + public virtual ILogLevelSettings MinLogLevel { get; set; } = new LogLevelSettings(); public virtual NotificationHubSettings NotificationHub { get; set; } = new NotificationHubSettings(); public virtual YubicoSettings Yubico { get; set; } = new YubicoSettings(); public virtual DuoSettings Duo { get; set; } = new DuoSettings(); @@ -71,6 +75,7 @@ public class GlobalSettings : IGlobalSettings public virtual ITwoFactorAuthSettings TwoFactorAuth { get; set; } = new TwoFactorAuthSettings(); public virtual DistributedIpRateLimitingSettings DistributedIpRateLimiting { get; set; } = new DistributedIpRateLimitingSettings(); + public virtual IPasswordlessAuthSettings PasswordlessAuth { get; set; } = new PasswordlessAuthSettings(); public string BuildExternalUri(string explicitValue, string name) { @@ -453,6 +458,7 @@ public class GlobalSettings : IGlobalSettings get => string.IsNullOrWhiteSpace(_apiUri) ? "https://api.bitwarden.com" : _apiUri; set => _apiUri = value; } + } public class AmazonSettings @@ -519,4 +525,8 @@ public class GlobalSettings : IGlobalSettings public int SlidingWindowSeconds { get; set; } = 120; } + public class PasswordlessAuthSettings : IPasswordlessAuthSettings + { + public bool KnownDevicesOnly { get; set; } = true; + } } diff --git a/src/Core/Settings/IGlobalSettings.cs b/src/Core/Settings/IGlobalSettings.cs index 1929da1f3..9ed58669c 100644 --- a/src/Core/Settings/IGlobalSettings.cs +++ b/src/Core/Settings/IGlobalSettings.cs @@ -15,4 +15,6 @@ public interface IGlobalSettings IBaseServiceUriSettings BaseServiceUri { get; set; } ITwoFactorAuthSettings TwoFactorAuth { get; set; } ISsoSettings Sso { get; set; } + ILogLevelSettings MinLogLevel { get; set; } + IPasswordlessAuthSettings PasswordlessAuth { get; set; } } diff --git a/src/Core/Settings/ILogLevelSettings.cs b/src/Core/Settings/ILogLevelSettings.cs new file mode 100644 index 000000000..b3cedf083 --- /dev/null +++ b/src/Core/Settings/ILogLevelSettings.cs @@ -0,0 +1,74 @@ +using Serilog.Events; + +namespace Bit.Core.Settings; + +public interface ILogLevelSettings +{ + IBillingLogLevelSettings BillingSettings { get; set; } + IApiLogLevelSettings ApiSettings { get; set; } + IIdentityLogLevelSettings IdentitySettings { get; set; } + IScimLogLevelSettings ScimSettings { get; set; } + ISsoLogLevelSettings SsoSettings { get; set; } + IAdminLogLevelSettings AdminSettings { get; set; } + IEventsLogLevelSettings EventsSettings { get; set; } + IEventsProcessorLogLevelSettings EventsProcessorSettings { get; set; } + IIconsLogLevelSettings IconsSettings { get; set; } + INotificationsLogLevelSettings NotificationsSettings { get; set; } +} + +public interface IBillingLogLevelSettings +{ + LogEventLevel Default { get; set; } + LogEventLevel Jobs { get; set; } +} + +public interface IApiLogLevelSettings +{ + LogEventLevel Default { get; set; } + LogEventLevel IdentityToken { get; set; } + LogEventLevel IpRateLimit { get; set; } +} + +public interface IIdentityLogLevelSettings +{ + LogEventLevel Default { get; set; } + LogEventLevel IdentityToken { get; set; } + LogEventLevel IpRateLimit { get; set; } +} + +public interface IScimLogLevelSettings +{ + LogEventLevel Default { get; set; } +} + +public interface ISsoLogLevelSettings +{ + LogEventLevel Default { get; set; } +} + +public interface IAdminLogLevelSettings +{ + LogEventLevel Default { get; set; } +} + +public interface IEventsLogLevelSettings +{ + LogEventLevel Default { get; set; } + LogEventLevel IdentityToken { get; set; } +} + +public interface IEventsProcessorLogLevelSettings +{ + LogEventLevel Default { get; set; } +} + +public interface IIconsLogLevelSettings +{ + LogEventLevel Default { get; set; } +} + +public interface INotificationsLogLevelSettings +{ + LogEventLevel Default { get; set; } + LogEventLevel IdentityToken { get; set; } +} diff --git a/src/Core/Settings/IPasswordlessAuthSettings.cs b/src/Core/Settings/IPasswordlessAuthSettings.cs new file mode 100644 index 000000000..98bd8112d --- /dev/null +++ b/src/Core/Settings/IPasswordlessAuthSettings.cs @@ -0,0 +1,6 @@ +namespace Bit.Core.Settings; + +public interface IPasswordlessAuthSettings +{ + bool KnownDevicesOnly { get; set; } +} diff --git a/src/Core/Settings/LoggingSettings/AdminLogLevelSettings.cs b/src/Core/Settings/LoggingSettings/AdminLogLevelSettings.cs new file mode 100644 index 000000000..d2c74dd07 --- /dev/null +++ b/src/Core/Settings/LoggingSettings/AdminLogLevelSettings.cs @@ -0,0 +1,8 @@ +using Serilog.Events; + +namespace Bit.Core.Settings.LoggingSettings; + +public class AdminLogLevelSettings : IAdminLogLevelSettings +{ + public LogEventLevel Default { get; set; } = LogEventLevel.Error; +} diff --git a/src/Core/Settings/LoggingSettings/ApiLogLevelSettings.cs b/src/Core/Settings/LoggingSettings/ApiLogLevelSettings.cs new file mode 100644 index 000000000..7961ab7e3 --- /dev/null +++ b/src/Core/Settings/LoggingSettings/ApiLogLevelSettings.cs @@ -0,0 +1,10 @@ +using Serilog.Events; + +namespace Bit.Core.Settings.LoggingSettings; + +public class ApiLogLevelSettings : IApiLogLevelSettings +{ + public LogEventLevel Default { get; set; } = LogEventLevel.Error; + public LogEventLevel IdentityToken { get; set; } = LogEventLevel.Fatal; + public LogEventLevel IpRateLimit { get; set; } = LogEventLevel.Information; +} diff --git a/src/Core/Settings/LoggingSettings/BillingLogLevelSettings.cs b/src/Core/Settings/LoggingSettings/BillingLogLevelSettings.cs new file mode 100644 index 000000000..b9e53e6bc --- /dev/null +++ b/src/Core/Settings/LoggingSettings/BillingLogLevelSettings.cs @@ -0,0 +1,9 @@ +using Serilog.Events; + +namespace Bit.Core.Settings.LoggingSettings; + +public class BillingLogLevelSettings : IBillingLogLevelSettings +{ + public LogEventLevel Default { get; set; } = LogEventLevel.Warning; + public LogEventLevel Jobs { get; set; } = LogEventLevel.Information; +} diff --git a/src/Core/Settings/LoggingSettings/EventsLogLevelSettings.cs b/src/Core/Settings/LoggingSettings/EventsLogLevelSettings.cs new file mode 100644 index 000000000..320174855 --- /dev/null +++ b/src/Core/Settings/LoggingSettings/EventsLogLevelSettings.cs @@ -0,0 +1,9 @@ +using Serilog.Events; + +namespace Bit.Core.Settings.LoggingSettings; + +public class EventsLogLevelSettings : IEventsLogLevelSettings +{ + public LogEventLevel Default { get; set; } = LogEventLevel.Error; + public LogEventLevel IdentityToken { get; set; } = LogEventLevel.Fatal; +} diff --git a/src/Core/Settings/LoggingSettings/EventsProcessorLogLevelSettings.cs b/src/Core/Settings/LoggingSettings/EventsProcessorLogLevelSettings.cs new file mode 100644 index 000000000..5aff18a21 --- /dev/null +++ b/src/Core/Settings/LoggingSettings/EventsProcessorLogLevelSettings.cs @@ -0,0 +1,8 @@ +using Serilog.Events; + +namespace Bit.Core.Settings.LoggingSettings; + +public class EventsProcessorLogLevelSettings : IEventsProcessorLogLevelSettings +{ + public LogEventLevel Default { get; set; } = LogEventLevel.Warning; +} diff --git a/src/Core/Settings/LoggingSettings/IconsLogLevelSettings.cs b/src/Core/Settings/LoggingSettings/IconsLogLevelSettings.cs new file mode 100644 index 000000000..c7b73ba68 --- /dev/null +++ b/src/Core/Settings/LoggingSettings/IconsLogLevelSettings.cs @@ -0,0 +1,8 @@ +using Serilog.Events; + +namespace Bit.Core.Settings.LoggingSettings; + +public class IconsLogLevelSettings : IIconsLogLevelSettings +{ + public LogEventLevel Default { get; set; } = LogEventLevel.Error; +} diff --git a/src/Core/Settings/LoggingSettings/IdentityLogLevelSettings.cs b/src/Core/Settings/LoggingSettings/IdentityLogLevelSettings.cs new file mode 100644 index 000000000..a823cb510 --- /dev/null +++ b/src/Core/Settings/LoggingSettings/IdentityLogLevelSettings.cs @@ -0,0 +1,10 @@ +using Serilog.Events; + +namespace Bit.Core.Settings.LoggingSettings; + +public class IdentityLogLevelSettings : IIdentityLogLevelSettings +{ + public LogEventLevel Default { get; set; } = LogEventLevel.Error; + public LogEventLevel IdentityToken { get; set; } = LogEventLevel.Fatal; + public LogEventLevel IpRateLimit { get; set; } = LogEventLevel.Information; +} diff --git a/src/Core/Settings/LoggingSettings/LogLevelSettings.cs b/src/Core/Settings/LoggingSettings/LogLevelSettings.cs new file mode 100644 index 000000000..1af05ebfd --- /dev/null +++ b/src/Core/Settings/LoggingSettings/LogLevelSettings.cs @@ -0,0 +1,16 @@ + +namespace Bit.Core.Settings.LoggingSettings; + +public class LogLevelSettings : ILogLevelSettings +{ + public IBillingLogLevelSettings BillingSettings { get; set; } = new BillingLogLevelSettings(); + public IApiLogLevelSettings ApiSettings { get; set; } = new ApiLogLevelSettings(); + public IIdentityLogLevelSettings IdentitySettings { get; set; } = new IdentityLogLevelSettings(); + public IScimLogLevelSettings ScimSettings { get; set; } = new ScimLogLevelSettings(); + public ISsoLogLevelSettings SsoSettings { get; set; } = new SsoLogLevelSettings(); + public IAdminLogLevelSettings AdminSettings { get; set; } = new AdminLogLevelSettings(); + public IEventsLogLevelSettings EventsSettings { get; set; } = new EventsLogLevelSettings(); + public IEventsProcessorLogLevelSettings EventsProcessorSettings { get; set; } = new EventsProcessorLogLevelSettings(); + public IIconsLogLevelSettings IconsSettings { get; set; } = new IconsLogLevelSettings(); + public INotificationsLogLevelSettings NotificationsSettings { get; set; } = new NotificationsLogLevelSettings(); +} diff --git a/src/Core/Settings/LoggingSettings/NotificationsLogLevelSettings.cs b/src/Core/Settings/LoggingSettings/NotificationsLogLevelSettings.cs new file mode 100644 index 000000000..3494fbfcc --- /dev/null +++ b/src/Core/Settings/LoggingSettings/NotificationsLogLevelSettings.cs @@ -0,0 +1,9 @@ +using Serilog.Events; + +namespace Bit.Core.Settings.LoggingSettings; + +public class NotificationsLogLevelSettings : INotificationsLogLevelSettings +{ + public LogEventLevel Default { get; set; } = LogEventLevel.Warning; + public LogEventLevel IdentityToken { get; set; } = LogEventLevel.Fatal; +} diff --git a/src/Core/Settings/LoggingSettings/ScimLogLevelSettings.cs b/src/Core/Settings/LoggingSettings/ScimLogLevelSettings.cs new file mode 100644 index 000000000..f297b17e9 --- /dev/null +++ b/src/Core/Settings/LoggingSettings/ScimLogLevelSettings.cs @@ -0,0 +1,8 @@ +using Serilog.Events; + +namespace Bit.Core.Settings.LoggingSettings; + +public class ScimLogLevelSettings : IScimLogLevelSettings +{ + public LogEventLevel Default { get; set; } = LogEventLevel.Warning; +} diff --git a/src/Core/Settings/LoggingSettings/SsoLogLevelSettings.cs b/src/Core/Settings/LoggingSettings/SsoLogLevelSettings.cs new file mode 100644 index 000000000..495ec41fd --- /dev/null +++ b/src/Core/Settings/LoggingSettings/SsoLogLevelSettings.cs @@ -0,0 +1,8 @@ +using Serilog.Events; + +namespace Bit.Core.Settings.LoggingSettings; + +public class SsoLogLevelSettings : ISsoLogLevelSettings +{ + public LogEventLevel Default { get; set; } = LogEventLevel.Error; +} diff --git a/src/Core/Tokens/DataProtectorTokenFactory.cs b/src/Core/Tokens/DataProtectorTokenFactory.cs index e0ec9811f..ad703cb58 100644 --- a/src/Core/Tokens/DataProtectorTokenFactory.cs +++ b/src/Core/Tokens/DataProtectorTokenFactory.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Logging; namespace Bit.Core.Tokens; @@ -6,15 +7,17 @@ public class DataProtectorTokenFactory : IDataProtectorTokenFactory where { private readonly IDataProtector _dataProtector; private readonly string _clearTextPrefix; + private readonly ILogger> _logger; - public DataProtectorTokenFactory(string clearTextPrefix, string purpose, IDataProtectionProvider dataProtectionProvider) + public DataProtectorTokenFactory(string clearTextPrefix, string purpose, IDataProtectionProvider dataProtectionProvider, ILogger> logger) { _dataProtector = dataProtectionProvider.CreateProtector(purpose); _clearTextPrefix = clearTextPrefix; + _logger = logger; } public string Protect(T data) => - data.ToToken().ProtectWith(_dataProtector).WithPrefix(_clearTextPrefix).ToString(); + data.ToToken().ProtectWith(_dataProtector, _logger).WithPrefix(_clearTextPrefix).ToString(); /// /// Unprotect token @@ -24,7 +27,7 @@ public class DataProtectorTokenFactory : IDataProtectorTokenFactory where /// The parsed tokenable /// Throws CryptographicException if fails to unprotect public T Unprotect(string token) => - Tokenable.FromToken(new Token(token).RemovePrefix(_clearTextPrefix).UnprotectWith(_dataProtector).ToString()); + Tokenable.FromToken(new Token(token).RemovePrefix(_clearTextPrefix).UnprotectWith(_dataProtector, _logger).ToString()); public bool TokenValid(string token) { @@ -45,8 +48,9 @@ public class DataProtectorTokenFactory : IDataProtectorTokenFactory where data = Unprotect(token); return true; } - catch + catch (Exception ex) { + _logger.LogInformation(ex, "Failed to unprotect token: {rawToken}", token); data = default; return false; } diff --git a/src/Core/Tokens/Token.cs b/src/Core/Tokens/Token.cs index a50b81fbb..a2b42dd4c 100644 --- a/src/Core/Tokens/Token.cs +++ b/src/Core/Tokens/Token.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Logging; namespace Bit.Core.Tokens; @@ -26,11 +27,28 @@ public class Token return new Token(_token[expectedPrefix.Length..]); } - public Token ProtectWith(IDataProtector dataProtector) => - new(dataProtector.Protect(ToString())); - public Token UnprotectWith(IDataProtector dataProtector) => - new(dataProtector.Unprotect(ToString())); + public Token ProtectWith(IDataProtector dataProtector, ILogger logger) + { + logger.LogDebug("Protecting token: {token}", this); + return new(dataProtector.Protect(ToString())); + } + + public Token UnprotectWith(IDataProtector dataProtector, ILogger logger) + { + var unprotected = ""; + try + { + unprotected = dataProtector.Unprotect(ToString()); + } + catch (Exception e) + { + logger.LogInformation(e, "Failed to unprotect token: {token}", this); + throw; + } + logger.LogDebug("Unprotected token: {token} to {decryptedToken}", this, unprotected); + return new(unprotected); + } public override string ToString() => _token; } diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index 0185281b8..4878109ce 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -70,32 +70,6 @@ public static class CoreHelpers return new Guid(guidArray); } - public static IEnumerable> Batch(this IEnumerable source, int size) - { - T[] bucket = null; - var count = 0; - foreach (var item in source) - { - if (bucket == null) - { - bucket = new T[size]; - } - bucket[count++] = item; - if (count != size) - { - continue; - } - yield return bucket.Select(x => x); - bucket = null; - count = 0; - } - // Return the last bucket with all remaining elements - if (bucket != null && count > 0) - { - yield return bucket.Take(count); - } - } - public static string CleanCertificateThumbprint(string thumbprint) { // Clean possible garbage characters from thumbprint copy/paste diff --git a/src/Core/Utilities/LoggerFactoryExtensions.cs b/src/Core/Utilities/LoggerFactoryExtensions.cs index 792225cdf..f6ea43882 100644 --- a/src/Core/Utilities/LoggerFactoryExtensions.cs +++ b/src/Core/Utilities/LoggerFactoryExtensions.cs @@ -20,7 +20,7 @@ public static class LoggerFactoryExtensions IHostApplicationLifetime applicationLifetime, GlobalSettings globalSettings) { - if (env.IsDevelopment()) + if (env.IsDevelopment() && !globalSettings.EnableDevLogging) { return; } @@ -31,9 +31,12 @@ public static class LoggerFactoryExtensions public static ILoggingBuilder AddSerilog( this ILoggingBuilder builder, WebHostBuilderContext context, - Func filter = null) + Func filter = null) { - if (context.HostingEnvironment.IsDevelopment()) + var globalSettings = new GlobalSettings(); + ConfigurationBinder.Bind(context.Configuration.GetSection("GlobalSettings"), globalSettings); + + if (context.HostingEnvironment.IsDevelopment() && !globalSettings.EnableDevLogging) { return builder; } @@ -49,13 +52,11 @@ public static class LoggerFactoryExtensions { return true; } - return filter(e); + return filter(e, globalSettings); } - var globalSettings = new GlobalSettings(); - ConfigurationBinder.Bind(context.Configuration.GetSection("GlobalSettings"), globalSettings); - var config = new LoggerConfiguration() + .MinimumLevel.Verbose() .Enrich.FromLogContext() .Filter.ByIncludingOnly(inclusionPredicate); diff --git a/src/Events/Controllers/CollectController.cs b/src/Events/Controllers/CollectController.cs index aaed0b358..2dc996cf2 100644 --- a/src/Events/Controllers/CollectController.cs +++ b/src/Events/Controllers/CollectController.cs @@ -3,7 +3,6 @@ using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Repositories; using Bit.Core.Services; -using Bit.Core.Utilities; using Bit.Events.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -95,7 +94,7 @@ public class CollectController : Controller } if (cipherEvents.Any()) { - foreach (var eventsBatch in cipherEvents.Batch(50)) + foreach (var eventsBatch in cipherEvents.Chunk(50)) { await _eventService.LogCipherEventsAsync(eventsBatch); } diff --git a/src/Events/Program.cs b/src/Events/Program.cs index 74f82cd41..e09cfc17e 100644 --- a/src/Events/Program.cs +++ b/src/Events/Program.cs @@ -1,5 +1,4 @@ using Bit.Core.Utilities; -using Serilog.Events; namespace Bit.Events; @@ -14,13 +13,13 @@ public class Program { webBuilder.UseStartup(); webBuilder.ConfigureLogging((hostingContext, logging) => - logging.AddSerilog(hostingContext, e => + logging.AddSerilog(hostingContext, (e, globalSettings) => { var context = e.Properties["SourceContext"].ToString(); if (context.Contains("IdentityServer4.Validation.TokenValidator") || context.Contains("IdentityServer4.Validation.TokenRequestValidator")) { - return e.Level > LogEventLevel.Error; + return e.Level >= globalSettings.MinLogLevel.EventsSettings.IdentityToken; } if (e.Properties.ContainsKey("RequestPath") && @@ -30,7 +29,7 @@ public class Program return false; } - return e.Level >= LogEventLevel.Error; + return e.Level >= globalSettings.MinLogLevel.EventsSettings.Default; })); }) .Build() diff --git a/src/EventsProcessor/Program.cs b/src/EventsProcessor/Program.cs index 0cf2d17fa..9b7a31e6f 100644 --- a/src/EventsProcessor/Program.cs +++ b/src/EventsProcessor/Program.cs @@ -1,5 +1,4 @@ using Bit.Core.Utilities; -using Serilog.Events; namespace Bit.EventsProcessor; @@ -13,7 +12,7 @@ public class Program { webBuilder.UseStartup(); webBuilder.ConfigureLogging((hostingContext, logging) => - logging.AddSerilog(hostingContext, e => e.Level >= LogEventLevel.Warning)); + logging.AddSerilog(hostingContext, (e, globalSettings) => e.Level >= globalSettings.MinLogLevel.EventsProcessorSettings.Default)); }) .Build() .Run(); diff --git a/src/Icons/Program.cs b/src/Icons/Program.cs index d57a6fd1c..237096b0b 100644 --- a/src/Icons/Program.cs +++ b/src/Icons/Program.cs @@ -1,5 +1,4 @@ using Bit.Core.Utilities; -using Serilog.Events; namespace Bit.Icons; @@ -13,7 +12,7 @@ public class Program { webBuilder.UseStartup(); webBuilder.ConfigureLogging((hostingContext, logging) => - logging.AddSerilog(hostingContext, e => e.Level >= LogEventLevel.Error)); + logging.AddSerilog(hostingContext, (e, globalSettings) => e.Level >= globalSettings.MinLogLevel.IconsSettings.Default)); }) .Build() .Run(); diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index 940e2ab97..e5c3c5ead 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -18,27 +18,32 @@ public class AccountsController : Controller private readonly ILogger _logger; private readonly IUserRepository _userRepository; private readonly IUserService _userService; + private readonly ICaptchaValidationService _captchaValidationService; public AccountsController( ILogger logger, IUserRepository userRepository, - IUserService userService) + IUserService userService, + ICaptchaValidationService captchaValidationService) { _logger = logger; _userRepository = userRepository; _userService = userService; + _captchaValidationService = captchaValidationService; } - // Moved from API, If you modify this endpoint, please update API as well. + // Moved from API, If you modify this endpoint, please update API as well. Self hosted installs still use the API endpoints. [HttpPost("register")] [CaptchaProtected] - public async Task PostRegister([FromBody] RegisterRequestModel model) + public async Task PostRegister([FromBody] RegisterRequestModel model) { - var result = await _userService.RegisterUserAsync(model.ToUser(), model.MasterPasswordHash, + var user = model.ToUser(); + var result = await _userService.RegisterUserAsync(user, model.MasterPasswordHash, model.Token, model.OrganizationUserId); if (result.Succeeded) { - return; + var captchaBypassToken = _captchaValidationService.GenerateCaptchaBypassToken(user); + return new RegisterResponseModel(captchaBypassToken); } foreach (var error in result.Errors.Where(e => e.Code != "DuplicateUserName")) @@ -50,7 +55,7 @@ public class AccountsController : Controller throw new BadRequestException(ModelState); } - // Moved from API, If you modify this endpoint, please update API as well. + // Moved from API, If you modify this endpoint, please update API as well. Self hosted installs still use the API endpoints. [HttpPost("prelogin")] public async Task PostPrelogin([FromBody] PreloginRequestModel model) { diff --git a/src/Core/IdentityServer/ApiClient.cs b/src/Identity/IdentityServer/ApiClient.cs similarity index 98% rename from src/Core/IdentityServer/ApiClient.cs rename to src/Identity/IdentityServer/ApiClient.cs index b289da001..8d2a294be 100644 --- a/src/Core/IdentityServer/ApiClient.cs +++ b/src/Identity/IdentityServer/ApiClient.cs @@ -1,7 +1,7 @@ using Bit.Core.Settings; using IdentityServer4.Models; -namespace Bit.Core.IdentityServer; +namespace Bit.Identity.IdentityServer; public class ApiClient : Client { diff --git a/src/Core/IdentityServer/ApiResources.cs b/src/Identity/IdentityServer/ApiResources.cs similarity index 96% rename from src/Core/IdentityServer/ApiResources.cs rename to src/Identity/IdentityServer/ApiResources.cs index 5a19fa2ca..07cb7b501 100644 --- a/src/Core/IdentityServer/ApiResources.cs +++ b/src/Identity/IdentityServer/ApiResources.cs @@ -1,7 +1,7 @@ using IdentityModel; using IdentityServer4.Models; -namespace Bit.Core.IdentityServer; +namespace Bit.Identity.IdentityServer; public class ApiResources { diff --git a/src/Core/IdentityServer/ApiScopes.cs b/src/Identity/IdentityServer/ApiScopes.cs similarity index 93% rename from src/Core/IdentityServer/ApiScopes.cs rename to src/Identity/IdentityServer/ApiScopes.cs index 2af512eb8..2e8fe983e 100644 --- a/src/Core/IdentityServer/ApiScopes.cs +++ b/src/Identity/IdentityServer/ApiScopes.cs @@ -1,6 +1,6 @@ using IdentityServer4.Models; -namespace Bit.Core.IdentityServer; +namespace Bit.Identity.IdentityServer; public class ApiScopes { diff --git a/src/Core/IdentityServer/AuthorizationCodeStore.cs b/src/Identity/IdentityServer/AuthorizationCodeStore.cs similarity index 95% rename from src/Core/IdentityServer/AuthorizationCodeStore.cs rename to src/Identity/IdentityServer/AuthorizationCodeStore.cs index fc07f7aa6..da63d9c4a 100644 --- a/src/Core/IdentityServer/AuthorizationCodeStore.cs +++ b/src/Identity/IdentityServer/AuthorizationCodeStore.cs @@ -4,9 +4,8 @@ using IdentityServer4.Models; using IdentityServer4.Services; using IdentityServer4.Stores; using IdentityServer4.Stores.Serialization; -using Microsoft.Extensions.Logging; -namespace Bit.Core.IdentityServer; +namespace Bit.Identity.IdentityServer; // ref: https://raw.githubusercontent.com/IdentityServer/IdentityServer4/3.1.3/src/IdentityServer4/src/Stores/Default/DefaultAuthorizationCodeStore.cs public class AuthorizationCodeStore : DefaultGrantStore, IAuthorizationCodeStore diff --git a/src/Core/IdentityServer/BaseRequestValidator.cs b/src/Identity/IdentityServer/BaseRequestValidator.cs similarity index 99% rename from src/Core/IdentityServer/BaseRequestValidator.cs rename to src/Identity/IdentityServer/BaseRequestValidator.cs index d2c72c132..c7d88e9e9 100644 --- a/src/Core/IdentityServer/BaseRequestValidator.cs +++ b/src/Identity/IdentityServer/BaseRequestValidator.cs @@ -2,6 +2,7 @@ using System.Reflection; using System.Security.Claims; using System.Text.Json; +using Bit.Core; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; @@ -15,9 +16,8 @@ using Bit.Core.Settings; using Bit.Core.Utilities; using IdentityServer4.Validation; using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Logging; -namespace Bit.Core.IdentityServer; +namespace Bit.Identity.IdentityServer; public abstract class BaseRequestValidator where T : class { diff --git a/src/Core/IdentityServer/ClientStore.cs b/src/Identity/IdentityServer/ClientStore.cs similarity index 99% rename from src/Core/IdentityServer/ClientStore.cs rename to src/Identity/IdentityServer/ClientStore.cs index 2e6fa06bd..b8fa2e421 100644 --- a/src/Core/IdentityServer/ClientStore.cs +++ b/src/Identity/IdentityServer/ClientStore.cs @@ -10,7 +10,7 @@ using IdentityModel; using IdentityServer4.Models; using IdentityServer4.Stores; -namespace Bit.Core.IdentityServer; +namespace Bit.Identity.IdentityServer; public class ClientStore : IClientStore { diff --git a/src/Core/IdentityServer/CustomTokenRequestValidator.cs b/src/Identity/IdentityServer/CustomTokenRequestValidator.cs similarity index 98% rename from src/Core/IdentityServer/CustomTokenRequestValidator.cs rename to src/Identity/IdentityServer/CustomTokenRequestValidator.cs index 1354af70a..b9bf16f58 100644 --- a/src/Core/IdentityServer/CustomTokenRequestValidator.cs +++ b/src/Identity/IdentityServer/CustomTokenRequestValidator.cs @@ -9,9 +9,8 @@ using IdentityModel; using IdentityServer4.Extensions; using IdentityServer4.Validation; using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Logging; -namespace Bit.Core.IdentityServer; +namespace Bit.Identity.IdentityServer; public class CustomTokenRequestValidator : BaseRequestValidator, ICustomTokenRequestValidator diff --git a/src/Core/IdentityServer/CustomValidatorRequestContext.cs b/src/Identity/IdentityServer/CustomValidatorRequestContext.cs similarity index 86% rename from src/Core/IdentityServer/CustomValidatorRequestContext.cs rename to src/Identity/IdentityServer/CustomValidatorRequestContext.cs index 66fdc1e7e..faf40b7c4 100644 --- a/src/Core/IdentityServer/CustomValidatorRequestContext.cs +++ b/src/Identity/IdentityServer/CustomValidatorRequestContext.cs @@ -1,7 +1,7 @@ using Bit.Core.Entities; using Bit.Core.Models.Business; -namespace Bit.Core.IdentityServer; +namespace Bit.Identity.IdentityServer; public class CustomValidatorRequestContext { diff --git a/src/Core/IdentityServer/PersistedGrantStore.cs b/src/Identity/IdentityServer/PersistedGrantStore.cs similarity index 98% rename from src/Core/IdentityServer/PersistedGrantStore.cs rename to src/Identity/IdentityServer/PersistedGrantStore.cs index a1b3294ba..9671cba44 100644 --- a/src/Core/IdentityServer/PersistedGrantStore.cs +++ b/src/Identity/IdentityServer/PersistedGrantStore.cs @@ -3,7 +3,7 @@ using IdentityServer4.Models; using IdentityServer4.Stores; using Grant = Bit.Core.Entities.Grant; -namespace Bit.Core.IdentityServer; +namespace Bit.Identity.IdentityServer; public class PersistedGrantStore : IPersistedGrantStore { diff --git a/src/Core/IdentityServer/ProfileService.cs b/src/Identity/IdentityServer/ProfileService.cs similarity index 98% rename from src/Core/IdentityServer/ProfileService.cs rename to src/Identity/IdentityServer/ProfileService.cs index 873ad6b5a..6b66b1850 100644 --- a/src/Core/IdentityServer/ProfileService.cs +++ b/src/Identity/IdentityServer/ProfileService.cs @@ -6,7 +6,7 @@ using Bit.Core.Utilities; using IdentityServer4.Models; using IdentityServer4.Services; -namespace Bit.Core.IdentityServer; +namespace Bit.Identity.IdentityServer; public class ProfileService : IProfileService { diff --git a/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs b/src/Identity/IdentityServer/ResourceOwnerPasswordValidator.cs similarity index 83% rename from src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs rename to src/Identity/IdentityServer/ResourceOwnerPasswordValidator.cs index 82b3cf50a..278baef06 100644 --- a/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs +++ b/src/Identity/IdentityServer/ResourceOwnerPasswordValidator.cs @@ -9,9 +9,8 @@ using Bit.Core.Utilities; using IdentityServer4.Models; using IdentityServer4.Validation; using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Logging; -namespace Bit.Core.IdentityServer; +namespace Bit.Identity.IdentityServer; public class ResourceOwnerPasswordValidator : BaseRequestValidator, IResourceOwnerPasswordValidator @@ -20,6 +19,7 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator userManager, IDeviceRepository deviceRepository, @@ -36,6 +36,7 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator(); webBuilder.ConfigureLogging((hostingContext, logging) => - logging.AddSerilog(hostingContext, e => + logging.AddSerilog(hostingContext, (e, globalSettings) => { var context = e.Properties["SourceContext"].ToString(); - if (context.Contains(typeof(IpRateLimitMiddleware).FullName) && - e.Level == LogEventLevel.Information) + if (context.Contains(typeof(IpRateLimitMiddleware).FullName)) { - return true; + return e.Level >= globalSettings.MinLogLevel.IdentitySettings.IpRateLimit; } if (context.Contains("IdentityServer4.Validation.TokenValidator") || context.Contains("IdentityServer4.Validation.TokenRequestValidator")) { - return e.Level > LogEventLevel.Error; + return e.Level >= globalSettings.MinLogLevel.IdentitySettings.IdentityToken; } - return e.Level >= LogEventLevel.Error; + return e.Level >= globalSettings.MinLogLevel.IdentitySettings.Default; })); }); } diff --git a/src/Identity/Utilities/ServiceCollectionExtensions.cs b/src/Identity/Utilities/ServiceCollectionExtensions.cs index df3a6dec8..46031c9cf 100644 --- a/src/Identity/Utilities/ServiceCollectionExtensions.cs +++ b/src/Identity/Utilities/ServiceCollectionExtensions.cs @@ -1,5 +1,5 @@ -using Bit.Core.IdentityServer; -using Bit.Core.Settings; +using Bit.Core.Settings; +using Bit.Identity.IdentityServer; using Bit.SharedWeb.Utilities; using IdentityServer4.ResponseHandling; using IdentityServer4.Services; diff --git a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs index 9c138f7b0..bb374e368 100644 --- a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs +++ b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs @@ -34,6 +34,7 @@ public static class DapperServiceCollectionExtensions services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); if (selfHosted) { diff --git a/src/Infrastructure.Dapper/Repositories/AuthRequestRepository.cs b/src/Infrastructure.Dapper/Repositories/AuthRequestRepository.cs new file mode 100644 index 000000000..1b843575b --- /dev/null +++ b/src/Infrastructure.Dapper/Repositories/AuthRequestRepository.cs @@ -0,0 +1,43 @@ +using System.Data; +using System.Data.SqlClient; +using Bit.Core.Entities; +using Bit.Core.Repositories; +using Bit.Core.Settings; +using Dapper; + +namespace Bit.Infrastructure.Dapper.Repositories; + +public class AuthRequestRepository : Repository, IAuthRequestRepository +{ + public AuthRequestRepository(GlobalSettings globalSettings) + : this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString) + { } + + public AuthRequestRepository(string connectionString, string readOnlyConnectionString) + : base(connectionString, readOnlyConnectionString) + { } + + public async Task DeleteExpiredAsync() + { + using (var connection = new SqlConnection(ConnectionString)) + { + return await connection.ExecuteAsync( + $"[{Schema}].[AuthRequest_DeleteIfExpired]", + null, + commandType: CommandType.StoredProcedure); + } + } + + public async Task> GetManyByUserIdAsync(Guid userId) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + "[{Schema}].[AuthRequest_ReadByUserId]", + new { UserId = userId }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } +} diff --git a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs index c8a99b274..06897f139 100644 --- a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs +++ b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs @@ -56,6 +56,7 @@ public static class EntityFrameworkServiceCollectionExtensions services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); if (selfHosted) { diff --git a/src/Infrastructure.EntityFramework/Models/AuthRequest.cs b/src/Infrastructure.EntityFramework/Models/AuthRequest.cs new file mode 100644 index 000000000..2e5420d90 --- /dev/null +++ b/src/Infrastructure.EntityFramework/Models/AuthRequest.cs @@ -0,0 +1,17 @@ +using AutoMapper; + +namespace Bit.Infrastructure.EntityFramework.Models; + +public class AuthRequest : Core.Entities.AuthRequest +{ + public virtual User User { get; set; } + public virtual Device ResponseDevice { get; set; } +} + +public class AuthRequestMapperProfile : Profile +{ + public AuthRequestMapperProfile() + { + CreateMap().ReverseMap(); + } +} diff --git a/src/Infrastructure.EntityFramework/Repositories/AuthRequestRepository.cs b/src/Infrastructure.EntityFramework/Repositories/AuthRequestRepository.cs new file mode 100644 index 000000000..2f5f7aa8e --- /dev/null +++ b/src/Infrastructure.EntityFramework/Repositories/AuthRequestRepository.cs @@ -0,0 +1,35 @@ +using AutoMapper; +using Bit.Core.Repositories; +using Bit.Infrastructure.EntityFramework.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Bit.Infrastructure.EntityFramework.Repositories; + +public class AuthRequestRepository : Repository, IAuthRequestRepository +{ + public AuthRequestRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) + : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.AuthRequests) + { } + public async Task DeleteExpiredAsync() + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var expiredRequests = await dbContext.AuthRequests.Where(a => a.CreationDate < DateTime.Now.AddMinutes(-15)).ToListAsync(); + dbContext.AuthRequests.RemoveRange(expiredRequests); + await dbContext.SaveChangesAsync(); + return 1; + } + } + + public async Task> GetManyByUserIdAsync(Guid userId) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var userAuthRequests = await dbContext.AuthRequests.Where(a => a.UserId.Equals(userId)).ToListAsync(); + return Mapper.Map>(userAuthRequests); + } + } +} diff --git a/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs b/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs index 88c2bb464..1df5cc258 100644 --- a/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs +++ b/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs @@ -39,6 +39,7 @@ public class DatabaseContext : DbContext public DbSet TaxRates { get; set; } public DbSet Transactions { get; set; } public DbSet Users { get; set; } + public DbSet AuthRequests { get; set; } protected override void OnModelCreating(ModelBuilder builder) { @@ -70,6 +71,7 @@ public class DatabaseContext : DbContext var eUser = builder.Entity(); var eOrganizationApiKey = builder.Entity(); var eOrganizationConnection = builder.Entity(); + var eAuthRequest = builder.Entity(); eCipher.Property(c => c.Id).ValueGeneratedNever(); eCollection.Property(c => c.Id).ValueGeneratedNever(); @@ -90,6 +92,7 @@ public class DatabaseContext : DbContext eUser.Property(c => c.Id).ValueGeneratedNever(); eOrganizationApiKey.Property(c => c.Id).ValueGeneratedNever(); eOrganizationConnection.Property(c => c.Id).ValueGeneratedNever(); + eAuthRequest.Property(ar => ar.Id).ValueGeneratedNever(); eCollectionCipher.HasKey(cc => new { cc.CollectionId, cc.CipherId }); eCollectionUser.HasKey(cu => new { cu.CollectionId, cu.OrganizationUserId }); @@ -135,5 +138,6 @@ public class DatabaseContext : DbContext eUser.ToTable(nameof(User)); eOrganizationApiKey.ToTable(nameof(OrganizationApiKey)); eOrganizationConnection.ToTable(nameof(OrganizationConnection)); + eAuthRequest.ToTable(nameof(AuthRequest)); } } diff --git a/src/Notifications/AnonymousNotificationsHub.cs b/src/Notifications/AnonymousNotificationsHub.cs new file mode 100644 index 000000000..e3e7d478c --- /dev/null +++ b/src/Notifications/AnonymousNotificationsHub.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.SignalR; + +namespace Bit.Notifications; + +[AllowAnonymous] +public class AnonymousNotificationsHub : Microsoft.AspNetCore.SignalR.Hub, INotificationHub +{ + public override async Task OnConnectedAsync() + { + var httpContext = Context.GetHttpContext(); + var token = httpContext.Request.Query["Token"].FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(token)) + { + await Groups.AddToGroupAsync(Context.ConnectionId, token); + } + await base.OnConnectedAsync(); + } +} diff --git a/src/Notifications/AzureQueueHostedService.cs b/src/Notifications/AzureQueueHostedService.cs index ba2e38d2c..f0cc28ac8 100644 --- a/src/Notifications/AzureQueueHostedService.cs +++ b/src/Notifications/AzureQueueHostedService.cs @@ -9,6 +9,7 @@ public class AzureQueueHostedService : IHostedService, IDisposable { private readonly ILogger _logger; private readonly IHubContext _hubContext; + private readonly IHubContext _anonymousHubContext; private readonly GlobalSettings _globalSettings; private Task _executingTask; @@ -18,11 +19,13 @@ public class AzureQueueHostedService : IHostedService, IDisposable public AzureQueueHostedService( ILogger logger, IHubContext hubContext, + IHubContext anonymousHubContext, GlobalSettings globalSettings) { _logger = logger; _hubContext = hubContext; _globalSettings = globalSettings; + _anonymousHubContext = anonymousHubContext; } public Task StartAsync(CancellationToken cancellationToken) @@ -62,7 +65,7 @@ public class AzureQueueHostedService : IHostedService, IDisposable try { await HubHelpers.SendNotificationToHubAsync( - message.DecodeMessageText(), _hubContext, cancellationToken); + message.DecodeMessageText(), _hubContext, _anonymousHubContext, cancellationToken); await _queueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt); } catch (Exception e) diff --git a/src/Notifications/Controllers/SendController.cs b/src/Notifications/Controllers/SendController.cs index 90fdac7d0..247036a64 100644 --- a/src/Notifications/Controllers/SendController.cs +++ b/src/Notifications/Controllers/SendController.cs @@ -25,7 +25,7 @@ public class SendController : Controller var notificationJson = await reader.ReadToEndAsync(); if (!string.IsNullOrWhiteSpace(notificationJson)) { - await HubHelpers.SendNotificationToHubAsync(notificationJson, _hubContext); + await HubHelpers.SendNotificationToHubAsync(notificationJson, _hubContext, null); } } } diff --git a/src/Notifications/HubHelpers.cs b/src/Notifications/HubHelpers.cs index 38b87e227..bf4a1ab94 100644 --- a/src/Notifications/HubHelpers.cs +++ b/src/Notifications/HubHelpers.cs @@ -7,8 +7,12 @@ namespace Bit.Notifications; public static class HubHelpers { - public static async Task SendNotificationToHubAsync(string notificationJson, - IHubContext hubContext, CancellationToken cancellationToken = default(CancellationToken)) + public static async Task SendNotificationToHubAsync( + string notificationJson, + IHubContext hubContext, + IHubContext anonymousHubContext, + CancellationToken cancellationToken = default(CancellationToken) + ) { var notification = JsonSerializer.Deserialize>(notificationJson); switch (notification.Type) @@ -61,6 +65,20 @@ public static class HubHelpers await hubContext.Clients.User(sendNotification.Payload.UserId.ToString()) .SendAsync("ReceiveMessage", sendNotification, cancellationToken); break; + case PushType.AuthRequestResponse: + var authRequestResponseNotification = + JsonSerializer.Deserialize>( + notificationJson); + await anonymousHubContext.Clients.Group(authRequestResponseNotification.Payload.Id.ToString()) + .SendAsync("AuthRequestResponseRecieved", authRequestResponseNotification, cancellationToken); + break; + case PushType.AuthRequest: + var authRequestNotification = + JsonSerializer.Deserialize>( + notificationJson); + await hubContext.Clients.User(authRequestNotification.Payload.UserId.ToString()) + .SendAsync("ReceiveMessage", authRequestNotification, cancellationToken); + break; default: break; } diff --git a/src/Notifications/INotificationHub.cs b/src/Notifications/INotificationHub.cs new file mode 100644 index 000000000..c1a795458 --- /dev/null +++ b/src/Notifications/INotificationHub.cs @@ -0,0 +1,7 @@ +namespace Bit.Notifications; + +public interface INotificationHub +{ + Task OnConnectedAsync(); + Task OnDisconnectedAsync(Exception exception); +} diff --git a/src/Notifications/Program.cs b/src/Notifications/Program.cs index 4834972ab..48221c2c4 100644 --- a/src/Notifications/Program.cs +++ b/src/Notifications/Program.cs @@ -14,13 +14,13 @@ public class Program { webBuilder.UseStartup(); webBuilder.ConfigureLogging((hostingContext, logging) => - logging.AddSerilog(hostingContext, e => + logging.AddSerilog(hostingContext, (e, globalSettings) => { var context = e.Properties["SourceContext"].ToString(); if (context.Contains("IdentityServer4.Validation.TokenValidator") || context.Contains("IdentityServer4.Validation.TokenRequestValidator")) { - return e.Level > LogEventLevel.Error; + return e.Level >= globalSettings.MinLogLevel.NotificationsSettings.IdentityToken; } if (e.Level == LogEventLevel.Error && @@ -41,7 +41,7 @@ public class Program return false; } - return e.Level >= LogEventLevel.Warning; + return e.Level >= globalSettings.MinLogLevel.NotificationsSettings.Default; })); }) .Build() diff --git a/src/Notifications/Startup.cs b/src/Notifications/Startup.cs index e2509e46c..14f88f7b2 100644 --- a/src/Notifications/Startup.cs +++ b/src/Notifications/Startup.cs @@ -110,7 +110,12 @@ public class Startup { endpoints.MapHub("/hub", options => { - options.ApplicationMaxBufferSize = 2048; // client => server messages are not even used + options.ApplicationMaxBufferSize = 2048; + options.TransportMaxBufferSize = 4096; + }); + endpoints.MapHub("/anonymous-hub", options => + { + options.ApplicationMaxBufferSize = 2048; options.TransportMaxBufferSize = 4096; }); endpoints.MapDefaultControllerRoute(); diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index a9a88b4fe..63f2a332f 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -33,8 +33,8 @@ using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Serilog.Context; using StackExchange.Redis; @@ -116,19 +116,22 @@ public static class ServiceCollectionExtensions new DataProtectorTokenFactory( EmergencyAccessInviteTokenable.ClearTextPrefix, EmergencyAccessInviteTokenable.DataProtectorPurpose, - serviceProvider.GetDataProtectionProvider()) + serviceProvider.GetDataProtectionProvider(), + serviceProvider.GetRequiredService>>()) ); services.AddSingleton>(serviceProvider => new DataProtectorTokenFactory( HCaptchaTokenable.ClearTextPrefix, HCaptchaTokenable.DataProtectorPurpose, - serviceProvider.GetDataProtectionProvider()) + serviceProvider.GetDataProtectionProvider(), + serviceProvider.GetRequiredService>>()) ); services.AddSingleton>(serviceProvider => new DataProtectorTokenFactory( SsoTokenable.ClearTextPrefix, SsoTokenable.DataProtectorPurpose, - serviceProvider.GetDataProtectionProvider())); + serviceProvider.GetDataProtectionProvider(), + serviceProvider.GetRequiredService>>())); } public static void AddDefaultServices(this IServiceCollection services, GlobalSettings globalSettings) @@ -354,39 +357,7 @@ public static class ServiceCollectionExtensions return identityBuilder; } - public static Tuple AddPasswordlessIdentityServices( - this IServiceCollection services, GlobalSettings globalSettings) where TUserStore : class - { - services.TryAddTransient(); - services.Configure(options => - { - options.TokenLifespan = TimeSpan.FromMinutes(15); - }); - var passwordlessIdentityBuilder = services.AddIdentity() - .AddUserStore() - .AddRoleStore() - .AddDefaultTokenProviders(); - - var regularIdentityBuilder = services.AddIdentityCore() - .AddUserStore(); - - services.TryAddScoped, PasswordlessSignInManager>(); - - services.ConfigureApplicationCookie(options => - { - options.LoginPath = "/login"; - options.LogoutPath = "/"; - options.AccessDeniedPath = "/login?accessDenied=true"; - options.Cookie.Name = $"Bitwarden_{globalSettings.ProjectName}"; - options.Cookie.HttpOnly = true; - options.ExpireTimeSpan = TimeSpan.FromDays(2); - options.ReturnUrlParameter = "returnUrl"; - options.SlidingExpiration = true; - }); - - return new Tuple(passwordlessIdentityBuilder, regularIdentityBuilder); - } public static void AddIdentityAuthenticationServices( this IServiceCollection services, GlobalSettings globalSettings, IWebHostEnvironment environment, diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index 8df155dd9..99f09ccbf 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -73,6 +73,12 @@ + + + + + + @@ -344,6 +350,7 @@ + @@ -378,6 +385,7 @@ + diff --git a/src/Sql/dbo/Stored Procedures/AuthRequest_Create.sql b/src/Sql/dbo/Stored Procedures/AuthRequest_Create.sql new file mode 100644 index 000000000..9dac3c0eb --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/AuthRequest_Create.sql @@ -0,0 +1,57 @@ +CREATE PROCEDURE [dbo].[AuthRequest_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @UserId UNIQUEIDENTIFIER, + @Type TINYINT, + @RequestDeviceIdentifier NVARCHAR(50), + @RequestDeviceType TINYINT, + @RequestIpAddress VARCHAR(50), + @RequestFingerprint VARCHAR(MAX), + @ResponseDeviceId UNIQUEIDENTIFIER, + @AccessCode VARCHAR(25), + @PublicKey VARCHAR(MAX), + @Key VARCHAR(MAX), + @MasterPasswordHash VARCHAR(MAX), + @CreationDate DATETIME2(7), + @ResponseDate DATETIME2(7), + @AuthenticationDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[AuthRequest] + ( + [Id], + [UserId], + [Type], + [RequestDeviceIdentifier], + [RequestDeviceType], + [RequestIpAddress], + [RequestFingerprint], + [ResponseDeviceId], + [AccessCode], + [PublicKey], + [Key], + [MasterPasswordHash], + [CreationDate], + [ResponseDate], + [AuthenticationDate] + ) + VALUES + ( + @Id, + @UserId, + @Type, + @RequestDeviceIdentifier, + @RequestDeviceType, + @RequestIpAddress, + @RequestFingerprint, + @ResponseDeviceId, + @AccessCode, + @PublicKey, + @Key, + @MasterPasswordHash, + @CreationDate, + @ResponseDate, + @AuthenticationDate + ) +END diff --git a/src/Sql/dbo/Stored Procedures/AuthRequest_DeleteById.sql b/src/Sql/dbo/Stored Procedures/AuthRequest_DeleteById.sql new file mode 100644 index 000000000..af3acc2e6 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/AuthRequest_DeleteById.sql @@ -0,0 +1,12 @@ +CREATE PROCEDURE [dbo].[AuthRequest_DeleteById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + DELETE + FROM + [dbo].[AuthRequest] + WHERE + [Id] = @Id +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/AuthRequest_DeleteIfExpired.sql b/src/Sql/dbo/Stored Procedures/AuthRequest_DeleteIfExpired.sql new file mode 100644 index 000000000..736729c7b --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/AuthRequest_DeleteIfExpired.sql @@ -0,0 +1,6 @@ +CREATE PROCEDURE [dbo].[AuthRequest_DeleteIfExpired] +AS +BEGIN + SET NOCOUNT OFF + DELETE FROM [dbo].[AuthRequest] WHERE [CreationDate] < DATEADD(minute, -15, GETUTCDATE()); +END diff --git a/src/Sql/dbo/Stored Procedures/AuthRequest_ReadById.sql b/src/Sql/dbo/Stored Procedures/AuthRequest_ReadById.sql new file mode 100644 index 000000000..84e30a26b --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/AuthRequest_ReadById.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[AuthRequest_ReadById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[AuthRequestView] + WHERE + [Id] = @Id +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/AuthRequest_ReadByUserId.sql b/src/Sql/dbo/Stored Procedures/AuthRequest_ReadByUserId.sql new file mode 100644 index 000000000..0654e9d7e --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/AuthRequest_ReadByUserId.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[AuthRequest_ReadByUserId] + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[AuthRequestView] + WHERE + [UserId] = @UserId +END diff --git a/src/Sql/dbo/Stored Procedures/AuthRequest_Update.sql b/src/Sql/dbo/Stored Procedures/AuthRequest_Update.sql new file mode 100644 index 000000000..0f29e0273 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/AuthRequest_Update.sql @@ -0,0 +1,40 @@ +CREATE PROCEDURE [dbo].[AuthRequest_Update] + @Id UNIQUEIDENTIFIER OUTPUT, + @UserId UNIQUEIDENTIFIER, + @Type SMALLINT, + @RequestDeviceIdentifier NVARCHAR(50), + @RequestDeviceType SMALLINT, + @RequestIpAddress VARCHAR(50), + @RequestFingerprint VARCHAR(MAX), + @ResponseDeviceId UNIQUEIDENTIFIER, + @AccessCode VARCHAR(25), + @PublicKey VARCHAR(MAX), + @Key VARCHAR(MAX), + @MasterPasswordHash VARCHAR(MAX), + @CreationDate DATETIME2 (7), + @ResponseDate DATETIME2 (7), + @AuthenticationDate DATETIME2 (7) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[AuthRequest] + SET + [UserId] = @UserId, + [Type] = @Type, + [RequestDeviceIdentifier] = @RequestDeviceIdentifier, + [RequestDeviceType] = @RequestDeviceType, + [RequestIpAddress] = @RequestIpAddress, + [RequestFingerprint] = @RequestFingerprint, + [ResponseDeviceId] = @ResponseDeviceId, + [AccessCode] = @AccessCode, + [PublicKey] = @PublicKey, + [Key] = @Key, + [MasterPasswordHash] = @MasterPasswordHash, + [CreationDate] = @CreationDate, + [ResponseDate] = @ResponseDate, + [AuthenticationDate] = @AuthenticationDate + WHERE + [Id] = @Id +END diff --git a/src/Sql/dbo/Tables/AuthRequest.sql b/src/Sql/dbo/Tables/AuthRequest.sql new file mode 100644 index 000000000..e02f59c51 --- /dev/null +++ b/src/Sql/dbo/Tables/AuthRequest.sql @@ -0,0 +1,23 @@ +CREATE TABLE [dbo].[AuthRequest] ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [UserId] UNIQUEIDENTIFIER NOT NULL, + [Type] SMALLINT NOT NULL, + [RequestDeviceIdentifier] NVARCHAR(50) NOT NULL, + [RequestDeviceType] SMALLINT NOT NULL, + [RequestIpAddress] VARCHAR(50) NOT NULL, + [RequestFingerprint] VARCHAR(MAX) NOT NULL, + [ResponseDeviceId] UNIQUEIDENTIFIER NULL, + [AccessCode] VARCHAR(25) NOT NULL, + [PublicKey] VARCHAR(MAX) NOT NULL, + [Key] VARCHAR(MAX) NULL, + [MasterPasswordHash] VARCHAR(MAX) NULL, + [CreationDate] DATETIME2 (7) NOT NULL, + [ResponseDate] DATETIME2 (7) NULL, + [AuthenticationDate] DATETIME2 (7) NULL, + CONSTRAINT [PK_AuthRequest] PRIMARY KEY CLUSTERED ([Id] ASC), + CONSTRAINT [FK_AuthRequest_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]), + CONSTRAINT [FK_AuthRequest_ResponseDevice] FOREIGN KEY ([ResponseDeviceId]) REFERENCES [dbo].[Device] ([Id]) +); + + +GO diff --git a/src/Sql/dbo/Views/AuthRequestView.sql b/src/Sql/dbo/Views/AuthRequestView.sql new file mode 100644 index 000000000..0b4eccb47 --- /dev/null +++ b/src/Sql/dbo/Views/AuthRequestView.sql @@ -0,0 +1,6 @@ +CREATE VIEW [dbo].[AuthRequestView] +AS +SELECT + * +FROM + [dbo].[AuthRequest] diff --git a/test/Api.Test/Controllers/AccountsControllerTests.cs b/test/Api.Test/Controllers/AccountsControllerTests.cs index 0b2747c38..142cc0b4d 100644 --- a/test/Api.Test/Controllers/AccountsControllerTests.cs +++ b/test/Api.Test/Controllers/AccountsControllerTests.cs @@ -30,6 +30,7 @@ public class AccountsControllerTests : IDisposable private readonly ISendRepository _sendRepository; private readonly ISendService _sendService; private readonly IProviderUserRepository _providerUserRepository; + private readonly ICaptchaValidationService _captchaValidationService; public AccountsControllerTests() { @@ -44,6 +45,7 @@ public class AccountsControllerTests : IDisposable _globalSettings = new GlobalSettings(); _sendRepository = Substitute.For(); _sendService = Substitute.For(); + _captchaValidationService = Substitute.For(); _sut = new AccountsController( _globalSettings, _cipherRepository, @@ -55,7 +57,8 @@ public class AccountsControllerTests : IDisposable _userRepository, _userService, _sendRepository, - _sendService + _sendService, + _captchaValidationService ); } diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs index c07a30184..7e5e4474a 100644 --- a/test/Core.Test/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/Services/OrganizationServiceTests.cs @@ -897,7 +897,7 @@ public class OrganizationServiceTests [BitAutoData(0, 100, 100, true, "")] [BitAutoData(0, null, 100, true, "")] [BitAutoData(1, 100, null, true, "")] - [BitAutoData(1, 100, 100, false, "Cannot invite new users. Seat limit has been reached")] + [BitAutoData(1, 100, 100, false, "Seat limit has been reached")] public void CanScale(int seatsToAdd, int? currentSeats, int? maxAutoscaleSeats, bool expectedResult, string expectedFailureMessage, Organization organization, SutProvider sutProvider) diff --git a/test/Core.Test/Utilities/CoreHelpersTests.cs b/test/Core.Test/Utilities/CoreHelpersTests.cs index 9b3909385..f66f9ca01 100644 --- a/test/Core.Test/Utilities/CoreHelpersTests.cs +++ b/test/Core.Test/Utilities/CoreHelpersTests.cs @@ -70,31 +70,6 @@ public class CoreHelpersTests Assert.Equal(expectedComb, comb); } - [Theory] - [InlineData(2, 5, new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 })] - [InlineData(2, 3, new[] { 1, 2, 3, 4, 5 })] - [InlineData(2, 1, new[] { 1, 2 })] - [InlineData(1, 1, new[] { 1 })] - [InlineData(2, 2, new[] { 1, 2, 3 })] - public void Batch_Success(int batchSize, int totalBatches, int[] collection) - { - // Arrange - var remainder = collection.Length % batchSize; - - // Act - var batches = collection.Batch(batchSize); - - // Assert - Assert.Equal(totalBatches, batches.Count()); - - foreach (var batch in batches.Take(totalBatches - 1)) - { - Assert.Equal(batchSize, batch.Count()); - } - - Assert.Equal(batches.Last().Count(), remainder == 0 ? batchSize : remainder); - } - /* [Fact] public void ToGuidIdArrayTVP_Success() diff --git a/test/Identity.IntegrationTest/Endpoints/IdentityServerTests.cs b/test/Identity.IntegrationTest/Endpoints/IdentityServerTests.cs index 14600ba26..3bcb45a69 100644 --- a/test/Identity.IntegrationTest/Endpoints/IdentityServerTests.cs +++ b/test/Identity.IntegrationTest/Endpoints/IdentityServerTests.cs @@ -3,6 +3,7 @@ using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.Api.Request.Accounts; using Bit.Core.Repositories; +using Bit.Identity.IdentityServer; using Bit.IntegrationTestCommon.Factories; using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.Helpers; @@ -279,7 +280,7 @@ public class IdentityServerTests : IClassFixture /// /// This test currently does not test any code that is not covered by other tests but - /// it shows that we probably have some dead code in + /// it shows that we probably have some dead code in /// for installation, organization, and user they split on a '.' but have already checked that at least one /// '.' exists in the client_id by checking it with /// I believe that idParts.Length > 1 will ALWAYS return true diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 54b585654..5318a55cd 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -20,16 +20,19 @@ public class AccountsControllerTests : IDisposable private readonly ILogger _logger; private readonly IUserRepository _userRepository; private readonly IUserService _userService; + private readonly ICaptchaValidationService _captchaValidationService; public AccountsControllerTests() { _logger = Substitute.For>(); _userRepository = Substitute.For(); _userService = Substitute.For(); + _captchaValidationService = Substitute.For(); _sut = new AccountsController( _logger, _userRepository, - _userService + _userService, + _captchaValidationService ); } diff --git a/test/Infrastructure.EFIntegration.Test/AutoFixture/AuthRequestFixtures.cs b/test/Infrastructure.EFIntegration.Test/AutoFixture/AuthRequestFixtures.cs new file mode 100644 index 000000000..e50c7a4af --- /dev/null +++ b/test/Infrastructure.EFIntegration.Test/AutoFixture/AuthRequestFixtures.cs @@ -0,0 +1,61 @@ +using AutoFixture; +using AutoFixture.Kernel; +using Bit.Core.Entities; +using Bit.Core.Test.AutoFixture.UserFixtures; +using Bit.Infrastructure.EFIntegration.Test.AutoFixture.Relays; +using Bit.Infrastructure.EntityFramework.Repositories; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; + +namespace Bit.Infrastructure.EFIntegration.Test.AutoFixture; + +internal class AuthRequestBuilder : ISpecimenBuilder +{ + public object Create(object request, ISpecimenContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + var type = request as Type; + if (type == null || type != typeof(AuthRequest)) + { + return new NoSpecimen(); + } + + var fixture = new Fixture(); + fixture.Customizations.Insert(0, new MaxLengthStringRelay()); + var obj = fixture.WithAutoNSubstitutions().Create(); + return obj; + } +} + +internal class EfAuthRequest : ICustomization +{ + public void Customize(IFixture fixture) + { + fixture.Customizations.Add(new IgnoreVirtualMembersCustomization()); + fixture.Customizations.Add(new GlobalSettingsBuilder()); + fixture.Customizations.Add(new AuthRequestBuilder()); + fixture.Customizations.Add(new DeviceBuilder()); + fixture.Customizations.Add(new UserBuilder()); + fixture.Customizations.Add(new EfRepositoryListBuilder()); + fixture.Customizations.Add(new EfRepositoryListBuilder()); + fixture.Customizations.Add(new EfRepositoryListBuilder()); + } +} + +internal class EfAuthRequestAutoDataAttribute : CustomAutoDataAttribute +{ + public EfAuthRequestAutoDataAttribute() : base(new SutProviderCustomization(), new EfAuthRequest()) + { } +} + +internal class InlineEfAuthRequestAutoDataAttribute : InlineCustomAutoDataAttribute +{ + public InlineEfAuthRequestAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization), + typeof(EfAuthRequest) }, values) + { } +} + diff --git a/test/Infrastructure.EFIntegration.Test/AutoFixture/EntityFrameworkRepositoryFixtures.cs b/test/Infrastructure.EFIntegration.Test/AutoFixture/EntityFrameworkRepositoryFixtures.cs index 4a403b70b..1fecaa6b9 100644 --- a/test/Infrastructure.EFIntegration.Test/AutoFixture/EntityFrameworkRepositoryFixtures.cs +++ b/test/Infrastructure.EFIntegration.Test/AutoFixture/EntityFrameworkRepositoryFixtures.cs @@ -63,6 +63,7 @@ public class EfRepositoryListBuilder : ISpecimenBuilder where T : BaseEntityF fixture.Customize(x => x.FromFactory(() => new MapperConfiguration(cfg => { + cfg.AddProfile(); cfg.AddProfile(); cfg.AddProfile(); cfg.AddProfile(); diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/AuthRequestRepositoryTests.cs b/test/Infrastructure.EFIntegration.Test/Repositories/AuthRequestRepositoryTests.cs new file mode 100644 index 000000000..994c8dca8 --- /dev/null +++ b/test/Infrastructure.EFIntegration.Test/Repositories/AuthRequestRepositoryTests.cs @@ -0,0 +1,50 @@ +using Bit.Core.Entities; +using Bit.Core.Test.AutoFixture.Attributes; +using Bit.Infrastructure.EFIntegration.Test.AutoFixture; +using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; +using Xunit; +using EfRepo = Bit.Infrastructure.EntityFramework.Repositories; +using SqlRepo = Bit.Infrastructure.Dapper.Repositories; + +namespace Bit.Infrastructure.EFIntegration.Test.Repositories; + +public class AuthRequestRepositoryTests +{ + [CiSkippedTheory, EfAuthRequestAutoData] + public async void CreateAsync_Works_DataMatches( + AuthRequest authRequest, + AuthRequestCompare equalityComparer, + List suts, + SqlRepo.AuthRequestRepository sqlAuthRequestRepo, + User user, + List efUserRepos, + SqlRepo.UserRepository sqlUserRepo + ) + { + authRequest.ResponseDeviceId = null; + var savedAuthRequests = new List(); + foreach (var sut in suts) + { + var i = suts.IndexOf(sut); + + var efUser = await efUserRepos[i].CreateAsync(user); + sut.ClearChangeTracking(); + authRequest.UserId = efUser.Id; + + var postEfAuthRequest = await sut.CreateAsync(authRequest); + sut.ClearChangeTracking(); + + var savedAuthRequest = await sut.GetByIdAsync(postEfAuthRequest.Id); + savedAuthRequests.Add(savedAuthRequest); + } + + var sqlUser = await sqlUserRepo.CreateAsync(user); + authRequest.UserId = sqlUser.Id; + var sqlAuthRequest = await sqlAuthRequestRepo.CreateAsync(authRequest); + var savedSqlAuthRequest = await sqlAuthRequestRepo.GetByIdAsync(sqlAuthRequest.Id); + savedAuthRequests.Add(savedSqlAuthRequest); + + var distinctItems = savedAuthRequests.Distinct(equalityComparer); + Assert.True(!distinctItems.Skip(1).Any()); + } +} diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/AuthRequestCompare.cs b/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/AuthRequestCompare.cs new file mode 100644 index 000000000..bbe2b14e8 --- /dev/null +++ b/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/AuthRequestCompare.cs @@ -0,0 +1,23 @@ +using System.Diagnostics.CodeAnalysis; +using Bit.Core.Entities; + +namespace Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; + +public class AuthRequestCompare : IEqualityComparer +{ + public bool Equals(AuthRequest x, AuthRequest y) + { + return x.AccessCode == y.AccessCode && + x.MasterPasswordHash == y.MasterPasswordHash && + x.PublicKey == y.PublicKey && + x.RequestDeviceIdentifier == y.RequestDeviceIdentifier && + x.RequestDeviceType == y.RequestDeviceType && + x.RequestIpAddress == y.RequestIpAddress && + x.RequestFingerprint == y.RequestFingerprint; + } + + public int GetHashCode([DisallowNull] AuthRequest obj) + { + return base.GetHashCode(); + } +} diff --git a/util/Migrator/DbScripts/2022-09-12_00_AuthRequestInit.sql b/util/Migrator/DbScripts/2022-09-12_00_AuthRequestInit.sql new file mode 100644 index 000000000..7541e4596 --- /dev/null +++ b/util/Migrator/DbScripts/2022-09-12_00_AuthRequestInit.sql @@ -0,0 +1,218 @@ +-- Create Auth Request table +IF OBJECT_ID('[dbo].[AuthRequest]') IS NOT NULL +BEGIN + DROP TABLE [dbo].[AuthRequest] +END + +IF OBJECT_ID('[dbo].[AuthRequest]') IS NULL +BEGIN +CREATE TABLE [dbo].[AuthRequest] ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [UserId] UNIQUEIDENTIFIER NOT NULL, + [Type] SMALLINT NOT NULL, + [RequestDeviceIdentifier] NVARCHAR(50) NOT NULL, + [RequestDeviceType] SMALLINT NOT NULL, + [RequestIpAddress] VARCHAR(50) NOT NULL, + [RequestFingerprint] VARCHAR(MAX) NOT NULL, + [ResponseDeviceId] UNIQUEIDENTIFIER NULL, + [AccessCode] VARCHAR(25) NOT NULL, + [PublicKey] VARCHAR(MAX) NOT NULL, + [Key] VARCHAR(MAX) NULL, + [MasterPasswordHash] VARCHAR(MAX) NULL, + [CreationDate] DATETIME2 (7) NOT NULL, + [ResponseDate] DATETIME2 (7) NULL, + [AuthenticationDate] DATETIME2 (7) NULL, + CONSTRAINT [PK_AuthRequest] PRIMARY KEY CLUSTERED ([Id] ASC), + CONSTRAINT [FK_AuthRequest_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]), + CONSTRAINT [FK_AuthRequest_ResponseDevice] FOREIGN KEY ([ResponseDeviceId]) REFERENCES [dbo].[Device] ([Id]) +); +END +GO + +-- Create View +IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'AuthRequestView') +BEGIN + DROP VIEW [dbo].[AuthRequestView] +END +GO + +CREATE VIEW [dbo].[AuthRequestView] +AS +SELECT + * +FROM + [dbo].[AuthRequest] +GO + +-- Auth Request CRUD sprocs +IF OBJECT_ID('[dbo].[AuthRequest_Create]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[AuthRequest_Create] +END +GO + +CREATE PROCEDURE [dbo].[AuthRequest_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @UserId UNIQUEIDENTIFIER, + @Type TINYINT, + @RequestDeviceIdentifier NVARCHAR(50), + @RequestDeviceType TINYINT, + @RequestIpAddress VARCHAR(50), + @RequestFingerprint VARCHAR(MAX), + @ResponseDeviceId UNIQUEIDENTIFIER, + @AccessCode VARCHAR(25), + @PublicKey VARCHAR(MAX), + @Key VARCHAR(MAX), + @MasterPasswordHash VARCHAR(MAX), + @CreationDate DATETIME2(7), + @ResponseDate DATETIME2(7), + @AuthenticationDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[AuthRequest] + ( + [Id], + [UserId], + [Type], + [RequestDeviceIdentifier], + [RequestDeviceType], + [RequestIpAddress], + [RequestFingerprint], + [ResponseDeviceId], + [AccessCode], + [PublicKey], + [Key], + [MasterPasswordHash], + [CreationDate], + [ResponseDate], + [AuthenticationDate] + ) + VALUES + ( + @Id, + @UserId, + @Type, + @RequestDeviceIdentifier, + @RequestDeviceType, + @RequestIpAddress, + @RequestFingerprint, + @ResponseDeviceId, + @AccessCode, + @PublicKey, + @Key, + @MasterPasswordHash, + @CreationDate, + @ResponseDate, + @AuthenticationDate + ) +END +GO + +IF OBJECT_ID('[dbo].[AuthRequest_Update]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[AuthRequest_Update] +END +GO + +CREATE PROCEDURE [dbo].[AuthRequest_Update] + @Id UNIQUEIDENTIFIER OUTPUT, + @ResponseDeviceId UNIQUEIDENTIFIER, + @Key VARCHAR(MAX), + @MasterPasswordHash VARCHAR(MAX), + @ResponseDate DATETIME2(7), + @AuthenticationDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[AuthRequest] + SET + [ResponseDeviceId] = @ResponseDeviceId, + [Key] = @Key, + [MasterPasswordHash] = @MasterPasswordHash, + [ResponseDate] = @ResponseDate, + [AuthenticationDate] = @AuthenticationDate + WHERE + [Id] = @Id +END +GO + +IF OBJECT_ID('[dbo].[AuthRequest_ReadById]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[AuthRequest_ReadById] +END +GO + +CREATE PROCEDURE [dbo].[AuthRequest_ReadById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[AuthRequestView] + WHERE + [Id] = @Id +END +GO + +IF OBJECT_ID('[dbo].[AuthRequest_DeleteById]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[AuthRequest_DeleteById] +END +GO + +CREATE PROCEDURE [dbo].[AuthRequest_DeleteById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + DELETE + FROM + [dbo].[AuthRequest] + WHERE + [Id] = @Id +END +GO + +IF OBJECT_ID('[dbo].[AuthRequest_DeleteIfExpired]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[AuthRequest_DeleteIfExpired] +END +GO + +CREATE PROCEDURE [dbo].[AuthRequest_DeleteIfExpired] +AS +BEGIN + SET NOCOUNT OFF + DELETE FROM [dbo].[AuthRequest] WHERE [CreationDate] < DATEADD(minute, -15, GETUTCDATE()); +END +GO + +IF OBJECT_ID('[dbo].[AuthRequest_ReadByUserId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[AuthRequest_ReadByUserId] +END +GO + +CREATE PROCEDURE [dbo].[AuthRequest_ReadByUserId] + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + + SELECT + * + FROM + [dbo].[AuthRequestView] + WHERE + [UserId] = @UserId +END +GO diff --git a/util/Migrator/DbScripts/2022-09-12_01_AuthRequestUpdate.sql b/util/Migrator/DbScripts/2022-09-12_01_AuthRequestUpdate.sql new file mode 100644 index 000000000..ebec22596 --- /dev/null +++ b/util/Migrator/DbScripts/2022-09-12_01_AuthRequestUpdate.sql @@ -0,0 +1,47 @@ +IF OBJECT_ID('[dbo].[AuthRequest_Update]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[AuthRequest_Update] +END +GO + +CREATE PROCEDURE [dbo].[AuthRequest_Update] + @Id UNIQUEIDENTIFIER OUTPUT, + @UserId UNIQUEIDENTIFIER, + @Type SMALLINT, + @RequestDeviceIdentifier NVARCHAR(50), + @RequestDeviceType SMALLINT, + @RequestIpAddress VARCHAR(50), + @RequestFingerprint VARCHAR(MAX), + @ResponseDeviceId UNIQUEIDENTIFIER, + @AccessCode VARCHAR(25), + @PublicKey VARCHAR(MAX), + @Key VARCHAR(MAX), + @MasterPasswordHash VARCHAR(MAX), + @CreationDate DATETIME2 (7), + @ResponseDate DATETIME2 (7), + @AuthenticationDate DATETIME2 (7) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[AuthRequest] + SET + [UserId] = @UserId, + [Type] = @Type, + [RequestDeviceIdentifier] = @RequestDeviceIdentifier, + [RequestDeviceType] = @RequestDeviceType, + [RequestIpAddress] = @RequestIpAddress, + [RequestFingerprint] = @RequestFingerprint, + [ResponseDeviceId] = @ResponseDeviceId, + [AccessCode] = @AccessCode, + [PublicKey] = @PublicKey, + [Key] = @Key, + [MasterPasswordHash] = @MasterPasswordHash, + [CreationDate] = @CreationDate, + [ResponseDate] = @ResponseDate, + [AuthenticationDate] = @AuthenticationDate + WHERE + [Id] = @Id +END +GO diff --git a/util/MsSql/Dockerfile b/util/MsSql/Dockerfile index 70e239050..330f78208 100644 --- a/util/MsSql/Dockerfile +++ b/util/MsSql/Dockerfile @@ -1,7 +1,9 @@ -FROM mcr.microsoft.com/mssql/server:2017-CU18-ubuntu-16.04 +FROM mcr.microsoft.com/mssql/server:2019-CU17-ubuntu-20.04 LABEL com.bitwarden.product="bitwarden" +USER root:root + RUN apt-get update \ && apt-get install -y --no-install-recommends \ gosu \ diff --git a/util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.Designer.cs b/util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.Designer.cs new file mode 100644 index 000000000..866620693 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.Designer.cs @@ -0,0 +1,1672 @@ +// +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("20220912144222_PasswordlessAuthRequests")] + partial class PasswordlessAuthRequests + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.4") + .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("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("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.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + 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("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.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + 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("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("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("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("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("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("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("Key") + .HasColumnType("longtext"); + + b.Property("LastFailedLoginDate") + .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.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.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + 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() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + 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.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"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.cs b/util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.cs new file mode 100644 index 000000000..d74294e22 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.cs @@ -0,0 +1,71 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +public partial class PasswordlessAuthRequests : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AuthRequest", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Type = table.Column(type: "tinyint unsigned", nullable: false), + RequestDeviceIdentifier = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + RequestDeviceType = table.Column(type: "tinyint unsigned", nullable: false), + RequestIpAddress = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + RequestFingerprint = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ResponseDeviceId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + AccessCode = table.Column(type: "varchar(25)", maxLength: 25, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PublicKey = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Key = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + MasterPasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CreationDate = table.Column(type: "datetime(6)", nullable: false), + ResponseDate = table.Column(type: "datetime(6)", nullable: true), + AuthenticationDate = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AuthRequest", x => x.Id); + table.ForeignKey( + name: "FK_AuthRequest_Device_ResponseDeviceId", + column: x => x.ResponseDeviceId, + principalTable: "Device", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_AuthRequest_User_UserId", + column: x => x.UserId, + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_AuthRequest_ResponseDeviceId", + table: "AuthRequest", + column: "ResponseDeviceId"); + + migrationBuilder.CreateIndex( + name: "IX_AuthRequest_UserId", + table: "AuthRequest", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AuthRequest"); + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index 471b285a4..59ca6e5a2 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -19,6 +19,65 @@ namespace Bit.MySqlMigrations.Migrations .HasAnnotation("ProductVersion", "6.0.4") .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("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") @@ -1208,6 +1267,23 @@ namespace Bit.MySqlMigrations.Migrations b.ToTable("User", (string)null); }); + 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") diff --git a/util/MySqlMigrations/Scripts/2022-09-12_00_PasswordlessAuth.sql b/util/MySqlMigrations/Scripts/2022-09-12_00_PasswordlessAuth.sql new file mode 100644 index 000000000..9959606f1 --- /dev/null +++ b/util/MySqlMigrations/Scripts/2022-09-12_00_PasswordlessAuth.sql @@ -0,0 +1,31 @@ +START TRANSACTION; + +CREATE TABLE `AuthRequest` ( + `Id` char(36) COLLATE ascii_general_ci NOT NULL, + `UserId` char(36) COLLATE ascii_general_ci NOT NULL, + `Type` tinyint unsigned NOT NULL, + `RequestDeviceIdentifier` varchar(50) CHARACTER SET utf8mb4 NULL, + `RequestDeviceType` tinyint unsigned NOT NULL, + `RequestIpAddress` varchar(50) CHARACTER SET utf8mb4 NULL, + `RequestFingerprint` longtext CHARACTER SET utf8mb4 NULL, + `ResponseDeviceId` char(36) COLLATE ascii_general_ci NULL, + `AccessCode` varchar(25) CHARACTER SET utf8mb4 NULL, + `PublicKey` longtext CHARACTER SET utf8mb4 NULL, + `Key` longtext CHARACTER SET utf8mb4 NULL, + `MasterPasswordHash` longtext CHARACTER SET utf8mb4 NULL, + `CreationDate` datetime(6) NOT NULL, + `ResponseDate` datetime(6) NULL, + `AuthenticationDate` datetime(6) NULL, + CONSTRAINT `PK_AuthRequest` PRIMARY KEY (`Id`), + CONSTRAINT `FK_AuthRequest_Device_ResponseDeviceId` FOREIGN KEY (`ResponseDeviceId`) REFERENCES `Device` (`Id`), + CONSTRAINT `FK_AuthRequest_User_UserId` FOREIGN KEY (`UserId`) REFERENCES `User` (`Id`) ON DELETE CASCADE +) CHARACTER SET=utf8mb4; + +CREATE INDEX `IX_AuthRequest_ResponseDeviceId` ON `AuthRequest` (`ResponseDeviceId`); + +CREATE INDEX `IX_AuthRequest_UserId` ON `AuthRequest` (`UserId`); + +INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`) +VALUES ('20220912144222_PasswordlessAuthRequests', '6.0.4'); + +COMMIT; diff --git a/util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.Designer.cs b/util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.Designer.cs new file mode 100644 index 000000000..ad123ddd6 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.Designer.cs @@ -0,0 +1,1680 @@ +// +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("20220830163921_PasswordlessAuthRequests")] + partial class PasswordlessAuthRequests + { + 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.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasColumnType("text"); + + 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") + .HasColumnType("text"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestFingerprint") + .HasColumnType("text"); + + b.Property("RequestIpAddress") + .HasColumnType("text"); + + 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("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.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + 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("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.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + 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("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("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("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("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("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("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("Key") + .HasColumnType("text"); + + b.Property("LastFailedLoginDate") + .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.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.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + 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() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + 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.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"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.cs b/util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.cs new file mode 100644 index 000000000..cef759de8 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.cs @@ -0,0 +1,905 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +public partial class PasswordlessAuthRequests : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "User", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RenewalReminderDate", + table: "User", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "PremiumExpirationDate", + table: "User", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "LastFailedLoginDate", + table: "User", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "User", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "AccountRevisionDate", + table: "User", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Transaction", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "SsoUser", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "SsoConfig", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "SsoConfig", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Send", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "ExpirationDate", + table: "Send", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DeletionDate", + table: "Send", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Send", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "ProviderUser", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "ProviderUser", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "ProviderOrganization", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "ProviderOrganization", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Provider", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Provider", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Policy", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Policy", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "OrganizationUser", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "OrganizationUser", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "ValidUntil", + table: "OrganizationSponsorship", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "LastSyncDate", + table: "OrganizationSponsorship", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "OrganizationApiKey", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Organization", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "OwnersNotifiedOfAutoscaling", + table: "Organization", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ExpirationDate", + table: "Organization", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Organization", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Installation", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Group", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Group", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "ExpirationDate", + table: "Grant", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Grant", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "ConsumedDate", + table: "Grant", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Folder", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Folder", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Date", + table: "Event", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "EmergencyAccess", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RecoveryInitiatedDate", + table: "EmergencyAccess", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "LastNotificationDate", + table: "EmergencyAccess", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "EmergencyAccess", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Device", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Device", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Collection", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Collection", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Cipher", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "DeletedDate", + table: "Cipher", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Cipher", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.CreateTable( + name: "AuthRequest", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + Type = table.Column(type: "smallint", nullable: false), + RequestDeviceIdentifier = table.Column(type: "text", nullable: true), + RequestDeviceType = table.Column(type: "smallint", nullable: false), + RequestIpAddress = table.Column(type: "text", nullable: true), + RequestFingerprint = table.Column(type: "text", nullable: true), + ResponseDeviceId = table.Column(type: "uuid", nullable: true), + AccessCode = table.Column(type: "text", nullable: true), + PublicKey = table.Column(type: "text", nullable: true), + Key = table.Column(type: "text", nullable: true), + MasterPasswordHash = table.Column(type: "text", nullable: true), + CreationDate = table.Column(type: "timestamp with time zone", nullable: false), + ResponseDate = table.Column(type: "timestamp with time zone", nullable: true), + AuthenticationDate = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AuthRequest", x => x.Id); + table.ForeignKey( + name: "FK_AuthRequest_Device_ResponseDeviceId", + column: x => x.ResponseDeviceId, + principalTable: "Device", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_AuthRequest_User_UserId", + column: x => x.UserId, + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AuthRequest_ResponseDeviceId", + table: "AuthRequest", + column: "ResponseDeviceId"); + + migrationBuilder.CreateIndex( + name: "IX_AuthRequest_UserId", + table: "AuthRequest", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AuthRequest"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "User", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RenewalReminderDate", + table: "User", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "PremiumExpirationDate", + table: "User", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "LastFailedLoginDate", + table: "User", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "User", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "AccountRevisionDate", + table: "User", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Transaction", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "SsoUser", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "SsoConfig", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "SsoConfig", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Send", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "ExpirationDate", + table: "Send", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "DeletionDate", + table: "Send", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Send", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "ProviderUser", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "ProviderUser", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "ProviderOrganization", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "ProviderOrganization", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Provider", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Provider", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Policy", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Policy", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "OrganizationUser", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "OrganizationUser", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "ValidUntil", + table: "OrganizationSponsorship", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "LastSyncDate", + table: "OrganizationSponsorship", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "OrganizationApiKey", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Organization", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "OwnersNotifiedOfAutoscaling", + table: "Organization", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ExpirationDate", + table: "Organization", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Organization", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Installation", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Group", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Group", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "ExpirationDate", + table: "Grant", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Grant", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "ConsumedDate", + table: "Grant", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Folder", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Folder", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Date", + table: "Event", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "EmergencyAccess", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RecoveryInitiatedDate", + table: "EmergencyAccess", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "LastNotificationDate", + table: "EmergencyAccess", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "EmergencyAccess", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Device", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Device", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Collection", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Collection", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "RevisionDate", + table: "Cipher", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "DeletedDate", + table: "Cipher", + type: "timestamp without time zone", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreationDate", + table: "Cipher", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + } +} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index cdef656ac..c81a0201b 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -23,6 +23,62 @@ namespace Bit.PostgresMigrations.Migrations NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasColumnType("text"); + + 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") + .HasColumnType("text"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestFingerprint") + .HasColumnType("text"); + + b.Property("RequestIpAddress") + .HasColumnType("text"); + + 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") @@ -32,13 +88,13 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Data") .HasColumnType("text"); b.Property("DeletedDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Favorites") .HasColumnType("text"); @@ -53,7 +109,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("smallint"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Type") .HasColumnType("smallint"); @@ -76,7 +132,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("ExternalId") .HasMaxLength(300) @@ -89,7 +145,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -167,7 +223,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Identifier") .HasMaxLength(50) @@ -182,7 +238,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(255)"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Type") .HasColumnType("smallint"); @@ -203,7 +259,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Email") .HasMaxLength(256) @@ -219,13 +275,13 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("LastNotificationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("RecoveryInitiatedDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Status") .HasColumnType("smallint"); @@ -260,7 +316,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("Date") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("DeviceType") .HasColumnType("smallint"); @@ -310,13 +366,13 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Name") .HasColumnType("text"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("UserId") .HasColumnType("uuid"); @@ -339,10 +395,10 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(200)"); b.Property("ConsumedDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Data") .HasColumnType("text"); @@ -352,7 +408,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(200)"); b.Property("ExpirationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("SessionId") .HasMaxLength(100) @@ -380,7 +436,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("boolean"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("ExternalId") .HasMaxLength(300) @@ -394,7 +450,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -429,7 +485,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Email") .HasMaxLength(256) @@ -481,13 +537,13 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(30)"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Enabled") .HasColumnType("boolean"); b.Property("ExpirationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Gateway") .HasColumnType("smallint"); @@ -523,7 +579,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(50)"); b.Property("OwnersNotifiedOfAutoscaling") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Plan") .HasMaxLength(50) @@ -542,7 +598,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Seats") .HasColumnType("integer"); @@ -610,7 +666,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Type") .HasColumnType("smallint"); @@ -656,7 +712,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(256)"); b.Property("LastSyncDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("OfferedToEmail") .HasMaxLength(256) @@ -678,7 +734,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("boolean"); b.Property("ValidUntil") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -698,7 +754,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("boolean"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Email") .HasMaxLength(256) @@ -721,7 +777,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Status") .HasColumnType("smallint"); @@ -747,7 +803,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Data") .HasColumnType("text"); @@ -759,7 +815,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Type") .HasColumnType("smallint"); @@ -798,7 +854,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Enabled") .HasColumnType("boolean"); @@ -807,7 +863,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Status") .HasColumnType("smallint"); @@ -826,7 +882,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Key") .HasColumnType("text"); @@ -838,7 +894,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Settings") .HasColumnType("text"); @@ -858,7 +914,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Email") .HasColumnType("text"); @@ -873,7 +929,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Status") .HasColumnType("smallint"); @@ -902,19 +958,19 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("integer"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Data") .HasColumnType("text"); b.Property("DeletionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Disabled") .HasColumnType("boolean"); b.Property("ExpirationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("HideEmail") .HasColumnType("boolean"); @@ -933,7 +989,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(300)"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Type") .HasColumnType("smallint"); @@ -959,7 +1015,7 @@ namespace Bit.PostgresMigrations.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Data") .HasColumnType("text"); @@ -971,7 +1027,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -989,7 +1045,7 @@ namespace Bit.PostgresMigrations.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("ExternalId") .HasMaxLength(50) @@ -1049,7 +1105,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("numeric"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Details") .HasMaxLength(100) @@ -1095,7 +1151,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("AccountRevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("ApiKey") .IsRequired() @@ -1103,7 +1159,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(30)"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Culture") .HasMaxLength(10) @@ -1151,7 +1207,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("LastFailedLoginDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("LicenseKey") .HasMaxLength(100) @@ -1176,7 +1232,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("boolean"); b.Property("PremiumExpirationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("PrivateKey") .HasColumnType("text"); @@ -1188,10 +1244,10 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("RenewalReminderDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("SecurityStamp") .IsRequired() @@ -1219,6 +1275,23 @@ namespace Bit.PostgresMigrations.Migrations b.ToTable("User", (string)null); }); + 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") diff --git a/util/PostgresMigrations/Scripts/2022-09-12_00_PasswordlessAuthRequests.psql b/util/PostgresMigrations/Scripts/2022-09-12_00_PasswordlessAuthRequests.psql new file mode 100644 index 000000000..0db38abc0 --- /dev/null +++ b/util/PostgresMigrations/Scripts/2022-09-12_00_PasswordlessAuthRequests.psql @@ -0,0 +1,133 @@ +START TRANSACTION; + +ALTER TABLE "User" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "User" ALTER COLUMN "RenewalReminderDate" TYPE timestamp with time zone; + +ALTER TABLE "User" ALTER COLUMN "PremiumExpirationDate" TYPE timestamp with time zone; + +ALTER TABLE "User" ALTER COLUMN "LastFailedLoginDate" TYPE timestamp with time zone; + +ALTER TABLE "User" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "User" ALTER COLUMN "AccountRevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "Transaction" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "SsoUser" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "SsoConfig" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "SsoConfig" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "Send" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "Send" ALTER COLUMN "ExpirationDate" TYPE timestamp with time zone; + +ALTER TABLE "Send" ALTER COLUMN "DeletionDate" TYPE timestamp with time zone; + +ALTER TABLE "Send" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "ProviderUser" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "ProviderUser" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "ProviderOrganization" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "ProviderOrganization" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "Provider" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "Provider" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "Policy" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "Policy" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "OrganizationUser" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "OrganizationUser" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "OrganizationSponsorship" ALTER COLUMN "ValidUntil" TYPE timestamp with time zone; + +ALTER TABLE "OrganizationSponsorship" ALTER COLUMN "LastSyncDate" TYPE timestamp with time zone; + +ALTER TABLE "OrganizationApiKey" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "Organization" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "Organization" ALTER COLUMN "OwnersNotifiedOfAutoscaling" TYPE timestamp with time zone; + +ALTER TABLE "Organization" ALTER COLUMN "ExpirationDate" TYPE timestamp with time zone; + +ALTER TABLE "Organization" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "Installation" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "Group" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "Group" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "Grant" ALTER COLUMN "ExpirationDate" TYPE timestamp with time zone; + +ALTER TABLE "Grant" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "Grant" ALTER COLUMN "ConsumedDate" TYPE timestamp with time zone; + +ALTER TABLE "Folder" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "Folder" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "Event" ALTER COLUMN "Date" TYPE timestamp with time zone; + +ALTER TABLE "EmergencyAccess" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "EmergencyAccess" ALTER COLUMN "RecoveryInitiatedDate" TYPE timestamp with time zone; + +ALTER TABLE "EmergencyAccess" ALTER COLUMN "LastNotificationDate" TYPE timestamp with time zone; + +ALTER TABLE "EmergencyAccess" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "Device" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "Device" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "Collection" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "Collection" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +ALTER TABLE "Cipher" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone; + +ALTER TABLE "Cipher" ALTER COLUMN "DeletedDate" TYPE timestamp with time zone; + +ALTER TABLE "Cipher" ALTER COLUMN "CreationDate" TYPE timestamp with time zone; + +CREATE TABLE "AuthRequest" ( + "Id" uuid NOT NULL, + "UserId" uuid NOT NULL, + "Type" smallint NOT NULL, + "RequestDeviceIdentifier" text NULL, + "RequestDeviceType" smallint NOT NULL, + "RequestIpAddress" text NULL, + "RequestFingerprint" text NULL, + "ResponseDeviceId" uuid NULL, + "AccessCode" text NULL, + "PublicKey" text NULL, + "Key" text NULL, + "MasterPasswordHash" text NULL, + "CreationDate" timestamp with time zone NOT NULL, + "ResponseDate" timestamp with time zone NULL, + "AuthenticationDate" timestamp with time zone NULL, + CONSTRAINT "PK_AuthRequest" PRIMARY KEY ("Id"), + CONSTRAINT "FK_AuthRequest_Device_ResponseDeviceId" FOREIGN KEY ("ResponseDeviceId") REFERENCES "Device" ("Id"), + CONSTRAINT "FK_AuthRequest_User_UserId" FOREIGN KEY ("UserId") REFERENCES "User" ("Id") ON DELETE CASCADE +); + +CREATE INDEX "IX_AuthRequest_ResponseDeviceId" ON "AuthRequest" ("ResponseDeviceId"); + +CREATE INDEX "IX_AuthRequest_UserId" ON "AuthRequest" ("UserId"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20220830163921_PasswordlessAuthRequests', '6.0.4'); + +COMMIT;