diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
new file mode 100644
index 000000000..94c55eb5b
--- /dev/null
+++ b/.config/dotnet-tools.json
@@ -0,0 +1,22 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "swashbuckle.aspnetcore.cli": {
+ "version": "6.5.0",
+ "commands": ["swagger"]
+ },
+ "coverlet.console": {
+ "version": "3.1.2",
+ "commands": ["coverlet"]
+ },
+ "dotnet-reportgenerator-globaltool": {
+ "version": "5.1.6",
+ "commands": ["reportgenerator"]
+ },
+ "dotnet-ef": {
+ "version": "6.0.12",
+ "commands": ["dotnet-ef"]
+ }
+ }
+}
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..ea060dc75
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,3 @@
+**/bin
+**/obj
+**/node_modules
diff --git a/.editorconfig b/.editorconfig
index 33b2aeac7..e53c670fa 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -5,6 +5,7 @@ root = true
# Don't use tabs for indentation.
[*]
+end_of_line = lf
indent_style = space
# (Please don't specify an indent_size here; that has too many unintended consequences.)
@@ -72,6 +73,15 @@ dotnet_naming_style.end_in_async.required_suffix = Async
dotnet_naming_style.end_in_async.capitalization = pascal_case
dotnet_naming_style.end_in_async.word_separator =
+# Obsolete warnings, this should be removed or changed to warning once we address some of the obsolete items.
+dotnet_diagnostic.CS0618.severity = suggestion
+
+# Obsolete warnings, this should be removed or changed to warning once we address some of the obsolete items.
+dotnet_diagnostic.CS0612.severity = suggestion
+
+# Remove unnecessary using directives https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0005
+dotnet_diagnostic.IDE0005.severity = warning
+
# CSharp code style settings:
[*.cs]
# Prefer "var" everywhere
@@ -104,6 +114,13 @@ csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
+# Namespace settings
+csharp_style_namespace_declarations = file_scoped:warning
+
+# Switch expression
+dotnet_diagnostic.CS8509.severity = error # missing switch case for named enum value
+dotnet_diagnostic.CS8524.severity = none # missing switch case for unnamed enum value
+
# All files
[*]
guidelines = 120
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 000000000..02b9803ea
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,11 @@
+# Apply .NET format https://github.com/bitwarden/server/pull/1764
+23b0a1f9df25058ab29785ecad9a233113c10889
+
+# Turn on file scoped namespaces (gets reverted) https://github.com/bitwarden/server/pull/2225
+34fb4cca2aa78deb84d4cbc359992a7c6bba7ea5
+
+# Revert filescoped https://github.com/bitwarden/server/pull/2227
+bae03feffecbef488cb52f5f5bc133dfdbbaa316
+
+# Run formatting for file scoped namespaces https://github.com/bitwarden/server/pull/2230
+7f5f010e1eea400300c47f776604ecf46c4b4f2d
diff --git a/.git-hooks/pre-commit b/.git-hooks/pre-commit
new file mode 100755
index 000000000..bc661efae
--- /dev/null
+++ b/.git-hooks/pre-commit
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+FILES=$(git diff --cached --name-only --diff-filter=ACM "*.cs")
+if [ -n "$FILES" ]
+then
+ dotnet format ./bitwarden-server.sln --no-restore --include $FILES
+ echo "$FILES" | xargs git add
+fi
diff --git a/.gitattributes b/.gitattributes
index 1d012697d..ecb84500f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,4 @@
*.sh eol=lf
+*.cs eol=lf
.dockerignore eol=lf
dockerfile eol=lf
\ No newline at end of file
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 000000000..a4645cd12
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,5 @@
+# Please sort lines alphabetically, this will ensure we don't accidentally add duplicates.
+#
+# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
+
+**/SecretsManager @bitwarden/pod-sm-dev
diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
index 7216f51c4..a19b25052 100644
--- a/.github/ISSUE_TEMPLATE/bug.yml
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -1,4 +1,4 @@
-name: Bug Report
+name: Server Bug Report
description: File a bug report
labels: [bug]
body:
@@ -6,7 +6,7 @@ body:
attributes:
value: |
Thanks for taking the time to fill out this bug report!
-
+
Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
- type: textarea
id: reproduce
@@ -71,3 +71,11 @@ body:
- Operating system: [e.g. Windows 10, Mac OS Catalina]
- Environment: [e.g. Docker, EKS, ECS, K8S]
- Hardware: [e.g. Intel 6-core, 8GB RAM]
+ - type: checkboxes
+ id: issue-tracking-info
+ attributes:
+ label: Issue Tracking Info
+ description: |
+ Issue tracking information
+ options:
+ - label: I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.
diff --git a/.github/ISSUE_TEMPLATE/bw-unified.yml b/.github/ISSUE_TEMPLATE/bw-unified.yml
new file mode 100644
index 000000000..c1284f183
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bw-unified.yml
@@ -0,0 +1,90 @@
+name: Bitwarden Unified Bug Report
+name: Bitwarden Unified Deployment Bug Report
+description: File a bug report
+labels: [bug, bw-unified-deploy]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report!
+
+ Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
+ - type: textarea
+ id: reproduce
+ attributes:
+ label: Steps To Reproduce
+ description: How can we reproduce the behavior.
+ value: |
+ 1. Go to '...'
+ 2. Click on '....'
+ 3. Scroll down to '....'
+ 4. Click on '...'
+ validations:
+ required: true
+ - type: textarea
+ id: expected
+ attributes:
+ label: Expected Result
+ description: A clear and concise description of what you expected to happen.
+ validations:
+ required: true
+ - type: textarea
+ id: actual
+ attributes:
+ label: Actual Result
+ description: A clear and concise description of what is happening.
+ validations:
+ required: true
+ - type: textarea
+ id: screenshots
+ attributes:
+ label: Screenshots or Videos
+ description: If applicable, add screenshots and/or a short video to help explain your problem.
+ - type: textarea
+ id: additional-context
+ attributes:
+ label: Additional Context
+ description: Add any other context about the problem here.
+ - type: input
+ id: version
+ attributes:
+ label: Githash Version
+ description: Please go to https://{your-bitwarden-domain}/api/config and copy the gitHash version
+ validations:
+ required: true
+ - type: textarea
+ id: environment-details
+ attributes:
+ label: Environment Details
+ description: If Self-Hosted please provide some additional environment details.
+ placeholder: |
+ - Operating system: [e.g. Windows 10, Mac OS Catalina]
+ - Environment: [e.g. Docker, EKS, ECS, K8S]
+ - Hardware: [e.g. Intel 6-core, 8GB RAM]
+ - type: textarea
+ id: database-image
+ attributes:
+ label: Database Image
+ description: Please include the image and version of your database
+ placeholder: |
+ # MariaDB Example
+ mariadb:10
+ # Postgres Example
+ postgres:14
+ - type: textarea
+ id: epic-label
+ attributes:
+ label: Issue-Link
+ description: Link to our pinned issue, tracking all Bitwarden Unified
+ value: |
+ https://github.com/bitwarden/server/issues/2480
+ validations:
+ required: true
+ - type: checkboxes
+ id: issue-tracking-info
+ attributes:
+ label: Issue Tracking Info
+ description: |
+ Issue tracking information
+ options:
+ - label: I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 590b84493..8ebc483a9 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,9 +1,14 @@
## Type of change
+
+
+
+```
- [ ] Bug fix
- [ ] New feature development
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
- [ ] Build/deploy pipeline (DevOps)
- [ ] Other
+```
## Objective
@@ -16,13 +21,10 @@
* **file.ext:** Description of what was changed and why
-## Testing requirements
-
-
-
-
## Before you submit
-- [ ] If making database changes - I have also updated Entity Framework queries and/or migrations
-- [ ] I have added **unit tests** where it makes sense to do so (encouraged but not required)
-- [ ] This change requires a **documentation update** (notify the documentation team)
-- [ ] This change has particular **deployment requirements** (notify the DevOps team)
+
+- Please check for formatting errors (`dotnet format --verify-no-changes`) (required)
+- If making database changes - make sure you also update Entity Framework queries and/or migrations
+- Please add **unit tests** where it makes sense to do so (encouraged but not required)
+- If this change requires a **documentation update** - notify the documentation team
+- If this change has particular **deployment requirements** - notify the DevOps team
diff --git a/.github/renovate.json b/.github/renovate.json
new file mode 100644
index 000000000..61ebaed0d
--- /dev/null
+++ b/.github/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/.github/workflows/automatic-issue-responses.yml b/.github/workflows/automatic-issue-responses.yml
new file mode 100644
index 000000000..970532ab1
--- /dev/null
+++ b/.github/workflows/automatic-issue-responses.yml
@@ -0,0 +1,64 @@
+---
+name: Automatic responses
+on:
+ issues:
+ types:
+ - labeled
+jobs:
+ close-issue:
+ name: 'Close issue with automatic response'
+ runs-on: ubuntu-20.04
+ permissions:
+ issues: write
+ steps:
+ # Feature request
+ - if: github.event.label.name == 'feature-request'
+ name: Feature request
+ uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
+ with:
+ comment: |
+ We use GitHub issues as a place to track bugs and other development related issues. The [Bitwarden Community Forums](https://community.bitwarden.com/) has a [Feature Requests](https://community.bitwarden.com/c/feature-requests) section for submitting, voting for, and discussing requests like this one.
+
+ Please [sign up on our forums](https://community.bitwarden.com/signup) and search to see if this request already exists. If so, you can vote for it and contribute to any discussions about it. If not, you can re-create the request there so that it can be properly tracked.
+
+ This issue will now be closed. Thanks!
+ # Intended behavior
+ - if: github.event.label.name == 'intended-behavior'
+ name: Intended behaviour
+ uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
+ with:
+ comment: |
+ Your issue appears to be describing the intended behavior of the software. If you want this to be changed, it would be a feature request.
+
+ We use GitHub issues as a place to track bugs and other development related issues. The [Bitwarden Community Forums](https://community.bitwarden.com/) has a [Feature Requests](https://community.bitwarden.com/c/feature-requests) section for submitting, voting for, and discussing requests like this one.
+
+ Please [sign up on our forums](https://community.bitwarden.com/signup) and search to see if this request already exists. If so, you can vote for it and contribute to any discussions about it. If not, you can re-create the request there so that it can be properly tracked.
+
+ This issue will now be closed. Thanks!
+ # Customer support request
+ - if: github.event.label.name == 'customer-support'
+ name: Customer Support request
+ uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
+ with:
+ comment: |
+ We use GitHub issues as a place to track bugs and other development related issues. Your issue appears to be a support request, or would otherwise be better handled by our dedicated Customer Success team.
+
+ Please contact us using our [Contact page](https://bitwarden.com/contact). You can include a link to this issue in the message content.
+
+ Alternatively, you can also search for an answer in our [help documentation](https://bitwarden.com/help/) or get help from other Bitwarden users on our [community forums](https://community.bitwarden.com/c/support/). The issue here will be closed.
+ # Resolved
+ - if: github.event.label.name == 'resolved'
+ name: Resolved
+ uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
+ with:
+ comment: |
+ We’ve closed this issue, as it appears the original problem has been resolved. If this happens again or continues to be an problem, please respond to this issue with any additional detail to assist with reproduction and root cause analysis.
+ # Stale
+ - if: github.event.label.name == 'stale'
+ name: Stale
+ uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
+ with:
+ comment: |
+ As we haven’t heard from you about this problem in some time, this issue will now be closed.
+
+ If this happens again or continues to be an problem, please respond to this issue with any additional detail to assist with reproduction and root cause analysis.
diff --git a/.github/workflows/build-self-host.yml b/.github/workflows/build-self-host.yml
new file mode 100644
index 000000000..e4ccb2eb2
--- /dev/null
+++ b/.github/workflows/build-self-host.yml
@@ -0,0 +1,166 @@
+---
+name: Build Self-Host
+
+on:
+ push:
+ branches-ignore:
+ - "l10n_master"
+ - "gh-pages"
+ paths-ignore:
+ - ".github/workflows/**"
+ workflow_dispatch:
+
+jobs:
+ build-docker:
+ name: Build Docker image
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
+
+ - name: Check Branch to Publish
+ env:
+ PUBLISH_BRANCHES: "master,rc,hotfix-rc"
+ id: publish-branch-check
+ run: |
+ IFS="," read -a publish_branches <<< $PUBLISH_BRANCHES
+
+ if [[ " ${publish_branches[*]} " =~ " ${GITHUB_REF:11} " ]]; then
+ echo "is_publish_branch=true" >> $GITHUB_ENV
+ else
+ echo "is_publish_branch=false" >> $GITHUB_ENV
+ fi
+
+ ########## Set up Docker ##########
+ - name: Set up QEMU emulators
+ uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325
+
+ ########## Login to Docker registries ##########
+ - name: Login to Azure - QA Subscription
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
+ with:
+ creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
+
+ - name: Login to Azure ACR
+ run: az acr login -n bitwardenqa
+
+ - name: Login to Azure - Prod Subscription
+ if: ${{ env.is_publish_branch == 'true' }}
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
+ with:
+ creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
+
+ - name: Retrieve secrets
+ if: ${{ env.is_publish_branch == 'true' }}
+ id: retrieve-secrets
+ 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"
+
+ - name: Log into Docker
+ if: ${{ env.is_publish_branch == 'true' }}
+ env:
+ DOCKER_USERNAME: ${{ steps.retrieve-secrets.outputs.docker-username }}
+ DOCKER_PASSWORD: ${{ steps.retrieve-secrets.outputs.docker-password }}
+ run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
+
+ - name: Setup Docker Trust
+ if: ${{ env.is_publish_branch == 'true' }}
+ env:
+ DCT_DELEGATION_KEY_ID: "c9bde8ec820701516491e5e03d3a6354e7bd66d05fa3df2b0062f68b116dc59c"
+ DCT_DELEGATE_KEY: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-key }}
+ DCT_REPO_PASSPHRASE: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-repo-passphrase }}
+ run: |
+ mkdir -p ~/.docker/trust/private
+ echo "$DCT_DELEGATE_KEY" > ~/.docker/trust/private/$DCT_DELEGATION_KEY_ID.key
+ echo "DOCKER_CONTENT_TRUST=1" >> $GITHUB_ENV
+ echo "DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$DCT_REPO_PASSPHRASE" >> $GITHUB_ENV
+
+ ########## Generate image tag and build Docker image ##########
+ - name: Generate Docker image tag
+ id: tag
+ run: |
+ IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
+ if [[ "$IMAGE_TAG" == "master" ]]; then
+ IMAGE_TAG=dev
+ elif [[ "$IMAGE_TAG" == "rc" ]] || [[ "$IMAGE_TAG" == "hotfix-rc" ]]; then
+ IMAGE_TAG=beta
+ fi
+
+ echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
+
+ - name: Generate tag list
+ id: tag-list
+ env:
+ IMAGE_TAG: ${{ steps.tag.outputs.image_tag }}
+ run: |
+ if [ "$IMAGE_TAG" = "dev" ] || [ "$IMAGE_TAG" = "beta" ]; then
+ echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG},bitwarden/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT
+ else
+ echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Build Docker image
+ uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5
+ with:
+ context: .
+ file: docker-unified/Dockerfile
+ platforms: |
+ linux/amd64,
+ linux/arm/v7,
+ linux/arm64/v8
+ push: true
+ tags: ${{ steps.tag-list.outputs.tags }}
+
+ - name: Log out of Docker and disable Docker Notary
+ if: ${{ env.is_publish_branch == 'true' }}
+ run: |
+ docker logout
+ echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
+
+ check-failures:
+ name: Check for failures
+ if: always()
+ runs-on: ubuntu-22.04
+ needs: build-docker
+ steps:
+ - name: Check if any job failed
+ if: |
+ github.ref == 'refs/heads/master'
+ || github.ref == 'refs/heads/rc'
+ || github.ref == 'refs/heads/hotfix-rc'
+ env:
+ BUILD_DOCKER_STATUS: ${{ needs.build-docker.result }}
+ run: |
+ if [ "$BUILD_DOCKER_STATUS" = "failure" ]; then
+ exit 1
+ fi
+
+ - name: Login to Azure - Prod Subscription
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
+ if: failure()
+ with:
+ creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
+
+ - name: Retrieve secrets
+ id: retrieve-secrets
+ 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
+ if: failure()
+ env:
+ SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
+ with:
+ status: ${{ job.status }}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6b16b9aa2..aa43052df 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -4,18 +4,19 @@ name: Build
on:
push:
branches-ignore:
- - 'l10n_master'
- - 'gh-pages'
+ - "l10n_master"
+ - "gh-pages"
+ paths-ignore:
+ - ".github/workflows/**"
workflow_dispatch:
- inputs: {}
jobs:
cloc:
name: CLOC
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
steps:
- name: Checkout repo
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
+ uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
- name: Install cloc
run: |
@@ -25,43 +26,46 @@ 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-22.04
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
+
+ - name: Verify Format
+ run: dotnet format --verify-no-changes
testing:
name: Testing
- runs-on: windows-2019
+ runs-on: windows-2022
+ env:
+ NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps:
- - name: Set up NuGet
- uses: nuget/setup-nuget@04b0c2b8d1b97922f67eca497d7cf0bf17b8ffe1
+ - name: Set up dotnet
+ uses: actions/setup-dotnet@9211491ffb35dd6a6657ca4f45d43dfe6e97c829
with:
- nuget-version: '5'
-
+ dotnet-version: "6.0.x"
- name: Set up MSBuild
- uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d
-
- - name: Set up Node
- uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
- with:
- node-version: '14'
-
- - name: Update NPM
- run: |
- npm install -g npm@7
+ uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
- name: Print environment
run: |
- nuget help | grep Version
- msbuild -version
dotnet --info
- node --version
- npm --version
+ msbuild -version
+ nuget help | grep Version
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
+ uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Restore
- run: msbuild /t:restore
+ run: dotnet restore --locked-mode
+ shell: pwsh
+
+ - name: Build OSS solution
+ run: msbuild bitwarden-server.sln /p:Configuration=Debug /p:DefineConstants="OSS" /verbosity:minimal
shell: pwsh
- name: Build solution
@@ -69,58 +73,69 @@ jobs:
shell: pwsh
- name: Test OSS solution
- run: dotnet test ./test --configuration Debug --no-build
+ run: dotnet test ./test --configuration Debug --no-build --logger "trx;LogFileName=oss-test-results.trx" || true
shell: pwsh
- name: Test Bitwarden solution
- run: dotnet test ./bitwarden_license/test/CmmCore.Test --configuration Debug --no-build
+ run: dotnet test ./bitwarden_license/test --configuration Debug --no-build --logger "trx;LogFileName=bw-test-results.trx" || true
shell: pwsh
+ - name: Report test results
+ uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226
+ if: always()
+ with:
+ name: Test Results
+ path: "**/*-test-results.trx"
+ reporter: dotnet-trx
+ fail-on-error: true
build-artifacts:
name: Build artifacts
- runs-on: ubuntu-20.04
- needs: testing
+ runs-on: ubuntu-22.04
+ needs:
+ - testing
+ - lint
strategy:
fail-fast: false
matrix:
include:
- - service_name: Admin
+ - project_name: Admin
base_path: ./src
- gulp: true
- - service_name: Api
+ node: true
+ - project_name: Api
base_path: ./src
- - service_name: Billing
+ - project_name: Billing
base_path: ./src
- - service_name: Events
+ - project_name: Events
base_path: ./src
- - service_name: EventsProcessor
+ - project_name: EventsProcessor
base_path: ./src
- - service_name: Icons
+ - project_name: Icons
base_path: ./src
- - service_name: Identity
+ - project_name: Identity
base_path: ./src
- - service_name: Notifications
+ - project_name: Notifications
base_path: ./src
- - service_name: Server
+ - project_name: Server
base_path: ./util
- - service_name: Setup
+ - project_name: Setup
base_path: ./util
- - service_name: Sso
+ - project_name: Sso
base_path: ./bitwarden_license/src
- gulp: true
+ node: true
+ - project_name: Scim
+ base_path: ./bitwarden_license/src
+ dotnet: true
steps:
- name: Checkout repo
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
+ uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Set up Node
- uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
+ uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a
with:
- node-version: '14'
-
- - name: Update NPM
- run: |
- npm install -g npm@7
+ cache: "npm"
+ cache-dependency-path: "**/package-lock.json"
+ node-version: "16"
- name: Print environment
run: |
@@ -128,288 +143,273 @@ jobs:
dotnet --info
node --version
npm --version
- gulp --version
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- - name: Set up Gulp
- if: ${{ matrix.gulp }}
- working-directory: ${{ matrix.base_path }}/${{ matrix.service_name }}
- run: |
- npm install -g gulp
-
- - name: Restore/Clean service
- working-directory: ${{ matrix.base_path }}/${{ matrix.service_name }}
+ - name: Restore/Clean project
+ working-directory: ${{ matrix.base_path }}/${{ matrix.project_name }}
run: |
echo "Restore"
dotnet restore
echo "Clean"
dotnet clean -c "Release" -o obj/build-output/publish
- - name: Execute Gulp
- if: ${{ matrix.gulp }}
- working-directory: ${{ matrix.base_path }}/${{ matrix.service_name }}
+ - name: Build node
+ if: ${{ matrix.node }}
+ working-directory: ${{ matrix.base_path }}/${{ matrix.project_name }}
run: |
- npm install
- gulp --gulpfile gulpfile.js build
+ npm ci
+ npm run build
- - name: Publish service
- working-directory: ${{ matrix.base_path }}/${{ matrix.service_name }}
+ - name: Publish project
+ working-directory: ${{ matrix.base_path }}/${{ matrix.project_name }}
run: |
echo "Publish"
dotnet publish -c "Release" -o obj/build-output/publish
cd obj/build-output/publish
- zip -r ${{ matrix.service_name }}.zip .
- mv ${{ matrix.service_name }}.zip ../../../
+ zip -r ${{ matrix.project_name }}.zip .
+ mv ${{ matrix.project_name }}.zip ../../../
pwd
ls -atlh ../../../
- - name: Upload service artifact
- uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
+ - name: Upload project artifact
+ uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
- name: ${{ matrix.service_name }}.zip
- path: ${{ matrix.base_path }}/${{ matrix.service_name }}/${{ matrix.service_name }}.zip
+ name: ${{ matrix.project_name }}.zip
+ path: ${{ matrix.base_path }}/${{ matrix.project_name }}/${{ matrix.project_name }}.zip
if-no-files-found: error
-
build-docker:
name: Build Docker images
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
needs: build-artifacts
strategy:
fail-fast: false
matrix:
include:
- - service_name: Admin
+ - project_name: Admin
base_path: ./src
- docker_repo: bitwarden
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
dotnet: true
- - service_name: Api
+ - project_name: Api
base_path: ./src
- docker_repo: bitwarden
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
dotnet: true
- - service_name: Attachments
+ - project_name: Attachments
base_path: ./util
- docker_repo: bitwarden
- - service_name: Events
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
+ - project_name: Events
base_path: ./src
- docker_repo: bitwarden
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
dotnet: true
- - service_name: EventsProcessor
+ - project_name: EventsProcessor
base_path: ./src
- docker_repo: bitwardenqa.azurecr.io
+ docker_repos: [bitwardenqa.azurecr.io]
dotnet: true
- - service_name: Icons
+ - project_name: Icons
base_path: ./src
- docker_repo: bitwarden
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
dotnet: true
- - service_name: Identity
+ - project_name: Identity
base_path: ./src
- docker_repo: bitwarden
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
dotnet: true
- - service_name: K8S-Proxy
+ - project_name: MsSql
base_path: ./util
- docker_repo: bitwarden
- - service_name: MsSql
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
+ - project_name: Nginx
base_path: ./util
- docker_repo: bitwarden
- - service_name: Nginx
- base_path: ./util
- docker_repo: bitwarden
- - service_name: Notifications
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
+ - project_name: Notifications
base_path: ./src
- docker_repo: bitwarden
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
dotnet: true
- - service_name: Server
+ - project_name: Server
base_path: ./util
- docker_repo: bitwarden
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
dotnet: true
- - service_name: Setup
+ - project_name: Setup
base_path: ./util
- docker_repo: bitwarden
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
dotnet: true
- - service_name: Sso
+ - project_name: Sso
base_path: ./bitwarden_license/src
- docker_repo: bitwarden
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
+ dotnet: true
+ - project_name: Scim
+ base_path: ./bitwarden_license/src
+ docker_repos: [bitwarden, bitwardenqa.azurecr.io]
+ dotnet: true
+ - project_name: Billing
+ base_path: ./src
+ docker_repos: [bitwardenqa.azurecr.io]
dotnet: true
steps:
- name: Checkout repo
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
+ uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- - name: Login to Azure - Prod Subscription
- uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
- with:
- creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
-
- - name: Retrieve secrets
- id: retrieve-secrets
- uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
- with:
- keyvault: "bitwarden-prod-kv"
- secrets: "aws-ecr-access-key-id,
- aws-ecr-secret-access-key,
- docker-password,
- docker-username,
- dct-delegate-2-repo-passphrase,
- dct-delegate-2-key"
-
- - name: Login to Azure - QA Subscription
- if: ${{ matrix.service_name }} == "EventsProcessor"
- uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
- with:
- creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
-
- - name: Log into Docker
- if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
- env:
- DOCKER_USERNAME: ${{ steps.retrieve-secrets.outputs.docker-username }}
- DOCKER_PASSWORD: ${{ steps.retrieve-secrets.outputs.docker-password }}
- run: |
- if [[ "${{ matrix.docker_repo }}" == "bitwardenqa.azurecr.io" ]]; then
- az acr login -n bitwardenqa
- else
- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- fi
-
- - name: Setup Docker Trust
- if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
- env:
- DCT_DELEGATION_KEY_ID: "c9bde8ec820701516491e5e03d3a6354e7bd66d05fa3df2b0062f68b116dc59c"
- DCT_DELEGATE_KEY: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-key }}
- run: |
- mkdir -p ~/.docker/trust/private
- echo "$DCT_DELEGATE_KEY" > ~/.docker/trust/private/$DCT_DELEGATION_KEY_ID.key
-
- - name: Setup service name
+ ########## Build Docker Image ##########
+ - name: Setup project name
id: setup
run: |
- SERVICE_NAME=$(echo "${{ matrix.service_name }}" | awk '{print tolower($0)}')
- echo "Matrix name: ${{ matrix.service_name }}"
- echo "SERVICE_NAME: $SERVICE_NAME"
- echo "::set-output name=service_name::$SERVICE_NAME"
+ PROJECT_NAME=$(echo "${{ matrix.project_name }}" | awk '{print tolower($0)}')
+ echo "Matrix name: ${{ matrix.project_name }}"
+ echo "PROJECT_NAME: $PROJECT_NAME"
+ echo "project_name=$PROJECT_NAME" >> $GITHUB_OUTPUT
- name: Get build artifact
if: ${{ matrix.dotnet }}
- uses: actions/download-artifact@3be87be14a055c47b01d3bd88f8fe02320a9bb60 # v2.0.10
+ uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741
with:
- name: ${{ matrix.service_name }}.zip
+ name: ${{ matrix.project_name }}.zip
- name: Setup build artifact
if: ${{ matrix.dotnet }}
run: |
- mkdir -p ${{ matrix.base_path}}/${{ matrix.service_name }}/obj/build-output/publish
- unzip ${{ matrix.service_name }}.zip \
- -d ${{ matrix.base_path }}/${{ matrix.service_name }}/obj/build-output/publish
+ mkdir -p ${{ matrix.base_path}}/${{ matrix.project_name }}/obj/build-output/publish
+ unzip ${{ matrix.project_name }}.zip \
+ -d ${{ matrix.base_path }}/${{ matrix.project_name }}/obj/build-output/publish
- - name: Build Docker images
+ - name: Build Docker image
+ env:
+ PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
+ run: docker build -t $PROJECT_NAME ${{ matrix.base_path }}/${{ matrix.project_name }}
+
+ ########## ACR ##########
+ - name: Login to Azure - QA Subscription
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
+ with:
+ creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
+
+ - name: Login to Azure ACR
+ run: az acr login -n bitwardenqa
+
+ - name: Tag and Push image to Azure ACR QA registry
+ env:
+ PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
+ REGISTRY: bitwardenqa.azurecr.io
run: |
- if [ "${{ matrix.service_name }}" = "K8S-Proxy" ]; then
- docker build -f ${{ matrix.base_path }}/Nginx/Dockerfile-k8s \
- -t ${{ steps.setup.outputs.service_name }} ${{ matrix.base_path }}/Nginx
- else
- docker build -t ${{ steps.setup.outputs.service_name }} \
- ${{ matrix.base_path }}/${{ matrix.service_name }}
+ IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
+ if [[ "$IMAGE_TAG" == "master" ]]; then
+ IMAGE_TAG=dev
fi
- - name: Docker Trust setup
+ docker tag $PROJECT_NAME \
+ $REGISTRY/$PROJECT_NAME:$IMAGE_TAG
+ docker push $REGISTRY/$PROJECT_NAME:$IMAGE_TAG
+
+ - name: Log out of Docker
+ run: docker logout
+
+ ########## DockerHub ##########
+ - name: Login to Azure - Prod Subscription
if: |
- matrix.docker_repo == 'bitwarden'
- && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix')
+ contains(matrix.docker_repos, 'bitwarden')
+ && (github.ref == 'refs/heads/master' ||
+ github.ref == 'refs/heads/rc' ||
+ github.ref == 'refs/heads/hotfix-rc')
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
+ with:
+ creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
+
+ - name: Retrieve secrets
+ if: |
+ contains(matrix.docker_repos, 'bitwarden')
+ && (github.ref == 'refs/heads/master' ||
+ github.ref == 'refs/heads/rc' ||
+ github.ref == 'refs/heads/hotfix-rc')
+ id: retrieve-secrets
+ 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"
+
+ - name: Log into Docker
+ if: |
+ contains(matrix.docker_repos, 'bitwarden')
+ && (github.ref == 'refs/heads/master' ||
+ github.ref == 'refs/heads/rc' ||
+ github.ref == 'refs/heads/hotfix-rc')
env:
+ DOCKER_USERNAME: ${{ steps.retrieve-secrets.outputs.docker-username }}
+ DOCKER_PASSWORD: ${{ steps.retrieve-secrets.outputs.docker-password }}
+ run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
+
+ - name: Setup Docker Trust
+ if: |
+ contains(matrix.docker_repos, 'bitwarden')
+ && (github.ref == 'refs/heads/master' ||
+ github.ref == 'refs/heads/rc' ||
+ github.ref == 'refs/heads/hotfix-rc')
+ env:
+ DCT_DELEGATION_KEY_ID: "c9bde8ec820701516491e5e03d3a6354e7bd66d05fa3df2b0062f68b116dc59c"
+ DCT_DELEGATE_KEY: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-key }}
DCT_REPO_PASSPHRASE: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-repo-passphrase }}
run: |
+ mkdir -p ~/.docker/trust/private
+ echo "$DCT_DELEGATE_KEY" > ~/.docker/trust/private/$DCT_DELEGATION_KEY_ID.key
echo "DOCKER_CONTENT_TRUST=1" >> $GITHUB_ENV
- echo "DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$DCT_REPO_PASSPHRASE" >> $GITHUB_ENV
+ echo "DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$DCT_REPO_PASSPHRASE" >> $GITHUB_ENV
- name: Tag and Push RC to Docker Hub
- if: github.ref == 'refs/heads/rc'
+ if: |
+ contains(matrix.docker_repos, 'bitwarden')
+ && (github.ref == 'refs/heads/master' ||
+ github.ref == 'refs/heads/rc' ||
+ github.ref == 'refs/heads/hotfix-rc')
+ env:
+ PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
+ REGISTRY: bitwarden
run: |
- docker tag ${{ steps.setup.outputs.service_name }} \
- ${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:rc
- docker push ${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:rc
+ IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
+ if [[ "$IMAGE_TAG" == "master" ]]; then
+ IMAGE_TAG=dev
+ fi
- - name: Tag and Push Hotfix to Docker Hub
- if: github.ref == 'refs/heads/hotfix'
- run: |
- docker tag ${{ steps.setup.outputs.service_name }} \
- ${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:hotfix
- docker push ${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:hotfix
-
- - name: Tag and Push Dev to Docker Hub
- if: github.ref == 'refs/heads/master'
- run: |
- docker tag ${{ steps.setup.outputs.service_name }} \
- ${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:dev
- docker push ${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:dev
+ docker tag $PROJECT_NAME \
+ $REGISTRY/$PROJECT_NAME:$IMAGE_TAG
+ docker push $REGISTRY/$PROJECT_NAME:$IMAGE_TAG
- name: Log out of Docker and disable Docker Notary
- if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
+ if: |
+ contains(matrix.docker_repos, 'bitwarden')
+ && (github.ref == 'refs/heads/master' ||
+ github.ref == 'refs/heads/rc' ||
+ github.ref == 'refs/heads/hotfix-rc')
run: |
docker logout
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
- - name: Configure AWS credentials
- uses: aws-actions/configure-aws-credentials@0d9a5be0dceea74e09396820e1e522ba4a110d2f # v1
- with:
- aws-access-key-id: ${{ steps.retrieve-secrets.outputs.aws-ecr-access-key-id }}
- aws-secret-access-key: ${{ steps.retrieve-secrets.outputs.aws-ecr-secret-access-key }}
- aws-region: us-east-1
-
- - name: Login to Amazon ECR
- id: login-ecr
- uses: aws-actions/amazon-ecr-login@aaf69d68aa3fb14c1d5a6be9ac61fe15b48453a2 # v1
-
- - name: Tag and Push RC to AWS ECR nonprod registry
- if: github.ref == 'refs/heads/rc'
- env:
- ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
- IMAGE_TAG: ${{ github.sha }}
- run: |
- docker tag ${{ steps.setup.outputs.service_name }} \
- $ECR_REGISTRY/nonprod/${{ steps.setup.outputs.service_name }}:rc-${IMAGE_TAG:(-8)}
- docker push $ECR_REGISTRY/nonprod/${{ steps.setup.outputs.service_name }}:rc-${IMAGE_TAG:(-8)}
-
- - name: Tag and Push Hotfix to AWS ECR nonprod registry
- if: github.ref == 'refs/heads/hotfix'
- env:
- ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
- IMAGE_TAG: ${{ github.sha }}
- run: |
- docker tag ${{ steps.setup.outputs.service_name }} \
- $ECR_REGISTRY/nonprod/${{ steps.setup.outputs.service_name }}:hotfix-${IMAGE_TAG:(-8)}
- docker push $ECR_REGISTRY/nonprod/${{ steps.setup.outputs.service_name }}:hotfix-${IMAGE_TAG:(-8)}
-
- - name: Tag and Push Dev to AWS ECR nonprod registry
- if: github.ref == 'refs/heads/master'
- env:
- ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
- IMAGE_TAG: ${{ github.sha }}
- run: |
- docker tag ${{ steps.setup.outputs.service_name }} \
- $ECR_REGISTRY/nonprod/${{ steps.setup.outputs.service_name }}:dev-${IMAGE_TAG:(-8)}
- docker push $ECR_REGISTRY/nonprod/${{ steps.setup.outputs.service_name }}:dev-${IMAGE_TAG:(-8)}
-
-
upload:
name: Upload
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
needs: build-docker
steps:
+ - name: Set up dotnet
+ uses: actions/setup-dotnet@9211491ffb35dd6a6657ca4f45d43dfe6e97c829
+ with:
+ dotnet-version: "6.0.x"
+
- name: Checkout repo
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
+ uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Restore
run: dotnet tool restore
- name: Make Docker stub
- if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
+ if: github.ref == 'refs/heads/master' ||
+ github.ref == 'refs/heads/rc' ||
+ github.ref == 'refs/heads/hotfix-rc'
run: |
if [[ "${{ github.ref }}" == "rc" ]]; then
SETUP_IMAGE="bitwarden/setup:rc"
- elif [[ "${{ github.ref }}" == "hotfix" ]]; then
- SETUP_IMAGE="bitwarden/setup:hotfix"
+ elif [[ "${{ github.ref }}" == "hotfix-rc" ]]; then
+ SETUP_IMAGE="bitwarden/setup:hotfix-rc"
else
SETUP_IMAGE="bitwarden/setup:dev"
fi
@@ -423,12 +423,24 @@ jobs:
touch $STUB_OUTPUT/env/uid.env
cd docker-stub; zip -r ../docker-stub.zip *; cd ..
+ - name: Make Docker stub checksum
+ if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
+ run: sha256sum docker-stub.zip > docker-stub-sha256.txt
+
- name: Upload Docker stub artifact
- if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
- uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
+ if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
+ uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: docker-stub.zip
- path: ./docker-stub.zip
+ path: docker-stub.zip
+ if-no-files-found: error
+
+ - name: Upload Docker stub checksum artifact
+ if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
+ uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
+ with:
+ name: docker-stub-sha256.txt
+ path: docker-stub-sha256.txt
if-no-files-found: error
- name: Build Swagger
@@ -446,21 +458,24 @@ jobs:
cd ../..
env:
ASPNETCORE_ENVIRONMENT: Production
- swaggerGen: 'True'
+ swaggerGen: "True"
+ DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX: 2
+ GLOBALSETTINGS__SQLSERVER__CONNECTIONSTRING: "placeholder"
- name: Upload Swagger artifact
- uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
+ uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: swagger.json
- path: ./swagger.json
+ path: swagger.json
if-no-files-found: error
check-failures:
name: Check for failures
if: always()
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
needs:
- cloc
+ - lint
- testing
- build-artifacts
- build-docker
@@ -470,9 +485,10 @@ jobs:
if: |
github.ref == 'refs/heads/master'
|| github.ref == 'refs/heads/rc'
- || github.ref == 'refs/heads/hotfix'
+ || github.ref == 'refs/heads/hotfix-rc'
env:
CLOC_STATUS: ${{ needs.cloc.result }}
+ LINT_STATUS: ${{ needs.lint.result }}
TESTING_STATUS: ${{ needs.testing.result }}
BUILD_ARTIFACTS_STATUS: ${{ needs.build-artifacts.result }}
BUILD_DOCKER_STATUS: ${{ needs.build-docker.result }}
@@ -480,6 +496,8 @@ jobs:
run: |
if [ "$CLOC_STATUS" = "failure" ]; then
exit 1
+ elif [ "$LINT_STATUS" = "failure" ]; then
+ exit 1
elif [ "$TESTING_STATUS" = "failure" ]; then
exit 1
elif [ "$BUILD_ARTIFACTS_STATUS" = "failure" ]; then
@@ -491,21 +509,21 @@ jobs:
fi
- name: Login to Azure - Prod Subscription
- uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
if: failure()
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
- uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
+ 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@e4e71685b9b239384b0f676a63c32367f59c2522 # 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/cleanup-after-pr.yml b/.github/workflows/cleanup-after-pr.yml
new file mode 100644
index 000000000..696d84c8f
--- /dev/null
+++ b/.github/workflows/cleanup-after-pr.yml
@@ -0,0 +1,66 @@
+---
+name: Clean After PR
+
+on:
+ pull_request:
+ types: [closed]
+
+jobs:
+ build-docker:
+ name: Remove feature branch docker images
+ runs-on: ubuntu-20.04
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
+
+ ########## ACR ##########
+ - name: Login to Azure - QA Subscription
+ uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
+ with:
+ creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
+
+ - name: Login to Azure ACR
+ run: az acr login -n bitwardenqa
+
+ ########## Remove Docker images ##########
+ - name: Remove the docker image from ACR
+ env:
+ REGISTRY_NAME: bitwardenqa
+ SERVICES: |
+ services:
+ - Admin
+ - Api
+ - Attachments
+ - Events
+ - EventsProcessor
+ - Icons
+ - Identity
+ - K8S-Proxy
+ - MsSql
+ - Nginx
+ - Notifications
+ - Server
+ - Setup
+ - Sso
+ run: |
+ for SERVICE in $(echo "${{ env.SERVICES }}" | yq e ".services[]" - )
+ do
+ SERVICE_NAME=$(echo $SERVICE | awk '{print tolower($0)}')
+ IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
+
+ echo "[*] Checking if remote exists: $REGISTRY_NAME.azurecr.io/$SERVICE_NAME:$IMAGE_TAG"
+ TAG_EXISTS=$(
+ az acr repository show-tags --name $REGISTRY_NAME --repository $SERVICE_NAME \
+ | jq --arg $TAG "$IMAGE_TAG" -e '. | any(. == "$TAG")'
+ )
+
+ if [[ "$TAG_EXISTS" == "true" ]]; then
+ echo "[*] Tag exists. Removing tag"
+ az acr repository delete --name $REGISTRY_NAME --image $SERVICE_NAME:$IMAGE_TAG --yes
+ else
+ echo "[*] Tag does not exist. No action needed"
+ fi
+ done
+
+ - name: Log out of Docker
+ run: docker logout
diff --git a/.github/workflows/container-registry-purge.yml b/.github/workflows/container-registry-purge.yml
new file mode 100644
index 000000000..8c9db2cbb
--- /dev/null
+++ b/.github/workflows/container-registry-purge.yml
@@ -0,0 +1,121 @@
+---
+name: Container Registry Purge
+
+on:
+ schedule:
+ - cron: '0 0 * * SUN'
+ workflow_dispatch:
+ inputs: {}
+
+jobs:
+ purge:
+ name: Purge old images
+ runs-on: ubuntu-20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - name: bitwardenqa
+ - name: bitwardenprod
+ steps:
+ - name: Login to Azure
+ if: matrix.name == 'bitwardenprod'
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
+ with:
+ creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
+
+ - name: Login to Azure
+ if: matrix.name == 'bitwardenqa'
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
+ with:
+ creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
+
+ - name: Purge images
+ env:
+ REGISTRY: ${{ matrix.name }}
+ AGO_DUR_VER: "180d"
+ AGO_DUR: "30d"
+ run: |
+ REPO_LIST=$(az acr repository list -n $REGISTRY -o tsv)
+ for REPO in $REPO_LIST
+ do
+
+ PURGE_LATEST=""
+ PURGE_VERSION=""
+ PURGE_ELSE=""
+
+ TAG_LIST=$(az acr repository show-tags -n $REGISTRY --repository $REPO -o tsv)
+ for TAG in $TAG_LIST
+ do
+ if [ $TAG = "latest" ] || [ $TAG = "dev" ]; then
+ PURGE_LATEST+="--filter '$REPO:$TAG' "
+ elif [[ $TAG =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
+ PURGE_VERSION+="--filter '$REPO:$TAG' "
+ else
+ PURGE_ELSE+="--filter '$REPO:$TAG' "
+ fi
+ done
+
+ if [ ! -z "$PURGE_LATEST" ]
+ then
+ PURGE_LATEST_CMD="acr purge $PURGE_LATEST --ago $AGO_DUR_VER --untagged --keep 1"
+ az acr run --cmd "$PURGE_LATEST_CMD" --registry $REGISTRY /dev/null &
+ fi
+
+ if [ ! -z "$PURGE_VERSION" ]
+ then
+ PURGE_VERSION_CMD="acr purge $PURGE_VERSION --ago $AGO_DUR_VER --untagged"
+ az acr run --cmd "$PURGE_VERSION_CMD" --registry $REGISTRY /dev/null &
+ fi
+
+ if [ ! -z "$PURGE_ELSE" ]
+ then
+ PURGE_ELSE_CMD="acr purge $PURGE_ELSE --ago $AGO_DUR --untagged"
+ az acr run --cmd "$PURGE_ELSE_CMD" --registry $REGISTRY /dev/null &
+ fi
+
+ wait
+
+ done
+
+
+ check-failures:
+ name: Check for failures
+ if: always()
+ runs-on: ubuntu-20.04
+ needs:
+ - purge
+ steps:
+ - name: Check if any job failed
+ if: |
+ github.ref == 'refs/heads/master'
+ || github.ref == 'refs/heads/rc'
+ || github.ref == 'refs/heads/hotfix-rc'
+ env:
+ PURGE_STATUS: ${{ needs.purge.result }}
+ run: |
+ if [ "$PURGE_STATUS" = "failure" ]; then
+ exit 1
+ fi
+
+ - name: Login to Azure - Prod Subscription
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
+ if: failure()
+ with:
+ creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
+
+ - name: Retrieve secrets
+ id: retrieve-secrets
+ uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
+ if: failure()
+ with:
+ keyvault: "bitwarden-prod-kv"
+ secrets: "devops-alerts-slack-webhook-url"
+
+ - name: Notify Slack on failure
+ uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33
+ if: failure()
+ env:
+ SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
+ with:
+ status: ${{ job.status }}
diff --git a/.github/workflows/database.yml b/.github/workflows/database.yml
new file mode 100644
index 000000000..0e4a0171b
--- /dev/null
+++ b/.github/workflows/database.yml
@@ -0,0 +1,107 @@
+---
+name: Validate Database
+
+on:
+ pull_request:
+ branches-ignore:
+ - 'l10n_master'
+ - 'gh-pages'
+ paths:
+ - 'src/Sql/**'
+ - 'util/Migrator/**'
+ push:
+ branches:
+ - 'master'
+ - 'rc'
+ paths:
+ - 'src/Sql/**'
+ - 'util/Migrator/**'
+ workflow_dispatch:
+ inputs: {}
+
+jobs:
+ build:
+ name: Build DACPAC
+ runs-on: windows-2022
+ env:
+ NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
+ steps:
+ - name: Set up dotnet
+ uses: actions/setup-dotnet@9211491ffb35dd6a6657ca4f45d43dfe6e97c829
+ with:
+ dotnet-version: '6.0.x'
+ - name: Set up MSBuild
+ uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
+
+ - name: Print environment
+ run: |
+ dotnet --info
+ msbuild -version
+ nuget help | grep Version
+ echo "GitHub ref: $GITHUB_REF"
+ echo "GitHub event: $GITHUB_EVENT"
+
+ - name: Checkout repo
+ uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
+
+ - name: Build DACPAC
+ run: msbuild src/Sql/Sql.sqlproj /p:Configuration=Release /verbosity:minimal
+ shell: pwsh
+
+ - name: Upload DACPAC
+ uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
+ with:
+ name: sql.dacpac
+ path: src/Sql/bin/Release/Sql.dacpac
+
+ validate:
+ name: Validate
+ runs-on: ubuntu-20.04
+ needs: build
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
+
+ - name: Download dacpac
+ uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741
+ with:
+ name: sql.dacpac
+
+ - name: Docker Compose up
+ working-directory: "dev"
+ run: |
+ cp .env.example .env
+ docker compose --profile mssql up -d
+ shell: pwsh
+
+ - name: Migrate
+ working-directory: "dev"
+ run: "pwsh ./migrate.ps1"
+ shell: pwsh
+
+ - name: Diff sqlproj to migrations
+ run: /usr/local/sqlpackage/sqlpackage /action:DeployReport /SourceFile:"Sql.dacpac" /TargetConnectionString:"Server=localhost;Database=vault_dev;User Id=SA;Password=SET_A_PASSWORD_HERE_123;Encrypt=True;TrustServerCertificate=True;" /OutputPath:"report.xml" /p:IgnoreColumnOrder=True /p:IgnoreComments=True
+ shell: pwsh
+
+ - name: Upload Report
+ uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
+ with:
+ name: report.xml
+ path: report.xml
+
+ - name: Validate XML
+ run: |
+ if grep -q "" "report.xml"; then
+ echo
+ echo "Migrations are out of sync with sqlproj!"
+ exit 1
+ else
+ echo "Report looks good"
+ fi
+ shell: bash
+
+ - name: Docker compose down
+ if: ${{ always() }}
+ working-directory: "dev"
+ run: docker compose down
+ shell: pwsh
diff --git a/.github/workflows/enforce-labels.yml b/.github/workflows/enforce-labels.yml
new file mode 100644
index 000000000..b698bf47d
--- /dev/null
+++ b/.github/workflows/enforce-labels.yml
@@ -0,0 +1,16 @@
+---
+name: Enforce PR labels
+
+on:
+ pull_request:
+ types: [labeled, unlabeled, opened, edited, synchronize]
+
+jobs:
+ enforce-label:
+ name: EnforceLabel
+ runs-on: ubuntu-20.04
+ steps:
+ - name: Enforce Label
+ uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024
+ with:
+ BANNED_LABELS: "hold,DB-migrations-changed,needs-qa"
diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml
deleted file mode 100644
index cb7759123..000000000
--- a/.github/workflows/linter.yml
+++ /dev/null
@@ -1,27 +0,0 @@
----
-name: Workflow Linter
-
-on:
- push:
- branches: add-workflow-linter
-# branches-ignore:
-# - 'l10n_master'
-# - 'gh-pages'
-# workflow_dispatch:
-# inputs: {}
-
-jobs:
- cloc:
- name: CLOC
- runs-on: ubuntu-20.04
- steps:
- - name: Checkout repo
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
-
- - name: Install cloc
- run: |
- sudo apt-get update
- sudo apt-get -y install cloc
-
- - name: Print lines of code
- run: cloc --include-lang C#,SQL,Razor,"Bourne Shell",PowerShell,HTML,CSS,Sass,JavaScript,TypeScript --vcs git
diff --git a/.github/workflows/protect-files.yml b/.github/workflows/protect-files.yml
new file mode 100644
index 000000000..c9461faf7
--- /dev/null
+++ b/.github/workflows/protect-files.yml
@@ -0,0 +1,55 @@
+# Runs if there are changes to the paths: list.
+# Starts a matrix job to check for modified files, then sets output based on the results.
+# The input decides if the label job is ran, adding a label to the PR.
+---
+
+name: Protect Files
+
+on:
+ pull_request:
+ types:
+ - opened
+ - synchronize
+ - unlabeled
+ paths:
+ - "util/Migrator/DbScripts/**.sql"
+
+jobs:
+ changed-files:
+ name: Check for file changes
+ runs-on: ubuntu-20.04
+ outputs:
+ changes: ${{steps.check-changes.outputs.changes_detected}}
+
+ strategy:
+ fail-fast: true
+ matrix:
+ include:
+ - name: Database Scripts
+ path: util/Migrator/DbScripts
+ label: "DB-migrations-changed"
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
+ with:
+ fetch-depth: 2
+
+ - name: Check for file changes
+ id: check-changes
+ run: |
+ MODIFIED_FILES=$(git diff --name-only --diff-filter=M HEAD~1)
+
+ for file in $MODIFIED_FILES
+ do
+ if [[ $file == *"${{ matrix.path }}"* ]]; then
+ echo "changes_detected=true" >> $GITHUB_OUTPUT
+ break
+ else echo "changes_detected=false" >> $GITHUB_OUTPUT
+ fi
+ done
+
+ - name: Add label to pull request
+ if: contains(steps.check-changes.outputs.changes_detected, true)
+ uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90
+ with:
+ add-labels: ${{ matrix.label }}
diff --git a/.github/workflows/qa-deploy.yml b/.github/workflows/qa-deploy.yml
deleted file mode 100644
index 35ef5cbc2..000000000
--- a/.github/workflows/qa-deploy.yml
+++ /dev/null
@@ -1,137 +0,0 @@
----
-name: QA Deploy
-
-on:
- workflow_dispatch:
- inputs:
- migrateDb:
- required: true
- default: "true"
- resetDb:
- required: true
- default: "false"
-
-jobs:
- reset-db:
- name: Reset Database
- if: ${{ github.event.inputs.resetDb == 'true' }}
- runs-on: ubuntu-20.04
- steps:
- - name: Reset Test Data - Stub
- run: |
- echo "placeholder for cleaning DB"
- echo "placeholder for loading test dataset"
-
-
- update-db:
- name: Update Database
- if: ${{ github.event.inputs.migrateDb == 'true' }}
- runs-on: ubuntu-20.04
- steps:
- - name: Checkout repo
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
-
- - name: Login to Azure
- uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
- with:
- creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
-
- - name: Retrieve secrets
- id: retrieve-secrets
- uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
- with:
- keyvault: "bitwarden-qa-kv"
- secrets: "mssql-server-host,
- mssql-admin-login,
- mssql-admin-login-password"
-
- - name: Migrate database
- env:
- MSSQL_HOST: ${{ steps.retrieve-secrets.outputs.mssql-server-host }}
- MSSQL_USER: ${{ steps.retrieve-secrets.outputs.mssql-admin-login }}
- MSSQL_PASS: ${{ steps.retrieve-secrets.outputs.mssql-admin-login-password }}
- working-directory: ./util/Migrator/DbScripts
- run: |
- echo "Running database migrations..."
- for f in `ls -v ./*.sql`; do
- echo "Executing file: ${f}..."
- sqlcmd -S $MSSQL_HOST -d vault -U $MSSQL_USER -P $MSSQL_PASS -I -i $f
- done;
-
-
- deploy:
- name: Deploy
- runs-on: ubuntu-20.04
- if: always()
- needs:
- - reset-db
- - update-db
- strategy:
- fail-fast: false
- matrix:
- include:
- - name: Api
- - name: Admin
- - name: Billing
- - name: Events
- - name: Sso
- - name: Identity
- steps:
- - name: Setup
- id: setup
- run: |
- NAME_LOWER=$(echo "${{ matrix.name }}" | awk '{print tolower($0)}')
- echo "Matrix name: ${{ matrix.name }}"
- echo "NAME_LOWER: $NAME_LOWER"
- echo "::set-output name=name_lower::$NAME_LOWER"
-
- BRANCH_NAME=$(echo "$GITHUB_REF" | sed "s#refs/heads/##g")
- echo "GITHUB_REF: $GITHUB_REF"
- echo "BRANCH_NAME: $BRANCH_NAME"
- echo "::set-output name=branch_name::$BRANCH_NAME"
-
- mkdir publish
-
- - name: Download latest ${{ matrix.name }} asset from ${{ env.branch_name }}
- uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
- env:
- branch_name: ${{ steps.setup.outputs.branch_name }}
- with:
- workflow: build.yml
- workflow_conclusion: success
- branch: ${{ env.branch_name }}
- artifacts: ${{ matrix.name }}.zip
-
- - name: Login to Azure
- uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
- with:
- creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
-
- - name: Retrieve secrets
- id: retrieve-secrets
- env:
- VAULT_NAME: "bitwarden-qa-kv"
- run: |
- webapp_name=$(
- az keyvault secret show --vault-name $VAULT_NAME \
- --name appservices-${{ steps.setup.outputs.name_lower }}-webapp-name \
- --query value --output tsv
- )
- echo "::add-mask::$webapp_name"
- echo "::set-output name=webapp-name::$webapp_name"
-
- - name: Stop App Service
- env:
- AZURE_RESOURCE_GROUP: "bw-qa-env"
- run: az webapp stop --name ${{ steps.retrieve-secrets.outputs.webapp-name }} --resource-group $AZURE_RESOURCE_GROUP
-
- - name: Deploy App
- uses: azure/webapps-deploy@798e43877120eda6a2a690a4f212c545e586ae31
- with:
- app-name: ${{ steps.retrieve-secrets.outputs.webapp-name }}
- package: ./${{ matrix.name }}.zip
-
- - name: Start App Service
- env:
- AZURE_RESOURCE_GROUP: "bw-qa-env"
- run: az webapp start --name ${{ steps.retrieve-secrets.outputs.webapp-name }} --resource-group $AZURE_RESOURCE_GROUP
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c56f642c7..70f46ab4c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,56 +1,58 @@
---
name: Release
+run-name: Release ${{ inputs.release_type }}
on:
workflow_dispatch:
- inputs: {}
-
+ inputs:
+ release_type:
+ description: "Release Options"
+ required: true
+ default: "Initial Release"
+ type: choice
+ options:
+ - Initial Release
+ - Redeploy
+ - Dry Run
jobs:
setup:
name: Setup
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
outputs:
- release_version: ${{ steps.version.outputs.package }}
+ release_version: ${{ steps.version.outputs.version }}
branch-name: ${{ steps.branch.outputs.branch-name }}
steps:
- name: Branch check
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
run: |
- if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix" ]]; then
+ if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
echo "==================================="
- echo "[!] Can only release from the 'rc' or 'hotfix' branches"
+ echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches"
echo "==================================="
exit 1
fi
- name: Checkout repo
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
+ uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
- name: Check Release Version
id: version
- run: |
- version=$( grep -o ".*" Directory.Build.props | grep -o "[0-9]*\.[0-9]*\.[0-9]*")
- previous_release_tag_version=$(
- curl -sL https://api.github.com/repos/$GITHUB_REPOSITORY/releases/latest | jq -r ".tag_name"
- )
-
- if [ "v$version" == "$previous_release_tag_version" ]; then
- echo "[!] Already released v$version. Please bump version to continue"
- exit 1
- fi
-
- echo "::set-output name=package::$version"
+ uses: bitwarden/gh-actions/release-version-check@4cf17a5ff15a995a2daf2b60ba371e5c9907c068
+ with:
+ release-type: ${{ github.event.inputs.release_type }}
+ project-type: dotnet
+ file: Directory.Build.props
- name: Get branch name
id: branch
run: |
BRANCH_NAME=$(basename ${{ github.ref }})
- echo "::set-output name=branch-name::$BRANCH_NAME"
-
+ echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
deploy:
name: Deploy
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
needs:
- setup
strategy:
@@ -70,18 +72,39 @@ jobs:
NAME_LOWER=$(echo "${{ matrix.name }}" | awk '{print tolower($0)}')
echo "Matrix name: ${{ matrix.name }}"
echo "NAME_LOWER: $NAME_LOWER"
- echo "::set-output name=name_lower::$NAME_LOWER"
+ echo "name_lower=$NAME_LOWER" >> $GITHUB_OUTPUT
+
+ - name: Create GitHub deployment for ${{ matrix.name }}
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
+ uses: chrnorm/deployment-action@1b599fe41a0ef1f95191e7f2eec4743f2d7dfc48
+ id: deployment
+ with:
+ token: "${{ secrets.GITHUB_TOKEN }}"
+ initial-status: "in_progress"
+ environment: "Production Cloud"
+ task: "deploy"
+ description: "Deploy from ${{ needs.setup.outputs.branch-name }} branch"
- name: Download latest Release ${{ matrix.name }} asset
- uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
+ uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a
with:
workflow: build.yml
workflow_conclusion: success
branch: ${{ needs.setup.outputs.branch-name }}
artifacts: ${{ matrix.name }}.zip
+ - name: Download latest Release ${{ matrix.name }} asset
+ if: ${{ github.event.inputs.release_type == 'Dry Run' }}
+ uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a
+ with:
+ workflow: build.yml
+ workflow_conclusion: success
+ branch: master
+ artifacts: ${{ matrix.name }}.zip
+
- name: Login to Azure
- uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
@@ -101,22 +124,50 @@ jobs:
--query value --output tsv
)
echo "::add-mask::$webapp_name"
- echo "::set-output name=webapp-name::$webapp_name"
+ echo "webapp-name=$webapp_name" >> $GITHUB_OUTPUT
echo "::add-mask::$publish_profile"
- echo "::set-output name=publish-profile::$publish_profile"
+ echo "publish-profile=$publish_profile" >> $GITHUB_OUTPUT
- name: Deploy App
- uses: azure/webapps-deploy@798e43877120eda6a2a690a4f212c545e586ae31
+ uses: azure/webapps-deploy@0b651ed7546ecfc75024011f76944cb9b381ef1e
with:
app-name: ${{ steps.retrieve-secrets.outputs.webapp-name }}
publish-profile: ${{ steps.retrieve-secrets.outputs.publish-profile }}
package: ./${{ matrix.name }}.zip
slot-name: "staging"
+ - name: Start staging slot
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
+ env:
+ SERVICE: ${{ matrix.name }}
+ WEBAPP_NAME: ${{ steps.retrieve-secrets.outputs.webapp-name }}
+ run: |
+ if [[ "$SERVICE" = "Api" ]] || [[ "$SERVICE" = "Identity" ]]; then
+ RESOURCE_GROUP=bitwardenappservices
+ else
+ RESOURCE_GROUP=bitwarden
+ fi
+ az webapp start -n $WEBAPP_NAME -g $RESOURCE_GROUP -s staging
+
+ - name: Update ${{ matrix.name }} deployment status to Success
+ if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
+ uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86
+ with:
+ token: "${{ secrets.GITHUB_TOKEN }}"
+ state: "success"
+ deployment-id: ${{ steps.deployment.outputs.deployment_id }}
+
+ - name: Update ${{ matrix.name }} deployment status to Failure
+ if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
+ uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86
+ with:
+ token: "${{ secrets.GITHUB_TOKEN }}"
+ state: "failure"
+ deployment-id: ${{ steps.deployment.outputs.deployment_id }}
release-docker:
name: Build Docker images
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
needs:
- setup
env:
@@ -126,94 +177,226 @@ jobs:
fail-fast: false
matrix:
include:
- - service_name: Admin
- - service_name: Api
- - service_name: Attachments
- - service_name: Events
- - service_name: Icons
- - service_name: Identity
- - service_name: K8S-Proxy
- - service_name: MsSql
- - service_name: Nginx
- - service_name: Notifications
- - service_name: Server
- - service_name: Setup
- - service_name: Sso
+ - project_name: Admin
+ origin_docker_repo: bitwarden
+ - project_name: Api
+ origin_docker_repo: bitwarden
+ - project_name: Attachments
+ origin_docker_repo: bitwarden
+ - project_name: Events
+ prod_acr: true
+ origin_docker_repo: bitwarden
+ - project_name: EventsProcessor
+ prod_acr: true
+ origin_docker_repo: bitwardenqa.azurecr.io
+ - project_name: Icons
+ origin_docker_repo: bitwarden
+ prod_acr: true
+ - project_name: Identity
+ origin_docker_repo: bitwarden
+ - project_name: MsSql
+ origin_docker_repo: bitwarden
+ - project_name: Nginx
+ origin_docker_repo: bitwarden
+ - project_name: Notifications
+ origin_docker_repo: bitwarden
+ - project_name: Server
+ origin_docker_repo: bitwarden
+ - project_name: Setup
+ origin_docker_repo: bitwarden
+ - project_name: Sso
+ origin_docker_repo: bitwarden
+ - project_name: Scim
+ origin_docker_repo: bitwarden
+ - project_name: Billing
+ origin_docker_repo: bitwardenqa.azurecr.io
steps:
- name: Print environment
+ env:
+ RELEASE_OPTION: ${{ github.event.inputs.release_type }}
run: |
whoami
docker --version
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
+ echo "Github Release Option: $RELEASE_OPTION"
+ - name: Checkout repo
+ uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+
+ - name: Setup project name
+ id: setup
+ run: |
+ PROJECT_NAME=$(echo "${{ matrix.project_name }}" | awk '{print tolower($0)}')
+ echo "Matrix name: ${{ matrix.project_name }}"
+ echo "PROJECT_NAME: $PROJECT_NAME"
+ echo "project_name=$PROJECT_NAME" >> $GITHUB_OUTPUT
+
+ ########## 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 }}
azure-keyvault-name: "bitwarden-prod-kv"
- - name: Checkout repo
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
-
- - name: Setup service name
- id: setup
- run: |
- SERVICE_NAME=$(echo "${{ matrix.service_name }}" | awk '{print tolower($0)}')
- echo "Matrix name: ${{ matrix.service_name }}"
- echo "SERVICE_NAME: $SERVICE_NAME"
- echo "::set-output name=service_name::$SERVICE_NAME"
-
- - name: Pull latest selfhost image
+ - name: Pull latest project image
+ if: matrix.origin_docker_repo == 'bitwarden'
env:
- SERVICE_NAME: ${{ steps.setup.outputs.service_name }}
- run: docker pull bitwarden/$SERVICE_NAME:$_BRANCH_NAME
+ PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
+ run: |
+ if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
+ docker pull bitwarden/$PROJECT_NAME:latest
+ else
+ docker pull bitwarden/$PROJECT_NAME:$_BRANCH_NAME
+ fi
- name: Tag version and latest
+ if: matrix.origin_docker_repo == 'bitwarden'
env:
- SERVICE_NAME: ${{ steps.setup.outputs.service_name }}
+ PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
run: |
- docker tag bitwarden/$SERVICE_NAME:$_BRANCH_NAME bitwarden/$SERVICE_NAME:$_RELEASE_VERSION
- docker tag bitwarden/$SERVICE_NAME:$_BRANCH_NAME bitwarden/$SERVICE_NAME:latest
-
- - name: List Docker images
- run: docker images
+ if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
+ docker tag bitwarden/$PROJECT_NAME:latest bitwarden/$PROJECT_NAME:dryrun
+ else
+ docker tag bitwarden/$PROJECT_NAME:$_BRANCH_NAME bitwarden/$PROJECT_NAME:$_RELEASE_VERSION
+ fi
- name: Push version and latest image
+ if: ${{ github.event.inputs.release_type != 'Dry Run' && matrix.origin_docker_repo == 'bitwarden' }}
env:
DOCKER_CONTENT_TRUST: 1
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
- SERVICE_NAME: ${{ steps.setup.outputs.service_name }}
+ PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
+ run: docker push bitwarden/$PROJECT_NAME:$_RELEASE_VERSION
+
+ - name: Log out of Docker and disable Docker Notary
+ if: matrix.origin_docker_repo == 'bitwarden'
run: |
- docker push bitwarden/$SERVICE_NAME:$_RELEASE_VERSION
- docker push bitwarden/$SERVICE_NAME:latest
+ docker logout
+ echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
+
+ ########## ACR QA ##########
+ - name: Login to Azure - QA Subscription
+ uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
+ with:
+ creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
+
+ - name: Login to Azure ACR
+ run: az acr login -n bitwardenqa
+
+ - name: Pull latest project image
+ if: matrix.origin_docker_repo == 'bitwardenqa.azurecr.io'
+ env:
+ PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
+ REGISTRY: bitwardenqa.azurecr.io
+ run: |
+ if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
+ docker pull $REGISTRY/$PROJECT_NAME:latest
+ else
+ docker pull $REGISTRY/$PROJECT_NAME:$_BRANCH_NAME
+ fi
+
+ - name: Tag version and latest
+ env:
+ PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
+ REGISTRY: bitwardenqa.azurecr.io
+ ORIGIN_REGISTRY: ${{ matrix.origin_docker_repo }}
+ run: |
+ if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
+ docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:latest $REGISTRY/$PROJECT_NAME:dryrun
+ else
+ docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:$_BRANCH_NAME $REGISTRY/$PROJECT_NAME:$_RELEASE_VERSION
+ docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:$_BRANCH_NAME $REGISTRY/$PROJECT_NAME:latest
+ fi
+
+ - name: Push version and latest image
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
+ env:
+ PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
+ REGISTRY: bitwardenqa.azurecr.io
+ run: |
+ docker push $REGISTRY/$PROJECT_NAME:latest
+ docker push $REGISTRY/$PROJECT_NAME:$_RELEASE_VERSION
- name: Log out of Docker
run: docker logout
+ ########## ACR PROD ##########
+ - name: Login to Azure - PROD Subscription
+ if: matrix.prod_acr == true
+ uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
+ with:
+ creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
+
+ - name: Login to Azure ACR
+ if: matrix.prod_acr == true
+ run: az acr login -n bitwardenprod
+
+ - name: Tag version and latest
+ if: matrix.prod_acr == true
+ env:
+ PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
+ REGISTRY: bitwardenprod.azurecr.io
+ ORIGIN_REGISTRY: ${{ matrix.origin_docker_repo }}
+ run: |
+ if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
+ docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:latest $REGISTRY/$PROJECT_NAME:dryrun
+ else
+ docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:$_BRANCH_NAME $REGISTRY/$PROJECT_NAME:$_RELEASE_VERSION
+ docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:$_BRANCH_NAME $REGISTRY/$PROJECT_NAME:latest
+ fi
+
+ - name: Push version and latest image
+ if: ${{ github.event.inputs.release_type != 'Dry Run' && matrix.prod_acr == true }}
+ env:
+ PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
+ REGISTRY: bitwardenprod.azurecr.io
+ run: |
+ docker push $REGISTRY/$PROJECT_NAME:$_RELEASE_VERSION
+ docker push $REGISTRY/$PROJECT_NAME:latest
+
+ - name: Log out of Docker
+ if: matrix.prod_acr == true
+ run: docker logout
release:
name: Create GitHub Release
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
needs:
- setup
- deploy
steps:
- name: Download latest Release docker-stub
- uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
+ uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a
with:
workflow: build.yml
workflow_conclusion: success
branch: ${{ needs.setup.outputs.branch-name }}
artifacts: "docker-stub.zip,
- swagger.json"
+ docker-stub-sha256.txt,
+ swagger.json"
+
+ - name: Download latest Release docker-stub
+ if: ${{ github.event.inputs.release_type == 'Dry Run' }}
+ uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a
+ with:
+ workflow: build.yml
+ workflow_conclusion: success
+ branch: master
+ artifacts: "docker-stub.zip,
+ docker-stub-sha256.txt,
+ swagger.json"
- name: Create release
- uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09
+ if: ${{ github.event.inputs.release_type != 'Dry Run' }}
+ uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01
with:
- artifacts: 'docker-stub.zip,
- swagger.json'
+ artifacts: "docker-stub.zip,
+ docker-stub-sha256.txt,
+ swagger.json"
commit: ${{ github.sha }}
tag: "v${{ needs.setup.outputs.release_version }}"
name: "Version ${{ needs.setup.outputs.release_version }}"
diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml
new file mode 100644
index 000000000..5cd154cf2
--- /dev/null
+++ b/.github/workflows/stale-bot.yml
@@ -0,0 +1,30 @@
+---
+name: 'Close stale issues and PRs'
+on:
+ workflow_dispatch:
+ schedule: # Run once a day at 5.23am (arbitrary but should avoid peak loads on the hour)
+ - cron: '23 5 * * *'
+
+jobs:
+ stale:
+ name: 'Check for stale issues and PRs'
+ runs-on: ubuntu-20.04
+ steps:
+ - name: 'Run stale action'
+ uses: actions/stale@3cc123766321e9f15a6676375c154ccffb12a358 # v5.0.0
+ with:
+ stale-issue-label: 'needs-reply'
+ stale-pr-label: 'needs-changes'
+ days-before-stale: -1 # Do not apply the stale labels automatically, this is a manual process
+ days-before-issue-close: 14 # Close issue if no further activity after X days
+ days-before-pr-close: 21 # Close PR if no further activity after X days
+ close-issue-message: |
+ We need more information before we can help you with your problem. As we haven’t heard from you recently, this issue will be closed.
+
+ If this happens again or continues to be an problem, please respond to this issue with the information we’ve requested and anything else relevant.
+ close-pr-message: |
+ We can’t merge your pull request until you make the changes we’ve requested. As we haven’t heard from you recently, this pull request will be closed.
+
+ If you’re still working on this, please respond here after you’ve made the changes we’ve requested and our team will re-open it for further review.
+
+ Please make sure to resolve any conflicts with the master branch before requesting another review.
diff --git a/.github/workflows/stop-staging-slots.yml b/.github/workflows/stop-staging-slots.yml
new file mode 100644
index 000000000..6fb22046c
--- /dev/null
+++ b/.github/workflows/stop-staging-slots.yml
@@ -0,0 +1,60 @@
+---
+name: Stop Staging Slots
+
+on:
+ workflow_dispatch:
+ inputs: {}
+
+
+jobs:
+ stop-slots:
+ name: Stop Slots
+ runs-on: ubuntu-20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - name: Api
+ - name: Admin
+ - name: Billing
+ - name: Events
+ - name: Sso
+ - name: Identity
+ steps:
+ - name: Setup
+ id: setup
+ run: |
+ NAME_LOWER=$(echo "${{ matrix.name }}" | awk '{print tolower($0)}')
+ echo "Matrix name: ${{ matrix.name }}"
+ echo "NAME_LOWER: $NAME_LOWER"
+ echo "name_lower=$NAME_LOWER" >> $GITHUB_OUTPUT
+
+ - name: Login to Azure
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
+ with:
+ creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
+
+ - name: Retrieve secrets
+ id: retrieve-secrets
+ env:
+ VAULT_NAME: "bitwarden-prod-kv"
+ run: |
+ webapp_name=$(
+ az keyvault secret show --vault-name $VAULT_NAME \
+ --name appservices-${{ steps.setup.outputs.name_lower }}-webapp-name \
+ --query value --output tsv
+ )
+ echo "::add-mask::$webapp_name"
+ echo "webapp-name=$webapp_name" >> $GITHUB_OUTPUT
+
+ - name: Stop staging slot
+ env:
+ SERVICE: ${{ matrix.name }}
+ WEBAPP_NAME: ${{ steps.retrieve-secrets.outputs.webapp-name }}
+ run: |
+ if [[ "$SERVICE" = "Api" ]] || [[ "$SERVICE" = "Identity" ]]; then
+ RESOURCE_GROUP=bitwardenappservices
+ else
+ RESOURCE_GROUP=bitwarden
+ fi
+ az webapp stop -n $WEBAPP_NAME -g $RESOURCE_GROUP -s staging
diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml
new file mode 100644
index 000000000..022b6073e
--- /dev/null
+++ b/.github/workflows/version-bump.yml
@@ -0,0 +1,93 @@
+---
+name: Version Bump
+
+on:
+ workflow_dispatch:
+ inputs:
+ version_number:
+ description: "New Version"
+ required: true
+
+jobs:
+ bump_props_version:
+ name: "Create version_bump_${{ github.event.inputs.version_number }} branch"
+ runs-on: ubuntu-20.04
+ steps:
+ - name: Checkout Branch
+ uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+
+ - name: Login to Azure - Prod Subscription
+ uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
+ with:
+ creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
+
+ - name: Retrieve secrets
+ id: retrieve-secrets
+ uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af
+ with:
+ keyvault: "bitwarden-prod-kv"
+ secrets: "github-gpg-private-key, github-gpg-private-key-passphrase"
+
+ - name: Import GPG key
+ uses: crazy-max/ghaction-import-gpg@c8bb57c57e8df1be8c73ff3d59deab1dbc00e0d1
+ with:
+ gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
+ passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
+ git_user_signingkey: true
+ git_commit_gpgsign: true
+
+ - name: Create Version Branch
+ run: git switch -c version_bump_${{ github.event.inputs.version_number }}
+
+ - name: Bump Version - Props
+ uses: bitwarden/gh-actions/version-bump@6a42772f8849107fd457cf47cd9c7e224be44e55
+ with:
+ version: ${{ github.event.inputs.version_number }}
+ file_path: "Directory.Build.props"
+
+ - name: Setup git
+ run: |
+ git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
+ git config --local user.name "bitwarden-devops-bot"
+
+ - name: Check if version changed
+ id: version-changed
+ run: |
+ if [ -n "$(git status --porcelain)" ]; then
+ echo "changes_to_commit=TRUE" >> $GITHUB_OUTPUT
+ else
+ echo "changes_to_commit=FALSE" >> $GITHUB_OUTPUT
+ echo "No changes to commit!";
+ fi
+
+ - name: Commit files
+ if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
+ run: git commit -m "Bumped version to ${{ github.event.inputs.version_number }}" -a
+
+ - name: Push changes
+ if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
+ run: git push -u origin version_bump_${{ github.event.inputs.version_number }}
+
+ - name: Create Version PR
+ if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
+ env:
+ PR_BRANCH: "version_bump_${{ github.event.inputs.version_number }}"
+ GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+ BASE_BRANCH: master
+ TITLE: "Bump version to ${{ github.event.inputs.version_number }}"
+ run: |
+ gh pr create --title "$TITLE" \
+ --base "$BASE" \
+ --head "$PR_BRANCH" \
+ --label "version update" \
+ --label "automated pr" \
+ --body "
+ ## Type of change
+ - [ ] Bug fix
+ - [ ] New feature development
+ - [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
+ - [ ] Build/deploy pipeline (DevOps)
+ - [X] Other
+
+ ## Objective
+ Automated version bump to ${{ github.event.inputs.version_number }}"
diff --git a/.github/workflows/workflow-linter.yml b/.github/workflows/workflow-linter.yml
new file mode 100644
index 000000000..9fda2eee0
--- /dev/null
+++ b/.github/workflows/workflow-linter.yml
@@ -0,0 +1,11 @@
+---
+name: Workflow Linter
+
+on:
+ pull_request:
+ paths:
+ - .github/workflows/**
+
+jobs:
+ call-workflow:
+ uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@master
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 000000000..7e09ae524
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,418 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "compounds": [
+ {
+ "name": "Min Server",
+ "configurations": [
+ "Identity",
+ "API"
+ ],
+ "presentation": {
+ "hidden": false,
+ "group": "AA_compounds",
+ "order": 1
+ },
+ "stopAll": true
+ },
+ {
+ "name": "Admin, API, Identity",
+ "configurations": [
+ "Admin",
+ "API",
+ "Identity"
+ ],
+ "presentation": {
+ "hidden": false,
+ "group": "AA_compounds",
+ "order": 3
+ },
+ "stopAll": true
+ },
+ {
+ "name": "Full Server",
+ "configurations": [
+ "Admin",
+ "API",
+ "EventsProcessor",
+ "Identity",
+ "Sso",
+ "Icons",
+ "Billing",
+ "Notifications"
+ ],
+ "presentation": {
+ "hidden": false,
+ "group": "AA_compounds",
+ "order": 4
+ },
+ "stopAll": true
+ },
+ {
+ "name": "Self Host: Bit",
+ "configurations": [
+ "Admin-SelfHost",
+ "API-SelfHost",
+ "EventsProcessor-SelfHost",
+ "Identity-SelfHost",
+ "Sso-SelfHost",
+ "Notifications-SelfHost"
+ ],
+ "presentation": {
+ "hidden": false,
+ "group": "AA_compounds",
+ "order": 2
+ },
+ "stopAll": true
+ },
+ {
+ "name": "Self Host: OSS",
+ "configurations": [
+ "Admin-SelfHost",
+ "API-SelfHost",
+ "EventsProcessor-SelfHost",
+ "Identity-SelfHost",
+ ],
+ "presentation": {
+ "hidden": false,
+ "group": "AA_compounds",
+ "order": 99
+ },
+ "stopAll": true
+ }
+ ],
+ "configurations": [
+ {
+ "name": "Identity",
+ "presentation": {
+ "hidden": false,
+ "group": "cloud",
+ "order": 10
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildIdentity",
+ "program": "${workspaceFolder}/src/Identity/bin/Debug/net6.0/Identity.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/Identity",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "API",
+ "presentation": {
+ "hidden": false,
+ "group": "cloud",
+ "order": 10
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildAPI",
+ "program": "${workspaceFolder}/src/Api/bin/Debug/net6.0/Api.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/Api",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "Billing",
+ "presentation": {
+ "hidden": false,
+ "group": "cloud",
+ "order": 10
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildBilling",
+ "program": "${workspaceFolder}/src/Billing/bin/Debug/net6.0/Billing.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/Billing",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "Admin",
+ "presentation": {
+ "hidden": false,
+ "group": "cloud",
+ "order": 20
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildAdmin",
+ "OS-COMMENT4": "If you have changed target frameworks, make sure to update the program path.",
+ "program": "${workspaceFolder}/src/Admin/bin/Debug/net6.0/Admin.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/Admin",
+ "stopAtEntry": false,
+ "OS-COMMENT5": "Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser",
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "Sso",
+ "presentation": {
+ "hidden": false,
+ "group": "cloud",
+ "order": 50
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildSso",
+ "program": "${workspaceFolder}/bitwarden_license/src/Sso/bin/Debug/net6.0/Sso.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/bitwarden_license/src/Sso",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "EventsProcessor",
+ "presentation": {
+ "hidden": false,
+ "group": "cloud",
+ "order": 90
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildEventsProcessor",
+ "program": "${workspaceFolder}/src/EventsProcessor/bin/Debug/net6.0/EventsProcessor.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/EventsProcessor",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "Icons",
+ "presentation": {
+ "hidden": false,
+ "group": "cloud",
+ "order": 90
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildIcons",
+ "program": "${workspaceFolder}/src/Icons/bin/Debug/net6.0/Icons.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/Icons",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "Notifications",
+ "presentation": {
+ "hidden": true,
+ "group": "cloud",
+ "order": 100
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildNotifications",
+ "program": "${workspaceFolder}/src/Notifications/bin/Debug/net6.0/Notifications.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/Notifications",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "Identity-SelfHost",
+ "presentation": {
+ "hidden": true,
+ "group": "self-host",
+ "order": 999
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildIdentity",
+ "program": "${workspaceFolder}/src/Identity/bin/Debug/net6.0/Identity.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/Identity",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "http://localhost:33657",
+ "developSelfHosted": "true"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "API-SelfHost",
+ "presentation": {
+ "hidden": true,
+ "group": "self-host",
+ "order": 999
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildAPI",
+ "program": "${workspaceFolder}/src/Api/bin/Debug/net6.0/Api.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/Api",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "http://localhost:4001",
+ "developSelfHosted": "true"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "Admin-SelfHost",
+ "presentation": {
+ "hidden": true,
+ "group": "self-host",
+ "order": 999
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildAdmin",
+ "OS-COMMENT4": "If you have changed target frameworks, make sure to update the program path.",
+ "program": "${workspaceFolder}/src/Admin/bin/Debug/net6.0/Admin.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/Admin",
+ "stopAtEntry": false,
+ "OS-COMMENT5": "Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser",
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "http://localhost:62912",
+ "developSelfHosted": "true"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "Sso-SelfHost",
+ "presentation": {
+ "hidden": true,
+ "group": "self-host",
+ "order": 999
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildSso",
+ "program": "${workspaceFolder}/bitwarden_license/src/Sso/bin/Debug/net6.0/Sso.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/bitwarden_license/src/Sso",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "http://localhost:51822",
+ "developSelfHosted": "true"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "Notifications-SelfHost",
+ "presentation": {
+ "hidden": true,
+ "group": "self-host",
+ "order": 999
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildNotifications",
+ "program": "${workspaceFolder}/src/Notifications/bin/Debug/net6.0/Notifications.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/Notifications",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "http://localhost:61841",
+ "developSelfHosted": "true"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": "EventsProcessor-SelfHost",
+ "presentation": {
+ "hidden": true,
+ "group": "self-host",
+ "order": 999
+ },
+ "requireExactSource": true,
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "buildEventsProcessor",
+ "program": "${workspaceFolder}/src/EventsProcessor/bin/Debug/net6.0/EventsProcessor.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/EventsProcessor",
+ "stopAtEntry": false,
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "http://localhost:46274",
+ "developSelfHosted": "true"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach",
+ "processId": "${command:pickProcess}"
+ }
+ ],
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 000000000..61d1f9240
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,157 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "buildIcons",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/src/Icons/Icons.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "buildPortal",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/bitwarden_license/src/Portal/Portal.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "buildSso",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/bitwarden_license/src/Sso/Sso.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "buildEventsProcessor",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/src/EventsProcessor/EventsProcessor.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "buildAdmin",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/src/Admin/Admin.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "buildIdentity",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/src/Identity/Identity.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "buildAPI",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/src/Api/Api.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ },
+ {
+ "label": "buildNotifications",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/src/Notifications/Notifications.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ },
+ {
+ "label": "buildBilling",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/src/Billing/Billing.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ },
+ {
+ "label": "clean",
+ "type": "shell",
+ "command": "dotnet clean",
+ "presentation": {
+ "echo": true,
+ "reveal": "always",
+ "focus": false,
+ "panel": "shared",
+ "showReuseMessage": true,
+ "clear": false
+ },
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "test",
+ "type": "shell",
+ "command": "dotnet test",
+ "group": {
+ "kind": "test",
+ "isDefault": true
+ },
+ "presentation": {
+ "echo": true,
+ "reveal": "always",
+ "focus": false,
+ "panel": "shared",
+ "showReuseMessage": true,
+ "clear": false
+ },
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5331e9f7b..2833057bc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,31 +1,3 @@
# How to Contribute
-Contributions of all kinds are welcome!
-
-Please visit our [Community Forums](https://community.bitwarden.com/) for general community discussion and the development roadmap.
-
-Here is how you can get involved:
-
-* **Request a new feature:** Go to the [Feature Requests category](https://community.bitwarden.com/c/feature-requests/) of the Community Forums. Please search existing feature requests before making a new one
-
-* **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
-
-* **Report a bug or submit a bugfix:** Use Github issues and pull requests
-
-* **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
-
-* **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums
-
-## Contributor Agreement
-
-Please sign the [Contributor Agreement](https://cla-assistant.io/bitwarden/server) if you intend on contributing to any Github repository. Pull requests cannot be accepted and merged unless the author has signed the Contributor Agreement.
-
-## Pull Request Guidelines
-
-* commit any pull requests against the `master` branch
-* include a link to your Community Forums post
-* please do **not** submit version number updates/bumps
-
-# Setup guide
-
-Please read the [Setup guide](https://github.com/bitwarden/server/blob/master/SETUP.md) for a step-by-step guide to set up your own local development server.
+Our [Contributing Guidelines](https://contributing.bitwarden.com/contributing/) are located in our [Contributing Documentation](https://contributing.bitwarden.com/). The documentation also includes recommended tooling, code style tips, and lots of other great information to get you started.
diff --git a/Directory.Build.props b/Directory.Build.props
index ffeffe4ff..ce11b46f5 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,9 +1,71 @@
-
+
-
- net5.0
- 1.44.1
- Bit.$(MSBuildProjectName)
-
+
+ net6.0
+
+ 2023.1.0
+ Bit.$(MSBuildProjectName)
+ true
+ enable
+ false
+
-
+
+
+
+ 17.1.0
+
+ 2.4.1
+
+ 2.4.3
+
+ 3.1.2
+
+ 6.0.3
+
+ 4.3.0
+
+ 4.17.0
+
+ 4.17.0
+
+
+
+
+
+
+
+
+
+
+
+ <_Parameter1>GitHash
+ <_Parameter2>$(SourceRevisionId)
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENSE_FAQ.md b/LICENSE_FAQ.md
index b84a2f700..84a46fe93 100644
--- a/LICENSE_FAQ.md
+++ b/LICENSE_FAQ.md
@@ -16,12 +16,12 @@ Our current software products have the following licenses:
*Bitwarden server:* The main Bitwarden server code is licensed under the AGPL 3.0 license.
-*CommCore and SSO integration:* Code for certain new modules that are designed and developed for use by larger
+*Commercial.Core and SSO integration:* Code for certain new modules that are designed and developed for use by larger
organizations and enterprise environments is released under the Bitwarden License, a "source available" license. The
Bitwarden License provides users access to product source code for non-production purposes such as development and
testing, but requires a paid subscription for production use of the product, and environments supporting production.
-Additionally the Api module by default includes CommCore which is under the Bitwarden License, however this can be
-disabled by using `/p:DefineConstants="OSS"` as an argument to `dotnet` while building the module.
+Additionally the Api module by default includes Commercial.Core which is under the Bitwarden License, however this can
+be disabled by using `/p:DefineConstants="OSS"` as an argument to `dotnet` while building the module.
# Frequently Asked Questions
diff --git a/README.md b/README.md
index c5be6e88f..e6c0dcd85 100644
--- a/README.md
+++ b/README.md
@@ -13,51 +13,15 @@
--------------------
+---
The Bitwarden Server project contains the APIs, database, and other core infrastructure items needed for the "backend" of all bitwarden client applications.
The server project is written in C# using .NET Core with ASP.NET Core. The database is written in T-SQL/SQL Server. The codebase can be developed, built, run, and deployed cross-platform on Windows, macOS, and Linux distributions.
-## Build/Run
+## Developer Documentation
-Please read the [Setup guide](https://github.com/bitwarden/server/blob/master/SETUP.md) for a step-by-step guide to set up your own local development server.
-
-### Requirements
-
-- [.NET 5.0 SDK](https://dotnet.microsoft.com/download)
-- [SQL Server 2017](https://docs.microsoft.com/en-us/sql/index)
-
-*These dependencies are free to use.*
-
-### Recommended Development Tooling
-
-- [Visual Studio](https://www.visualstudio.com/vs/) (Windows and macOS)
-- [Visual Studio Code](https://code.visualstudio.com/) (other)
-
-*These tools are free to use.*
-
-### API
-
-```
-cd src/Api
-dotnet restore
-dotnet build
-dotnet run
-```
-
-visit http://localhost:4000/alive
-
-### Identity
-
-```
-cd src/Identity
-dotnet restore
-dotnet build
-dotnet run
-```
-
-visit http://localhost:33657/.well-known/openid-configuration
+Please refer to the [Server Setup Guide](https://contributing.bitwarden.com/getting-started/server/guide) in the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
## Deploy
@@ -76,13 +40,13 @@ Full documentation for deploying Bitwarden with Docker can be found in our help
- [Docker](https://www.docker.com/community-edition#/download)
- [Docker Compose](https://docs.docker.com/compose/install/) (already included with some Docker installations)
-*These dependencies are free to use.*
+_These dependencies are free to use._
### Linux & macOS
```
-curl -s -o bitwarden.sh \
- https://raw.githubusercontent.com/bitwarden/server/master/scripts/bitwarden.sh \
+curl -s -L -o bitwarden.sh \
+ "https://func.bitwarden.com/api/dl/?app=self-host&platform=linux" \
&& chmod +x bitwarden.sh
./bitwarden.sh install
./bitwarden.sh start
@@ -92,15 +56,40 @@ curl -s -o bitwarden.sh \
```
Invoke-RestMethod -OutFile bitwarden.ps1 `
- -Uri https://raw.githubusercontent.com/bitwarden/server/master/scripts/bitwarden.ps1
+ -Uri "https://func.bitwarden.com/api/dl/?app=self-host&platform=windows"
.\bitwarden.ps1 -install
.\bitwarden.ps1 -start
```
+## We're Hiring!
+
+Interested in contributing in a big way? Consider joining our team! We're hiring for many positions. Please take a look at our [Careers page](https://bitwarden.com/careers/) to see what opportunities are currently open as well as what it's like to work at Bitwarden.
+
## Contribute
-Code contributions are welcome! Visual Studio or VS Code is highly recommended if you are working on this project. Please commit any pull requests against the `master` branch. Please see [`CONTRIBUTING.md`](CONTRIBUTING.md) for more info (and feel free to contribute to that guide as well).
+Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution.
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file. We also run a program on [HackerOne](https://hackerone.com/bitwarden).
No grant of any rights in the trademarks, service marks, or logos of Bitwarden is made (except as may be necessary to comply with the notice requirements as applicable), and use of any Bitwarden trademarks must comply with [Bitwarden Trademark Guidelines](https://github.com/bitwarden/server/blob/master/TRADEMARK_GUIDELINES.md).
+
+### Dotnet-format
+
+Consider installing our git pre-commit hook for automatic formatting.
+
+```bash
+git config --local core.hooksPath .git-hooks
+```
+
+### File Scoped Namespaces
+
+We recently migrated to using file scoped namespaces to save some horizontal space. All previous branches will need to update to avoid large merge conflicts using the following steps:
+
+1. Check out your local Branch
+2. Run `git merge 9b7aef0763ad14e229b337c3b5b27cb411009792`
+3. Resolve any merge conflicts, commit.
+4. Run `dotnet format`
+5. Commit
+6. Run `git merge -Xours 7f5f010e1eea400300c47f776604ecf46c4b4f2d`
+7. Fix Merge conflicts
+8. Push
diff --git a/SECURITY.md b/SECURITY.md
index ef94f0b49..e6edb96da 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -1,39 +1,11 @@
-Bitwarden believes that working with security researchers across the globe is crucial to keeping our
-users safe. If you believe you've found a security issue in our product or service, we encourage you to
-notify us. We welcome working with you to resolve the issue promptly. Thanks in advance!
+Bitwarden believes that working with security researchers across the globe is crucial to keeping our users safe. If you believe you've found a security issue in our product or service, we encourage you to please submit a report through our [HackerOne Program](https://hackerone.com/bitwarden/). We welcome working with you to resolve the issue promptly. Thanks in advance!
# Disclosure Policy
-- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every
- effort to quickly resolve the issue.
-- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a
- third-party. We may publicly disclose the issue before resolving it, if appropriate.
-- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or
- degradation of our service. Only interact with accounts you own or with explicit permission of the
- account holder.
-- If you would like to encrypt your report, please use the PGP key with long ID
- `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
-
-# In-scope
-
-- Security issues in any current release of Bitwarden. This includes the web vault, browser extension,
- and mobile apps (iOS and Android). Product downloads are available at https://bitwarden.com. Source
- code is available at https://github.com/bitwarden.
-
-# Exclusions
-
-The following bug classes are out-of scope:
-
-- Bugs that are already reported on any of Bitwarden's issue trackers (https://github.com/bitwarden),
- or that we already know of. Note that some of our issue tracking is private.
-- Issues in an upstream software dependency (ex: Xamarin, ASP.NET) which are already reported to the
- upstream maintainer.
-- Attacks requiring physical access to a user's device.
-- Self-XSS
-- Issues related to software or protocols not under Bitwarden's control
-- Vulnerabilities in outdated versions of Bitwarden
-- Missing security best practices that do not directly lead to a vulnerability
-- Issues that do not have any impact on the general public
+- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue.
+- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a third-party. We may publicly disclose the issue before resolving it, if appropriate.
+- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our service. Only interact with accounts you own or with explicit permission of the account holder.
+- If you would like to encrypt your report, please use the PGP key with long ID `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
While researching, we'd like to ask you to refrain from:
@@ -42,4 +14,8 @@ While researching, we'd like to ask you to refrain from:
- Social engineering (including phishing) of Bitwarden staff or contractors
- Any physical attempts against Bitwarden property or data centers
+# We want to help you!
+
+If you have something that you feel is close to exploitation, or if you'd like some information regarding the internal API, or generally have any questions regarding the app that would help in your efforts, please email us at https://bitwarden.com/contact and ask for that information. As stated above, Bitwarden wants to help you find issues, and is more than willing to help.
+
Thank you for helping keep Bitwarden and our users safe!
diff --git a/SETUP.md b/SETUP.md
deleted file mode 100644
index 3f0cd199b..000000000
--- a/SETUP.md
+++ /dev/null
@@ -1,197 +0,0 @@
-# Server Architecture
-
-The Server is divided into a number of services. Each service is a Visual Studio project in the Server solution. These are:
-
-* Admin
-* Api
-* Icons
-* Identity
-* Notifications
-* SQL
-
-Each service is built and run separately. The Bitwarden clients can use different servers for different services.
-
-This means that you don't need to run all services locally for a development environment. You can run only those services that you intend to modify, and use Bitwarden.com or a self-hosted instance for all other services required.
-
-By default some of the services depends on the Bitwarden licensed `CommCore`, however it can easily be disabled by including the `/p:DefineConstants="OSS"` as an argument to `dotnet`.
-
-# Local Development Environment Setup
-
-This guide will show you how to set up the Api, Identity and SQL projects for development. These are the minimum projects for any development work. You may need to set up additional projects depending on the changes you want to make.
-
-We recommend using [Visual Studio](https://visualstudio.microsoft.com/vs/), and [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.1) which is used for the helper scripts.
-
-## Docker containers
-
-To simplify the setup process we provide a [Docker Compose](https://docs.docker.com/compose/) application model. This is split up into multiple service profiles to facilitate easily customization.
-
-Some settings can be customized by modifying the `dev/.env` file, such as the `MSSQL_PASSWORD` which should be modified before starting the project.
-
-```bash
-# Copy the example environment file
-cp ./.env.example ./.env
-
-# We recommend running the following command when developing for self-hosted
-docker compose --profile mssql --profile mail up
-
-# We also provide a storage profile which uses Azurite to emulate some services used by the cloud instance
-# Usually only needed by internal Bitwarden developers
-docker compose --profile cloud --profile mail up
-```
-
-### SQL Server
-
-We recommend changing the `MSSQL_PASSWORD` variable in `dev/.env` to avoid exposing the sqlserver with a default password. **Note**: changing this after first running docker compose may require a re-creation of the storage volume. To do this, stop the running containers and run `docker volume rm bitwardenserver_mssql_dev_data`. (**Warning:** this will delete your development database.)
-
-We provide a helper script which will create the development database `vault_dev` and also run all migrations. This command should be run after starting docker the first time, as well as after syncing against upstream and after creating a new migration.
-
-```powershell
-.\dev\migrate.ps1
-
-# You can also re-run the last migration using
-.\dev\migrate.ps1 -r
-```
-
-**Note:** If all or many migrations are skipped even though this is a new database, make sure that there is not a `last_migration` file located in `dev/.data/mssql`. If there is, remove it and run the helper script again. This can happen if you create the database, run migrations, then delete it.
-
-### Azurite
-
-[Azurite](https://github.com/Azure/Azurite) is a emulator for Azure Storage API and supports Blob, Queues and Table storage. We use it to avoid a hard dependency on online services for cloud development.
-
-To bootstrap the local Azurite instance please run the following command:
-```powershell
-# This script requires the Az module, which can be installed using
-Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force
-
-.\dev\setup_azurite.ps1
-```
-
-### Mailcatcher
-
-Since the server uses emails for many user interactions a working SMTP server is a requirement, we provide a pre-setup instance of [MailCatcher](https://mailcatcher.me/) which exposes a web interface at http://localhost:1080.
-
-## Certificates
-In order to run Bitwarden, we require two certificates which for local development can be resolved by using self signed certificates.
-
-### Windows
-
-We provide a helper script which will generate and add the certificates to the users Certificate Store. After running the script it will output the thumbprints needed for the next step. The certificates can later be accessed using `certml.msc` under `Personal/Certificates`.
-
-```powershell
-.\create_certificates_windows.ps1
-
- PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\My
-Thumbprint Subject
----------- -------
-0BE8A0072214AB37C6928968752F698EEC3A68B5 CN=Bitwarden Identity Server Dev
-C3A6CECAD3DB580F91A52FC9C767FE780300D8AB CN=Bitwarden Data Protection Dev
-```
-
-### MacOS
-
-We provide a helper script which will generate the certificates and add them to the keychain.
-
-**Note:** You should update the Trust options for each certificate to `always trust` using *Keychain Access*.
-
-```bash
-./create_certificates_mac.sh
-
-Certificate fingerprints:
-Identity Server Dev: 0BE8A0072214AB37C6928968752F698EEC3A68B5
-Data Protection Dev: C3A6CECAD3DB580F91A52FC9C767FE780300D8AB
-```
-
-## User Secrets
-User secrets are a method for managing application settings on a per-developer basis. They are stored outside of the local git repository so that they are not pushed to remote.
-
-User secrets override the settings in `appsettings.json` of each project. Your user secrets file should match the structure of the `appsettings.json` file for the settings you intend to override.
-
-For more information, see: [Safe storage of app secrets in development in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-5.0).
-
-### Automated Helper script
-
-We provide a helper scripts which simplifies setting user secrets for all projects in the repository.
-
-Start by copying the `secret.json.example` file to `secrets.json` and modify the existing settings and add any other required setting. Afterwards run the following command which will add the settings to each project in the bitwarden repository.
-
-```powershell
-.\setup_secrets.ps1
-
-# The script also supports an optional flag which removes all existing settings before re-applying them
-.\setup_secrets.ps1 -clear 1
-```
-
-### Manually creating and modifying
-
-It is also possible to manually create and modify the user secrets using either the `dotnet` CLI or `Visual Studio` on Windows. For more details see [Appendix A](#user-secrets).
-
-### Required User Secrets
-
-**selfhosted**: It is highly recommended that you use the `selfHosted: true` setting when running a local development environment. This tells the system not to use cloud services, assuming that you are running your own local SQL instance.
-
-**sqlServer__connectionString**: this provides the information required for the Server to connect to the SQL instance. See the example connection string in `secrets.json.example`. You may need to change the default password in the connection string.
-
-**licenseDirectory**: this must be set to avoid errors, but it can be set to an arbitrary empty folder.
-
-**installation__key** and **installation__id**: request your own private Installation Id and Installation Key for self-hosting: https://bitwarden.com/host/.
-
-## Running and Debugging
-After you have completed the above steps, you should be ready to launch your development environment for the Api and Identity projects.
-
-### Visual Studio
-
-To debug:
-* On Windows, right-click on each project > click **Debug** > click **Start New Instance**
-* On MacOS, right-click each project > click **Start Debugging Project**
-
-To run without debugging, open a terminal and navigate to the location of the .csproj file for that project (usually in `src/ProjectName`). Start the project with:
-
-```bash
-dotnet run
-```
-
-NOTE: check the output of the running project to find the port it is listening on. If this is different to the default in `appsettings.json`, you may need to update your user secrets to override this (typically the Api user secrets for the Identity URL).
-
-### Rider
-From within Rider, launch both the Api project and the Identity project by clicking the "play" button for each project separately.
-
-### Testing your deployment
-* To test the deployment of each project, navigate to the following pages in your browser. You should see server output and no errors:
- * Test the Api deployment: http://localhost:4000/alive
- * Test the Identity deployment: http://localhost:33656/.well-known/openid-configuration
-* If your test was successful, you can connect a GUI client to the dev environment by following the instructions here: [Change your client application's environment](https://bitwarden.com/help/article/change-client-environment/). If you are following this guide, you should only set the API Server URL and Identity Server URL to localhost:port and leave all other fields blank.
-* If you are using the CLI client, you will also need to set the Node environment variables for your self-signed certificates by following the instructions here: [The Bitwarden command-line tool (CLI) > Self-signed certificates](https://bitwarden.com/help/article/cli/#self-signed-certificates).
-
-### Troubleshooting
-* If you get a 404 error, the projects may be listening on a non-default port. Check the output of your running projects to check the port they are listening on.
-
-
-# Appendix A (User Secrets)
-
-### Editing user secrets - Visual Studio on Windows
-Right-click on the project in the Solution Explorer and click **Manage User Secrets**.
-
-### Editing user secrets - Visual Studio on macOS
-Open a terminal and navigate to the project directory. Once there, initiate and create the blank user secrets file by running:
-
-```bash
-dotnet user-secrets init
-```
-
-Add a user secret by running:
-
-```bash
-dotnet user-secrets set "" ""
-```
-
-View currently set secrets by running:
-
-```bash
-dotnet user-secrets list
-```
-
-By default, user secret files are located in `~/.microsoft/usersecrets//secrets.json`. After the file has been created, you can edit this directly with VSCode, which is much easier than using the command line tool.
-
-### Editing user secrets - Rider
-* Navigate to **Preferences -> Plugins** and Install .NET Core User Secrets
-* Right click on the a project and click **Tools** > **Open project user secrets**
diff --git a/bitwarden-server.sln b/bitwarden-server.sln
index 410afae94..a8c3e2255 100644
--- a/bitwarden-server.sln
+++ b/bitwarden-server.sln
@@ -61,18 +61,56 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sso", "bitwarden_license\sr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Icons.Test", "test\Icons.Test\Icons.Test.csproj", "{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommCore", "bitwarden_license\src\CommCore\CommCore.csproj", "{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Commercial.Core", "bitwarden_license\src\Commercial.Core\Commercial.Core.csproj", "{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}"
ProjectSection(ProjectDependencies) = postProject
{3973D21B-A692-4B60-9B70-3631C057423A} = {3973D21B-A692-4B60-9B70-3631C057423A}
EndProjectSection
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommCore.Test", "bitwarden_license\test\CmmCore.Test\CommCore.Test.csproj", "{0E99A21B-684B-4C59-9831-90F775CAB6F7}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Commercial.Core.Test", "bitwarden_license\test\Commercial.Core.Test\Commercial.Core.Test.csproj", "{0E99A21B-684B-4C59-9831-90F775CAB6F7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test - Bitwarden License", "test - Bitwarden License", "{287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySqlMigrations", "util\MySqlMigrations\MySqlMigrations.csproj", "{BDC1D592-5947-47ED-9903-7CDBB12A50C8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlMigrations", "util\MySqlMigrations\MySqlMigrations.csproj", "{BDC1D592-5947-47ED-9903-7CDBB12A50C8}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PostgresMigrations", "util\PostgresMigrations\PostgresMigrations.csproj", "{F72E0229-2EF7-49B3-9004-FF4C0043816E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PostgresMigrations", "util\PostgresMigrations\PostgresMigrations.csproj", "{F72E0229-2EF7-49B3-9004-FF4C0043816E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "test\Common\Common.csproj", "{17DA09D7-0212-4009-879E-6B9CFDE5FA60}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure.Dapper", "src\Infrastructure.Dapper\Infrastructure.Dapper.csproj", "{AD933445-27CE-4D30-A6ED-9065309464AD}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedWeb", "src\SharedWeb\SharedWeb.csproj", "{713D44C0-1BC1-4024-96A3-A98A49F33908}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure.EntityFramework", "src\Infrastructure.EntityFramework\Infrastructure.EntityFramework.csproj", "{ED880735-0250-43C7-9662-FDC7C7416E7F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Billing.Test", "test\Billing.Test\Billing.Test.csproj", "{B8639B10-2157-44BC-8CE1-D9EB4B50971F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.Test", "test\Identity.Test\Identity.Test.csproj", "{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.IntegrationTest", "test\Identity.IntegrationTest\Identity.IntegrationTest.csproj", "{0D3B2BD2-53F3-421D-AD8F-C19B954C796B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTestCommon", "test\IntegrationTestCommon\IntegrationTestCommon.csproj", "{0923DE59-5FB1-44F2-9302-A09D2236B470}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scim", "bitwarden_license\src\Scim\Scim.csproj", "{BC3B3F8C-621A-4CB8-9563-6EC0A2C8C747}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlServerEFScaffold", "util\SqlServerEFScaffold\SqlServerEFScaffold.csproj", "{2F2E8BB0-6838-48DA-B581-71B9F13DE364}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commercial.Infrastructure.EntityFramework", "bitwarden_license\src\Commercial.Infrastructure.EntityFramework\Commercial.Infrastructure.EntityFramework.csproj", "{5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.EFIntegration.Test", "test\Infrastructure.EFIntegration.Test\Infrastructure.EFIntegration.Test.csproj", "{7EFB1124-F40A-40EB-9EDA-94FD540AA8FD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.IntegrationTest", "test\Api.IntegrationTest\Api.IntegrationTest.csproj", "{CBE96C6D-A4D6-46E1-94C5-42D6CAD8531C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scim.IntegrationTest", "bitwarden_license\test\Scim.IntegrationTest\Scim.IntegrationTest.csproj", "{FE998849-5FC8-41A2-B7C9-9227901471A0}"
+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}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scim.Test", "bitwarden_license\test\Scim.Test\Scim.Test.csproj", "{B1595DA3-4C60-41AA-8BF0-499A5F75A885}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.IntegrationTest", "test\Infrastructure.IntegrationTest\Infrastructure.IntegrationTest.csproj", "{7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqliteMigrations", "util\SqliteMigrations\SqliteMigrations.csproj", "{07143DFA-F242-47A4-A15E-39C9314D4140}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -150,15 +188,6 @@ Global
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Release|Any CPU.Build.0 = Release|Any CPU
- {BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Release|Any CPU.Build.0 = Release|Any CPU
- {F72E0229-2EF7-49B3-9004-FF4C0043816E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F72E0229-2EF7-49B3-9004-FF4C0043816E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F72E0229-2EF7-49B3-9004-FF4C0043816E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F72E0229-2EF7-49B3-9004-FF4C0043816E}.Release|Any CPU.Build.0 = Release|Any CPU
-
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -167,6 +196,86 @@ Global
{0E99A21B-684B-4C59-9831-90F775CAB6F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E99A21B-684B-4C59-9831-90F775CAB6F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E99A21B-684B-4C59-9831-90F775CAB6F7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F72E0229-2EF7-49B3-9004-FF4C0043816E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F72E0229-2EF7-49B3-9004-FF4C0043816E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F72E0229-2EF7-49B3-9004-FF4C0043816E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F72E0229-2EF7-49B3-9004-FF4C0043816E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {17DA09D7-0212-4009-879E-6B9CFDE5FA60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {17DA09D7-0212-4009-879E-6B9CFDE5FA60}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {17DA09D7-0212-4009-879E-6B9CFDE5FA60}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {17DA09D7-0212-4009-879E-6B9CFDE5FA60}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AD933445-27CE-4D30-A6ED-9065309464AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AD933445-27CE-4D30-A6ED-9065309464AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AD933445-27CE-4D30-A6ED-9065309464AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AD933445-27CE-4D30-A6ED-9065309464AD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {713D44C0-1BC1-4024-96A3-A98A49F33908}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {713D44C0-1BC1-4024-96A3-A98A49F33908}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {713D44C0-1BC1-4024-96A3-A98A49F33908}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {713D44C0-1BC1-4024-96A3-A98A49F33908}.Release|Any CPU.Build.0 = Release|Any CPU
+ {ED880735-0250-43C7-9662-FDC7C7416E7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ED880735-0250-43C7-9662-FDC7C7416E7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ED880735-0250-43C7-9662-FDC7C7416E7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ED880735-0250-43C7-9662-FDC7C7416E7F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B8639B10-2157-44BC-8CE1-D9EB4B50971F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B8639B10-2157-44BC-8CE1-D9EB4B50971F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B8639B10-2157-44BC-8CE1-D9EB4B50971F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B8639B10-2157-44BC-8CE1-D9EB4B50971F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0D3B2BD2-53F3-421D-AD8F-C19B954C796B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0D3B2BD2-53F3-421D-AD8F-C19B954C796B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0D3B2BD2-53F3-421D-AD8F-C19B954C796B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0D3B2BD2-53F3-421D-AD8F-C19B954C796B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0923DE59-5FB1-44F2-9302-A09D2236B470}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0923DE59-5FB1-44F2-9302-A09D2236B470}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0923DE59-5FB1-44F2-9302-A09D2236B470}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0923DE59-5FB1-44F2-9302-A09D2236B470}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BC3B3F8C-621A-4CB8-9563-6EC0A2C8C747}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BC3B3F8C-621A-4CB8-9563-6EC0A2C8C747}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BC3B3F8C-621A-4CB8-9563-6EC0A2C8C747}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BC3B3F8C-621A-4CB8-9563-6EC0A2C8C747}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F2E8BB0-6838-48DA-B581-71B9F13DE364}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2F2E8BB0-6838-48DA-B581-71B9F13DE364}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F2E8BB0-6838-48DA-B581-71B9F13DE364}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2F2E8BB0-6838-48DA-B581-71B9F13DE364}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7EFB1124-F40A-40EB-9EDA-94FD540AA8FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7EFB1124-F40A-40EB-9EDA-94FD540AA8FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7EFB1124-F40A-40EB-9EDA-94FD540AA8FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7EFB1124-F40A-40EB-9EDA-94FD540AA8FD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CBE96C6D-A4D6-46E1-94C5-42D6CAD8531C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CBE96C6D-A4D6-46E1-94C5-42D6CAD8531C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CBE96C6D-A4D6-46E1-94C5-42D6CAD8531C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CBE96C6D-A4D6-46E1-94C5-42D6CAD8531C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FE998849-5FC8-41A2-B7C9-9227901471A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FE998849-5FC8-41A2-B7C9-9227901471A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FE998849-5FC8-41A2-B7C9-9227901471A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FE998849-5FC8-41A2-B7C9-9227901471A0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9C8F8255-5F74-4085-AB9C-9075CF6DDC61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {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
+ {7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {07143DFA-F242-47A4-A15E-39C9314D4140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {07143DFA-F242-47A4-A15E-39C9314D4140}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {07143DFA-F242-47A4-A15E-39C9314D4140}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {07143DFA-F242-47A4-A15E-39C9314D4140}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -189,10 +298,28 @@ Global
{860DE301-0B3E-4717-9C21-A9B4C3C2B121} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{4866AF64-6640-4C65-A662-A31E02FF9064} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
- {BDC1D592-5947-47ED-9903-7CDBB12A50C8} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
- {F72E0229-2EF7-49B3-9004-FF4C0043816E} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
{0E99A21B-684B-4C59-9831-90F775CAB6F7} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
+ {BDC1D592-5947-47ED-9903-7CDBB12A50C8} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
+ {F72E0229-2EF7-49B3-9004-FF4C0043816E} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
+ {17DA09D7-0212-4009-879E-6B9CFDE5FA60} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
+ {AD933445-27CE-4D30-A6ED-9065309464AD} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
+ {713D44C0-1BC1-4024-96A3-A98A49F33908} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
+ {ED880735-0250-43C7-9662-FDC7C7416E7F} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
+ {B8639B10-2157-44BC-8CE1-D9EB4B50971F} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
+ {310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
+ {0D3B2BD2-53F3-421D-AD8F-C19B954C796B} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
+ {0923DE59-5FB1-44F2-9302-A09D2236B470} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
+ {BC3B3F8C-621A-4CB8-9563-6EC0A2C8C747} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
+ {2F2E8BB0-6838-48DA-B581-71B9F13DE364} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
+ {5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
+ {7EFB1124-F40A-40EB-9EDA-94FD540AA8FD} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
+ {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}
+ {7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
+ {07143DFA-F242-47A4-A15E-39C9314D4140} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
diff --git a/bitwarden_license/src/CommCore/Services/ProviderService.cs b/bitwarden_license/src/CommCore/Services/ProviderService.cs
deleted file mode 100644
index 9bc513ece..000000000
--- a/bitwarden_license/src/CommCore/Services/ProviderService.cs
+++ /dev/null
@@ -1,513 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Bit.Core.Context;
-using Bit.Core.Enums;
-using Bit.Core.Enums.Provider;
-using Bit.Core.Exceptions;
-using Bit.Core.Models.Business;
-using Bit.Core.Models.Business.Provider;
-using Bit.Core.Models.Data;
-using Bit.Core.Models.Table;
-using Bit.Core.Models.Table.Provider;
-using Bit.Core.Repositories;
-using Bit.Core.Services;
-using Bit.Core.Settings;
-using Bit.Core.Utilities;
-using Microsoft.AspNetCore.DataProtection;
-
-namespace Bit.CommCore.Services
-{
- public class ProviderService : IProviderService
- {
- public static PlanType[] ProviderDisllowedOrganizationTypes = new[] { PlanType.Free, PlanType.FamiliesAnnually, PlanType.FamiliesAnnually2019 };
-
- private readonly IDataProtector _dataProtector;
- private readonly IMailService _mailService;
- private readonly IEventService _eventService;
- private readonly GlobalSettings _globalSettings;
- private readonly IProviderRepository _providerRepository;
- private readonly IProviderUserRepository _providerUserRepository;
- private readonly IProviderOrganizationRepository _providerOrganizationRepository;
- private readonly IOrganizationRepository _organizationRepository;
- private readonly IUserRepository _userRepository;
- private readonly IUserService _userService;
- private readonly IOrganizationService _organizationService;
- private readonly ICurrentContext _currentContext;
-
- public ProviderService(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository,
- IProviderOrganizationRepository providerOrganizationRepository, IUserRepository userRepository,
- IUserService userService, IOrganizationService organizationService, IMailService mailService,
- IDataProtectionProvider dataProtectionProvider, IEventService eventService,
- IOrganizationRepository organizationRepository, GlobalSettings globalSettings,
- ICurrentContext currentContext)
- {
- _providerRepository = providerRepository;
- _providerUserRepository = providerUserRepository;
- _providerOrganizationRepository = providerOrganizationRepository;
- _organizationRepository = organizationRepository;
- _userRepository = userRepository;
- _userService = userService;
- _organizationService = organizationService;
- _mailService = mailService;
- _eventService = eventService;
- _globalSettings = globalSettings;
- _dataProtector = dataProtectionProvider.CreateProtector("ProviderServiceDataProtector");
- _currentContext = currentContext;
- }
-
- public async Task CreateAsync(string ownerEmail)
- {
- var owner = await _userRepository.GetByEmailAsync(ownerEmail);
- if (owner == null)
- {
- throw new BadRequestException("Invalid owner. Owner must be an existing Bitwarden user.");
- }
-
- var provider = new Provider
- {
- Status = ProviderStatusType.Pending,
- Enabled = true,
- UseEvents = true,
- };
- await _providerRepository.CreateAsync(provider);
-
- var providerUser = new ProviderUser
- {
- ProviderId = provider.Id,
- UserId = owner.Id,
- Type = ProviderUserType.ProviderAdmin,
- Status = ProviderUserStatusType.Confirmed,
- };
- await _providerUserRepository.CreateAsync(providerUser);
- await SendProviderSetupInviteEmailAsync(provider, owner.Email);
- }
-
- public async Task CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key)
- {
- var owner = await _userService.GetUserByIdAsync(ownerUserId);
- if (owner == null)
- {
- throw new BadRequestException("Invalid owner.");
- }
-
- if (provider.Status != ProviderStatusType.Pending)
- {
- throw new BadRequestException("Provider is already setup.");
- }
-
- if (!CoreHelpers.TokenIsValid("ProviderSetupInvite", _dataProtector, token, owner.Email, provider.Id,
- _globalSettings.OrganizationInviteExpirationHours))
- {
- throw new BadRequestException("Invalid token.");
- }
-
- var providerUser = await _providerUserRepository.GetByProviderUserAsync(provider.Id, ownerUserId);
- if (!(providerUser is { Type: ProviderUserType.ProviderAdmin }))
- {
- throw new BadRequestException("Invalid owner.");
- }
-
- provider.Status = ProviderStatusType.Created;
- await _providerRepository.UpsertAsync(provider);
-
- providerUser.Key = key;
- await _providerUserRepository.ReplaceAsync(providerUser);
-
- return provider;
- }
-
- public async Task UpdateAsync(Provider provider, bool updateBilling = false)
- {
- if (provider.Id == default)
- {
- throw new ArgumentException("Cannot create provider this way.");
- }
-
- await _providerRepository.ReplaceAsync(provider);
- }
-
- public async Task> InviteUserAsync(ProviderUserInvite invite)
- {
- if (!_currentContext.ProviderManageUsers(invite.ProviderId))
- {
- throw new InvalidOperationException("Invalid permissions.");
- }
-
- var emails = invite?.UserIdentifiers;
- var invitingUser = await _providerUserRepository.GetByProviderUserAsync(invite.ProviderId, invite.InvitingUserId);
-
- var provider = await _providerRepository.GetByIdAsync(invite.ProviderId);
- if (provider == null || emails == null || !emails.Any())
- {
- throw new NotFoundException();
- }
-
- var providerUsers = new List();
- foreach (var email in emails)
- {
- // Make sure user is not already invited
- var existingProviderUserCount =
- await _providerUserRepository.GetCountByProviderAsync(invite.ProviderId, email, false);
- if (existingProviderUserCount > 0)
- {
- continue;
- }
-
- var providerUser = new ProviderUser
- {
- ProviderId = invite.ProviderId,
- UserId = null,
- Email = email.ToLowerInvariant(),
- Key = null,
- Type = invite.Type,
- Status = ProviderUserStatusType.Invited,
- CreationDate = DateTime.UtcNow,
- RevisionDate = DateTime.UtcNow,
- };
-
- await _providerUserRepository.CreateAsync(providerUser);
-
- await SendInviteAsync(providerUser, provider);
- providerUsers.Add(providerUser);
- }
-
- await _eventService.LogProviderUsersEventAsync(providerUsers.Select(pu => (pu, EventType.ProviderUser_Invited, null as DateTime?)));
-
- return providerUsers;
- }
-
- public async Task>> ResendInvitesAsync(ProviderUserInvite invite)
- {
- if (!_currentContext.ProviderManageUsers(invite.ProviderId))
- {
- throw new BadRequestException("Invalid permissions.");
- }
-
- var providerUsers = await _providerUserRepository.GetManyAsync(invite.UserIdentifiers);
- var provider = await _providerRepository.GetByIdAsync(invite.ProviderId);
-
- var result = new List>();
- foreach (var providerUser in providerUsers)
- {
- if (providerUser.Status != ProviderUserStatusType.Invited || providerUser.ProviderId != invite.ProviderId)
- {
- result.Add(Tuple.Create(providerUser, "User invalid."));
- continue;
- }
-
- await SendInviteAsync(providerUser, provider);
- result.Add(Tuple.Create(providerUser, ""));
- }
-
- return result;
- }
-
- public async Task AcceptUserAsync(Guid providerUserId, User user, string token)
- {
- var providerUser = await _providerUserRepository.GetByIdAsync(providerUserId);
- if (providerUser == null)
- {
- throw new BadRequestException("User invalid.");
- }
-
- if (providerUser.Status != ProviderUserStatusType.Invited)
- {
- throw new BadRequestException("Already accepted.");
- }
-
- if (!CoreHelpers.TokenIsValid("ProviderUserInvite", _dataProtector, token, user.Email, providerUser.Id,
- _globalSettings.OrganizationInviteExpirationHours))
- {
- throw new BadRequestException("Invalid token.");
- }
-
- if (string.IsNullOrWhiteSpace(providerUser.Email) ||
- !providerUser.Email.Equals(user.Email, StringComparison.InvariantCultureIgnoreCase))
- {
- throw new BadRequestException("User email does not match invite.");
- }
-
- providerUser.Status = ProviderUserStatusType.Accepted;
- providerUser.UserId = user.Id;
- providerUser.Email = null;
-
- await _providerUserRepository.ReplaceAsync(providerUser);
-
- return providerUser;
- }
-
- public async Task>> ConfirmUsersAsync(Guid providerId, Dictionary keys,
- Guid confirmingUserId)
- {
- var providerUsers = await _providerUserRepository.GetManyAsync(keys.Keys);
- var validProviderUsers = providerUsers
- .Where(u => u.UserId != null)
- .ToList();
-
- if (!validProviderUsers.Any())
- {
- return new List>();
- }
-
- var validOrganizationUserIds = validProviderUsers.Select(u => u.UserId.Value).ToList();
-
- var provider = await _providerRepository.GetByIdAsync(providerId);
- var users = await _userRepository.GetManyAsync(validOrganizationUserIds);
-
- var keyedFilteredUsers = validProviderUsers.ToDictionary(u => u.UserId.Value, u => u);
-
- var result = new List>();
- var events = new List<(ProviderUser, EventType, DateTime?)>();
-
- foreach (var user in users)
- {
- if (!keyedFilteredUsers.ContainsKey(user.Id))
- {
- continue;
- }
- var providerUser = keyedFilteredUsers[user.Id];
- try
- {
- if (providerUser.Status != ProviderUserStatusType.Accepted || providerUser.ProviderId != providerId)
- {
- throw new BadRequestException("Invalid user.");
- }
-
- providerUser.Status = ProviderUserStatusType.Confirmed;
- providerUser.Key = keys[providerUser.Id];
- providerUser.Email = null;
-
- await _providerUserRepository.ReplaceAsync(providerUser);
- events.Add((providerUser, EventType.ProviderUser_Confirmed, null));
- await _mailService.SendProviderConfirmedEmailAsync(provider.Name, user.Email);
- result.Add(Tuple.Create(providerUser, ""));
- }
- catch (BadRequestException e)
- {
- result.Add(Tuple.Create(providerUser, e.Message));
- }
- }
-
- await _eventService.LogProviderUsersEventAsync(events);
-
- return result;
- }
-
- public async Task SaveUserAsync(ProviderUser user, Guid savingUserId)
- {
- if (user.Id.Equals(default))
- {
- throw new BadRequestException("Invite the user first.");
- }
-
- if (user.Type != ProviderUserType.ProviderAdmin &&
- !await HasConfirmedProviderAdminExceptAsync(user.ProviderId, new[] { user.Id }))
- {
- throw new BadRequestException("Provider must have at least one confirmed ProviderAdmin.");
- }
-
- await _providerUserRepository.ReplaceAsync(user);
- await _eventService.LogProviderUserEventAsync(user, EventType.ProviderUser_Updated);
- }
-
- public async Task>> DeleteUsersAsync(Guid providerId,
- IEnumerable providerUserIds, Guid deletingUserId)
- {
- var provider = await _providerRepository.GetByIdAsync(providerId);
-
- if (provider == null)
- {
- throw new NotFoundException();
- }
-
- var providerUsers = await _providerUserRepository.GetManyAsync(providerUserIds);
- var users = await _userRepository.GetManyAsync(providerUsers.Where(pu => pu.UserId.HasValue)
- .Select(pu => pu.UserId.Value));
- var keyedUsers = users.ToDictionary(u => u.Id);
-
- if (!await HasConfirmedProviderAdminExceptAsync(providerId, providerUserIds))
- {
- throw new BadRequestException("Provider must have at least one confirmed ProviderAdmin.");
- }
-
- var result = new List>();
- var deletedUserIds = new List();
- var events = new List<(ProviderUser, EventType, DateTime?)>();
-
- foreach (var providerUser in providerUsers)
- {
- try
- {
- if (providerUser.ProviderId != providerId)
- {
- throw new BadRequestException("Invalid user.");
- }
- if (providerUser.UserId == deletingUserId)
- {
- throw new BadRequestException("You cannot remove yourself.");
- }
-
- events.Add((providerUser, EventType.ProviderUser_Removed, null));
-
- var user = keyedUsers.GetValueOrDefault(providerUser.UserId.GetValueOrDefault());
- var email = user == null ? providerUser.Email : user.Email;
- if (!string.IsNullOrWhiteSpace(email))
- {
- await _mailService.SendProviderUserRemoved(provider.Name, email);
- }
-
- result.Add(Tuple.Create(providerUser, ""));
- deletedUserIds.Add(providerUser.Id);
- }
- catch (BadRequestException e)
- {
- result.Add(Tuple.Create(providerUser, e.Message));
- }
-
- await _providerUserRepository.DeleteManyAsync(deletedUserIds);
- }
-
- await _eventService.LogProviderUsersEventAsync(events);
-
- return result;
- }
-
- public async Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key)
- {
- var po = await _providerOrganizationRepository.GetByOrganizationId(organizationId);
- if (po != null)
- {
- throw new BadRequestException("Organization already belongs to a provider.");
- }
-
- var organization = await _organizationRepository.GetByIdAsync(organizationId);
- ThrowOnInvalidPlanType(organization.PlanType);
-
- var providerOrganization = new ProviderOrganization
- {
- ProviderId = providerId,
- OrganizationId = organizationId,
- Key = key,
- };
-
- await _providerOrganizationRepository.CreateAsync(providerOrganization);
- await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_Added);
- }
-
- public async Task CreateOrganizationAsync(Guid providerId,
- OrganizationSignup organizationSignup, string clientOwnerEmail, User user)
- {
- ThrowOnInvalidPlanType(organizationSignup.Plan);
-
- var (organization, _) = await _organizationService.SignUpAsync(organizationSignup, true);
-
- var providerOrganization = new ProviderOrganization
- {
- ProviderId = providerId,
- OrganizationId = organization.Id,
- Key = organizationSignup.OwnerKey,
- };
-
- await _providerOrganizationRepository.CreateAsync(providerOrganization);
- await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_Created);
-
- await _organizationService.InviteUsersAsync(organization.Id, user.Id,
- new (OrganizationUserInvite, string)[]
- {
- (
- new OrganizationUserInvite
- {
- Emails = new[] { clientOwnerEmail },
- AccessAll = true,
- Type = OrganizationUserType.Owner,
- Permissions = null,
- Collections = Array.Empty(),
- },
- null
- )
- });
-
- return providerOrganization;
- }
-
- public async Task RemoveOrganizationAsync(Guid providerId, Guid providerOrganizationId, Guid removingUserId)
- {
- var providerOrganization = await _providerOrganizationRepository.GetByIdAsync(providerOrganizationId);
- if (providerOrganization == null || providerOrganization.ProviderId != providerId)
- {
- throw new BadRequestException("Invalid organization.");
- }
-
- if (!await _organizationService.HasConfirmedOwnersExceptAsync(providerOrganization.OrganizationId, new Guid[] { }, includeProvider: false))
- {
- throw new BadRequestException("Organization needs to have at least one confirmed owner.");
- }
-
- await _providerOrganizationRepository.DeleteAsync(providerOrganization);
- await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_Removed);
- }
-
- public async Task ResendProviderSetupInviteEmailAsync(Guid providerId, Guid ownerId)
- {
- var provider = await _providerRepository.GetByIdAsync(providerId);
- var owner = await _userRepository.GetByIdAsync(ownerId);
- if (owner == null)
- {
- throw new BadRequestException("Invalid owner.");
- }
- await SendProviderSetupInviteEmailAsync(provider, owner.Email);
- }
-
- private async Task SendProviderSetupInviteEmailAsync(Provider provider, string ownerEmail)
- {
- var token = _dataProtector.Protect($"ProviderSetupInvite {provider.Id} {ownerEmail} {CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow)}");
- await _mailService.SendProviderSetupInviteEmailAsync(provider, token, ownerEmail);
- }
-
- public async Task LogProviderAccessToOrganizationAsync(Guid organizationId)
- {
- if (organizationId == default)
- {
- return;
- }
-
- var providerOrganization = await _providerOrganizationRepository.GetByOrganizationId(organizationId);
- var organization = await _organizationRepository.GetByIdAsync(organizationId);
- if (providerOrganization != null)
- {
- await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_VaultAccessed);
- }
- if (organization != null)
- {
- await _eventService.LogOrganizationEventAsync(organization, EventType.Organization_VaultAccessed);
- }
- }
-
- private async Task SendInviteAsync(ProviderUser providerUser, Provider provider)
- {
- var nowMillis = CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow);
- var token = _dataProtector.Protect(
- $"ProviderUserInvite {providerUser.Id} {providerUser.Email} {nowMillis}");
- await _mailService.SendProviderInviteEmailAsync(provider.Name, providerUser, token, providerUser.Email);
- }
-
- private async Task HasConfirmedProviderAdminExceptAsync(Guid providerId, IEnumerable providerUserIds)
- {
- var providerAdmins = await _providerUserRepository.GetManyByProviderAsync(providerId,
- ProviderUserType.ProviderAdmin);
- var confirmedOwners = providerAdmins.Where(o => o.Status == ProviderUserStatusType.Confirmed);
- var confirmedOwnersIds = confirmedOwners.Select(u => u.Id);
- return confirmedOwnersIds.Except(providerUserIds).Any();
- }
-
- private void ThrowOnInvalidPlanType(PlanType requestedType)
- {
- if (ProviderDisllowedOrganizationTypes.Contains(requestedType))
- {
- throw new BadRequestException($"Providers cannot manage organizations with the requested plan type ({requestedType}). Only Teams and Enterprise accounts are allowed.");
- }
- }
- }
-}
diff --git a/bitwarden_license/src/CommCore/Utilities/ServiceCollectionExtensions.cs b/bitwarden_license/src/CommCore/Utilities/ServiceCollectionExtensions.cs
deleted file mode 100644
index bd8447120..000000000
--- a/bitwarden_license/src/CommCore/Utilities/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Bit.CommCore.Services;
-using Bit.Core.Services;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Bit.CommCore.Utilities
-{
- public static class ServiceCollectionExtensions
- {
- public static void AddCommCoreServices(this IServiceCollection services)
- {
- services.AddScoped();
- }
- }
-}
diff --git a/bitwarden_license/src/CommCore/CommCore.csproj b/bitwarden_license/src/Commercial.Core/Commercial.Core.csproj
similarity index 62%
rename from bitwarden_license/src/CommCore/CommCore.csproj
rename to bitwarden_license/src/Commercial.Core/Commercial.Core.csproj
index a562ebbe9..dfc63666d 100644
--- a/bitwarden_license/src/CommCore/CommCore.csproj
+++ b/bitwarden_license/src/Commercial.Core/Commercial.Core.csproj
@@ -1,9 +1,5 @@
-
- net5.0
-
-
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/CreateAccessPoliciesCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/CreateAccessPoliciesCommand.cs
new file mode 100644
index 000000000..05af497e6
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/CreateAccessPoliciesCommand.cs
@@ -0,0 +1,45 @@
+using Bit.Core.Exceptions;
+using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
+using Bit.Core.SecretsManager.Entities;
+using Bit.Core.SecretsManager.Repositories;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
+
+public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
+{
+ private readonly IAccessPolicyRepository _accessPolicyRepository;
+
+ public CreateAccessPoliciesCommand(IAccessPolicyRepository accessPolicyRepository)
+ {
+ _accessPolicyRepository = accessPolicyRepository;
+ }
+
+ public async Task> CreateAsync(List accessPolicies)
+ {
+ var distinctAccessPolicies = accessPolicies.DistinctBy(baseAccessPolicy =>
+ {
+ return baseAccessPolicy switch
+ {
+ UserProjectAccessPolicy ap => new Tuple(ap.OrganizationUserId, ap.GrantedProjectId),
+ GroupProjectAccessPolicy ap => new Tuple(ap.GroupId, ap.GrantedProjectId),
+ ServiceAccountProjectAccessPolicy ap => new Tuple(ap.ServiceAccountId, ap.GrantedProjectId),
+ _ => throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy))
+ };
+ }).ToList();
+
+ if (accessPolicies.Count != distinctAccessPolicies.Count)
+ {
+ throw new BadRequestException("Resources must be unique");
+ }
+
+ foreach (var accessPolicy in accessPolicies)
+ {
+ if (await _accessPolicyRepository.AccessPolicyExists(accessPolicy))
+ {
+ throw new BadRequestException("Resource already exists");
+ }
+ }
+
+ return await _accessPolicyRepository.CreateManyAsync(accessPolicies);
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/DeleteAccessPolicyCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/DeleteAccessPolicyCommand.cs
new file mode 100644
index 000000000..0795d6290
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/DeleteAccessPolicyCommand.cs
@@ -0,0 +1,27 @@
+using Bit.Core.Exceptions;
+using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
+using Bit.Core.SecretsManager.Repositories;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
+
+public class DeleteAccessPolicyCommand : IDeleteAccessPolicyCommand
+{
+ private readonly IAccessPolicyRepository _accessPolicyRepository;
+
+ public DeleteAccessPolicyCommand(IAccessPolicyRepository accessPolicyRepository)
+ {
+ _accessPolicyRepository = accessPolicyRepository;
+ }
+
+
+ public async Task DeleteAsync(Guid id)
+ {
+ var accessPolicy = await _accessPolicyRepository.GetByIdAsync(id);
+ if (accessPolicy == null)
+ {
+ throw new NotFoundException();
+ }
+
+ await _accessPolicyRepository.DeleteAsync(id);
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/UpdateAccessPolicyCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/UpdateAccessPolicyCommand.cs
new file mode 100644
index 000000000..b7ebd1706
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/UpdateAccessPolicyCommand.cs
@@ -0,0 +1,32 @@
+using Bit.Core.Exceptions;
+using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
+using Bit.Core.SecretsManager.Entities;
+using Bit.Core.SecretsManager.Repositories;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
+
+public class UpdateAccessPolicyCommand : IUpdateAccessPolicyCommand
+{
+ private readonly IAccessPolicyRepository _accessPolicyRepository;
+
+ public UpdateAccessPolicyCommand(IAccessPolicyRepository accessPolicyRepository)
+ {
+ _accessPolicyRepository = accessPolicyRepository;
+ }
+
+ public async Task UpdateAsync(Guid id, bool read, bool write)
+ {
+ var accessPolicy = await _accessPolicyRepository.GetByIdAsync(id);
+ if (accessPolicy == null)
+ {
+ throw new NotFoundException();
+ }
+
+ accessPolicy.Read = read;
+ accessPolicy.Write = write;
+ accessPolicy.RevisionDate = DateTime.UtcNow;
+
+ await _accessPolicyRepository.ReplaceAsync(accessPolicy);
+ return accessPolicy;
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessTokens/CreateAccessTokenCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessTokens/CreateAccessTokenCommand.cs
new file mode 100644
index 000000000..e8d179e79
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessTokens/CreateAccessTokenCommand.cs
@@ -0,0 +1,55 @@
+using Bit.Core.Context;
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
+using Bit.Core.SecretsManager.Entities;
+using Bit.Core.SecretsManager.Repositories;
+using Bit.Core.Utilities;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.AccessTokens;
+
+public class CreateAccessTokenCommand : ICreateAccessTokenCommand
+{
+ private readonly IApiKeyRepository _apiKeyRepository;
+ private readonly int _clientSecretMaxLength = 30;
+ private readonly ICurrentContext _currentContext;
+ private readonly IServiceAccountRepository _serviceAccountRepository;
+
+ public CreateAccessTokenCommand(
+ IApiKeyRepository apiKeyRepository,
+ ICurrentContext currentContext,
+ IServiceAccountRepository serviceAccountRepository)
+ {
+ _apiKeyRepository = apiKeyRepository;
+ _currentContext = currentContext;
+ _serviceAccountRepository = serviceAccountRepository;
+ }
+
+ public async Task CreateAsync(ApiKey apiKey, Guid userId)
+ {
+ if (apiKey.ServiceAccountId == null)
+ {
+ throw new BadRequestException();
+ }
+
+ var serviceAccount = await _serviceAccountRepository.GetByIdAsync(apiKey.ServiceAccountId.Value);
+ var orgAdmin = await _currentContext.OrganizationAdmin(serviceAccount.OrganizationId);
+ var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
+
+ var hasAccess = accessClient switch
+ {
+ AccessClientType.NoAccessCheck => true,
+ AccessClientType.User => await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(
+ apiKey.ServiceAccountId.Value, userId),
+ _ => false,
+ };
+
+ if (!hasAccess)
+ {
+ throw new UnauthorizedAccessException();
+ }
+
+ apiKey.ClientSecret = CoreHelpers.SecureRandomString(_clientSecretMaxLength);
+ return await _apiKeyRepository.CreateAsync(apiKey);
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/CreateProjectCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/CreateProjectCommand.cs
new file mode 100644
index 000000000..6e86088dc
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/CreateProjectCommand.cs
@@ -0,0 +1,20 @@
+using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
+using Bit.Core.SecretsManager.Entities;
+using Bit.Core.SecretsManager.Repositories;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.Projects;
+
+public class CreateProjectCommand : ICreateProjectCommand
+{
+ private readonly IProjectRepository _projectRepository;
+
+ public CreateProjectCommand(IProjectRepository projectRepository)
+ {
+ _projectRepository = projectRepository;
+ }
+
+ public async Task CreateAsync(Project project)
+ {
+ return await _projectRepository.CreateAsync(project);
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/DeleteProjectCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/DeleteProjectCommand.cs
new file mode 100644
index 000000000..8451f1d40
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/DeleteProjectCommand.cs
@@ -0,0 +1,75 @@
+using Bit.Core.Context;
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
+using Bit.Core.SecretsManager.Entities;
+using Bit.Core.SecretsManager.Repositories;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.Projects;
+
+public class DeleteProjectCommand : IDeleteProjectCommand
+{
+ private readonly IProjectRepository _projectRepository;
+ private readonly ICurrentContext _currentContext;
+
+ public DeleteProjectCommand(IProjectRepository projectRepository, ICurrentContext currentContext)
+ {
+ _projectRepository = projectRepository;
+ _currentContext = currentContext;
+ }
+
+ public async Task>> DeleteProjects(List ids, Guid userId)
+ {
+ if (ids.Any() != true || userId == new Guid())
+ {
+ throw new ArgumentNullException();
+ }
+
+ var projects = (await _projectRepository.GetManyByIds(ids))?.ToList();
+
+ if (projects?.Any() != true || projects.Count != ids.Count)
+ {
+ throw new NotFoundException();
+ }
+
+ // Ensure all projects belongs to the same organization
+ var organizationId = projects.First().OrganizationId;
+ if (projects.Any(p => p.OrganizationId != organizationId))
+ {
+ throw new UnauthorizedAccessException();
+ }
+
+ var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
+ var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
+
+ var results = new List>(projects.Count);
+ var deleteIds = new List();
+
+ foreach (var project in projects)
+ {
+ var hasAccess = accessClient switch
+ {
+ AccessClientType.NoAccessCheck => true,
+ AccessClientType.User => await _projectRepository.UserHasWriteAccessToProject(project.Id, userId),
+ _ => false,
+ };
+
+ if (!hasAccess)
+ {
+ results.Add(new Tuple(project, "access denied"));
+ }
+ else
+ {
+ results.Add(new Tuple(project, ""));
+ deleteIds.Add(project.Id);
+ }
+ }
+
+ if (deleteIds.Count > 0)
+ {
+ await _projectRepository.DeleteManyByIdAsync(deleteIds);
+ }
+ return results;
+ }
+}
+
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/UpdateProjectCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/UpdateProjectCommand.cs
new file mode 100644
index 000000000..74799547a
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/UpdateProjectCommand.cs
@@ -0,0 +1,50 @@
+using Bit.Core.Context;
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
+using Bit.Core.SecretsManager.Entities;
+using Bit.Core.SecretsManager.Repositories;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.Projects;
+
+public class UpdateProjectCommand : IUpdateProjectCommand
+{
+ private readonly IProjectRepository _projectRepository;
+ private readonly ICurrentContext _currentContext;
+
+ public UpdateProjectCommand(IProjectRepository projectRepository, ICurrentContext currentContext)
+ {
+ _projectRepository = projectRepository;
+ _currentContext = currentContext;
+ }
+
+ public async Task UpdateAsync(Project updatedProject, Guid userId)
+ {
+ var project = await _projectRepository.GetByIdAsync(updatedProject.Id);
+ if (project == null)
+ {
+ throw new NotFoundException();
+ }
+
+ var orgAdmin = await _currentContext.OrganizationAdmin(project.OrganizationId);
+ var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
+
+ var hasAccess = accessClient switch
+ {
+ AccessClientType.NoAccessCheck => true,
+ AccessClientType.User => await _projectRepository.UserHasWriteAccessToProject(updatedProject.Id, userId),
+ _ => false,
+ };
+
+ if (!hasAccess)
+ {
+ throw new UnauthorizedAccessException();
+ }
+
+ project.Name = updatedProject.Name;
+ project.RevisionDate = DateTime.UtcNow;
+
+ await _projectRepository.ReplaceAsync(project);
+ return project;
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/CreateSecretCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/CreateSecretCommand.cs
new file mode 100644
index 000000000..d224247c1
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/CreateSecretCommand.cs
@@ -0,0 +1,20 @@
+using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
+using Bit.Core.SecretsManager.Entities;
+using Bit.Core.SecretsManager.Repositories;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.Secrets;
+
+public class CreateSecretCommand : ICreateSecretCommand
+{
+ private readonly ISecretRepository _secretRepository;
+
+ public CreateSecretCommand(ISecretRepository secretRepository)
+ {
+ _secretRepository = secretRepository;
+ }
+
+ public async Task CreateAsync(Secret secret)
+ {
+ return await _secretRepository.CreateAsync(secret);
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/DeleteSecretCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/DeleteSecretCommand.cs
new file mode 100644
index 000000000..b04a8f911
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/DeleteSecretCommand.cs
@@ -0,0 +1,44 @@
+using Bit.Core.Exceptions;
+using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
+using Bit.Core.SecretsManager.Entities;
+using Bit.Core.SecretsManager.Repositories;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.Secrets;
+
+public class DeleteSecretCommand : IDeleteSecretCommand
+{
+ private readonly ISecretRepository _secretRepository;
+
+ public DeleteSecretCommand(ISecretRepository secretRepository)
+ {
+ _secretRepository = secretRepository;
+ }
+
+ public async Task>> DeleteSecrets(List ids)
+ {
+ var secrets = await _secretRepository.GetManyByIds(ids);
+
+ if (secrets?.Any() != true)
+ {
+ throw new NotFoundException();
+ }
+
+ var results = ids.Select(id =>
+ {
+ var secret = secrets.FirstOrDefault(secret => secret.Id == id);
+ if (secret == null)
+ {
+ throw new NotFoundException();
+ }
+ // TODO Once permissions are implemented add check for each secret here.
+ else
+ {
+ return new Tuple(secret, "");
+ }
+ }).ToList();
+
+ await _secretRepository.SoftDeleteManyByIdAsync(ids);
+ return results;
+ }
+}
+
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/UpdateSecretCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/UpdateSecretCommand.cs
new file mode 100644
index 000000000..4bd0cf4f3
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/UpdateSecretCommand.cs
@@ -0,0 +1,33 @@
+using Bit.Core.Exceptions;
+using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
+using Bit.Core.SecretsManager.Entities;
+using Bit.Core.SecretsManager.Repositories;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.Secrets;
+
+public class UpdateSecretCommand : IUpdateSecretCommand
+{
+ private readonly ISecretRepository _secretRepository;
+
+ public UpdateSecretCommand(ISecretRepository secretRepository)
+ {
+ _secretRepository = secretRepository;
+ }
+
+ public async Task UpdateAsync(Secret secret)
+ {
+ var existingSecret = await _secretRepository.GetByIdAsync(secret.Id);
+ if (existingSecret == null)
+ {
+ throw new NotFoundException();
+ }
+
+ secret.OrganizationId = existingSecret.OrganizationId;
+ secret.CreationDate = existingSecret.CreationDate;
+ secret.DeletedDate = existingSecret.DeletedDate;
+ secret.RevisionDate = DateTime.UtcNow;
+
+ await _secretRepository.UpdateAsync(secret);
+ return secret;
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/ServiceAccounts/CreateServiceAccountCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/ServiceAccounts/CreateServiceAccountCommand.cs
new file mode 100644
index 000000000..f3d379947
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/ServiceAccounts/CreateServiceAccountCommand.cs
@@ -0,0 +1,20 @@
+using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
+using Bit.Core.SecretsManager.Entities;
+using Bit.Core.SecretsManager.Repositories;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
+
+public class CreateServiceAccountCommand : ICreateServiceAccountCommand
+{
+ private readonly IServiceAccountRepository _serviceAccountRepository;
+
+ public CreateServiceAccountCommand(IServiceAccountRepository serviceAccountRepository)
+ {
+ _serviceAccountRepository = serviceAccountRepository;
+ }
+
+ public async Task CreateAsync(ServiceAccount serviceAccount)
+ {
+ return await _serviceAccountRepository.CreateAsync(serviceAccount);
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/ServiceAccounts/UpdateServiceAccountCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/ServiceAccounts/UpdateServiceAccountCommand.cs
new file mode 100644
index 000000000..efc77fc04
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/ServiceAccounts/UpdateServiceAccountCommand.cs
@@ -0,0 +1,50 @@
+using Bit.Core.Context;
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
+using Bit.Core.SecretsManager.Entities;
+using Bit.Core.SecretsManager.Repositories;
+
+namespace Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
+
+public class UpdateServiceAccountCommand : IUpdateServiceAccountCommand
+{
+ private readonly IServiceAccountRepository _serviceAccountRepository;
+ private readonly ICurrentContext _currentContext;
+
+ public UpdateServiceAccountCommand(IServiceAccountRepository serviceAccountRepository, ICurrentContext currentContext)
+ {
+ _serviceAccountRepository = serviceAccountRepository;
+ _currentContext = currentContext;
+ }
+
+ public async Task UpdateAsync(ServiceAccount updatedServiceAccount, Guid userId)
+ {
+ var serviceAccount = await _serviceAccountRepository.GetByIdAsync(updatedServiceAccount.Id);
+ if (serviceAccount == null)
+ {
+ throw new NotFoundException();
+ }
+
+ var orgAdmin = await _currentContext.OrganizationAdmin(serviceAccount.OrganizationId);
+ var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
+
+ var hasAccess = accessClient switch
+ {
+ AccessClientType.NoAccessCheck => true,
+ AccessClientType.User => await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(updatedServiceAccount.Id, userId),
+ _ => false,
+ };
+
+ if (!hasAccess)
+ {
+ throw new UnauthorizedAccessException();
+ }
+
+ serviceAccount.Name = updatedServiceAccount.Name;
+ serviceAccount.RevisionDate = DateTime.UtcNow;
+
+ await _serviceAccountRepository.ReplaceAsync(serviceAccount);
+ return serviceAccount;
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerCollectionExtensions.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerCollectionExtensions.cs
new file mode 100644
index 000000000..dc645d1a1
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerCollectionExtensions.cs
@@ -0,0 +1,32 @@
+using Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
+using Bit.Commercial.Core.SecretsManager.Commands.AccessTokens;
+using Bit.Commercial.Core.SecretsManager.Commands.Projects;
+using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
+using Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
+using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
+using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
+using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
+using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
+using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Bit.Commercial.Core.SecretsManager;
+
+public static class SecretsManagerCollectionExtensions
+{
+ public static void AddSecretsManagerServices(this IServiceCollection services)
+ {
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerServiceCollectionExtensions.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerServiceCollectionExtensions.cs
new file mode 100644
index 000000000..e6c733164
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerServiceCollectionExtensions.cs
@@ -0,0 +1,11 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Bit.Commercial.Core.SecretsManager;
+
+public static class SecretsManagerServiceCollectionExtensions
+{
+ public static void AddCommercialSecretsManagerServices(this IServiceCollection services)
+ {
+ services.AddSecretsManagerServices();
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/Services/ProviderService.cs b/bitwarden_license/src/Commercial.Core/Services/ProviderService.cs
new file mode 100644
index 000000000..cf3b68efa
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/Services/ProviderService.cs
@@ -0,0 +1,508 @@
+using Bit.Core.Context;
+using Bit.Core.Entities;
+using Bit.Core.Entities.Provider;
+using Bit.Core.Enums;
+using Bit.Core.Enums.Provider;
+using Bit.Core.Exceptions;
+using Bit.Core.Models.Business;
+using Bit.Core.Models.Business.Provider;
+using Bit.Core.Models.Data;
+using Bit.Core.Repositories;
+using Bit.Core.Services;
+using Bit.Core.Settings;
+using Bit.Core.Utilities;
+using Microsoft.AspNetCore.DataProtection;
+
+namespace Bit.Commercial.Core.Services;
+
+public class ProviderService : IProviderService
+{
+ public static PlanType[] ProviderDisllowedOrganizationTypes = new[] { PlanType.Free, PlanType.FamiliesAnnually, PlanType.FamiliesAnnually2019 };
+
+ private readonly IDataProtector _dataProtector;
+ private readonly IMailService _mailService;
+ private readonly IEventService _eventService;
+ private readonly GlobalSettings _globalSettings;
+ private readonly IProviderRepository _providerRepository;
+ private readonly IProviderUserRepository _providerUserRepository;
+ private readonly IProviderOrganizationRepository _providerOrganizationRepository;
+ private readonly IOrganizationRepository _organizationRepository;
+ private readonly IUserRepository _userRepository;
+ private readonly IUserService _userService;
+ private readonly IOrganizationService _organizationService;
+ private readonly ICurrentContext _currentContext;
+
+ public ProviderService(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository,
+ IProviderOrganizationRepository providerOrganizationRepository, IUserRepository userRepository,
+ IUserService userService, IOrganizationService organizationService, IMailService mailService,
+ IDataProtectionProvider dataProtectionProvider, IEventService eventService,
+ IOrganizationRepository organizationRepository, GlobalSettings globalSettings,
+ ICurrentContext currentContext)
+ {
+ _providerRepository = providerRepository;
+ _providerUserRepository = providerUserRepository;
+ _providerOrganizationRepository = providerOrganizationRepository;
+ _organizationRepository = organizationRepository;
+ _userRepository = userRepository;
+ _userService = userService;
+ _organizationService = organizationService;
+ _mailService = mailService;
+ _eventService = eventService;
+ _globalSettings = globalSettings;
+ _dataProtector = dataProtectionProvider.CreateProtector("ProviderServiceDataProtector");
+ _currentContext = currentContext;
+ }
+
+ public async Task CreateAsync(string ownerEmail)
+ {
+ var owner = await _userRepository.GetByEmailAsync(ownerEmail);
+ if (owner == null)
+ {
+ throw new BadRequestException("Invalid owner. Owner must be an existing Bitwarden user.");
+ }
+
+ var provider = new Provider
+ {
+ Status = ProviderStatusType.Pending,
+ Enabled = true,
+ UseEvents = true,
+ };
+ await _providerRepository.CreateAsync(provider);
+
+ var providerUser = new ProviderUser
+ {
+ ProviderId = provider.Id,
+ UserId = owner.Id,
+ Type = ProviderUserType.ProviderAdmin,
+ Status = ProviderUserStatusType.Confirmed,
+ };
+ await _providerUserRepository.CreateAsync(providerUser);
+ await SendProviderSetupInviteEmailAsync(provider, owner.Email);
+ }
+
+ public async Task CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key)
+ {
+ var owner = await _userService.GetUserByIdAsync(ownerUserId);
+ if (owner == null)
+ {
+ throw new BadRequestException("Invalid owner.");
+ }
+
+ if (provider.Status != ProviderStatusType.Pending)
+ {
+ throw new BadRequestException("Provider is already setup.");
+ }
+
+ if (!CoreHelpers.TokenIsValid("ProviderSetupInvite", _dataProtector, token, owner.Email, provider.Id,
+ _globalSettings.OrganizationInviteExpirationHours))
+ {
+ throw new BadRequestException("Invalid token.");
+ }
+
+ var providerUser = await _providerUserRepository.GetByProviderUserAsync(provider.Id, ownerUserId);
+ if (!(providerUser is { Type: ProviderUserType.ProviderAdmin }))
+ {
+ throw new BadRequestException("Invalid owner.");
+ }
+
+ provider.Status = ProviderStatusType.Created;
+ await _providerRepository.UpsertAsync(provider);
+
+ providerUser.Key = key;
+ await _providerUserRepository.ReplaceAsync(providerUser);
+
+ return provider;
+ }
+
+ public async Task UpdateAsync(Provider provider, bool updateBilling = false)
+ {
+ if (provider.Id == default)
+ {
+ throw new ArgumentException("Cannot create provider this way.");
+ }
+
+ await _providerRepository.ReplaceAsync(provider);
+ }
+
+ public async Task> InviteUserAsync(ProviderUserInvite invite)
+ {
+ if (!_currentContext.ProviderManageUsers(invite.ProviderId))
+ {
+ throw new InvalidOperationException("Invalid permissions.");
+ }
+
+ var emails = invite?.UserIdentifiers;
+ var invitingUser = await _providerUserRepository.GetByProviderUserAsync(invite.ProviderId, invite.InvitingUserId);
+
+ var provider = await _providerRepository.GetByIdAsync(invite.ProviderId);
+ if (provider == null || emails == null || !emails.Any())
+ {
+ throw new NotFoundException();
+ }
+
+ var providerUsers = new List();
+ foreach (var email in emails)
+ {
+ // Make sure user is not already invited
+ var existingProviderUserCount =
+ await _providerUserRepository.GetCountByProviderAsync(invite.ProviderId, email, false);
+ if (existingProviderUserCount > 0)
+ {
+ continue;
+ }
+
+ var providerUser = new ProviderUser
+ {
+ ProviderId = invite.ProviderId,
+ UserId = null,
+ Email = email.ToLowerInvariant(),
+ Key = null,
+ Type = invite.Type,
+ Status = ProviderUserStatusType.Invited,
+ CreationDate = DateTime.UtcNow,
+ RevisionDate = DateTime.UtcNow,
+ };
+
+ await _providerUserRepository.CreateAsync(providerUser);
+
+ await SendInviteAsync(providerUser, provider);
+ providerUsers.Add(providerUser);
+ }
+
+ await _eventService.LogProviderUsersEventAsync(providerUsers.Select(pu => (pu, EventType.ProviderUser_Invited, null as DateTime?)));
+
+ return providerUsers;
+ }
+
+ public async Task>> ResendInvitesAsync(ProviderUserInvite invite)
+ {
+ if (!_currentContext.ProviderManageUsers(invite.ProviderId))
+ {
+ throw new BadRequestException("Invalid permissions.");
+ }
+
+ var providerUsers = await _providerUserRepository.GetManyAsync(invite.UserIdentifiers);
+ var provider = await _providerRepository.GetByIdAsync(invite.ProviderId);
+
+ var result = new List>();
+ foreach (var providerUser in providerUsers)
+ {
+ if (providerUser.Status != ProviderUserStatusType.Invited || providerUser.ProviderId != invite.ProviderId)
+ {
+ result.Add(Tuple.Create(providerUser, "User invalid."));
+ continue;
+ }
+
+ await SendInviteAsync(providerUser, provider);
+ result.Add(Tuple.Create(providerUser, ""));
+ }
+
+ return result;
+ }
+
+ public async Task AcceptUserAsync(Guid providerUserId, User user, string token)
+ {
+ var providerUser = await _providerUserRepository.GetByIdAsync(providerUserId);
+ if (providerUser == null)
+ {
+ throw new BadRequestException("User invalid.");
+ }
+
+ if (providerUser.Status != ProviderUserStatusType.Invited)
+ {
+ throw new BadRequestException("Already accepted.");
+ }
+
+ if (!CoreHelpers.TokenIsValid("ProviderUserInvite", _dataProtector, token, user.Email, providerUser.Id,
+ _globalSettings.OrganizationInviteExpirationHours))
+ {
+ throw new BadRequestException("Invalid token.");
+ }
+
+ if (string.IsNullOrWhiteSpace(providerUser.Email) ||
+ !providerUser.Email.Equals(user.Email, StringComparison.InvariantCultureIgnoreCase))
+ {
+ throw new BadRequestException("User email does not match invite.");
+ }
+
+ providerUser.Status = ProviderUserStatusType.Accepted;
+ providerUser.UserId = user.Id;
+ providerUser.Email = null;
+
+ await _providerUserRepository.ReplaceAsync(providerUser);
+
+ return providerUser;
+ }
+
+ public async Task>> ConfirmUsersAsync(Guid providerId, Dictionary keys,
+ Guid confirmingUserId)
+ {
+ var providerUsers = await _providerUserRepository.GetManyAsync(keys.Keys);
+ var validProviderUsers = providerUsers
+ .Where(u => u.UserId != null)
+ .ToList();
+
+ if (!validProviderUsers.Any())
+ {
+ return new List>();
+ }
+
+ var validOrganizationUserIds = validProviderUsers.Select(u => u.UserId.Value).ToList();
+
+ var provider = await _providerRepository.GetByIdAsync(providerId);
+ var users = await _userRepository.GetManyAsync(validOrganizationUserIds);
+
+ var keyedFilteredUsers = validProviderUsers.ToDictionary(u => u.UserId.Value, u => u);
+
+ var result = new List>();
+ var events = new List<(ProviderUser, EventType, DateTime?)>();
+
+ foreach (var user in users)
+ {
+ if (!keyedFilteredUsers.ContainsKey(user.Id))
+ {
+ continue;
+ }
+ var providerUser = keyedFilteredUsers[user.Id];
+ try
+ {
+ if (providerUser.Status != ProviderUserStatusType.Accepted || providerUser.ProviderId != providerId)
+ {
+ throw new BadRequestException("Invalid user.");
+ }
+
+ providerUser.Status = ProviderUserStatusType.Confirmed;
+ providerUser.Key = keys[providerUser.Id];
+ providerUser.Email = null;
+
+ await _providerUserRepository.ReplaceAsync(providerUser);
+ events.Add((providerUser, EventType.ProviderUser_Confirmed, null));
+ await _mailService.SendProviderConfirmedEmailAsync(provider.Name, user.Email);
+ result.Add(Tuple.Create(providerUser, ""));
+ }
+ catch (BadRequestException e)
+ {
+ result.Add(Tuple.Create(providerUser, e.Message));
+ }
+ }
+
+ await _eventService.LogProviderUsersEventAsync(events);
+
+ return result;
+ }
+
+ public async Task SaveUserAsync(ProviderUser user, Guid savingUserId)
+ {
+ if (user.Id.Equals(default))
+ {
+ throw new BadRequestException("Invite the user first.");
+ }
+
+ if (user.Type != ProviderUserType.ProviderAdmin &&
+ !await HasConfirmedProviderAdminExceptAsync(user.ProviderId, new[] { user.Id }))
+ {
+ throw new BadRequestException("Provider must have at least one confirmed ProviderAdmin.");
+ }
+
+ await _providerUserRepository.ReplaceAsync(user);
+ await _eventService.LogProviderUserEventAsync(user, EventType.ProviderUser_Updated);
+ }
+
+ public async Task>> DeleteUsersAsync(Guid providerId,
+ IEnumerable providerUserIds, Guid deletingUserId)
+ {
+ var provider = await _providerRepository.GetByIdAsync(providerId);
+
+ if (provider == null)
+ {
+ throw new NotFoundException();
+ }
+
+ var providerUsers = await _providerUserRepository.GetManyAsync(providerUserIds);
+ var users = await _userRepository.GetManyAsync(providerUsers.Where(pu => pu.UserId.HasValue)
+ .Select(pu => pu.UserId.Value));
+ var keyedUsers = users.ToDictionary(u => u.Id);
+
+ if (!await HasConfirmedProviderAdminExceptAsync(providerId, providerUserIds))
+ {
+ throw new BadRequestException("Provider must have at least one confirmed ProviderAdmin.");
+ }
+
+ var result = new List>();
+ var deletedUserIds = new List();
+ var events = new List<(ProviderUser, EventType, DateTime?)>();
+
+ foreach (var providerUser in providerUsers)
+ {
+ try
+ {
+ if (providerUser.ProviderId != providerId)
+ {
+ throw new BadRequestException("Invalid user.");
+ }
+ if (providerUser.UserId == deletingUserId)
+ {
+ throw new BadRequestException("You cannot remove yourself.");
+ }
+
+ events.Add((providerUser, EventType.ProviderUser_Removed, null));
+
+ var user = keyedUsers.GetValueOrDefault(providerUser.UserId.GetValueOrDefault());
+ var email = user == null ? providerUser.Email : user.Email;
+ if (!string.IsNullOrWhiteSpace(email))
+ {
+ await _mailService.SendProviderUserRemoved(provider.Name, email);
+ }
+
+ result.Add(Tuple.Create(providerUser, ""));
+ deletedUserIds.Add(providerUser.Id);
+ }
+ catch (BadRequestException e)
+ {
+ result.Add(Tuple.Create(providerUser, e.Message));
+ }
+
+ await _providerUserRepository.DeleteManyAsync(deletedUserIds);
+ }
+
+ await _eventService.LogProviderUsersEventAsync(events);
+
+ return result;
+ }
+
+ public async Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key)
+ {
+ var po = await _providerOrganizationRepository.GetByOrganizationId(organizationId);
+ if (po != null)
+ {
+ throw new BadRequestException("Organization already belongs to a provider.");
+ }
+
+ var organization = await _organizationRepository.GetByIdAsync(organizationId);
+ ThrowOnInvalidPlanType(organization.PlanType);
+
+ var providerOrganization = new ProviderOrganization
+ {
+ ProviderId = providerId,
+ OrganizationId = organizationId,
+ Key = key,
+ };
+
+ await _providerOrganizationRepository.CreateAsync(providerOrganization);
+ await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_Added);
+ }
+
+ public async Task CreateOrganizationAsync(Guid providerId,
+ OrganizationSignup organizationSignup, string clientOwnerEmail, User user)
+ {
+ ThrowOnInvalidPlanType(organizationSignup.Plan);
+
+ var (organization, _) = await _organizationService.SignUpAsync(organizationSignup, true);
+
+ var providerOrganization = new ProviderOrganization
+ {
+ ProviderId = providerId,
+ OrganizationId = organization.Id,
+ Key = organizationSignup.OwnerKey,
+ };
+
+ await _providerOrganizationRepository.CreateAsync(providerOrganization);
+ await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_Created);
+
+ await _organizationService.InviteUsersAsync(organization.Id, user.Id,
+ new (OrganizationUserInvite, string)[]
+ {
+ (
+ new OrganizationUserInvite
+ {
+ Emails = new[] { clientOwnerEmail },
+ AccessAll = true,
+ Type = OrganizationUserType.Owner,
+ Permissions = null,
+ Collections = Array.Empty(),
+ },
+ null
+ )
+ });
+
+ return providerOrganization;
+ }
+
+ public async Task RemoveOrganizationAsync(Guid providerId, Guid providerOrganizationId, Guid removingUserId)
+ {
+ var providerOrganization = await _providerOrganizationRepository.GetByIdAsync(providerOrganizationId);
+ if (providerOrganization == null || providerOrganization.ProviderId != providerId)
+ {
+ throw new BadRequestException("Invalid organization.");
+ }
+
+ if (!await _organizationService.HasConfirmedOwnersExceptAsync(providerOrganization.OrganizationId, new Guid[] { }, includeProvider: false))
+ {
+ throw new BadRequestException("Organization needs to have at least one confirmed owner.");
+ }
+
+ await _providerOrganizationRepository.DeleteAsync(providerOrganization);
+ await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_Removed);
+ }
+
+ public async Task ResendProviderSetupInviteEmailAsync(Guid providerId, Guid ownerId)
+ {
+ var provider = await _providerRepository.GetByIdAsync(providerId);
+ var owner = await _userRepository.GetByIdAsync(ownerId);
+ if (owner == null)
+ {
+ throw new BadRequestException("Invalid owner.");
+ }
+ await SendProviderSetupInviteEmailAsync(provider, owner.Email);
+ }
+
+ private async Task SendProviderSetupInviteEmailAsync(Provider provider, string ownerEmail)
+ {
+ var token = _dataProtector.Protect($"ProviderSetupInvite {provider.Id} {ownerEmail} {CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow)}");
+ await _mailService.SendProviderSetupInviteEmailAsync(provider, token, ownerEmail);
+ }
+
+ public async Task LogProviderAccessToOrganizationAsync(Guid organizationId)
+ {
+ if (organizationId == default)
+ {
+ return;
+ }
+
+ var providerOrganization = await _providerOrganizationRepository.GetByOrganizationId(organizationId);
+ var organization = await _organizationRepository.GetByIdAsync(organizationId);
+ if (providerOrganization != null)
+ {
+ await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_VaultAccessed);
+ }
+ if (organization != null)
+ {
+ await _eventService.LogOrganizationEventAsync(organization, EventType.Organization_VaultAccessed);
+ }
+ }
+
+ private async Task SendInviteAsync(ProviderUser providerUser, Provider provider)
+ {
+ var nowMillis = CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow);
+ var token = _dataProtector.Protect(
+ $"ProviderUserInvite {providerUser.Id} {providerUser.Email} {nowMillis}");
+ await _mailService.SendProviderInviteEmailAsync(provider.Name, providerUser, token, providerUser.Email);
+ }
+
+ private async Task HasConfirmedProviderAdminExceptAsync(Guid providerId, IEnumerable providerUserIds)
+ {
+ var providerAdmins = await _providerUserRepository.GetManyByProviderAsync(providerId,
+ ProviderUserType.ProviderAdmin);
+ var confirmedOwners = providerAdmins.Where(o => o.Status == ProviderUserStatusType.Confirmed);
+ var confirmedOwnersIds = confirmedOwners.Select(u => u.Id);
+ return confirmedOwnersIds.Except(providerUserIds).Any();
+ }
+
+ private void ThrowOnInvalidPlanType(PlanType requestedType)
+ {
+ if (ProviderDisllowedOrganizationTypes.Contains(requestedType))
+ {
+ throw new BadRequestException($"Providers cannot manage organizations with the requested plan type ({requestedType}). Only Teams and Enterprise accounts are allowed.");
+ }
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/Utilities/ServiceCollectionExtensions.cs b/bitwarden_license/src/Commercial.Core/Utilities/ServiceCollectionExtensions.cs
new file mode 100644
index 000000000..6440f27ab
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/Utilities/ServiceCollectionExtensions.cs
@@ -0,0 +1,13 @@
+using Bit.Commercial.Core.Services;
+using Bit.Core.Services;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Bit.Commercial.Core.Utilities;
+
+public static class ServiceCollectionExtensions
+{
+ public static void AddCommercialCoreServices(this IServiceCollection services)
+ {
+ services.AddScoped();
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Core/packages.lock.json b/bitwarden_license/src/Commercial.Core/packages.lock.json
new file mode 100644
index 000000000..730790bc8
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Core/packages.lock.json
@@ -0,0 +1,2586 @@
+{
+ "version": 1,
+ "dependencies": {
+ "net6.0": {
+ "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"
+ }
+ },
+ "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.24.0",
+ "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
+ "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.6.0",
+ "contentHash": "EycyMsb6rD2PK9P0SyibFfEhvWWttdrYhyPF4f41uzdB/44yQlV+2Wehxyg489Rj6gbPvSPgbKq0xsHJBhipZA==",
+ "dependencies": {
+ "Azure.Core": "1.24.0",
+ "Microsoft.Identity.Client": "4.39.0",
+ "Microsoft.Identity.Client.Extensions.Msal": "2.19.3",
+ "System.Memory": "4.5.4",
+ "System.Security.Cryptography.ProtectedData": "4.7.0",
+ "System.Text.Json": "4.7.2",
+ "System.Threading.Tasks.Extensions": "4.5.4"
+ }
+ },
+ "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"
+ }
+ },
+ "Fido2": {
+ "type": "Transitive",
+ "resolved": "3.0.1",
+ "contentHash": "S0Bz1vfcKlO4Jase3AWp5XnQ746psf4oGx5kL+D2A10j1SsjoAOAIIpanSwfi0cEepDHgk1bClcOKY5TjOzGdA==",
+ "dependencies": {
+ "Fido2.Models": "3.0.1",
+ "Microsoft.Extensions.Http": "6.0.0",
+ "NSec.Cryptography": "22.4.0",
+ "System.Formats.Cbor": "6.0.0",
+ "System.IdentityModel.Tokens.Jwt": "6.17.0"
+ }
+ },
+ "Fido2.AspNet": {
+ "type": "Transitive",
+ "resolved": "3.0.1",
+ "contentHash": "5n5shEXD7RFUyTesjUHGDjkpgES7j4KotQo1GwUcS08k+fx+1tl/zCFHJ9RFDuUwO+S681ZILT2PyA67IPYpaA==",
+ "dependencies": {
+ "Fido2": "3.0.1",
+ "Fido2.Models": "3.0.1"
+ }
+ },
+ "Fido2.Models": {
+ "type": "Transitive",
+ "resolved": "3.0.1",
+ "contentHash": "mgjcuGETuYSCUEaZG+jQeeuuEMkDLc4GDJHBvKDdOz6oSOWp5adPdWP4btZx7Pi+9fu4szN3JIjJmby67MaILw=="
+ },
+ "Handlebars.Net": {
+ "type": "Transitive",
+ "resolved": "2.1.2",
+ "contentHash": "p60QyeBYpZmcZdIXRMqs9XySIBaxJ0lj3+QD0EJVr4ybTigOTCumXMMin5dPwjo9At1UwkDZ3gGwa1lmGjG6DA==",
+ "dependencies": {
+ "Microsoft.CSharp": "4.7.0"
+ }
+ },
+ "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"
+ }
+ },
+ "libsodium": {
+ "type": "Transitive",
+ "resolved": "1.0.18.2",
+ "contentHash": "flArHoVdscSzyV8ZdPV+bqqY2TTFlaN+xZf/vIqsmHI51KVcD/mOdUPaK3n/k/wGKz8dppiktXUqSmf3AXFgig=="
+ },
+ "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.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.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.CSharp": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
+ },
+ "Microsoft.Data.SqlClient": {
+ "type": "Transitive",
+ "resolved": "5.0.1",
+ "contentHash": "uu8dfrsx081cSbEevWuZAvqdmANDGJkbLBL2G3j0LAZxX1Oy8RCVAaC4Lcuak6jNicWP6CWvHqBTIEmQNSxQlw==",
+ "dependencies": {
+ "Azure.Identity": "1.6.0",
+ "Microsoft.Data.SqlClient.SNI.runtime": "5.0.1",
+ "Microsoft.Identity.Client": "4.45.0",
+ "Microsoft.IdentityModel.JsonWebTokens": "6.21.0",
+ "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.21.0",
+ "Microsoft.SqlServer.Server": "1.0.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": "5.0.1",
+ "contentHash": "y0X5MxiNdbITJYoafJ2ruaX6hqO0twpCGR/ipiDOe85JKLU8WL4TuAQfDe5qtt3bND5Je26HnrarLSAMMnVTNg=="
+ },
+ "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": "3.1.8",
+ "contentHash": "u04q7+tgc8l6pQ5HOcr6scgapkQQHnrhpGoCaaAZd24R36/NxGsGxuhSmhHOrQx9CsBLe2CVBN/4CkLlxtnnXw==",
+ "dependencies": {
+ "Microsoft.Extensions.Caching.Abstractions": "3.1.8",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.8",
+ "Microsoft.Extensions.Logging.Abstractions": "3.1.8",
+ "Microsoft.Extensions.Options": "3.1.8"
+ }
+ },
+ "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.Http": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
+ "Microsoft.Extensions.Logging": "6.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "6.0.0",
+ "Microsoft.Extensions.Options": "6.0.0"
+ }
+ },
+ "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.45.0",
+ "contentHash": "ircobISCLWbtE5eEoLKU+ldfZ8O41vg4lcy38KRj/znH17jvBiAl8oxcyNp89CsuqE3onxIpn21Ca7riyDDrRw==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Abstractions": "6.18.0"
+ }
+ },
+ "Microsoft.Identity.Client.Extensions.Msal": {
+ "type": "Transitive",
+ "resolved": "2.19.3",
+ "contentHash": "zVVZjn8aW7W79rC1crioDgdOwaFTQorsSO6RgVlDDjc7MvbEGz071wSNrjVhzR0CdQn6Sefx7Abf1o7vasmrLg==",
+ "dependencies": {
+ "Microsoft.Identity.Client": "4.38.0",
+ "System.Security.Cryptography.ProtectedData": "4.5.0"
+ }
+ },
+ "Microsoft.IdentityModel.Abstractions": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "XeE6LQtD719Qs2IG7HDi1TSw9LIkDbJ33xFiOBoHbApVw/8GpIBCbW+t7RwOjErUDyXZvjhZliwRkkLb8Z1uzg=="
+ },
+ "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.21.0",
+ "contentHash": "d3h1/BaMeylKTkdP6XwRCxuOoDJZ44V9xaXr6gl5QxmpnZGdoK3bySo3OQN8ehRLJHShb94ElLUvoXyglQtgAw==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Tokens": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Logging": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "tuEhHIQwvBEhMf8I50hy8FHmRSUkffDFP5EdLsSDV4qRcl2wvOPkQxYqEzWkh+ytW6sbdJGEXElGhmhDfAxAKg==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Abstractions": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Protocols": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "0FqY5cTLQKtHrClzHEI+QxJl8OBT2vUiEQQB7UKk832JDiJJmetzYZ3AdSrPjN/3l3nkhByeWzXnhrX0JbifKg==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Logging": "6.21.0",
+ "Microsoft.IdentityModel.Tokens": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Protocols.OpenIdConnect": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "vtSKL7n6EnAsLyxmiviusm6LKrblT2ndnNqN6rvVq6iIHAnPCK9E2DkDx6h1Jrpy1cvbp40r0cnTg23nhEAGTA==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Protocols": "6.21.0",
+ "System.IdentityModel.Tokens.Jwt": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Tokens": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "AAEHZvZyb597a+QJSmtxH3n2P1nIJGpZ4Q89GTenknRx6T6zyfzf592yW/jA5e8EHN4tNMjjXHQaYWEq5+L05w==",
+ "dependencies": {
+ "Microsoft.CSharp": "4.5.0",
+ "Microsoft.IdentityModel.Logging": "6.21.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.SqlServer.Server": {
+ "type": "Transitive",
+ "resolved": "1.0.0",
+ "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug=="
+ },
+ "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"
+ }
+ },
+ "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=="
+ },
+ "NSec.Cryptography": {
+ "type": "Transitive",
+ "resolved": "22.4.0",
+ "contentHash": "lEntcPYd7h3aZ8xxi/y/4TML7o8w0GEGqd+w4L1omqFLbdCBmhxJAeO2YBmv/fXbJKgKCQLm7+TD4bR605PEUQ==",
+ "dependencies": {
+ "libsodium": "[1.0.18.2, 1.0.19)"
+ }
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ },
+ "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.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=="
+ },
+ "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": "1.7.0",
+ "contentHash": "RVSM6wZUo6L2y6P3vN6gjUtyJ2IF2RVtrepF3J7nrDKfFQd5u/SnSUFclchYQis8/k5scHy9E+fVeKVQLnnkzw=="
+ },
+ "System.Collections.NonGeneric": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==",
+ "dependencies": {
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Globalization": "4.0.11",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Threading": "4.0.11"
+ }
+ },
+ "System.Collections.Specialized": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "/HKQyVP0yH1I0YtK7KJL/28snxHNH/bi+0lgk/+MbURF6ULhAE31MDI+NZDerNWu264YbxklXCCygISgm+HMug==",
+ "dependencies": {
+ "System.Collections.NonGeneric": "4.0.1",
+ "System.Globalization": "4.0.11",
+ "System.Globalization.Extensions": "4.0.1",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Threading": "4.0.11"
+ }
+ },
+ "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.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.0.0",
+ "contentHash": "6WVCczFZKXwpWpzd/iJkYnsmWTSFFiU24Xx/YdHXBcu+nFI/ehTgeqdJQFbtRPzbrO3KtRNjvkhtj4t5/WwWsA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.0.1",
+ "System.Collections": "4.0.11",
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Globalization": "4.0.11",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Threading": "4.0.11",
+ "runtime.native.System": "4.0.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.0.11",
+ "contentHash": "db34f6LHYM0U0JpE+sOmjar27BnqTVkbLJhgfwMpTdgTigG/Hna3m2MYVwnFzGGKnEJk2UXFuoVTr8WUbU91/A==",
+ "dependencies": {
+ "System.Collections": "4.0.11",
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Globalization": "4.0.11",
+ "System.Linq": "4.1.0",
+ "System.Linq.Expressions": "4.1.0",
+ "System.ObjectModel": "4.0.12",
+ "System.Reflection": "4.1.0",
+ "System.Reflection.Emit": "4.0.1",
+ "System.Reflection.Emit.ILGeneration": "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.Threading": "4.0.11"
+ }
+ },
+ "System.Formats.Asn1": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "T6fD00dQ3NTbPDy31m4eQUwKW84s03z0N2C8HpOklyeaDgaJPa/TexP4/SkORMSOwc7WhKifnA6Ya33AkzmafA=="
+ },
+ "System.Formats.Cbor": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "mGaLOoiw7KurJagOOcIsWUoCT5ACIiGxKlCcbYQASefBGXjnCcKTq5Hdjb94eEAKg38zXKlHw4c6EjzgBl9dIw=="
+ },
+ "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.21.0",
+ "contentHash": "JRD8AuypBE+2zYxT3dMJomQVsPYsCqlyZhWel3J1d5nzQokSRyTueF+Q4ID3Jcu6zSZKuzOdJ1MLTkbQsDqcvQ==",
+ "dependencies": {
+ "Microsoft.IdentityModel.JsonWebTokens": "6.21.0",
+ "Microsoft.IdentityModel.Tokens": "6.21.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.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"
+ }
+ },
+ "YubicoDotNetClient": {
+ "type": "Transitive",
+ "resolved": "1.2.0",
+ "contentHash": "uP5F3Ko1gqZi3lwS2R/jAAwhBxXs/6PKDpS6FdQjsBA5qmF0hQmbtfxM6QHTXOMoWbUtfetG7+LtgmG8T5zDIg==",
+ "dependencies": {
+ "NETStandard.Library": "1.6.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.1, )",
+ "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": "[5.0.1, )",
+ "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, )"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/Commercial.Infrastructure.EntityFramework.csproj b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/Commercial.Infrastructure.EntityFramework.csproj
new file mode 100644
index 000000000..7e94399c0
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/Commercial.Infrastructure.EntityFramework.csproj
@@ -0,0 +1,13 @@
+
+
+
+ enable
+
+
+
+
+
+
+
+
+
diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/AccessPolicyRepository.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/AccessPolicyRepository.cs
new file mode 100644
index 000000000..dd56e08fc
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/AccessPolicyRepository.cs
@@ -0,0 +1,184 @@
+using AutoMapper;
+using Bit.Core.SecretsManager.Repositories;
+using Bit.Infrastructure.EntityFramework.Repositories;
+using Bit.Infrastructure.EntityFramework.SecretsManager.Models;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Bit.Commercial.Infrastructure.EntityFramework.SecretsManager.Repositories;
+
+public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPolicyRepository
+{
+ public AccessPolicyRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) : base(serviceScopeFactory,
+ mapper)
+ {
+ }
+
+ public async Task> CreateManyAsync(List baseAccessPolicies)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ foreach (var baseAccessPolicy in baseAccessPolicies)
+ {
+ baseAccessPolicy.SetNewId();
+ switch (baseAccessPolicy)
+ {
+ case Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy:
+ {
+ var entity =
+ Mapper.Map(accessPolicy);
+ await dbContext.AddAsync(entity);
+ break;
+ }
+ case Core.SecretsManager.Entities.UserServiceAccountAccessPolicy accessPolicy:
+ {
+ var entity =
+ Mapper.Map(accessPolicy);
+ await dbContext.AddAsync(entity);
+ break;
+ }
+ case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy:
+ {
+ var entity = Mapper.Map(accessPolicy);
+ await dbContext.AddAsync(entity);
+ break;
+ }
+ case Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy accessPolicy:
+ {
+ var entity = Mapper.Map(accessPolicy);
+ await dbContext.AddAsync(entity);
+ break;
+ }
+ case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy:
+ {
+ var entity = Mapper.Map(accessPolicy);
+ await dbContext.AddAsync(entity);
+ break;
+ }
+ }
+ }
+
+ await dbContext.SaveChangesAsync();
+ return baseAccessPolicies;
+ }
+ }
+
+ public async Task AccessPolicyExists(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ switch (baseAccessPolicy)
+ {
+ case Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy:
+ {
+ var policy = await dbContext.UserProjectAccessPolicy
+ .Where(c => c.OrganizationUserId == accessPolicy.OrganizationUserId &&
+ c.GrantedProjectId == accessPolicy.GrantedProjectId)
+ .FirstOrDefaultAsync();
+ return policy != null;
+ }
+ case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy:
+ {
+ var policy = await dbContext.GroupProjectAccessPolicy
+ .Where(c => c.GroupId == accessPolicy.GroupId &&
+ c.GrantedProjectId == accessPolicy.GrantedProjectId)
+ .FirstOrDefaultAsync();
+ return policy != null;
+ }
+ case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy:
+ {
+ var policy = await dbContext.ServiceAccountProjectAccessPolicy
+ .Where(c => c.ServiceAccountId == accessPolicy.ServiceAccountId &&
+ c.GrantedProjectId == accessPolicy.GrantedProjectId)
+ .FirstOrDefaultAsync();
+ return policy != null;
+ }
+ default:
+ throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy));
+ }
+ }
+ }
+
+ public async Task GetByIdAsync(Guid id)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var entity = await dbContext.AccessPolicies.Where(ap => ap.Id == id)
+ .Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
+ .Include(ap => ((GroupProjectAccessPolicy)ap).Group)
+ .Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
+ .FirstOrDefaultAsync();
+
+ if (entity == null)
+ {
+ return null;
+ }
+
+ return MapToCore(entity);
+ }
+ }
+
+ public async Task ReplaceAsync(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var entity = await dbContext.AccessPolicies.FindAsync(baseAccessPolicy.Id);
+ if (entity != null)
+ {
+ dbContext.AccessPolicies.Attach(entity);
+ entity.Write = baseAccessPolicy.Write;
+ entity.Read = baseAccessPolicy.Read;
+ entity.RevisionDate = baseAccessPolicy.RevisionDate;
+ await dbContext.SaveChangesAsync();
+ }
+ }
+ }
+
+ public async Task?> GetManyByProjectId(Guid id)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+
+ var entities = await dbContext.AccessPolicies.Where(ap =>
+ ((UserProjectAccessPolicy)ap).GrantedProjectId == id ||
+ ((GroupProjectAccessPolicy)ap).GrantedProjectId == id ||
+ ((ServiceAccountProjectAccessPolicy)ap).GrantedProjectId == id)
+ .Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
+ .Include(ap => ((GroupProjectAccessPolicy)ap).Group)
+ .Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
+ .ToListAsync();
+
+ return !entities.Any() ? null : entities.Select(MapToCore);
+ }
+ }
+
+ public async Task DeleteAsync(Guid id)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var entity = await dbContext.AccessPolicies.FindAsync(id);
+ if (entity != null)
+ {
+ dbContext.Remove(entity);
+ await dbContext.SaveChangesAsync();
+ }
+ }
+ }
+
+ private Core.SecretsManager.Entities.BaseAccessPolicy MapToCore(BaseAccessPolicy baseAccessPolicyEntity)
+ {
+ return baseAccessPolicyEntity switch
+ {
+ UserProjectAccessPolicy ap => Mapper.Map(ap),
+ GroupProjectAccessPolicy ap => Mapper.Map(ap),
+ ServiceAccountProjectAccessPolicy ap => Mapper.Map(ap),
+ _ => throw new ArgumentException("Unsupported access policy type")
+ };
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs
new file mode 100644
index 000000000..bf35a47dc
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs
@@ -0,0 +1,106 @@
+using System.Linq.Expressions;
+using AutoMapper;
+using Bit.Core.Enums;
+using Bit.Core.SecretsManager.Repositories;
+using Bit.Infrastructure.EntityFramework.Repositories;
+using Bit.Infrastructure.EntityFramework.SecretsManager.Models;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Bit.Commercial.Infrastructure.EntityFramework.SecretsManager.Repositories;
+
+public class ProjectRepository : Repository, IProjectRepository
+{
+ public ProjectRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
+ : base(serviceScopeFactory, mapper, db => db.Project)
+ { }
+
+ public override async Task GetByIdAsync(Guid id)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var project = await dbContext.Project
+ .Where(c => c.Id == id && c.DeletedDate == null)
+ .FirstOrDefaultAsync();
+ return Mapper.Map(project);
+ }
+ }
+
+ public async Task> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId, AccessClientType accessType)
+ {
+ using var scope = ServiceScopeFactory.CreateScope();
+ var dbContext = GetDatabaseContext(scope);
+ var query = dbContext.Project.Where(p => p.OrganizationId == organizationId && p.DeletedDate == null);
+
+ query = accessType switch
+ {
+ AccessClientType.NoAccessCheck => query,
+ AccessClientType.User => query.Where(UserHasReadAccessToProject(userId)),
+ AccessClientType.ServiceAccount => query.Where(ServiceAccountHasReadAccessToProject(userId)),
+ _ => throw new ArgumentOutOfRangeException(nameof(accessType), accessType, null),
+ };
+
+ var projects = await query.OrderBy(p => p.RevisionDate).ToListAsync();
+ return Mapper.Map>(projects);
+ }
+
+ private static Expression> UserHasReadAccessToProject(Guid userId) => p =>
+ p.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) ||
+ p.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read));
+
+ private static Expression> UserHasWriteAccessToProject(Guid userId) => p =>
+ p.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Write) ||
+ p.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Write));
+
+ private static Expression> ServiceAccountHasReadAccessToProject(Guid serviceAccountId) => p =>
+ p.ServiceAccountAccessPolicies.Any(ap => ap.ServiceAccount.Id == serviceAccountId && ap.Read);
+
+ public async Task DeleteManyByIdAsync(IEnumerable ids)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var projects = dbContext.Project.Where(c => ids.Contains(c.Id));
+ await projects.ForEachAsync(project =>
+ {
+ dbContext.Remove(project);
+ });
+ await dbContext.SaveChangesAsync();
+ }
+ }
+
+ public async Task> GetManyByIds(IEnumerable ids)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var projects = await dbContext.Project
+ .Where(c => ids.Contains(c.Id) && c.DeletedDate == null)
+ .ToListAsync();
+ return Mapper.Map>(projects);
+ }
+ }
+
+ public async Task UserHasReadAccessToProject(Guid id, Guid userId)
+ {
+ using var scope = ServiceScopeFactory.CreateScope();
+ var dbContext = GetDatabaseContext(scope);
+ var query = dbContext.Project
+ .Where(p => p.Id == id)
+ .Where(UserHasReadAccessToProject(userId));
+
+ return await query.AnyAsync();
+ }
+
+ public async Task UserHasWriteAccessToProject(Guid id, Guid userId)
+ {
+ using var scope = ServiceScopeFactory.CreateScope();
+ var dbContext = GetDatabaseContext(scope);
+ var query = dbContext.Project
+ .Where(p => p.Id == id)
+ .Where(UserHasWriteAccessToProject(userId));
+
+ return await query.AnyAsync();
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/SecretRepository.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/SecretRepository.cs
new file mode 100644
index 000000000..a17af71e6
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/SecretRepository.cs
@@ -0,0 +1,139 @@
+using AutoMapper;
+using Bit.Core.SecretsManager.Repositories;
+using Bit.Infrastructure.EntityFramework;
+using Bit.Infrastructure.EntityFramework.Repositories;
+using Bit.Infrastructure.EntityFramework.SecretsManager.Models;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Bit.Commercial.Infrastructure.EntityFramework.SecretsManager.Repositories;
+
+public class SecretRepository : Repository, ISecretRepository
+{
+ public SecretRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
+ : base(serviceScopeFactory, mapper, db => db.Secret)
+ { }
+
+ public override async Task GetByIdAsync(Guid id)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var secret = await dbContext.Secret
+ .Include("Projects")
+ .Where(c => c.Id == id && c.DeletedDate == null)
+ .FirstOrDefaultAsync();
+ return Mapper.Map(secret);
+ }
+ }
+
+ public async Task> GetManyByIds(IEnumerable ids)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var secrets = await dbContext.Secret
+ .Where(c => ids.Contains(c.Id) && c.DeletedDate == null)
+ .ToListAsync();
+ return Mapper.Map>(secrets);
+ }
+ }
+
+ public async Task> GetManyByOrganizationIdAsync(Guid organizationId)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var secrets = await dbContext.Secret
+ .Where(c => c.OrganizationId == organizationId && c.DeletedDate == null)
+ .Include("Projects")
+ .OrderBy(c => c.RevisionDate)
+ .ToListAsync();
+
+ return Mapper.Map>(secrets);
+ }
+ }
+
+ public async Task> GetManyByProjectIdAsync(Guid projectId)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var secrets = await dbContext.Secret
+ .Where(s => s.Projects.Any(p => p.Id == projectId) && s.DeletedDate == null).Include("Projects")
+ .OrderBy(s => s.RevisionDate).ToListAsync();
+
+ return Mapper.Map>(secrets);
+ }
+ }
+
+ public override async Task CreateAsync(Core.SecretsManager.Entities.Secret secret)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ secret.SetNewId();
+ var entity = Mapper.Map(secret);
+
+ if (secret.Projects?.Count > 0)
+ {
+ foreach (var p in entity.Projects)
+ {
+ dbContext.Attach(p);
+ }
+ }
+
+ await dbContext.AddAsync(entity);
+ await dbContext.SaveChangesAsync();
+ secret.Id = entity.Id;
+ return secret;
+ }
+ }
+
+ public async Task UpdateAsync(Core.SecretsManager.Entities.Secret secret)
+ {
+
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var mappedEntity = Mapper.Map(secret);
+ var entity = await dbContext.Secret
+ .Include("Projects")
+ .FirstAsync(s => s.Id == secret.Id);
+
+ foreach (var p in entity.Projects?.Where(p => mappedEntity.Projects.All(mp => mp.Id != p.Id)))
+ {
+ entity.Projects.Remove(p);
+ }
+
+ // Add new relationships
+ foreach (var project in mappedEntity.Projects?.Where(p => entity.Projects.All(ep => ep.Id != p.Id)))
+ {
+ var p = dbContext.AttachToOrGet(_ => _.Id == project.Id, () => project);
+ entity.Projects.Add(p);
+ }
+
+ dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity);
+ await dbContext.SaveChangesAsync();
+ }
+
+ return secret;
+ }
+
+ public async Task SoftDeleteManyByIdAsync(IEnumerable ids)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var dbContext = GetDatabaseContext(scope);
+ var utcNow = DateTime.UtcNow;
+ var secrets = dbContext.Secret.Where(c => ids.Contains(c.Id));
+ await secrets.ForEachAsync(secret =>
+ {
+ dbContext.Attach(secret);
+ secret.DeletedDate = utcNow;
+ secret.RevisionDate = utcNow;
+ });
+ await dbContext.SaveChangesAsync();
+ }
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ServiceAccountRepository.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ServiceAccountRepository.cs
new file mode 100644
index 000000000..4632abc8f
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ServiceAccountRepository.cs
@@ -0,0 +1,64 @@
+using System.Linq.Expressions;
+using AutoMapper;
+using Bit.Core.Enums;
+using Bit.Core.SecretsManager.Repositories;
+using Bit.Infrastructure.EntityFramework.Repositories;
+using Bit.Infrastructure.EntityFramework.SecretsManager.Models;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Bit.Commercial.Infrastructure.EntityFramework.SecretsManager.Repositories;
+
+public class ServiceAccountRepository : Repository, IServiceAccountRepository
+{
+ public ServiceAccountRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
+ : base(serviceScopeFactory, mapper, db => db.ServiceAccount)
+ { }
+
+ public async Task> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId, AccessClientType accessType)
+ {
+ using var scope = ServiceScopeFactory.CreateScope();
+ var dbContext = GetDatabaseContext(scope);
+ var query = dbContext.ServiceAccount.Where(c => c.OrganizationId == organizationId);
+
+ query = accessType switch
+ {
+ AccessClientType.NoAccessCheck => query,
+ AccessClientType.User => query.Where(UserHasReadAccessToServiceAccount(userId)),
+ _ => throw new ArgumentOutOfRangeException(nameof(accessType), accessType, null),
+ };
+
+ var serviceAccounts = await query.OrderBy(c => c.RevisionDate).ToListAsync();
+ return Mapper.Map>(serviceAccounts);
+ }
+
+ public async Task UserHasReadAccessToServiceAccount(Guid id, Guid userId)
+ {
+ using var scope = ServiceScopeFactory.CreateScope();
+ var dbContext = GetDatabaseContext(scope);
+ var query = dbContext.ServiceAccount
+ .Where(sa => sa.Id == id)
+ .Where(UserHasReadAccessToServiceAccount(userId));
+
+ return await query.AnyAsync();
+ }
+
+ public async Task UserHasWriteAccessToServiceAccount(Guid id, Guid userId)
+ {
+ using var scope = ServiceScopeFactory.CreateScope();
+ var dbContext = GetDatabaseContext(scope);
+ var query = dbContext.ServiceAccount
+ .Where(sa => sa.Id == id)
+ .Where(UserHasWriteAccessToServiceAccount(userId));
+
+ return await query.AnyAsync();
+ }
+
+ private static Expression> UserHasReadAccessToServiceAccount(Guid userId) => sa =>
+ sa.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) ||
+ sa.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read));
+
+ private static Expression> UserHasWriteAccessToServiceAccount(Guid userId) => sa =>
+ sa.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Write) ||
+ sa.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Write));
+}
diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/SecretsManagerEFServiceCollectionExtensions.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/SecretsManagerEFServiceCollectionExtensions.cs
new file mode 100644
index 000000000..d6c884807
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/SecretsManagerEFServiceCollectionExtensions.cs
@@ -0,0 +1,16 @@
+using Bit.Commercial.Infrastructure.EntityFramework.SecretsManager.Repositories;
+using Bit.Core.SecretsManager.Repositories;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Bit.Commercial.Infrastructure.EntityFramework.SecretsManager;
+
+public static class SecretsManagerEfServiceCollectionExtensions
+{
+ public static void AddSecretsManagerEfRepositories(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ }
+}
diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/packages.lock.json b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/packages.lock.json
new file mode 100644
index 000000000..f2b6cb0b2
--- /dev/null
+++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/packages.lock.json
@@ -0,0 +1,2772 @@
+{
+ "version": 1,
+ "dependencies": {
+ "net6.0": {
+ "AutoMapper.Extensions.Microsoft.DependencyInjection": {
+ "type": "Direct",
+ "requested": "[11.0.0, )",
+ "resolved": "11.0.0",
+ "contentHash": "0asw5WxdCFh2OTi9Gv+oKyH9SzxwYQSnO8TV5Dd0GggovILzJW4UimP26JAcxc3yB5NnC5urooZ1BBs8ElpiBw==",
+ "dependencies": {
+ "AutoMapper": "11.0.0",
+ "Microsoft.Extensions.Options": "6.0.0"
+ }
+ },
+ "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"
+ }
+ },
+ "AutoMapper": {
+ "type": "Transitive",
+ "resolved": "11.0.0",
+ "contentHash": "+596AnKykYCk9RxXCEF4GYuapSebQtFVvIA1oVG1rrRkCLAC7AkWehJ0brCfYUbdDW3v1H/p0W3hob7JoXGjMw==",
+ "dependencies": {
+ "Microsoft.CSharp": "4.7.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.24.0",
+ "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
+ "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.6.0",
+ "contentHash": "EycyMsb6rD2PK9P0SyibFfEhvWWttdrYhyPF4f41uzdB/44yQlV+2Wehxyg489Rj6gbPvSPgbKq0xsHJBhipZA==",
+ "dependencies": {
+ "Azure.Core": "1.24.0",
+ "Microsoft.Identity.Client": "4.39.0",
+ "Microsoft.Identity.Client.Extensions.Msal": "2.19.3",
+ "System.Memory": "4.5.4",
+ "System.Security.Cryptography.ProtectedData": "4.7.0",
+ "System.Text.Json": "4.7.2",
+ "System.Threading.Tasks.Extensions": "4.5.4"
+ }
+ },
+ "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"
+ }
+ },
+ "Fido2": {
+ "type": "Transitive",
+ "resolved": "3.0.1",
+ "contentHash": "S0Bz1vfcKlO4Jase3AWp5XnQ746psf4oGx5kL+D2A10j1SsjoAOAIIpanSwfi0cEepDHgk1bClcOKY5TjOzGdA==",
+ "dependencies": {
+ "Fido2.Models": "3.0.1",
+ "Microsoft.Extensions.Http": "6.0.0",
+ "NSec.Cryptography": "22.4.0",
+ "System.Formats.Cbor": "6.0.0",
+ "System.IdentityModel.Tokens.Jwt": "6.17.0"
+ }
+ },
+ "Fido2.AspNet": {
+ "type": "Transitive",
+ "resolved": "3.0.1",
+ "contentHash": "5n5shEXD7RFUyTesjUHGDjkpgES7j4KotQo1GwUcS08k+fx+1tl/zCFHJ9RFDuUwO+S681ZILT2PyA67IPYpaA==",
+ "dependencies": {
+ "Fido2": "3.0.1",
+ "Fido2.Models": "3.0.1"
+ }
+ },
+ "Fido2.Models": {
+ "type": "Transitive",
+ "resolved": "3.0.1",
+ "contentHash": "mgjcuGETuYSCUEaZG+jQeeuuEMkDLc4GDJHBvKDdOz6oSOWp5adPdWP4btZx7Pi+9fu4szN3JIjJmby67MaILw=="
+ },
+ "Handlebars.Net": {
+ "type": "Transitive",
+ "resolved": "2.1.2",
+ "contentHash": "p60QyeBYpZmcZdIXRMqs9XySIBaxJ0lj3+QD0EJVr4ybTigOTCumXMMin5dPwjo9At1UwkDZ3gGwa1lmGjG6DA==",
+ "dependencies": {
+ "Microsoft.CSharp": "4.7.0"
+ }
+ },
+ "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"
+ }
+ },
+ "libsodium": {
+ "type": "Transitive",
+ "resolved": "1.0.18.2",
+ "contentHash": "flArHoVdscSzyV8ZdPV+bqqY2TTFlaN+xZf/vIqsmHI51KVcD/mOdUPaK3n/k/wGKz8dppiktXUqSmf3AXFgig=="
+ },
+ "linq2db": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "6/u1EzQlV25bhN0Ej/I5dLV5Hgxun+ww/TX2VnMBnSVytED2VzQGeFIO/14I624GkfPOtB79x1ooL3F18dAbdw=="
+ },
+ "linq2db.EntityFrameworkCore": {
+ "type": "Transitive",
+ "resolved": "6.11.0",
+ "contentHash": "mS+L6HyVHP3oJaTuFVZswNmPpTfYleGjDTo2IWBJmYXZpSQ5EPw1DpHzmyLAiQd+93ofy0Ala+9HWzv6/k73ZQ==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore.Relational": "6.0.5",
+ "linq2db": "4.4.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.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.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.CSharp": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
+ },
+ "Microsoft.Data.SqlClient": {
+ "type": "Transitive",
+ "resolved": "5.0.1",
+ "contentHash": "uu8dfrsx081cSbEevWuZAvqdmANDGJkbLBL2G3j0LAZxX1Oy8RCVAaC4Lcuak6jNicWP6CWvHqBTIEmQNSxQlw==",
+ "dependencies": {
+ "Azure.Identity": "1.6.0",
+ "Microsoft.Data.SqlClient.SNI.runtime": "5.0.1",
+ "Microsoft.Identity.Client": "4.45.0",
+ "Microsoft.IdentityModel.JsonWebTokens": "6.21.0",
+ "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.21.0",
+ "Microsoft.SqlServer.Server": "1.0.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": "5.0.1",
+ "contentHash": "y0X5MxiNdbITJYoafJ2ruaX6hqO0twpCGR/ipiDOe85JKLU8WL4TuAQfDe5qtt3bND5Je26HnrarLSAMMnVTNg=="
+ },
+ "Microsoft.Data.Sqlite.Core": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "bui5wPPqq9OwTL5A+YJPcVStTPrOFcLwg/kAVWyqdjrTief4kTK/3bNv0MqUDVNgAUG8pcFbtdc674CIh1F3gw==",
+ "dependencies": {
+ "SQLitePCLRaw.core": "2.1.2"
+ }
+ },
+ "Microsoft.EntityFrameworkCore": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "xb10XFoPf/gWu8ik5v7xnVyUY7W21LBOLtT7PidzwYVdnE3aKuQ/bIZLcQuY7rdDNT89/wse2q5FRjm207cIMQ==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore.Abstractions": "6.0.12",
+ "Microsoft.EntityFrameworkCore.Analyzers": "6.0.12",
+ "Microsoft.Extensions.Caching.Memory": "6.0.1",
+ "Microsoft.Extensions.DependencyInjection": "6.0.1",
+ "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.12",
+ "contentHash": "hvRytAcLhrb35HmtMjYWsNZZLt39ryuN7j04lDchRa9VToreyqgo5gMniTdQ6MfCflxtGnDes65V/Y2pjbEyWg=="
+ },
+ "Microsoft.EntityFrameworkCore.Analyzers": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "ZDUY+KlsIyKdfvIJeNdqRiPExFQ5GRZVdx/Cp52vhpCJRImYv34O0Xfmw2eiLu4qe1jmM2pTzAAFKELaKwtj/w=="
+ },
+ "Microsoft.EntityFrameworkCore.Relational": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "HBtRGHtF0Vf+BIQTkRGiopmE5rLYhj59xPpd17S1tLgYpiHDVbepCuHwh5H63fzjO99Z4tW5wmmEGF7KnD91WQ==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore": "6.0.12",
+ "Microsoft.Extensions.Configuration.Abstractions": "6.0.0"
+ }
+ },
+ "Microsoft.EntityFrameworkCore.Sqlite": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "2Hutlqt07bnWZFtYqT1lj0otX8ygMyBikysGnfQNF2TK3i5GqSTeJ8tqNi/URiI9II7Cyl15A0rflXmFoySuIw==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore.Sqlite.Core": "6.0.12",
+ "SQLitePCLRaw.bundle_e_sqlite3": "2.1.2"
+ }
+ },
+ "Microsoft.EntityFrameworkCore.Sqlite.Core": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "07vKE7+t9Z2BfGmHuJwNZNv8m1GWt7ZpYYHFh1tQg1oC6FJ78bSaFzLawsf2NK6CLhbB8DBsjE0rRhxMJ4rXsA==",
+ "dependencies": {
+ "Microsoft.Data.Sqlite.Core": "6.0.12",
+ "Microsoft.EntityFrameworkCore.Relational": "6.0.12",
+ "Microsoft.Extensions.DependencyModel": "6.0.0"
+ }
+ },
+ "Microsoft.EntityFrameworkCore.SqlServer": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "bdKnSz1w+WZz9QYWhs3wwGuMn4YssjdR+HOBpzChQ6C3+dblq4Pammm5fzugcPOhTgCiWftOT2jPOT5hEy4bYg==",
+ "dependencies": {
+ "Microsoft.Data.SqlClient": "2.1.4",
+ "Microsoft.EntityFrameworkCore.Relational": "6.0.12"
+ }
+ },
+ "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.1",
+ "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==",
+ "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": "6.0.0",
+ "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==",
+ "dependencies": {
+ "System.Buffers": "4.5.1",
+ "System.Memory": "4.5.4",
+ "System.Runtime.CompilerServices.Unsafe": "6.0.0",
+ "System.Text.Encodings.Web": "6.0.0",
+ "System.Text.Json": "6.0.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.Http": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
+ "Microsoft.Extensions.Logging": "6.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "6.0.0",
+ "Microsoft.Extensions.Options": "6.0.0"
+ }
+ },
+ "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.45.0",
+ "contentHash": "ircobISCLWbtE5eEoLKU+ldfZ8O41vg4lcy38KRj/znH17jvBiAl8oxcyNp89CsuqE3onxIpn21Ca7riyDDrRw==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Abstractions": "6.18.0"
+ }
+ },
+ "Microsoft.Identity.Client.Extensions.Msal": {
+ "type": "Transitive",
+ "resolved": "2.19.3",
+ "contentHash": "zVVZjn8aW7W79rC1crioDgdOwaFTQorsSO6RgVlDDjc7MvbEGz071wSNrjVhzR0CdQn6Sefx7Abf1o7vasmrLg==",
+ "dependencies": {
+ "Microsoft.Identity.Client": "4.38.0",
+ "System.Security.Cryptography.ProtectedData": "4.5.0"
+ }
+ },
+ "Microsoft.IdentityModel.Abstractions": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "XeE6LQtD719Qs2IG7HDi1TSw9LIkDbJ33xFiOBoHbApVw/8GpIBCbW+t7RwOjErUDyXZvjhZliwRkkLb8Z1uzg=="
+ },
+ "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.21.0",
+ "contentHash": "d3h1/BaMeylKTkdP6XwRCxuOoDJZ44V9xaXr6gl5QxmpnZGdoK3bySo3OQN8ehRLJHShb94ElLUvoXyglQtgAw==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Tokens": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Logging": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "tuEhHIQwvBEhMf8I50hy8FHmRSUkffDFP5EdLsSDV4qRcl2wvOPkQxYqEzWkh+ytW6sbdJGEXElGhmhDfAxAKg==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Abstractions": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Protocols": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "0FqY5cTLQKtHrClzHEI+QxJl8OBT2vUiEQQB7UKk832JDiJJmetzYZ3AdSrPjN/3l3nkhByeWzXnhrX0JbifKg==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Logging": "6.21.0",
+ "Microsoft.IdentityModel.Tokens": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Protocols.OpenIdConnect": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "vtSKL7n6EnAsLyxmiviusm6LKrblT2ndnNqN6rvVq6iIHAnPCK9E2DkDx6h1Jrpy1cvbp40r0cnTg23nhEAGTA==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Protocols": "6.21.0",
+ "System.IdentityModel.Tokens.Jwt": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Tokens": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "AAEHZvZyb597a+QJSmtxH3n2P1nIJGpZ4Q89GTenknRx6T6zyfzf592yW/jA5e8EHN4tNMjjXHQaYWEq5+L05w==",
+ "dependencies": {
+ "Microsoft.CSharp": "4.5.0",
+ "Microsoft.IdentityModel.Logging": "6.21.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.SqlServer.Server": {
+ "type": "Transitive",
+ "resolved": "1.0.0",
+ "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug=="
+ },
+ "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.8",
+ "contentHash": "wKa8MJEJaj0xQXUQZGv7q/KfPID23jSSvFFtljMworrv7dNajr0GN8PCU1SpywqHjMWdYEfK29DY1aYbiISbQg==",
+ "dependencies": {
+ "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+ }
+ },
+ "Npgsql.EntityFrameworkCore.PostgreSQL": {
+ "type": "Transitive",
+ "resolved": "6.0.8",
+ "contentHash": "YJRpO+3wXQyWuwRUCVJj/Rsn46sY0bZ6uCGOEFApiRe0ZYJ6N6TxZUWKbTNJYjesickcLGzynOerpSbDJX1AYg==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore": "6.0.12",
+ "Microsoft.EntityFrameworkCore.Abstractions": "6.0.12",
+ "Microsoft.EntityFrameworkCore.Relational": "6.0.12",
+ "Npgsql": "6.0.8"
+ }
+ },
+ "NSec.Cryptography": {
+ "type": "Transitive",
+ "resolved": "22.4.0",
+ "contentHash": "lEntcPYd7h3aZ8xxi/y/4TML7o8w0GEGqd+w4L1omqFLbdCBmhxJAeO2YBmv/fXbJKgKCQLm7+TD4bR605PEUQ==",
+ "dependencies": {
+ "libsodium": "[1.0.18.2, 1.0.19)"
+ }
+ },
+ "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.2",
+ "contentHash": "KvlZ800CnEuEGnxj5OT1fCKGjQXxW5kpPlCP91JqBYG+2Z3927eqXmlX6LLKUt4swqE8ZsEQ+Zkpab8bqstf4g==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore.Relational": "[6.0.7, 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"
+ }
+ },
+ "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.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=="
+ },
+ "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"
+ }
+ },
+ "SQLitePCLRaw.bundle_e_sqlite3": {
+ "type": "Transitive",
+ "resolved": "2.1.2",
+ "contentHash": "ilkvNhrTersLmIVAcDwwPqfhUFCg19Z1GVMvCSi3xk6Akq94f4qadLORQCq/T8+9JgMiPs+F/NECw5uauviaNw==",
+ "dependencies": {
+ "SQLitePCLRaw.lib.e_sqlite3": "2.1.2",
+ "SQLitePCLRaw.provider.e_sqlite3": "2.1.2"
+ }
+ },
+ "SQLitePCLRaw.core": {
+ "type": "Transitive",
+ "resolved": "2.1.2",
+ "contentHash": "A8EBepVqY2lnAp3a8jnhbgzF2tlj2S3HcJQGANTYg/TbYbKa8Z5cM1h74An/vy0svhfzT7tVY0sFmUglLgv+2g==",
+ "dependencies": {
+ "System.Memory": "4.5.3"
+ }
+ },
+ "SQLitePCLRaw.lib.e_sqlite3": {
+ "type": "Transitive",
+ "resolved": "2.1.2",
+ "contentHash": "zibGtku8M4Eea1R3ZCAxc86QbNvyEN17mAcQkvWKBuHvRpMiK2g5anG4R5Be7cWKSd1i6baYz8y4dMMAKcXKPg=="
+ },
+ "SQLitePCLRaw.provider.e_sqlite3": {
+ "type": "Transitive",
+ "resolved": "2.1.2",
+ "contentHash": "lxCZarZdvAsMl2zw9bXHrXK6RxVhB4b23iTFhCOdHFhxfbsxLxWf+ocvswJwR/9Wh/E//ddMi+wJGqUKV7VwoA==",
+ "dependencies": {
+ "SQLitePCLRaw.core": "2.1.2"
+ }
+ },
+ "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.0.1",
+ "contentHash": "hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==",
+ "dependencies": {
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Globalization": "4.0.11",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Threading": "4.0.11"
+ }
+ },
+ "System.Collections.Specialized": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "/HKQyVP0yH1I0YtK7KJL/28snxHNH/bi+0lgk/+MbURF6ULhAE31MDI+NZDerNWu264YbxklXCCygISgm+HMug==",
+ "dependencies": {
+ "System.Collections.NonGeneric": "4.0.1",
+ "System.Globalization": "4.0.11",
+ "System.Globalization.Extensions": "4.0.1",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Threading": "4.0.11"
+ }
+ },
+ "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.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.0.0",
+ "contentHash": "6WVCczFZKXwpWpzd/iJkYnsmWTSFFiU24Xx/YdHXBcu+nFI/ehTgeqdJQFbtRPzbrO3KtRNjvkhtj4t5/WwWsA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.0.1",
+ "System.Collections": "4.0.11",
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Globalization": "4.0.11",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Threading": "4.0.11",
+ "runtime.native.System": "4.0.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.0.11",
+ "contentHash": "db34f6LHYM0U0JpE+sOmjar27BnqTVkbLJhgfwMpTdgTigG/Hna3m2MYVwnFzGGKnEJk2UXFuoVTr8WUbU91/A==",
+ "dependencies": {
+ "System.Collections": "4.0.11",
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Globalization": "4.0.11",
+ "System.Linq": "4.1.0",
+ "System.Linq.Expressions": "4.1.0",
+ "System.ObjectModel": "4.0.12",
+ "System.Reflection": "4.1.0",
+ "System.Reflection.Emit": "4.0.1",
+ "System.Reflection.Emit.ILGeneration": "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.Threading": "4.0.11"
+ }
+ },
+ "System.Formats.Asn1": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "T6fD00dQ3NTbPDy31m4eQUwKW84s03z0N2C8HpOklyeaDgaJPa/TexP4/SkORMSOwc7WhKifnA6Ya33AkzmafA=="
+ },
+ "System.Formats.Cbor": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "mGaLOoiw7KurJagOOcIsWUoCT5ACIiGxKlCcbYQASefBGXjnCcKTq5Hdjb94eEAKg38zXKlHw4c6EjzgBl9dIw=="
+ },
+ "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.21.0",
+ "contentHash": "JRD8AuypBE+2zYxT3dMJomQVsPYsCqlyZhWel3J1d5nzQokSRyTueF+Q4ID3Jcu6zSZKuzOdJ1MLTkbQsDqcvQ==",
+ "dependencies": {
+ "Microsoft.IdentityModel.JsonWebTokens": "6.21.0",
+ "Microsoft.IdentityModel.Tokens": "6.21.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.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"
+ }
+ },
+ "YubicoDotNetClient": {
+ "type": "Transitive",
+ "resolved": "1.2.0",
+ "contentHash": "uP5F3Ko1gqZi3lwS2R/jAAwhBxXs/6PKDpS6FdQjsBA5qmF0hQmbtfxM6QHTXOMoWbUtfetG7+LtgmG8T5zDIg==",
+ "dependencies": {
+ "NETStandard.Library": "1.6.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.1, )",
+ "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": "[5.0.1, )",
+ "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.entityframework": {
+ "type": "Project",
+ "dependencies": {
+ "AutoMapper.Extensions.Microsoft.DependencyInjection": "[11.0.0, )",
+ "Core": "[2023.1.0, )",
+ "Microsoft.EntityFrameworkCore.Relational": "[6.0.12, )",
+ "Microsoft.EntityFrameworkCore.SqlServer": "[6.0.12, )",
+ "Microsoft.EntityFrameworkCore.Sqlite": "[6.0.12, )",
+ "Npgsql.EntityFrameworkCore.PostgreSQL": "[6.0.8, )",
+ "Pomelo.EntityFrameworkCore.MySql": "[6.0.2, )",
+ "linq2db.EntityFrameworkCore": "[6.11.0, )"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/bitwarden_license/src/Scim/.dockerignore b/bitwarden_license/src/Scim/.dockerignore
new file mode 100644
index 000000000..fc12f2514
--- /dev/null
+++ b/bitwarden_license/src/Scim/.dockerignore
@@ -0,0 +1,4 @@
+*
+!obj/build-output/publish/*
+!obj/Docker/empty/
+!entrypoint.sh
diff --git a/bitwarden_license/src/Scim/Context/IScimContext.cs b/bitwarden_license/src/Scim/Context/IScimContext.cs
new file mode 100644
index 000000000..1e7010bd2
--- /dev/null
+++ b/bitwarden_license/src/Scim/Context/IScimContext.cs
@@ -0,0 +1,20 @@
+using Bit.Core.Entities;
+using Bit.Core.Enums;
+using Bit.Core.Models.OrganizationConnectionConfigs;
+using Bit.Core.Repositories;
+using Bit.Core.Settings;
+
+namespace Bit.Scim.Context;
+
+public interface IScimContext
+{
+ ScimProviderType RequestScimProvider { get; set; }
+ ScimConfig ScimConfiguration { get; set; }
+ Guid? OrganizationId { get; set; }
+ Organization Organization { get; set; }
+ Task BuildAsync(
+ HttpContext httpContext,
+ GlobalSettings globalSettings,
+ IOrganizationRepository organizationRepository,
+ IOrganizationConnectionRepository organizationConnectionRepository);
+}
diff --git a/bitwarden_license/src/Scim/Context/ScimContext.cs b/bitwarden_license/src/Scim/Context/ScimContext.cs
new file mode 100644
index 000000000..ae8d30807
--- /dev/null
+++ b/bitwarden_license/src/Scim/Context/ScimContext.cs
@@ -0,0 +1,63 @@
+using Bit.Core.Entities;
+using Bit.Core.Enums;
+using Bit.Core.Models.OrganizationConnectionConfigs;
+using Bit.Core.Repositories;
+using Bit.Core.Settings;
+
+namespace Bit.Scim.Context;
+
+public class ScimContext : IScimContext
+{
+ private bool _builtHttpContext;
+
+ public ScimProviderType RequestScimProvider { get; set; } = ScimProviderType.Default;
+ public ScimConfig ScimConfiguration { get; set; }
+ public Guid? OrganizationId { get; set; }
+ public Organization Organization { get; set; }
+
+ public async virtual Task BuildAsync(
+ HttpContext httpContext,
+ GlobalSettings globalSettings,
+ IOrganizationRepository organizationRepository,
+ IOrganizationConnectionRepository organizationConnectionRepository)
+ {
+ if (_builtHttpContext)
+ {
+ return;
+ }
+
+ _builtHttpContext = true;
+
+ string orgIdString = null;
+ if (httpContext.Request.RouteValues.TryGetValue("organizationId", out var orgIdObject))
+ {
+ orgIdString = orgIdObject?.ToString();
+ }
+
+ if (Guid.TryParse(orgIdString, out var orgId))
+ {
+ OrganizationId = orgId;
+ Organization = await organizationRepository.GetByIdAsync(orgId);
+ if (Organization != null)
+ {
+ var scimConnections = await organizationConnectionRepository.GetByOrganizationIdTypeAsync(Organization.Id,
+ OrganizationConnectionType.Scim);
+ ScimConfiguration = scimConnections?.FirstOrDefault()?.GetConfig();
+ }
+ }
+
+ if (RequestScimProvider == ScimProviderType.Default &&
+ httpContext.Request.Headers.TryGetValue("User-Agent", out var userAgent))
+ {
+ if (userAgent.ToString().StartsWith("Okta"))
+ {
+ RequestScimProvider = ScimProviderType.Okta;
+ }
+ }
+ if (RequestScimProvider == ScimProviderType.Default &&
+ httpContext.Request.Headers.ContainsKey("Adscimversion"))
+ {
+ RequestScimProvider = ScimProviderType.AzureAd;
+ }
+ }
+}
diff --git a/bitwarden_license/src/Scim/Controllers/InfoController.cs b/bitwarden_license/src/Scim/Controllers/InfoController.cs
new file mode 100644
index 000000000..9959bfb0a
--- /dev/null
+++ b/bitwarden_license/src/Scim/Controllers/InfoController.cs
@@ -0,0 +1,22 @@
+using Bit.Core.Utilities;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Bit.Scim.Controllers;
+
+[AllowAnonymous]
+public class InfoController : Controller
+{
+ [HttpGet("~/alive")]
+ [HttpGet("~/now")]
+ public DateTime GetAlive()
+ {
+ return DateTime.UtcNow;
+ }
+
+ [HttpGet("~/version")]
+ public JsonResult GetVersion()
+ {
+ return Json(AssemblyHelpers.GetVersion());
+ }
+}
diff --git a/bitwarden_license/src/Scim/Controllers/v2/GroupsController.cs b/bitwarden_license/src/Scim/Controllers/v2/GroupsController.cs
new file mode 100644
index 000000000..8a3c02f5e
--- /dev/null
+++ b/bitwarden_license/src/Scim/Controllers/v2/GroupsController.cs
@@ -0,0 +1,109 @@
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.OrganizationFeatures.Groups.Interfaces;
+using Bit.Core.Repositories;
+using Bit.Scim.Groups.Interfaces;
+using Bit.Scim.Models;
+using Bit.Scim.Utilities;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Bit.Scim.Controllers.v2;
+
+[Authorize("Scim")]
+[Route("v2/{organizationId}/groups")]
+[ExceptionHandlerFilter]
+public class GroupsController : Controller
+{
+ private readonly IGroupRepository _groupRepository;
+ private readonly IOrganizationRepository _organizationRepository;
+ private readonly IGetGroupsListQuery _getGroupsListQuery;
+ private readonly IDeleteGroupCommand _deleteGroupCommand;
+ private readonly IPatchGroupCommand _patchGroupCommand;
+ private readonly IPostGroupCommand _postGroupCommand;
+ private readonly IPutGroupCommand _putGroupCommand;
+ private readonly ILogger _logger;
+
+ public GroupsController(
+ IGroupRepository groupRepository,
+ IOrganizationRepository organizationRepository,
+ IGetGroupsListQuery getGroupsListQuery,
+ IDeleteGroupCommand deleteGroupCommand,
+ IPatchGroupCommand patchGroupCommand,
+ IPostGroupCommand postGroupCommand,
+ IPutGroupCommand putGroupCommand,
+ ILogger logger)
+ {
+ _groupRepository = groupRepository;
+ _organizationRepository = organizationRepository;
+ _getGroupsListQuery = getGroupsListQuery;
+ _deleteGroupCommand = deleteGroupCommand;
+ _patchGroupCommand = patchGroupCommand;
+ _postGroupCommand = postGroupCommand;
+ _putGroupCommand = putGroupCommand;
+ _logger = logger;
+ }
+
+ [HttpGet("{id}")]
+ public async Task Get(Guid organizationId, Guid id)
+ {
+ var group = await _groupRepository.GetByIdAsync(id);
+ if (group == null || group.OrganizationId != organizationId)
+ {
+ throw new NotFoundException("Group not found.");
+ }
+ return Ok(new ScimGroupResponseModel(group));
+ }
+
+ [HttpGet("")]
+ public async Task Get(
+ Guid organizationId,
+ [FromQuery] string filter,
+ [FromQuery] int? count,
+ [FromQuery] int? startIndex)
+ {
+ var groupsListQueryResult = await _getGroupsListQuery.GetGroupsListAsync(organizationId, filter, count, startIndex);
+ var scimListResponseModel = new ScimListResponseModel
+ {
+ Resources = groupsListQueryResult.groupList.Select(g => new ScimGroupResponseModel(g)).ToList(),
+ ItemsPerPage = count.GetValueOrDefault(groupsListQueryResult.groupList.Count()),
+ TotalResults = groupsListQueryResult.totalResults,
+ StartIndex = startIndex.GetValueOrDefault(1),
+ };
+ return Ok(scimListResponseModel);
+ }
+
+ [HttpPost("")]
+ public async Task Post(Guid organizationId, [FromBody] ScimGroupRequestModel model)
+ {
+ var organization = await _organizationRepository.GetByIdAsync(organizationId);
+ var group = await _postGroupCommand.PostGroupAsync(organization, model);
+ var scimGroupResponseModel = new ScimGroupResponseModel(group);
+ return new CreatedResult(Url.Action(nameof(Get), new { group.OrganizationId, group.Id }), scimGroupResponseModel);
+ }
+
+ [HttpPut("{id}")]
+ public async Task Put(Guid organizationId, Guid id, [FromBody] ScimGroupRequestModel model)
+ {
+ var organization = await _organizationRepository.GetByIdAsync(organizationId);
+ var group = await _putGroupCommand.PutGroupAsync(organization, id, model);
+ var response = new ScimGroupResponseModel(group);
+
+ return Ok(response);
+ }
+
+ [HttpPatch("{id}")]
+ public async Task Patch(Guid organizationId, Guid id, [FromBody] ScimPatchModel model)
+ {
+ var organization = await _organizationRepository.GetByIdAsync(organizationId);
+ await _patchGroupCommand.PatchGroupAsync(organization, id, model);
+ return new NoContentResult();
+ }
+
+ [HttpDelete("{id}")]
+ public async Task Delete(Guid organizationId, Guid id)
+ {
+ await _deleteGroupCommand.DeleteGroupAsync(organizationId, id, EventSystemUser.SCIM);
+ return new NoContentResult();
+ }
+}
diff --git a/bitwarden_license/src/Scim/Controllers/v2/UsersController.cs b/bitwarden_license/src/Scim/Controllers/v2/UsersController.cs
new file mode 100644
index 000000000..ea58a90af
--- /dev/null
+++ b/bitwarden_license/src/Scim/Controllers/v2/UsersController.cs
@@ -0,0 +1,125 @@
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
+using Bit.Core.Repositories;
+using Bit.Core.Services;
+using Bit.Scim.Models;
+using Bit.Scim.Users.Interfaces;
+using Bit.Scim.Utilities;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Bit.Scim.Controllers.v2;
+
+[Authorize("Scim")]
+[Route("v2/{organizationId}/users")]
+[ExceptionHandlerFilter]
+public class UsersController : Controller
+{
+ private readonly IUserService _userService;
+ private readonly IOrganizationUserRepository _organizationUserRepository;
+ private readonly IOrganizationService _organizationService;
+ private readonly IGetUsersListQuery _getUsersListQuery;
+ private readonly IDeleteOrganizationUserCommand _deleteOrganizationUserCommand;
+ private readonly IPatchUserCommand _patchUserCommand;
+ private readonly IPostUserCommand _postUserCommand;
+ private readonly ILogger _logger;
+
+ public UsersController(
+ IUserService userService,
+ IOrganizationUserRepository organizationUserRepository,
+ IOrganizationService organizationService,
+ IGetUsersListQuery getUsersListQuery,
+ IDeleteOrganizationUserCommand deleteOrganizationUserCommand,
+ IPatchUserCommand patchUserCommand,
+ IPostUserCommand postUserCommand,
+ ILogger logger)
+ {
+ _userService = userService;
+ _organizationUserRepository = organizationUserRepository;
+ _organizationService = organizationService;
+ _getUsersListQuery = getUsersListQuery;
+ _deleteOrganizationUserCommand = deleteOrganizationUserCommand;
+ _patchUserCommand = patchUserCommand;
+ _postUserCommand = postUserCommand;
+ _logger = logger;
+ }
+
+ [HttpGet("{id}")]
+ public async Task Get(Guid organizationId, Guid id)
+ {
+ var orgUser = await _organizationUserRepository.GetDetailsByIdAsync(id);
+ if (orgUser == null || orgUser.OrganizationId != organizationId)
+ {
+ throw new NotFoundException("User not found.");
+ }
+ return Ok(new ScimUserResponseModel(orgUser));
+ }
+
+ [HttpGet("")]
+ public async Task Get(
+ Guid organizationId,
+ [FromQuery] string filter,
+ [FromQuery] int? count,
+ [FromQuery] int? startIndex)
+ {
+ var usersListQueryResult = await _getUsersListQuery.GetUsersListAsync(organizationId, filter, count, startIndex);
+ var scimListResponseModel = new ScimListResponseModel
+ {
+ Resources = usersListQueryResult.userList.Select(u => new ScimUserResponseModel(u)).ToList(),
+ ItemsPerPage = count.GetValueOrDefault(usersListQueryResult.userList.Count()),
+ TotalResults = usersListQueryResult.totalResults,
+ StartIndex = startIndex.GetValueOrDefault(1),
+ };
+ return Ok(scimListResponseModel);
+ }
+
+ [HttpPost("")]
+ public async Task Post(Guid organizationId, [FromBody] ScimUserRequestModel model)
+ {
+ var orgUser = await _postUserCommand.PostUserAsync(organizationId, model);
+ var scimUserResponseModel = new ScimUserResponseModel(orgUser);
+ return new CreatedResult(Url.Action(nameof(Get), new { orgUser.OrganizationId, orgUser.Id }), scimUserResponseModel);
+ }
+
+ [HttpPut("{id}")]
+ public async Task Put(Guid organizationId, Guid id, [FromBody] ScimUserRequestModel model)
+ {
+ var orgUser = await _organizationUserRepository.GetByIdAsync(id);
+ if (orgUser == null || orgUser.OrganizationId != organizationId)
+ {
+ return new NotFoundObjectResult(new ScimErrorResponseModel
+ {
+ Status = 404,
+ Detail = "User not found."
+ });
+ }
+
+ if (model.Active && orgUser.Status == OrganizationUserStatusType.Revoked)
+ {
+ await _organizationService.RestoreUserAsync(orgUser, null, _userService);
+ }
+ else if (!model.Active && orgUser.Status != OrganizationUserStatusType.Revoked)
+ {
+ await _organizationService.RevokeUserAsync(orgUser, null);
+ }
+
+ // Have to get full details object for response model
+ var orgUserDetails = await _organizationUserRepository.GetDetailsByIdAsync(id);
+ return new ObjectResult(new ScimUserResponseModel(orgUserDetails));
+ }
+
+ [HttpPatch("{id}")]
+ public async Task Patch(Guid organizationId, Guid id, [FromBody] ScimPatchModel model)
+ {
+ await _patchUserCommand.PatchUserAsync(organizationId, id, model);
+ return new NoContentResult();
+ }
+
+ [HttpDelete("{id}")]
+ public async Task Delete(Guid organizationId, Guid id)
+ {
+ await _deleteOrganizationUserCommand.DeleteUserAsync(organizationId, id, EventSystemUser.SCIM);
+ return new NoContentResult();
+ }
+}
diff --git a/bitwarden_license/src/Scim/Dockerfile b/bitwarden_license/src/Scim/Dockerfile
new file mode 100644
index 000000000..f63cb82ce
--- /dev/null
+++ b/bitwarden_license/src/Scim/Dockerfile
@@ -0,0 +1,20 @@
+FROM mcr.microsoft.com/dotnet/aspnet:6.0
+
+LABEL com.bitwarden.product="bitwarden"
+
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+ gosu \
+ curl \
+ && rm -rf /var/lib/apt/lists/*
+
+ENV ASPNETCORE_URLS http://+:5000
+WORKDIR /app
+EXPOSE 5000
+COPY obj/build-output/publish .
+COPY entrypoint.sh /
+RUN chmod +x /entrypoint.sh
+
+HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
+
+ENTRYPOINT ["/entrypoint.sh"]
diff --git a/bitwarden_license/src/Scim/Groups/GetGroupsListQuery.cs b/bitwarden_license/src/Scim/Groups/GetGroupsListQuery.cs
new file mode 100644
index 000000000..1afab3a0f
--- /dev/null
+++ b/bitwarden_license/src/Scim/Groups/GetGroupsListQuery.cs
@@ -0,0 +1,64 @@
+using Bit.Core.Entities;
+using Bit.Core.Repositories;
+using Bit.Scim.Groups.Interfaces;
+
+namespace Bit.Scim.Groups;
+
+public class GetGroupsListQuery : IGetGroupsListQuery
+{
+ private readonly IGroupRepository _groupRepository;
+
+ public GetGroupsListQuery(IGroupRepository groupRepository)
+ {
+ _groupRepository = groupRepository;
+ }
+
+ public async Task<(IEnumerable groupList, int totalResults)> GetGroupsListAsync(Guid organizationId, string filter, int? count, int? startIndex)
+ {
+ string nameFilter = null;
+ string externalIdFilter = null;
+ if (!string.IsNullOrWhiteSpace(filter))
+ {
+ if (filter.StartsWith("displayName eq "))
+ {
+ nameFilter = filter.Substring(15).Trim('"');
+ }
+ else if (filter.StartsWith("externalId eq "))
+ {
+ externalIdFilter = filter.Substring(14).Trim('"');
+ }
+ }
+
+ var groupList = new List();
+ var groups = await _groupRepository.GetManyByOrganizationIdAsync(organizationId);
+ var totalResults = 0;
+ if (!string.IsNullOrWhiteSpace(nameFilter))
+ {
+ var group = groups.FirstOrDefault(g => g.Name == nameFilter);
+ if (group != null)
+ {
+ groupList.Add(group);
+ }
+ totalResults = groupList.Count;
+ }
+ else if (!string.IsNullOrWhiteSpace(externalIdFilter))
+ {
+ var group = groups.FirstOrDefault(ou => ou.ExternalId == externalIdFilter);
+ if (group != null)
+ {
+ groupList.Add(group);
+ }
+ totalResults = groupList.Count;
+ }
+ else if (string.IsNullOrWhiteSpace(filter) && startIndex.HasValue && count.HasValue)
+ {
+ groupList = groups.OrderBy(g => g.Name)
+ .Skip(startIndex.Value - 1)
+ .Take(count.Value)
+ .ToList();
+ totalResults = groups.Count;
+ }
+
+ return (groupList, totalResults);
+ }
+}
diff --git a/bitwarden_license/src/Scim/Groups/Interfaces/IGetGroupsListQuery.cs b/bitwarden_license/src/Scim/Groups/Interfaces/IGetGroupsListQuery.cs
new file mode 100644
index 000000000..d2cf5ef0a
--- /dev/null
+++ b/bitwarden_license/src/Scim/Groups/Interfaces/IGetGroupsListQuery.cs
@@ -0,0 +1,8 @@
+using Bit.Core.Entities;
+
+namespace Bit.Scim.Groups.Interfaces;
+
+public interface IGetGroupsListQuery
+{
+ Task<(IEnumerable groupList, int totalResults)> GetGroupsListAsync(Guid organizationId, string filter, int? count, int? startIndex);
+}
diff --git a/bitwarden_license/src/Scim/Groups/Interfaces/IPatchGroupCommand.cs b/bitwarden_license/src/Scim/Groups/Interfaces/IPatchGroupCommand.cs
new file mode 100644
index 000000000..181f0c70a
--- /dev/null
+++ b/bitwarden_license/src/Scim/Groups/Interfaces/IPatchGroupCommand.cs
@@ -0,0 +1,9 @@
+using Bit.Core.Entities;
+using Bit.Scim.Models;
+
+namespace Bit.Scim.Groups.Interfaces;
+
+public interface IPatchGroupCommand
+{
+ Task PatchGroupAsync(Organization organization, Guid id, ScimPatchModel model);
+}
diff --git a/bitwarden_license/src/Scim/Groups/Interfaces/IPostGroupCommand.cs b/bitwarden_license/src/Scim/Groups/Interfaces/IPostGroupCommand.cs
new file mode 100644
index 000000000..76e80f08d
--- /dev/null
+++ b/bitwarden_license/src/Scim/Groups/Interfaces/IPostGroupCommand.cs
@@ -0,0 +1,9 @@
+using Bit.Core.Entities;
+using Bit.Scim.Models;
+
+namespace Bit.Scim.Groups.Interfaces;
+
+public interface IPostGroupCommand
+{
+ Task PostGroupAsync(Organization organization, ScimGroupRequestModel model);
+}
diff --git a/bitwarden_license/src/Scim/Groups/Interfaces/IPutGroupCommand.cs b/bitwarden_license/src/Scim/Groups/Interfaces/IPutGroupCommand.cs
new file mode 100644
index 000000000..052a4e554
--- /dev/null
+++ b/bitwarden_license/src/Scim/Groups/Interfaces/IPutGroupCommand.cs
@@ -0,0 +1,9 @@
+using Bit.Core.Entities;
+using Bit.Scim.Models;
+
+namespace Bit.Scim.Groups.Interfaces;
+
+public interface IPutGroupCommand
+{
+ Task PutGroupAsync(Organization organization, Guid id, ScimGroupRequestModel model);
+}
diff --git a/bitwarden_license/src/Scim/Groups/PatchGroupCommand.cs b/bitwarden_license/src/Scim/Groups/PatchGroupCommand.cs
new file mode 100644
index 000000000..750dcec46
--- /dev/null
+++ b/bitwarden_license/src/Scim/Groups/PatchGroupCommand.cs
@@ -0,0 +1,153 @@
+using System.Text.Json;
+using Bit.Core.Entities;
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.OrganizationFeatures.Groups.Interfaces;
+using Bit.Core.Repositories;
+using Bit.Core.Services;
+using Bit.Scim.Groups.Interfaces;
+using Bit.Scim.Models;
+
+namespace Bit.Scim.Groups;
+
+public class PatchGroupCommand : IPatchGroupCommand
+{
+ private readonly IGroupRepository _groupRepository;
+ private readonly IGroupService _groupService;
+ private readonly IUpdateGroupCommand _updateGroupCommand;
+ private readonly ILogger _logger;
+
+ public PatchGroupCommand(
+ IGroupRepository groupRepository,
+ IGroupService groupService,
+ IUpdateGroupCommand updateGroupCommand,
+ ILogger logger)
+ {
+ _groupRepository = groupRepository;
+ _groupService = groupService;
+ _updateGroupCommand = updateGroupCommand;
+ _logger = logger;
+ }
+
+ public async Task PatchGroupAsync(Organization organization, Guid id, ScimPatchModel model)
+ {
+ var group = await _groupRepository.GetByIdAsync(id);
+ if (group == null || group.OrganizationId != organization.Id)
+ {
+ throw new NotFoundException("Group not found.");
+ }
+
+ var operationHandled = false;
+ foreach (var operation in model.Operations)
+ {
+ // Replace operations
+ if (operation.Op?.ToLowerInvariant() == "replace")
+ {
+ // Replace a list of members
+ if (operation.Path?.ToLowerInvariant() == "members")
+ {
+ var ids = GetOperationValueIds(operation.Value);
+ await _groupRepository.UpdateUsersAsync(group.Id, ids);
+ operationHandled = true;
+ }
+ // Replace group name from path
+ else if (operation.Path?.ToLowerInvariant() == "displayname")
+ {
+ group.Name = operation.Value.GetString();
+ await _updateGroupCommand.UpdateGroupAsync(group, organization, EventSystemUser.SCIM);
+ operationHandled = true;
+ }
+ // Replace group name from value object
+ else if (string.IsNullOrWhiteSpace(operation.Path) &&
+ operation.Value.TryGetProperty("displayName", out var displayNameProperty))
+ {
+ group.Name = displayNameProperty.GetString();
+ await _updateGroupCommand.UpdateGroupAsync(group, organization, EventSystemUser.SCIM);
+ operationHandled = true;
+ }
+ }
+ // Add a single member
+ else if (operation.Op?.ToLowerInvariant() == "add" &&
+ !string.IsNullOrWhiteSpace(operation.Path) &&
+ operation.Path.ToLowerInvariant().StartsWith("members[value eq "))
+ {
+ var addId = GetOperationPathId(operation.Path);
+ if (addId.HasValue)
+ {
+ var orgUserIds = (await _groupRepository.GetManyUserIdsByIdAsync(group.Id)).ToHashSet();
+ orgUserIds.Add(addId.Value);
+ await _groupRepository.UpdateUsersAsync(group.Id, orgUserIds);
+ operationHandled = true;
+ }
+ }
+ // Add a list of members
+ else if (operation.Op?.ToLowerInvariant() == "add" &&
+ operation.Path?.ToLowerInvariant() == "members")
+ {
+ var orgUserIds = (await _groupRepository.GetManyUserIdsByIdAsync(group.Id)).ToHashSet();
+ foreach (var v in GetOperationValueIds(operation.Value))
+ {
+ orgUserIds.Add(v);
+ }
+ await _groupRepository.UpdateUsersAsync(group.Id, orgUserIds);
+ operationHandled = true;
+ }
+ // Remove a single member
+ else if (operation.Op?.ToLowerInvariant() == "remove" &&
+ !string.IsNullOrWhiteSpace(operation.Path) &&
+ operation.Path.ToLowerInvariant().StartsWith("members[value eq "))
+ {
+ var removeId = GetOperationPathId(operation.Path);
+ if (removeId.HasValue)
+ {
+ await _groupService.DeleteUserAsync(group, removeId.Value, EventSystemUser.SCIM);
+ operationHandled = true;
+ }
+ }
+ // Remove a list of members
+ else if (operation.Op?.ToLowerInvariant() == "remove" &&
+ operation.Path?.ToLowerInvariant() == "members")
+ {
+ var orgUserIds = (await _groupRepository.GetManyUserIdsByIdAsync(group.Id)).ToHashSet();
+ foreach (var v in GetOperationValueIds(operation.Value))
+ {
+ orgUserIds.Remove(v);
+ }
+ await _groupRepository.UpdateUsersAsync(group.Id, orgUserIds);
+ operationHandled = true;
+ }
+ }
+
+ if (!operationHandled)
+ {
+ _logger.LogWarning("Group patch operation not handled: {0} : ",
+ string.Join(", ", model.Operations.Select(o => $"{o.Op}:{o.Path}")));
+ }
+ }
+
+ private List GetOperationValueIds(JsonElement objArray)
+ {
+ var ids = new List();
+ foreach (var obj in objArray.EnumerateArray())
+ {
+ if (obj.TryGetProperty("value", out var valueProperty))
+ {
+ if (valueProperty.TryGetGuid(out var guid))
+ {
+ ids.Add(guid);
+ }
+ }
+ }
+ return ids;
+ }
+
+ private Guid? GetOperationPathId(string path)
+ {
+ // Parse Guid from string like: members[value eq "{GUID}"}]
+ if (Guid.TryParse(path.Substring(18).Replace("\"]", string.Empty), out var id))
+ {
+ return id;
+ }
+ return null;
+ }
+}
diff --git a/bitwarden_license/src/Scim/Groups/PostGroupCommand.cs b/bitwarden_license/src/Scim/Groups/PostGroupCommand.cs
new file mode 100644
index 000000000..4da336fb7
--- /dev/null
+++ b/bitwarden_license/src/Scim/Groups/PostGroupCommand.cs
@@ -0,0 +1,77 @@
+using Bit.Core.Entities;
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.OrganizationFeatures.Groups.Interfaces;
+using Bit.Core.Repositories;
+using Bit.Scim.Context;
+using Bit.Scim.Groups.Interfaces;
+using Bit.Scim.Models;
+
+namespace Bit.Scim.Groups;
+
+public class PostGroupCommand : IPostGroupCommand
+{
+ private readonly IGroupRepository _groupRepository;
+ private readonly IScimContext _scimContext;
+ private readonly ICreateGroupCommand _createGroupCommand;
+
+ public PostGroupCommand(
+ IGroupRepository groupRepository,
+ IOrganizationRepository organizationRepository,
+ IScimContext scimContext,
+ ICreateGroupCommand createGroupCommand)
+ {
+ _groupRepository = groupRepository;
+ _scimContext = scimContext;
+ _createGroupCommand = createGroupCommand;
+ }
+
+ public async Task PostGroupAsync(Organization organization, ScimGroupRequestModel model)
+ {
+ if (string.IsNullOrWhiteSpace(model.DisplayName))
+ {
+ throw new BadRequestException();
+ }
+
+ var groups = await _groupRepository.GetManyByOrganizationIdAsync(organization.Id);
+ if (!string.IsNullOrWhiteSpace(model.ExternalId) && groups.Any(g => g.ExternalId == model.ExternalId))
+ {
+ throw new ConflictException();
+ }
+
+ var group = model.ToGroup(organization.Id);
+ await _createGroupCommand.CreateGroupAsync(group, organization, EventSystemUser.SCIM, collections: null);
+ await UpdateGroupMembersAsync(group, model);
+
+ return group;
+ }
+
+ private async Task UpdateGroupMembersAsync(Group group, ScimGroupRequestModel model)
+ {
+ if (_scimContext.RequestScimProvider != Core.Enums.ScimProviderType.Okta)
+ {
+ return;
+ }
+
+ if (model.Members == null)
+ {
+ return;
+ }
+
+ var memberIds = new List();
+ foreach (var id in model.Members.Select(i => i.Value))
+ {
+ if (Guid.TryParse(id, out var guidId))
+ {
+ memberIds.Add(guidId);
+ }
+ }
+
+ if (!memberIds.Any())
+ {
+ return;
+ }
+
+ await _groupRepository.UpdateUsersAsync(group.Id, memberIds);
+ }
+}
diff --git a/bitwarden_license/src/Scim/Groups/PutGroupCommand.cs b/bitwarden_license/src/Scim/Groups/PutGroupCommand.cs
new file mode 100644
index 000000000..ec2cd170d
--- /dev/null
+++ b/bitwarden_license/src/Scim/Groups/PutGroupCommand.cs
@@ -0,0 +1,66 @@
+using Bit.Core.Entities;
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.OrganizationFeatures.Groups.Interfaces;
+using Bit.Core.Repositories;
+using Bit.Scim.Context;
+using Bit.Scim.Groups.Interfaces;
+using Bit.Scim.Models;
+
+namespace Bit.Scim.Groups;
+
+public class PutGroupCommand : IPutGroupCommand
+{
+ private readonly IGroupRepository _groupRepository;
+ private readonly IScimContext _scimContext;
+ private readonly IUpdateGroupCommand _updateGroupCommand;
+
+ public PutGroupCommand(
+ IGroupRepository groupRepository,
+ IScimContext scimContext,
+ IUpdateGroupCommand updateGroupCommand)
+ {
+ _groupRepository = groupRepository;
+ _scimContext = scimContext;
+ _updateGroupCommand = updateGroupCommand;
+ }
+
+ public async Task PutGroupAsync(Organization organization, Guid id, ScimGroupRequestModel model)
+ {
+ var group = await _groupRepository.GetByIdAsync(id);
+ if (group == null || group.OrganizationId != organization.Id)
+ {
+ throw new NotFoundException("Group not found.");
+ }
+
+ group.Name = model.DisplayName;
+ await _updateGroupCommand.UpdateGroupAsync(group, organization, EventSystemUser.SCIM);
+ await UpdateGroupMembersAsync(group, model);
+
+ return group;
+ }
+
+ private async Task UpdateGroupMembersAsync(Group group, ScimGroupRequestModel model)
+ {
+ if (_scimContext.RequestScimProvider != Core.Enums.ScimProviderType.Okta)
+ {
+ return;
+ }
+
+ if (model.Members == null)
+ {
+ return;
+ }
+
+ var memberIds = new List();
+ foreach (var id in model.Members.Select(i => i.Value))
+ {
+ if (Guid.TryParse(id, out var guidId))
+ {
+ memberIds.Add(guidId);
+ }
+ }
+
+ await _groupRepository.UpdateUsersAsync(group.Id, memberIds);
+ }
+}
diff --git a/bitwarden_license/src/Scim/Models/BaseScimGroupModel.cs b/bitwarden_license/src/Scim/Models/BaseScimGroupModel.cs
new file mode 100644
index 000000000..150885fb5
--- /dev/null
+++ b/bitwarden_license/src/Scim/Models/BaseScimGroupModel.cs
@@ -0,0 +1,17 @@
+using Bit.Scim.Utilities;
+
+namespace Bit.Scim.Models;
+
+public abstract class BaseScimGroupModel : BaseScimModel
+{
+ public BaseScimGroupModel(bool initSchema = false)
+ {
+ if (initSchema)
+ {
+ Schemas = new List { ScimConstants.Scim2SchemaGroup };
+ }
+ }
+
+ public string DisplayName { get; set; }
+ public string ExternalId { get; set; }
+}
diff --git a/bitwarden_license/src/Scim/Models/BaseScimModel.cs b/bitwarden_license/src/Scim/Models/BaseScimModel.cs
new file mode 100644
index 000000000..8f3adfbe4
--- /dev/null
+++ b/bitwarden_license/src/Scim/Models/BaseScimModel.cs
@@ -0,0 +1,14 @@
+namespace Bit.Scim.Models;
+
+public abstract class BaseScimModel
+{
+ public BaseScimModel()
+ { }
+
+ public BaseScimModel(string schema)
+ {
+ Schemas = new List { schema };
+ }
+
+ public List Schemas { get; set; }
+}
diff --git a/bitwarden_license/src/Scim/Models/BaseScimUserModel.cs b/bitwarden_license/src/Scim/Models/BaseScimUserModel.cs
new file mode 100644
index 000000000..d3c69d574
--- /dev/null
+++ b/bitwarden_license/src/Scim/Models/BaseScimUserModel.cs
@@ -0,0 +1,55 @@
+using Bit.Scim.Utilities;
+
+namespace Bit.Scim.Models;
+
+public abstract class BaseScimUserModel : BaseScimModel
+{
+ public BaseScimUserModel(bool initSchema = false)
+ {
+ if (initSchema)
+ {
+ Schemas = new List { ScimConstants.Scim2SchemaUser };
+ }
+ }
+
+ public string UserName { get; set; }
+ public NameModel Name { get; set; }
+ public List Emails { get; set; }
+ public string PrimaryEmail => Emails?.FirstOrDefault(e => e.Primary)?.Value;
+ public string WorkEmail => Emails?.FirstOrDefault(e => e.Type == "work")?.Value;
+ public string DisplayName { get; set; }
+ public bool Active { get; set; }
+ public List Groups { get; set; }
+ public string ExternalId { get; set; }
+
+ public class NameModel
+ {
+ public NameModel() { }
+
+ public NameModel(string name)
+ {
+ Formatted = name;
+ }
+
+ public string Formatted { get; set; }
+ public string GivenName { get; set; }
+ public string MiddleName { get; set; }
+ public string FamilyName { get; set; }
+ }
+
+ public class EmailModel
+ {
+ public EmailModel() { }
+
+ public EmailModel(string email)
+ {
+ Primary = true;
+ Value = email;
+ Type = "work";
+ }
+
+ public bool Primary { get; set; }
+ public string Value { get; set; }
+ public string Type { get; set; }
+ }
+}
diff --git a/bitwarden_license/src/Scim/Models/ScimErrorResponseModel.cs b/bitwarden_license/src/Scim/Models/ScimErrorResponseModel.cs
new file mode 100644
index 000000000..d1dce35ef
--- /dev/null
+++ b/bitwarden_license/src/Scim/Models/ScimErrorResponseModel.cs
@@ -0,0 +1,13 @@
+using Bit.Scim.Utilities;
+
+namespace Bit.Scim.Models;
+
+public class ScimErrorResponseModel : BaseScimModel
+{
+ public ScimErrorResponseModel()
+ : base(ScimConstants.Scim2SchemaError)
+ { }
+
+ public string Detail { get; set; }
+ public int Status { get; set; }
+}
diff --git a/bitwarden_license/src/Scim/Models/ScimGroupRequestModel.cs b/bitwarden_license/src/Scim/Models/ScimGroupRequestModel.cs
new file mode 100644
index 000000000..ac99eca2e
--- /dev/null
+++ b/bitwarden_license/src/Scim/Models/ScimGroupRequestModel.cs
@@ -0,0 +1,30 @@
+using Bit.Core.Entities;
+using Bit.Core.Utilities;
+
+namespace Bit.Scim.Models;
+
+public class ScimGroupRequestModel : BaseScimGroupModel
+{
+ public ScimGroupRequestModel()
+ : base(false)
+ { }
+
+ public Group ToGroup(Guid organizationId)
+ {
+ var externalId = string.IsNullOrWhiteSpace(ExternalId) ? CoreHelpers.RandomString(15) : ExternalId;
+ return new Group
+ {
+ Name = DisplayName,
+ ExternalId = externalId,
+ OrganizationId = organizationId
+ };
+ }
+
+ public List Members { get; set; }
+
+ public class GroupMembersModel
+ {
+ public string Value { get; set; }
+ public string Display { get; set; }
+ }
+}
diff --git a/bitwarden_license/src/Scim/Models/ScimGroupResponseModel.cs b/bitwarden_license/src/Scim/Models/ScimGroupResponseModel.cs
new file mode 100644
index 000000000..d5bd64a32
--- /dev/null
+++ b/bitwarden_license/src/Scim/Models/ScimGroupResponseModel.cs
@@ -0,0 +1,25 @@
+using Bit.Core.Entities;
+
+namespace Bit.Scim.Models;
+
+public class ScimGroupResponseModel : BaseScimGroupModel
+{
+ public ScimGroupResponseModel()
+ : base(true)
+ {
+ Meta = new ScimMetaModel("Group");
+ }
+
+ public ScimGroupResponseModel(Group group)
+ : this()
+ {
+ Id = group.Id.ToString();
+ DisplayName = group.Name;
+ ExternalId = group.ExternalId;
+ Meta.Created = group.CreationDate;
+ Meta.LastModified = group.RevisionDate;
+ }
+
+ public string Id { get; set; }
+ public ScimMetaModel Meta { get; private set; }
+}
diff --git a/bitwarden_license/src/Scim/Models/ScimListResponseModel.cs b/bitwarden_license/src/Scim/Models/ScimListResponseModel.cs
new file mode 100644
index 000000000..77ab52356
--- /dev/null
+++ b/bitwarden_license/src/Scim/Models/ScimListResponseModel.cs
@@ -0,0 +1,15 @@
+using Bit.Scim.Utilities;
+
+namespace Bit.Scim.Models;
+
+public class ScimListResponseModel : BaseScimModel
+{
+ public ScimListResponseModel()
+ : base(ScimConstants.Scim2SchemaListResponse)
+ { }
+
+ public int TotalResults { get; set; }
+ public int StartIndex { get; set; }
+ public int ItemsPerPage { get; set; }
+ public List Resources { get; set; }
+}
diff --git a/bitwarden_license/src/Scim/Models/ScimMetaModel.cs b/bitwarden_license/src/Scim/Models/ScimMetaModel.cs
new file mode 100644
index 000000000..862c054b7
--- /dev/null
+++ b/bitwarden_license/src/Scim/Models/ScimMetaModel.cs
@@ -0,0 +1,13 @@
+namespace Bit.Scim.Models;
+
+public class ScimMetaModel
+{
+ public ScimMetaModel(string resourceType)
+ {
+ ResourceType = resourceType;
+ }
+
+ public string ResourceType { get; set; }
+ public DateTime? Created { get; set; }
+ public DateTime? LastModified { get; set; }
+}
diff --git a/bitwarden_license/src/Scim/Models/ScimPatchModel.cs b/bitwarden_license/src/Scim/Models/ScimPatchModel.cs
new file mode 100644
index 000000000..6707ced85
--- /dev/null
+++ b/bitwarden_license/src/Scim/Models/ScimPatchModel.cs
@@ -0,0 +1,18 @@
+using System.Text.Json;
+
+namespace Bit.Scim.Models;
+
+public class ScimPatchModel : BaseScimModel
+{
+ public ScimPatchModel()
+ : base() { }
+
+ public List Operations { get; set; }
+
+ public class OperationModel
+ {
+ public string Op { get; set; }
+ public string Path { get; set; }
+ public JsonElement Value { get; set; }
+ }
+}
diff --git a/bitwarden_license/src/Scim/Models/ScimUserRequestModel.cs b/bitwarden_license/src/Scim/Models/ScimUserRequestModel.cs
new file mode 100644
index 000000000..a489e03ad
--- /dev/null
+++ b/bitwarden_license/src/Scim/Models/ScimUserRequestModel.cs
@@ -0,0 +1,8 @@
+namespace Bit.Scim.Models;
+
+public class ScimUserRequestModel : BaseScimUserModel
+{
+ public ScimUserRequestModel()
+ : base(false)
+ { }
+}
diff --git a/bitwarden_license/src/Scim/Models/ScimUserResponseModel.cs b/bitwarden_license/src/Scim/Models/ScimUserResponseModel.cs
new file mode 100644
index 000000000..95d5184da
--- /dev/null
+++ b/bitwarden_license/src/Scim/Models/ScimUserResponseModel.cs
@@ -0,0 +1,28 @@
+using Bit.Core.Models.Data.Organizations.OrganizationUsers;
+
+namespace Bit.Scim.Models;
+
+public class ScimUserResponseModel : BaseScimUserModel
+{
+ public ScimUserResponseModel()
+ : base(true)
+ {
+ Meta = new ScimMetaModel("User");
+ Groups = new List();
+ }
+
+ public ScimUserResponseModel(OrganizationUserUserDetails orgUser)
+ : this()
+ {
+ Id = orgUser.Id.ToString();
+ ExternalId = orgUser.ExternalId;
+ UserName = orgUser.Email;
+ DisplayName = orgUser.Name;
+ Emails = new List { new EmailModel(orgUser.Email) };
+ Name = new NameModel(orgUser.Name);
+ Active = orgUser.Status != Core.Enums.OrganizationUserStatusType.Revoked;
+ }
+
+ public string Id { get; set; }
+ public ScimMetaModel Meta { get; private set; }
+}
diff --git a/bitwarden_license/src/Scim/Program.cs b/bitwarden_license/src/Scim/Program.cs
new file mode 100644
index 000000000..5d7d505aa
--- /dev/null
+++ b/bitwarden_license/src/Scim/Program.cs
@@ -0,0 +1,32 @@
+using Bit.Core.Utilities;
+
+namespace Bit.Scim;
+
+public class Program
+{
+ public static void Main(string[] args)
+ {
+ Host
+ .CreateDefaultBuilder(args)
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder.UseStartup();
+ webBuilder.ConfigureLogging((hostingContext, logging) =>
+ logging.AddSerilog(hostingContext, (e, globalSettings) =>
+ {
+ var context = e.Properties["SourceContext"].ToString();
+
+ if (e.Properties.ContainsKey("RequestPath") &&
+ !string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
+ (context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
+ {
+ return false;
+ }
+
+ return e.Level >= globalSettings.MinLogLevel.ScimSettings.Default;
+ }));
+ })
+ .Build()
+ .Run();
+ }
+}
diff --git a/bitwarden_license/src/Scim/Properties/launchSettings.json b/bitwarden_license/src/Scim/Properties/launchSettings.json
new file mode 100644
index 000000000..a391acb2b
--- /dev/null
+++ b/bitwarden_license/src/Scim/Properties/launchSettings.json
@@ -0,0 +1,29 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:44558/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": false,
+ "launchUrl": "http://localhost:44558",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Scim": {
+ "commandName": "Project",
+ "launchBrowser": false,
+ "launchUrl": "http://localhost:44558",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:44559"
+ }
+ }
+}
diff --git a/bitwarden_license/src/Scim/Scim.csproj b/bitwarden_license/src/Scim/Scim.csproj
new file mode 100644
index 000000000..3387d74e0
--- /dev/null
+++ b/bitwarden_license/src/Scim/Scim.csproj
@@ -0,0 +1,17 @@
+
+
+
+ bitwarden-Scim
+ false
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bitwarden_license/src/Scim/ScimSettings.cs b/bitwarden_license/src/Scim/ScimSettings.cs
new file mode 100644
index 000000000..ef4ebfb50
--- /dev/null
+++ b/bitwarden_license/src/Scim/ScimSettings.cs
@@ -0,0 +1,5 @@
+namespace Bit.Scim;
+
+public class ScimSettings
+{
+}
diff --git a/bitwarden_license/src/Scim/Startup.cs b/bitwarden_license/src/Scim/Startup.cs
new file mode 100644
index 000000000..4aaccd9ed
--- /dev/null
+++ b/bitwarden_license/src/Scim/Startup.cs
@@ -0,0 +1,120 @@
+using System.Globalization;
+using Bit.Core.Context;
+using Bit.Core.Settings;
+using Bit.Core.Utilities;
+using Bit.Scim.Context;
+using Bit.Scim.Utilities;
+using Bit.SharedWeb.Utilities;
+using IdentityModel;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Stripe;
+
+namespace Bit.Scim;
+
+public class Startup
+{
+ public Startup(IWebHostEnvironment env, IConfiguration configuration)
+ {
+ CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
+ Configuration = configuration;
+ Environment = env;
+ }
+
+ public IConfiguration Configuration { get; }
+ public IWebHostEnvironment Environment { get; set; }
+
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // Options
+ services.AddOptions();
+
+ // Settings
+ var globalSettings = services.AddGlobalSettingsServices(Configuration, Environment);
+ services.Configure(Configuration.GetSection("ScimSettings"));
+
+ // Data Protection
+ services.AddCustomDataProtectionServices(Environment, globalSettings);
+
+ // Stripe Billing
+ StripeConfiguration.ApiKey = globalSettings.Stripe.ApiKey;
+ StripeConfiguration.MaxNetworkRetries = globalSettings.Stripe.MaxNetworkRetries;
+
+ // Repositories
+ services.AddDatabaseRepositories(globalSettings);
+
+ // Context
+ services.AddScoped();
+ services.AddScoped();
+
+ // Authentication
+ services.AddAuthentication(ApiKeyAuthenticationOptions.DefaultScheme)
+ .AddScheme(
+ ApiKeyAuthenticationOptions.DefaultScheme, null);
+
+ services.AddAuthorization(config =>
+ {
+ config.AddPolicy("Scim", policy =>
+ {
+ policy.RequireAuthenticatedUser();
+ policy.RequireClaim(JwtClaimTypes.Scope, "api.scim");
+ });
+ });
+
+ // Identity
+ services.AddCustomIdentityServices(globalSettings);
+
+ // Services
+ services.AddBaseServices(globalSettings);
+ services.AddDefaultServices(globalSettings);
+
+ services.TryAddSingleton();
+
+ // Mvc
+ services.AddMvc(config =>
+ {
+ config.Filters.Add(new LoggingExceptionHandlerFilterAttribute());
+ });
+ services.Configure(options => options.LowercaseUrls = true);
+
+ services.AddScimGroupCommands();
+ services.AddScimGroupQueries();
+ services.AddScimUserQueries();
+ services.AddScimUserCommands();
+ }
+
+ public void Configure(
+ IApplicationBuilder app,
+ IWebHostEnvironment env,
+ IHostApplicationLifetime appLifetime,
+ GlobalSettings globalSettings)
+ {
+ app.UseSerilog(env, appLifetime, globalSettings);
+
+ // Add general security headers
+ app.UseMiddleware();
+
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ // Default Middleware
+ app.UseDefaultMiddleware(env, globalSettings);
+
+ // Add routing
+ app.UseRouting();
+
+ // Add Scim context
+ app.UseMiddleware();
+
+ // Add authentication and authorization to the request pipeline.
+ app.UseAuthentication();
+ app.UseAuthorization();
+
+ // Add current context
+ app.UseMiddleware();
+
+ // Add MVC to the request pipeline.
+ app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
+ }
+}
diff --git a/bitwarden_license/src/Scim/Users/GetUsersListQuery.cs b/bitwarden_license/src/Scim/Users/GetUsersListQuery.cs
new file mode 100644
index 000000000..51250250f
--- /dev/null
+++ b/bitwarden_license/src/Scim/Users/GetUsersListQuery.cs
@@ -0,0 +1,69 @@
+using Bit.Core.Models.Data.Organizations.OrganizationUsers;
+using Bit.Core.Repositories;
+using Bit.Scim.Users.Interfaces;
+
+namespace Bit.Scim.Users;
+
+public class GetUsersListQuery : IGetUsersListQuery
+{
+ private readonly IOrganizationUserRepository _organizationUserRepository;
+
+ public GetUsersListQuery(IOrganizationUserRepository organizationUserRepository)
+ {
+ _organizationUserRepository = organizationUserRepository;
+ }
+
+ public async Task<(IEnumerable userList, int totalResults)> GetUsersListAsync(Guid organizationId, string filter, int? count, int? startIndex)
+ {
+ string emailFilter = null;
+ string usernameFilter = null;
+ string externalIdFilter = null;
+ if (!string.IsNullOrWhiteSpace(filter))
+ {
+ if (filter.StartsWith("userName eq "))
+ {
+ usernameFilter = filter.Substring(12).Trim('"').ToLowerInvariant();
+ if (usernameFilter.Contains("@"))
+ {
+ emailFilter = usernameFilter;
+ }
+ }
+ else if (filter.StartsWith("externalId eq "))
+ {
+ externalIdFilter = filter.Substring(14).Trim('"');
+ }
+ }
+
+ var userList = new List();
+ var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);
+ var totalResults = 0;
+ if (!string.IsNullOrWhiteSpace(emailFilter))
+ {
+ var orgUser = orgUsers.FirstOrDefault(ou => ou.Email.ToLowerInvariant() == emailFilter);
+ if (orgUser != null)
+ {
+ userList.Add(orgUser);
+ }
+ totalResults = userList.Count;
+ }
+ else if (!string.IsNullOrWhiteSpace(externalIdFilter))
+ {
+ var orgUser = orgUsers.FirstOrDefault(ou => ou.ExternalId == externalIdFilter);
+ if (orgUser != null)
+ {
+ userList.Add(orgUser);
+ }
+ totalResults = userList.Count;
+ }
+ else if (string.IsNullOrWhiteSpace(filter) && startIndex.HasValue && count.HasValue)
+ {
+ userList = orgUsers.OrderBy(ou => ou.Email)
+ .Skip(startIndex.Value - 1)
+ .Take(count.Value)
+ .ToList();
+ totalResults = orgUsers.Count;
+ }
+
+ return (userList, totalResults);
+ }
+}
diff --git a/bitwarden_license/src/Scim/Users/Interfaces/IGetUsersListQuery.cs b/bitwarden_license/src/Scim/Users/Interfaces/IGetUsersListQuery.cs
new file mode 100644
index 000000000..265c6a8e7
--- /dev/null
+++ b/bitwarden_license/src/Scim/Users/Interfaces/IGetUsersListQuery.cs
@@ -0,0 +1,8 @@
+using Bit.Core.Models.Data.Organizations.OrganizationUsers;
+
+namespace Bit.Scim.Users.Interfaces;
+
+public interface IGetUsersListQuery
+{
+ Task<(IEnumerable userList, int totalResults)> GetUsersListAsync(Guid organizationId, string filter, int? count, int? startIndex);
+}
diff --git a/bitwarden_license/src/Scim/Users/Interfaces/IPatchUserCommand.cs b/bitwarden_license/src/Scim/Users/Interfaces/IPatchUserCommand.cs
new file mode 100644
index 000000000..dcc349a0c
--- /dev/null
+++ b/bitwarden_license/src/Scim/Users/Interfaces/IPatchUserCommand.cs
@@ -0,0 +1,8 @@
+using Bit.Scim.Models;
+
+namespace Bit.Scim.Users.Interfaces;
+
+public interface IPatchUserCommand
+{
+ Task PatchUserAsync(Guid organizationId, Guid id, ScimPatchModel model);
+}
diff --git a/bitwarden_license/src/Scim/Users/Interfaces/IPostUserCommand.cs b/bitwarden_license/src/Scim/Users/Interfaces/IPostUserCommand.cs
new file mode 100644
index 000000000..05dd05510
--- /dev/null
+++ b/bitwarden_license/src/Scim/Users/Interfaces/IPostUserCommand.cs
@@ -0,0 +1,9 @@
+using Bit.Core.Models.Data.Organizations.OrganizationUsers;
+using Bit.Scim.Models;
+
+namespace Bit.Scim.Users.Interfaces;
+
+public interface IPostUserCommand
+{
+ Task PostUserAsync(Guid organizationId, ScimUserRequestModel model);
+}
diff --git a/bitwarden_license/src/Scim/Users/PatchUserCommand.cs b/bitwarden_license/src/Scim/Users/PatchUserCommand.cs
new file mode 100644
index 000000000..075807a58
--- /dev/null
+++ b/bitwarden_license/src/Scim/Users/PatchUserCommand.cs
@@ -0,0 +1,87 @@
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.Repositories;
+using Bit.Core.Services;
+using Bit.Scim.Models;
+using Bit.Scim.Users.Interfaces;
+
+namespace Bit.Scim.Users;
+
+public class PatchUserCommand : IPatchUserCommand
+{
+ private readonly IUserService _userService;
+ private readonly IOrganizationUserRepository _organizationUserRepository;
+ private readonly IOrganizationService _organizationService;
+ private readonly ILogger _logger;
+
+ public PatchUserCommand(
+ IUserService userService,
+ IOrganizationUserRepository organizationUserRepository,
+ IOrganizationService organizationService,
+ ILogger logger)
+ {
+ _userService = userService;
+ _organizationUserRepository = organizationUserRepository;
+ _organizationService = organizationService;
+ _logger = logger;
+ }
+
+ public async Task PatchUserAsync(Guid organizationId, Guid id, ScimPatchModel model)
+ {
+ var orgUser = await _organizationUserRepository.GetByIdAsync(id);
+ if (orgUser == null || orgUser.OrganizationId != organizationId)
+ {
+ throw new NotFoundException("User not found.");
+ }
+
+ var operationHandled = false;
+ foreach (var operation in model.Operations)
+ {
+ // Replace operations
+ if (operation.Op?.ToLowerInvariant() == "replace")
+ {
+ // Active from path
+ if (operation.Path?.ToLowerInvariant() == "active")
+ {
+ var active = operation.Value.ToString()?.ToLowerInvariant();
+ var handled = await HandleActiveOperationAsync(orgUser, active == "true");
+ if (!operationHandled)
+ {
+ operationHandled = handled;
+ }
+ }
+ // Active from value object
+ else if (string.IsNullOrWhiteSpace(operation.Path) &&
+ operation.Value.TryGetProperty("active", out var activeProperty))
+ {
+ var handled = await HandleActiveOperationAsync(orgUser, activeProperty.GetBoolean());
+ if (!operationHandled)
+ {
+ operationHandled = handled;
+ }
+ }
+ }
+ }
+
+ if (!operationHandled)
+ {
+ _logger.LogWarning("User patch operation not handled: {operation} : ",
+ string.Join(", ", model.Operations.Select(o => $"{o.Op}:{o.Path}")));
+ }
+ }
+
+ private async Task HandleActiveOperationAsync(Core.Entities.OrganizationUser orgUser, bool active)
+ {
+ if (active && orgUser.Status == OrganizationUserStatusType.Revoked)
+ {
+ await _organizationService.RestoreUserAsync(orgUser, EventSystemUser.SCIM, _userService);
+ return true;
+ }
+ else if (!active && orgUser.Status != OrganizationUserStatusType.Revoked)
+ {
+ await _organizationService.RevokeUserAsync(orgUser, EventSystemUser.SCIM);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/bitwarden_license/src/Scim/Users/PostUserCommand.cs b/bitwarden_license/src/Scim/Users/PostUserCommand.cs
new file mode 100644
index 000000000..3526cf5ab
--- /dev/null
+++ b/bitwarden_license/src/Scim/Users/PostUserCommand.cs
@@ -0,0 +1,88 @@
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.Models.Data;
+using Bit.Core.Models.Data.Organizations.OrganizationUsers;
+using Bit.Core.Repositories;
+using Bit.Core.Services;
+using Bit.Core.Utilities;
+using Bit.Scim.Context;
+using Bit.Scim.Models;
+using Bit.Scim.Users.Interfaces;
+
+namespace Bit.Scim.Users;
+
+public class PostUserCommand : IPostUserCommand
+{
+ private readonly IOrganizationUserRepository _organizationUserRepository;
+ private readonly IOrganizationService _organizationService;
+ private readonly IScimContext _scimContext;
+
+ public PostUserCommand(
+ IOrganizationUserRepository organizationUserRepository,
+ IOrganizationService organizationService,
+ IScimContext scimContext)
+ {
+ _organizationUserRepository = organizationUserRepository;
+ _organizationService = organizationService;
+ _scimContext = scimContext;
+ }
+
+ public async Task PostUserAsync(Guid organizationId, ScimUserRequestModel model)
+ {
+ var email = model.PrimaryEmail?.ToLowerInvariant();
+ if (string.IsNullOrWhiteSpace(email))
+ {
+ switch (_scimContext.RequestScimProvider)
+ {
+ case ScimProviderType.AzureAd:
+ email = model.UserName?.ToLowerInvariant();
+ break;
+ default:
+ email = model.WorkEmail?.ToLowerInvariant();
+ if (string.IsNullOrWhiteSpace(email))
+ {
+ email = model.Emails?.FirstOrDefault()?.Value?.ToLowerInvariant();
+ }
+ break;
+ }
+ }
+
+ if (string.IsNullOrWhiteSpace(email) || !model.Active)
+ {
+ throw new BadRequestException();
+ }
+
+ var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);
+ var orgUserByEmail = orgUsers.FirstOrDefault(ou => ou.Email?.ToLowerInvariant() == email);
+ if (orgUserByEmail != null)
+ {
+ throw new ConflictException();
+ }
+
+ string externalId = null;
+ if (!string.IsNullOrWhiteSpace(model.ExternalId))
+ {
+ externalId = model.ExternalId;
+ }
+ else if (!string.IsNullOrWhiteSpace(model.UserName))
+ {
+ externalId = model.UserName;
+ }
+ else
+ {
+ externalId = CoreHelpers.RandomString(15);
+ }
+
+ var orgUserByExternalId = orgUsers.FirstOrDefault(ou => ou.ExternalId == externalId);
+ if (orgUserByExternalId != null)
+ {
+ throw new ConflictException();
+ }
+
+ var invitedOrgUser = await _organizationService.InviteUserAsync(organizationId, EventSystemUser.SCIM, email,
+ OrganizationUserType.User, false, externalId, new List(), new List());
+ var orgUser = await _organizationUserRepository.GetDetailsByIdAsync(invitedOrgUser.Id);
+
+ return orgUser;
+ }
+}
diff --git a/bitwarden_license/src/Scim/Utilities/ApiKeyAuthenticationHandler.cs b/bitwarden_license/src/Scim/Utilities/ApiKeyAuthenticationHandler.cs
new file mode 100644
index 000000000..4e7e7ceb7
--- /dev/null
+++ b/bitwarden_license/src/Scim/Utilities/ApiKeyAuthenticationHandler.cs
@@ -0,0 +1,89 @@
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using Bit.Core.Enums;
+using Bit.Core.Repositories;
+using Bit.Scim.Context;
+using IdentityModel;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.Extensions.Options;
+
+namespace Bit.Scim.Utilities;
+
+public class ApiKeyAuthenticationHandler : AuthenticationHandler
+{
+ private readonly IOrganizationRepository _organizationRepository;
+ private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
+ private readonly IScimContext _scimContext;
+
+ public ApiKeyAuthenticationHandler(
+ IOptionsMonitor options,
+ ILoggerFactory logger,
+ UrlEncoder encoder,
+ ISystemClock clock,
+ IOrganizationRepository organizationRepository,
+ IOrganizationApiKeyRepository organizationApiKeyRepository,
+ IScimContext scimContext) :
+ base(options, logger, encoder, clock)
+ {
+ _organizationRepository = organizationRepository;
+ _organizationApiKeyRepository = organizationApiKeyRepository;
+ _scimContext = scimContext;
+ }
+
+ protected override async Task HandleAuthenticateAsync()
+ {
+ var endpoint = Context.GetEndpoint();
+ if (endpoint?.Metadata?.GetMetadata() != null)
+ {
+ return AuthenticateResult.NoResult();
+ }
+
+ if (!_scimContext.OrganizationId.HasValue || _scimContext.Organization == null)
+ {
+ Logger.LogWarning("No organization.");
+ return AuthenticateResult.Fail("Invalid parameters");
+ }
+
+ if (!Request.Headers.TryGetValue("Authorization", out var authHeader) || authHeader.Count != 1)
+ {
+ Logger.LogWarning("An API request was received without the Authorization header");
+ return AuthenticateResult.Fail("Invalid parameters");
+ }
+ var apiKey = authHeader.ToString();
+ if (apiKey.StartsWith("Bearer "))
+ {
+ apiKey = apiKey.Substring(7);
+ }
+
+ if (!_scimContext.Organization.Enabled || !_scimContext.Organization.UseScim ||
+ _scimContext.ScimConfiguration == null || !_scimContext.ScimConfiguration.Enabled)
+ {
+ Logger.LogInformation("Org {organizationId} not able to use Scim.", _scimContext.OrganizationId);
+ return AuthenticateResult.Fail("Invalid parameters");
+ }
+
+ var orgApiKey = (await _organizationApiKeyRepository
+ .GetManyByOrganizationIdTypeAsync(_scimContext.Organization.Id, OrganizationApiKeyType.Scim))
+ .FirstOrDefault();
+ if (orgApiKey?.ApiKey != apiKey)
+ {
+ Logger.LogWarning("An API request was received with an invalid API key: {apiKey}", apiKey);
+ return AuthenticateResult.Fail("Invalid parameters");
+ }
+
+ Logger.LogInformation("Org {organizationId} authenticated", _scimContext.OrganizationId);
+
+ var claims = new[]
+ {
+ new Claim(JwtClaimTypes.ClientId, $"organization.{_scimContext.OrganizationId.Value}"),
+ new Claim("client_sub", _scimContext.OrganizationId.Value.ToString()),
+ new Claim(JwtClaimTypes.Scope, "api.scim"),
+ };
+ var identity = new ClaimsIdentity(claims, nameof(ApiKeyAuthenticationHandler));
+ var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity),
+ ApiKeyAuthenticationOptions.DefaultScheme);
+
+ return AuthenticateResult.Success(ticket);
+ }
+}
diff --git a/bitwarden_license/src/Scim/Utilities/ApiKeyAuthenticationOptions.cs b/bitwarden_license/src/Scim/Utilities/ApiKeyAuthenticationOptions.cs
new file mode 100644
index 000000000..f0015226b
--- /dev/null
+++ b/bitwarden_license/src/Scim/Utilities/ApiKeyAuthenticationOptions.cs
@@ -0,0 +1,8 @@
+using Microsoft.AspNetCore.Authentication;
+
+namespace Bit.Scim.Utilities;
+
+public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
+{
+ public const string DefaultScheme = "ScimApiKey";
+}
diff --git a/bitwarden_license/src/Scim/Utilities/ExceptionHandlerFilterAttribute.cs b/bitwarden_license/src/Scim/Utilities/ExceptionHandlerFilterAttribute.cs
new file mode 100644
index 000000000..004a8b844
--- /dev/null
+++ b/bitwarden_license/src/Scim/Utilities/ExceptionHandlerFilterAttribute.cs
@@ -0,0 +1,43 @@
+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;
+ }
+ else if (exception is BadRequestException)
+ {
+ statusCode = StatusCodes.Status400BadRequest;
+ }
+ else if (exception is ConflictException)
+ {
+ statusCode = StatusCodes.Status409Conflict;
+ }
+
+ scimErrorResponseModel.Status = statusCode;
+
+ context.HttpContext.Response.StatusCode = statusCode;
+ context.Result = new ObjectResult(scimErrorResponseModel);
+ }
+}
diff --git a/bitwarden_license/src/Scim/Utilities/ScimConstants.cs b/bitwarden_license/src/Scim/Utilities/ScimConstants.cs
new file mode 100644
index 000000000..219be6534
--- /dev/null
+++ b/bitwarden_license/src/Scim/Utilities/ScimConstants.cs
@@ -0,0 +1,9 @@
+namespace Bit.Scim.Utilities;
+
+public static class ScimConstants
+{
+ public const string Scim2SchemaListResponse = "urn:ietf:params:scim:api:messages:2.0:ListResponse";
+ public const string Scim2SchemaError = "urn:ietf:params:scim:api:messages:2.0:Error";
+ public const string Scim2SchemaUser = "urn:ietf:params:scim:schemas:core:2.0:User";
+ public const string Scim2SchemaGroup = "urn:ietf:params:scim:schemas:core:2.0:Group";
+}
diff --git a/bitwarden_license/src/Scim/Utilities/ScimContextMiddleware.cs b/bitwarden_license/src/Scim/Utilities/ScimContextMiddleware.cs
new file mode 100644
index 000000000..6d5f3e1bf
--- /dev/null
+++ b/bitwarden_license/src/Scim/Utilities/ScimContextMiddleware.cs
@@ -0,0 +1,22 @@
+using Bit.Core.Repositories;
+using Bit.Core.Settings;
+using Bit.Scim.Context;
+
+namespace Bit.Scim.Utilities;
+
+public class ScimContextMiddleware
+{
+ private readonly RequestDelegate _next;
+
+ public ScimContextMiddleware(RequestDelegate next)
+ {
+ _next = next;
+ }
+
+ public async Task Invoke(HttpContext httpContext, IScimContext scimContext, GlobalSettings globalSettings,
+ IOrganizationRepository organizationRepository, IOrganizationConnectionRepository organizationConnectionRepository)
+ {
+ await scimContext.BuildAsync(httpContext, globalSettings, organizationRepository, organizationConnectionRepository);
+ await _next.Invoke(httpContext);
+ }
+}
diff --git a/bitwarden_license/src/Scim/Utilities/ScimServiceCollectionExtensions.cs b/bitwarden_license/src/Scim/Utilities/ScimServiceCollectionExtensions.cs
new file mode 100644
index 000000000..fff70760d
--- /dev/null
+++ b/bitwarden_license/src/Scim/Utilities/ScimServiceCollectionExtensions.cs
@@ -0,0 +1,35 @@
+using Bit.Core.OrganizationFeatures.OrganizationUsers;
+using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
+using Bit.Scim.Groups;
+using Bit.Scim.Groups.Interfaces;
+using Bit.Scim.Users;
+using Bit.Scim.Users.Interfaces;
+
+namespace Bit.Scim.Utilities;
+
+public static class ScimServiceCollectionExtensions
+{
+ public static void AddScimGroupCommands(this IServiceCollection services)
+ {
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ }
+
+ public static void AddScimGroupQueries(this IServiceCollection services)
+ {
+ services.AddScoped();
+ }
+
+ public static void AddScimUserCommands(this IServiceCollection services)
+ {
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ }
+
+ public static void AddScimUserQueries(this IServiceCollection services)
+ {
+ services.AddScoped();
+ }
+}
diff --git a/bitwarden_license/src/Scim/appsettings.Development.json b/bitwarden_license/src/Scim/appsettings.Development.json
new file mode 100644
index 000000000..32253a93c
--- /dev/null
+++ b/bitwarden_license/src/Scim/appsettings.Development.json
@@ -0,0 +1,35 @@
+{
+ "globalSettings": {
+ "baseServiceUri": {
+ "vault": "https://localhost:8080",
+ "api": "http://localhost:4000",
+ "identity": "http://localhost:33656",
+ "admin": "http://localhost:62911",
+ "notifications": "http://localhost:61840",
+ "sso": "http://localhost:51822",
+ "internalNotifications": "http://localhost:61840",
+ "internalAdmin": "http://localhost:62911",
+ "internalIdentity": "http://localhost:33656",
+ "internalApi": "http://localhost:4000",
+ "internalVault": "https://localhost:8080",
+ "internalSso": "http://localhost:51822",
+ "internalScim": "http://localhost:44559"
+ },
+ "mail": {
+ "smtp": {
+ "host": "localhost",
+ "port": 10250
+ }
+ },
+ "attachment": {
+ "connectionString": "UseDevelopmentStorage=true",
+ "baseUrl": "http://localhost:4000/attachments/"
+ },
+ "events": {
+ "connectionString": "UseDevelopmentStorage=true"
+ },
+ "storage": {
+ "connectionString": "UseDevelopmentStorage=true"
+ }
+ }
+}
diff --git a/bitwarden_license/src/Scim/appsettings.Production.json b/bitwarden_license/src/Scim/appsettings.Production.json
new file mode 100644
index 000000000..d9efbcda1
--- /dev/null
+++ b/bitwarden_license/src/Scim/appsettings.Production.json
@@ -0,0 +1,42 @@
+{
+ "globalSettings": {
+ "baseServiceUri": {
+ "vault": "https://vault.bitwarden.com",
+ "api": "https://api.bitwarden.com",
+ "identity": "https://identity.bitwarden.com",
+ "admin": "https://admin.bitwarden.com",
+ "notifications": "https://notifications.bitwarden.com",
+ "sso": "https://sso.bitwarden.com",
+ "internalNotifications": "https://notifications.bitwarden.com",
+ "internalAdmin": "https://admin.bitwarden.com",
+ "internalIdentity": "https://identity.bitwarden.com",
+ "internalApi": "https://api.bitwarden.com",
+ "internalVault": "https://vault.bitwarden.com",
+ "internalSso": "https://sso.bitwarden.com",
+ "internalScim": "https://scim.bitwarden.com"
+ },
+ "braintree": {
+ "production": true
+ },
+ "bitPay": {
+ "production": true
+ }
+ },
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
+ },
+ "Console": {
+ "IncludeScopes": true,
+ "LogLevel": {
+ "Default": "Warning",
+ "System": "Warning",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+ }
+}
diff --git a/bitwarden_license/src/Scim/appsettings.QA.json b/bitwarden_license/src/Scim/appsettings.QA.json
new file mode 100644
index 000000000..f4491a055
--- /dev/null
+++ b/bitwarden_license/src/Scim/appsettings.QA.json
@@ -0,0 +1,42 @@
+{
+ "globalSettings": {
+ "baseServiceUri": {
+ "vault": "https://vault.qa.bitwarden.pw",
+ "api": "https://api.qa.bitwarden.pw",
+ "identity": "https://identity.qa.bitwarden.pw",
+ "admin": "https://admin.qa.bitwarden.pw",
+ "notifications": "https://notifications.qa.bitwarden.pw",
+ "sso": "https://sso.qa.bitwarden.pw",
+ "internalNotifications": "https://notifications.qa.bitwarden.pw",
+ "internalAdmin": "https://admin.qa.bitwarden.pw",
+ "internalIdentity": "https://identity.qa.bitwarden.pw",
+ "internalApi": "https://api.qa.bitwarden.pw",
+ "internalVault": "https://vault.qa.bitwarden.pw",
+ "internalSso": "https://sso.qa.bitwarden.pw",
+ "internalScim": "https://scim.qa.bitwarden.pw"
+ },
+ "braintree": {
+ "production": false
+ },
+ "bitPay": {
+ "production": false
+ }
+ },
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
+ },
+ "Console": {
+ "IncludeScopes": true,
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Debug",
+ "Microsoft": "Debug",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+ }
+}
diff --git a/bitwarden_license/src/Scim/appsettings.json b/bitwarden_license/src/Scim/appsettings.json
new file mode 100644
index 000000000..630896a65
--- /dev/null
+++ b/bitwarden_license/src/Scim/appsettings.json
@@ -0,0 +1,63 @@
+{
+ "globalSettings": {
+ "selfHosted": false,
+ "siteName": "Bitwarden",
+ "projectName": "Scim",
+ "stripe": {
+ "apiKey": "SECRET"
+ },
+ "sqlServer": {
+ "connectionString": "SECRET"
+ },
+ "mail": {
+ "sendGridApiKey": "SECRET",
+ "amazonConfigSetName": "Email",
+ "replyToEmail": "no-reply@bitwarden.com"
+ },
+ "identityServer": {
+ "certificateThumbprint": "SECRET"
+ },
+ "dataProtection": {
+ "certificateThumbprint": "SECRET"
+ },
+ "storage": {
+ "connectionString": "SECRET"
+ },
+ "events": {
+ "connectionString": "SECRET"
+ },
+ "serviceBus": {
+ "connectionString": "SECRET",
+ "applicationCacheTopicName": "SECRET"
+ },
+ "documentDb": {
+ "uri": "SECRET",
+ "key": "SECRET"
+ },
+ "sentry": {
+ "dsn": "SECRET"
+ },
+ "notificationHub": {
+ "connectionString": "SECRET",
+ "hubName": "SECRET"
+ },
+ "braintree": {
+ "production": false,
+ "merchantId": "SECRET",
+ "publicKey": "SECRET",
+ "privateKey": "SECRET"
+ },
+ "bitPay": {
+ "production": false,
+ "token": "SECRET",
+ "notificationUrl": "https://bitwarden.com/SECRET"
+ },
+ "amazon": {
+ "accessKeyId": "SECRET",
+ "accessKeySecret": "SECRET",
+ "region": "SECRET"
+ }
+ },
+ "scimSettings": {
+ }
+}
diff --git a/bitwarden_license/src/Scim/build.ps1 b/bitwarden_license/src/Scim/build.ps1
new file mode 100644
index 000000000..21ed14161
--- /dev/null
+++ b/bitwarden_license/src/Scim/build.ps1
@@ -0,0 +1,12 @@
+$dir = Split-Path -Parent $MyInvocation.MyCommand.Path
+
+echo "`n## Building Scim"
+
+echo "`nBuilding app"
+echo ".NET Core version $(dotnet --version)"
+echo "Restore"
+dotnet restore $dir\Scim.csproj
+echo "Clean"
+dotnet clean $dir\Scim.csproj -c "Release" -o $dir\obj\Azure\publish
+echo "Publish"
+dotnet publish $dir\Scim.csproj -c "Release" -o $dir\obj\Azure\publish
diff --git a/bitwarden_license/src/Scim/build.sh b/bitwarden_license/src/Scim/build.sh
new file mode 100644
index 000000000..b6deb89b4
--- /dev/null
+++ b/bitwarden_license/src/Scim/build.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+set -e
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && "pwd" )"
+
+echo -e "\n## Building SCIM"
+
+echo -e "\nBuilding app"
+echo ".NET Core version $(dotnet --version)"
+echo "Restore"
+dotnet restore "$DIR/Scim.csproj"
+echo "Clean"
+dotnet clean "$DIR/Scim.csproj" -c "Release" -o "$DIR/obj/build-output/publish"
+echo "Publish"
+dotnet publish "$DIR/Scim.csproj" -c "Release" -o "$DIR/obj/build-output/publish"
diff --git a/bitwarden_license/src/Scim/entrypoint.sh b/bitwarden_license/src/Scim/entrypoint.sh
new file mode 100644
index 000000000..0d54517bb
--- /dev/null
+++ b/bitwarden_license/src/Scim/entrypoint.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# Setup
+
+GROUPNAME="bitwarden"
+USERNAME="bitwarden"
+
+LUID=${LOCAL_UID:-0}
+LGID=${LOCAL_GID:-0}
+
+# Step down from host root to well-known nobody/nogroup user
+
+if [ $LUID -eq 0 ]
+then
+ LUID=65534
+fi
+if [ $LGID -eq 0 ]
+then
+ LGID=65534
+fi
+
+# Create user and group
+
+groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
+groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
+useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
+usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
+mkhomedir_helper $USERNAME
+
+# The rest...
+
+chown -R $USERNAME:$GROUPNAME /app
+mkdir -p /etc/bitwarden/core
+mkdir -p /etc/bitwarden/logs
+mkdir -p /etc/bitwarden/ca-certificates
+chown -R $USERNAME:$GROUPNAME /etc/bitwarden
+
+cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
+ && update-ca-certificates
+
+exec gosu $USERNAME:$GROUPNAME dotnet /app/Scim.dll
diff --git a/bitwarden_license/src/Scim/packages.lock.json b/bitwarden_license/src/Scim/packages.lock.json
new file mode 100644
index 000000000..e0ee3394c
--- /dev/null
+++ b/bitwarden_license/src/Scim/packages.lock.json
@@ -0,0 +1,3063 @@
+{
+ "version": 1,
+ "dependencies": {
+ "net6.0": {
+ "Microsoft.VisualStudio.Web.CodeGeneration.Design": {
+ "type": "Direct",
+ "requested": "[5.0.2, )",
+ "resolved": "5.0.2",
+ "contentHash": "9eTZV7W+S2iO2AJD03xXyXJZ+Nf71Y25gMXhqyXb8bB63jPfn+VQhV8I1lb6J+NR3jW98m5EB9QBftBSrjgiYQ==",
+ "dependencies": {
+ "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": "5.0.2"
+ }
+ },
+ "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"
+ }
+ },
+ "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.24.0",
+ "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
+ "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.6.0",
+ "contentHash": "EycyMsb6rD2PK9P0SyibFfEhvWWttdrYhyPF4f41uzdB/44yQlV+2Wehxyg489Rj6gbPvSPgbKq0xsHJBhipZA==",
+ "dependencies": {
+ "Azure.Core": "1.24.0",
+ "Microsoft.Identity.Client": "4.39.0",
+ "Microsoft.Identity.Client.Extensions.Msal": "2.19.3",
+ "System.Memory": "4.5.4",
+ "System.Security.Cryptography.ProtectedData": "4.7.0",
+ "System.Text.Json": "4.7.2",
+ "System.Threading.Tasks.Extensions": "4.5.4"
+ }
+ },
+ "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"
+ }
+ },
+ "Dapper": {
+ "type": "Transitive",
+ "resolved": "2.0.123",
+ "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
+ },
+ "Fido2": {
+ "type": "Transitive",
+ "resolved": "3.0.1",
+ "contentHash": "S0Bz1vfcKlO4Jase3AWp5XnQ746psf4oGx5kL+D2A10j1SsjoAOAIIpanSwfi0cEepDHgk1bClcOKY5TjOzGdA==",
+ "dependencies": {
+ "Fido2.Models": "3.0.1",
+ "Microsoft.Extensions.Http": "6.0.0",
+ "NSec.Cryptography": "22.4.0",
+ "System.Formats.Cbor": "6.0.0",
+ "System.IdentityModel.Tokens.Jwt": "6.17.0"
+ }
+ },
+ "Fido2.AspNet": {
+ "type": "Transitive",
+ "resolved": "3.0.1",
+ "contentHash": "5n5shEXD7RFUyTesjUHGDjkpgES7j4KotQo1GwUcS08k+fx+1tl/zCFHJ9RFDuUwO+S681ZILT2PyA67IPYpaA==",
+ "dependencies": {
+ "Fido2": "3.0.1",
+ "Fido2.Models": "3.0.1"
+ }
+ },
+ "Fido2.Models": {
+ "type": "Transitive",
+ "resolved": "3.0.1",
+ "contentHash": "mgjcuGETuYSCUEaZG+jQeeuuEMkDLc4GDJHBvKDdOz6oSOWp5adPdWP4btZx7Pi+9fu4szN3JIjJmby67MaILw=="
+ },
+ "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"
+ }
+ },
+ "libsodium": {
+ "type": "Transitive",
+ "resolved": "1.0.18.2",
+ "contentHash": "flArHoVdscSzyV8ZdPV+bqqY2TTFlaN+xZf/vIqsmHI51KVcD/mOdUPaK3n/k/wGKz8dppiktXUqSmf3AXFgig=="
+ },
+ "linq2db": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "6/u1EzQlV25bhN0Ej/I5dLV5Hgxun+ww/TX2VnMBnSVytED2VzQGeFIO/14I624GkfPOtB79x1ooL3F18dAbdw=="
+ },
+ "linq2db.EntityFrameworkCore": {
+ "type": "Transitive",
+ "resolved": "6.11.0",
+ "contentHash": "mS+L6HyVHP3oJaTuFVZswNmPpTfYleGjDTo2IWBJmYXZpSQ5EPw1DpHzmyLAiQd+93ofy0Ala+9HWzv6/k73ZQ==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore.Relational": "6.0.5",
+ "linq2db": "4.4.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.CSharp": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
+ },
+ "Microsoft.Data.SqlClient": {
+ "type": "Transitive",
+ "resolved": "5.0.1",
+ "contentHash": "uu8dfrsx081cSbEevWuZAvqdmANDGJkbLBL2G3j0LAZxX1Oy8RCVAaC4Lcuak6jNicWP6CWvHqBTIEmQNSxQlw==",
+ "dependencies": {
+ "Azure.Identity": "1.6.0",
+ "Microsoft.Data.SqlClient.SNI.runtime": "5.0.1",
+ "Microsoft.Identity.Client": "4.45.0",
+ "Microsoft.IdentityModel.JsonWebTokens": "6.21.0",
+ "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.21.0",
+ "Microsoft.SqlServer.Server": "1.0.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": "5.0.1",
+ "contentHash": "y0X5MxiNdbITJYoafJ2ruaX6hqO0twpCGR/ipiDOe85JKLU8WL4TuAQfDe5qtt3bND5Je26HnrarLSAMMnVTNg=="
+ },
+ "Microsoft.Data.Sqlite.Core": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "bui5wPPqq9OwTL5A+YJPcVStTPrOFcLwg/kAVWyqdjrTief4kTK/3bNv0MqUDVNgAUG8pcFbtdc674CIh1F3gw==",
+ "dependencies": {
+ "SQLitePCLRaw.core": "2.1.2"
+ }
+ },
+ "Microsoft.EntityFrameworkCore": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "xb10XFoPf/gWu8ik5v7xnVyUY7W21LBOLtT7PidzwYVdnE3aKuQ/bIZLcQuY7rdDNT89/wse2q5FRjm207cIMQ==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore.Abstractions": "6.0.12",
+ "Microsoft.EntityFrameworkCore.Analyzers": "6.0.12",
+ "Microsoft.Extensions.Caching.Memory": "6.0.1",
+ "Microsoft.Extensions.DependencyInjection": "6.0.1",
+ "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.12",
+ "contentHash": "hvRytAcLhrb35HmtMjYWsNZZLt39ryuN7j04lDchRa9VToreyqgo5gMniTdQ6MfCflxtGnDes65V/Y2pjbEyWg=="
+ },
+ "Microsoft.EntityFrameworkCore.Analyzers": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "ZDUY+KlsIyKdfvIJeNdqRiPExFQ5GRZVdx/Cp52vhpCJRImYv34O0Xfmw2eiLu4qe1jmM2pTzAAFKELaKwtj/w=="
+ },
+ "Microsoft.EntityFrameworkCore.Relational": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "HBtRGHtF0Vf+BIQTkRGiopmE5rLYhj59xPpd17S1tLgYpiHDVbepCuHwh5H63fzjO99Z4tW5wmmEGF7KnD91WQ==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore": "6.0.12",
+ "Microsoft.Extensions.Configuration.Abstractions": "6.0.0"
+ }
+ },
+ "Microsoft.EntityFrameworkCore.Sqlite": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "2Hutlqt07bnWZFtYqT1lj0otX8ygMyBikysGnfQNF2TK3i5GqSTeJ8tqNi/URiI9II7Cyl15A0rflXmFoySuIw==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore.Sqlite.Core": "6.0.12",
+ "SQLitePCLRaw.bundle_e_sqlite3": "2.1.2"
+ }
+ },
+ "Microsoft.EntityFrameworkCore.Sqlite.Core": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "07vKE7+t9Z2BfGmHuJwNZNv8m1GWt7ZpYYHFh1tQg1oC6FJ78bSaFzLawsf2NK6CLhbB8DBsjE0rRhxMJ4rXsA==",
+ "dependencies": {
+ "Microsoft.Data.Sqlite.Core": "6.0.12",
+ "Microsoft.EntityFrameworkCore.Relational": "6.0.12",
+ "Microsoft.Extensions.DependencyModel": "6.0.0"
+ }
+ },
+ "Microsoft.EntityFrameworkCore.SqlServer": {
+ "type": "Transitive",
+ "resolved": "6.0.12",
+ "contentHash": "bdKnSz1w+WZz9QYWhs3wwGuMn4YssjdR+HOBpzChQ6C3+dblq4Pammm5fzugcPOhTgCiWftOT2jPOT5hEy4bYg==",
+ "dependencies": {
+ "Microsoft.Data.SqlClient": "2.1.4",
+ "Microsoft.EntityFrameworkCore.Relational": "6.0.12"
+ }
+ },
+ "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.1",
+ "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==",
+ "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": "6.0.0",
+ "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==",
+ "dependencies": {
+ "System.Buffers": "4.5.1",
+ "System.Memory": "4.5.4",
+ "System.Runtime.CompilerServices.Unsafe": "6.0.0",
+ "System.Text.Encodings.Web": "6.0.0",
+ "System.Text.Json": "6.0.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.Http": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
+ "Microsoft.Extensions.Logging": "6.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "6.0.0",
+ "Microsoft.Extensions.Options": "6.0.0"
+ }
+ },
+ "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.45.0",
+ "contentHash": "ircobISCLWbtE5eEoLKU+ldfZ8O41vg4lcy38KRj/znH17jvBiAl8oxcyNp89CsuqE3onxIpn21Ca7riyDDrRw==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Abstractions": "6.18.0"
+ }
+ },
+ "Microsoft.Identity.Client.Extensions.Msal": {
+ "type": "Transitive",
+ "resolved": "2.19.3",
+ "contentHash": "zVVZjn8aW7W79rC1crioDgdOwaFTQorsSO6RgVlDDjc7MvbEGz071wSNrjVhzR0CdQn6Sefx7Abf1o7vasmrLg==",
+ "dependencies": {
+ "Microsoft.Identity.Client": "4.38.0",
+ "System.Security.Cryptography.ProtectedData": "4.5.0"
+ }
+ },
+ "Microsoft.IdentityModel.Abstractions": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "XeE6LQtD719Qs2IG7HDi1TSw9LIkDbJ33xFiOBoHbApVw/8GpIBCbW+t7RwOjErUDyXZvjhZliwRkkLb8Z1uzg=="
+ },
+ "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.21.0",
+ "contentHash": "d3h1/BaMeylKTkdP6XwRCxuOoDJZ44V9xaXr6gl5QxmpnZGdoK3bySo3OQN8ehRLJHShb94ElLUvoXyglQtgAw==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Tokens": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Logging": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "tuEhHIQwvBEhMf8I50hy8FHmRSUkffDFP5EdLsSDV4qRcl2wvOPkQxYqEzWkh+ytW6sbdJGEXElGhmhDfAxAKg==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Abstractions": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Protocols": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "0FqY5cTLQKtHrClzHEI+QxJl8OBT2vUiEQQB7UKk832JDiJJmetzYZ3AdSrPjN/3l3nkhByeWzXnhrX0JbifKg==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Logging": "6.21.0",
+ "Microsoft.IdentityModel.Tokens": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Protocols.OpenIdConnect": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "vtSKL7n6EnAsLyxmiviusm6LKrblT2ndnNqN6rvVq6iIHAnPCK9E2DkDx6h1Jrpy1cvbp40r0cnTg23nhEAGTA==",
+ "dependencies": {
+ "Microsoft.IdentityModel.Protocols": "6.21.0",
+ "System.IdentityModel.Tokens.Jwt": "6.21.0"
+ }
+ },
+ "Microsoft.IdentityModel.Tokens": {
+ "type": "Transitive",
+ "resolved": "6.21.0",
+ "contentHash": "AAEHZvZyb597a+QJSmtxH3n2P1nIJGpZ4Q89GTenknRx6T6zyfzf592yW/jA5e8EHN4tNMjjXHQaYWEq5+L05w==",
+ "dependencies": {
+ "Microsoft.CSharp": "4.5.0",
+ "Microsoft.IdentityModel.Logging": "6.21.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.SqlServer.Server": {
+ "type": "Transitive",
+ "resolved": "1.0.0",
+ "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug=="
+ },
+ "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.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.8",
+ "contentHash": "wKa8MJEJaj0xQXUQZGv7q/KfPID23jSSvFFtljMworrv7dNajr0GN8PCU1SpywqHjMWdYEfK29DY1aYbiISbQg==",
+ "dependencies": {
+ "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+ }
+ },
+ "Npgsql.EntityFrameworkCore.PostgreSQL": {
+ "type": "Transitive",
+ "resolved": "6.0.8",
+ "contentHash": "YJRpO+3wXQyWuwRUCVJj/Rsn46sY0bZ6uCGOEFApiRe0ZYJ6N6TxZUWKbTNJYjesickcLGzynOerpSbDJX1AYg==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore": "6.0.12",
+ "Microsoft.EntityFrameworkCore.Abstractions": "6.0.12",
+ "Microsoft.EntityFrameworkCore.Relational": "6.0.12",
+ "Npgsql": "6.0.8"
+ }
+ },
+ "NSec.Cryptography": {
+ "type": "Transitive",
+ "resolved": "22.4.0",
+ "contentHash": "lEntcPYd7h3aZ8xxi/y/4TML7o8w0GEGqd+w4L1omqFLbdCBmhxJAeO2YBmv/fXbJKgKCQLm7+TD4bR605PEUQ==",
+ "dependencies": {
+ "libsodium": "[1.0.18.2, 1.0.19)"
+ }
+ },
+ "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.2",
+ "contentHash": "KvlZ800CnEuEGnxj5OT1fCKGjQXxW5kpPlCP91JqBYG+2Z3927eqXmlX6LLKUt4swqE8ZsEQ+Zkpab8bqstf4g==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore.Relational": "[6.0.7, 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"
+ }
+ },
+ "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.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=="
+ },
+ "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"
+ }
+ },
+ "SQLitePCLRaw.bundle_e_sqlite3": {
+ "type": "Transitive",
+ "resolved": "2.1.2",
+ "contentHash": "ilkvNhrTersLmIVAcDwwPqfhUFCg19Z1GVMvCSi3xk6Akq94f4qadLORQCq/T8+9JgMiPs+F/NECw5uauviaNw==",
+ "dependencies": {
+ "SQLitePCLRaw.lib.e_sqlite3": "2.1.2",
+ "SQLitePCLRaw.provider.e_sqlite3": "2.1.2"
+ }
+ },
+ "SQLitePCLRaw.core": {
+ "type": "Transitive",
+ "resolved": "2.1.2",
+ "contentHash": "A8EBepVqY2lnAp3a8jnhbgzF2tlj2S3HcJQGANTYg/TbYbKa8Z5cM1h74An/vy0svhfzT7tVY0sFmUglLgv+2g==",
+ "dependencies": {
+ "System.Memory": "4.5.3"
+ }
+ },
+ "SQLitePCLRaw.lib.e_sqlite3": {
+ "type": "Transitive",
+ "resolved": "2.1.2",
+ "contentHash": "zibGtku8M4Eea1R3ZCAxc86QbNvyEN17mAcQkvWKBuHvRpMiK2g5anG4R5Be7cWKSd1i6baYz8y4dMMAKcXKPg=="
+ },
+ "SQLitePCLRaw.provider.e_sqlite3": {
+ "type": "Transitive",
+ "resolved": "2.1.2",
+ "contentHash": "lxCZarZdvAsMl2zw9bXHrXK6RxVhB4b23iTFhCOdHFhxfbsxLxWf+ocvswJwR/9Wh/E//ddMi+wJGqUKV7VwoA==",
+ "dependencies": {
+ "SQLitePCLRaw.core": "2.1.2"
+ }
+ },
+ "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.0.1",
+ "contentHash": "hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==",
+ "dependencies": {
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Globalization": "4.0.11",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Threading": "4.0.11"
+ }
+ },
+ "System.Collections.Specialized": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "/HKQyVP0yH1I0YtK7KJL/28snxHNH/bi+0lgk/+MbURF6ULhAE31MDI+NZDerNWu264YbxklXCCygISgm+HMug==",
+ "dependencies": {
+ "System.Collections.NonGeneric": "4.0.1",
+ "System.Globalization": "4.0.11",
+ "System.Globalization.Extensions": "4.0.1",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Threading": "4.0.11"
+ }
+ },
+ "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.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.0.0",
+ "contentHash": "6WVCczFZKXwpWpzd/iJkYnsmWTSFFiU24Xx/YdHXBcu+nFI/ehTgeqdJQFbtRPzbrO3KtRNjvkhtj4t5/WwWsA==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.0.1",
+ "System.Collections": "4.0.11",
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Globalization": "4.0.11",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Threading": "4.0.11",
+ "runtime.native.System": "4.0.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.0.11",
+ "contentHash": "db34f6LHYM0U0JpE+sOmjar27BnqTVkbLJhgfwMpTdgTigG/Hna3m2MYVwnFzGGKnEJk2UXFuoVTr8WUbU91/A==",
+ "dependencies": {
+ "System.Collections": "4.0.11",
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Globalization": "4.0.11",
+ "System.Linq": "4.1.0",
+ "System.Linq.Expressions": "4.1.0",
+ "System.ObjectModel": "4.0.12",
+ "System.Reflection": "4.1.0",
+ "System.Reflection.Emit": "4.0.1",
+ "System.Reflection.Emit.ILGeneration": "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.Threading": "4.0.11"
+ }
+ },
+ "System.Formats.Asn1": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "T6fD00dQ3NTbPDy31m4eQUwKW84s03z0N2C8HpOklyeaDgaJPa/TexP4/SkORMSOwc7WhKifnA6Ya33AkzmafA=="
+ },
+ "System.Formats.Cbor": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "mGaLOoiw7KurJagOOcIsWUoCT5ACIiGxKlCcbYQASefBGXjnCcKTq5Hdjb94eEAKg38zXKlHw4c6EjzgBl9dIw=="
+ },
+ "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.21.0",
+ "contentHash": "JRD8AuypBE+2zYxT3dMJomQVsPYsCqlyZhWel3J1d5nzQokSRyTueF+Q4ID3Jcu6zSZKuzOdJ1MLTkbQsDqcvQ==",
+ "dependencies": {
+ "Microsoft.IdentityModel.JsonWebTokens": "6.21.0",
+ "Microsoft.IdentityModel.Tokens": "6.21.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"
+ }
+ },
+ "YubicoDotNetClient": {
+ "type": "Transitive",
+ "resolved": "1.2.0",
+ "contentHash": "uP5F3Ko1gqZi3lwS2R/jAAwhBxXs/6PKDpS6FdQjsBA5qmF0hQmbtfxM6QHTXOMoWbUtfetG7+LtgmG8T5zDIg==",
+ "dependencies": {
+ "NETStandard.Library": "1.6.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.1, )",
+ "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": "[5.0.1, )",
+ "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": "[2023.1.0, )",
+ "Dapper": "[2.0.123, )"
+ }
+ },
+ "infrastructure.entityframework": {
+ "type": "Project",
+ "dependencies": {
+ "AutoMapper.Extensions.Microsoft.DependencyInjection": "[11.0.0, )",
+ "Core": "[2023.1.0, )",
+ "Microsoft.EntityFrameworkCore.Relational": "[6.0.12, )",
+ "Microsoft.EntityFrameworkCore.SqlServer": "[6.0.12, )",
+ "Microsoft.EntityFrameworkCore.Sqlite": "[6.0.12, )",
+ "Npgsql.EntityFrameworkCore.PostgreSQL": "[6.0.8, )",
+ "Pomelo.EntityFrameworkCore.MySql": "[6.0.2, )",
+ "linq2db.EntityFrameworkCore": "[6.11.0, )"
+ }
+ },
+ "sharedweb": {
+ "type": "Project",
+ "dependencies": {
+ "Core": "[2023.1.0, )",
+ "Infrastructure.Dapper": "[2023.1.0, )",
+ "Infrastructure.EntityFramework": "[2023.1.0, )"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/bitwarden_license/src/Sso/Controllers/AccountController.cs b/bitwarden_license/src/Sso/Controllers/AccountController.cs
index 3ef99f0fa..fdbcf0f0d 100644
--- a/bitwarden_license/src/Sso/Controllers/AccountController.cs
+++ b/bitwarden_license/src/Sso/Controllers/AccountController.cs
@@ -1,8 +1,16 @@
-using Bit.Core;
+using System.Security.Claims;
+using Bit.Core;
+using Bit.Core.Entities;
using Bit.Core.Enums;
-using Bit.Core.Models.Table;
+using Bit.Core.Models;
+using Bit.Core.Models.Api;
+using Bit.Core.Models.Business.Tokenables;
+using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Services;
+using Bit.Core.Settings;
+using Bit.Core.Tokens;
+using Bit.Core.Utilities;
using Bit.Sso.Models;
using Bit.Sso.Utilities;
using IdentityModel;
@@ -12,675 +20,690 @@ using IdentityServer4.Services;
using IdentityServer4.Stores;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
-using Microsoft.Extensions.Logging;
-using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Security.Claims;
-using System.Threading.Tasks;
-using Bit.Core.Models;
-using Bit.Core.Models.Api;
-using Bit.Core.Utilities;
-using System.Text.Json;
-using Bit.Core.Models.Data;
-using Bit.Core.Settings;
-namespace Bit.Sso.Controllers
+namespace Bit.Sso.Controllers;
+
+public class AccountController : Controller
{
- public class AccountController : Controller
+ private readonly IAuthenticationSchemeProvider _schemeProvider;
+ private readonly IClientStore _clientStore;
+
+ private readonly IIdentityServerInteractionService _interaction;
+ private readonly ILogger _logger;
+ private readonly IOrganizationRepository _organizationRepository;
+ private readonly IOrganizationUserRepository _organizationUserRepository;
+ private readonly IOrganizationService _organizationService;
+ private readonly ISsoConfigRepository _ssoConfigRepository;
+ private readonly ISsoUserRepository _ssoUserRepository;
+ private readonly IUserRepository _userRepository;
+ private readonly IPolicyRepository _policyRepository;
+ private readonly IUserService _userService;
+ private readonly II18nService _i18nService;
+ private readonly UserManager _userManager;
+ private readonly IGlobalSettings _globalSettings;
+ private readonly Core.Services.IEventService _eventService;
+ private readonly IDataProtectorTokenFactory _dataProtector;
+
+ public AccountController(
+ IAuthenticationSchemeProvider schemeProvider,
+ IClientStore clientStore,
+ IIdentityServerInteractionService interaction,
+ ILogger logger,
+ IOrganizationRepository organizationRepository,
+ IOrganizationUserRepository organizationUserRepository,
+ IOrganizationService organizationService,
+ ISsoConfigRepository ssoConfigRepository,
+ ISsoUserRepository ssoUserRepository,
+ IUserRepository userRepository,
+ IPolicyRepository policyRepository,
+ IUserService userService,
+ II18nService i18nService,
+ UserManager userManager,
+ IGlobalSettings globalSettings,
+ Core.Services.IEventService eventService,
+ IDataProtectorTokenFactory dataProtector)
{
- private readonly IAuthenticationSchemeProvider _schemeProvider;
- private readonly IClientStore _clientStore;
+ _schemeProvider = schemeProvider;
+ _clientStore = clientStore;
+ _interaction = interaction;
+ _logger = logger;
+ _organizationRepository = organizationRepository;
+ _organizationUserRepository = organizationUserRepository;
+ _organizationService = organizationService;
+ _userRepository = userRepository;
+ _ssoConfigRepository = ssoConfigRepository;
+ _ssoUserRepository = ssoUserRepository;
+ _policyRepository = policyRepository;
+ _userService = userService;
+ _i18nService = i18nService;
+ _userManager = userManager;
+ _eventService = eventService;
+ _globalSettings = globalSettings;
+ _dataProtector = dataProtector;
+ }
- private readonly IIdentityServerInteractionService _interaction;
- private readonly ILogger _logger;
- private readonly IOrganizationRepository _organizationRepository;
- private readonly IOrganizationUserRepository _organizationUserRepository;
- private readonly IOrganizationService _organizationService;
- private readonly ISsoConfigRepository _ssoConfigRepository;
- private readonly ISsoUserRepository _ssoUserRepository;
- private readonly IUserRepository _userRepository;
- private readonly IPolicyRepository _policyRepository;
- private readonly IUserService _userService;
- private readonly II18nService _i18nService;
- private readonly UserManager _userManager;
- private readonly IGlobalSettings _globalSettings;
- private readonly Core.Services.IEventService _eventService;
-
- public AccountController(
- IAuthenticationSchemeProvider schemeProvider,
- IClientStore clientStore,
- IIdentityServerInteractionService interaction,
- ILogger logger,
- IOrganizationRepository organizationRepository,
- IOrganizationUserRepository organizationUserRepository,
- IOrganizationService organizationService,
- ISsoConfigRepository ssoConfigRepository,
- ISsoUserRepository ssoUserRepository,
- IUserRepository userRepository,
- IPolicyRepository policyRepository,
- IUserService userService,
- II18nService i18nService,
- UserManager userManager,
- IGlobalSettings globalSettings,
- Core.Services.IEventService eventService)
+ [HttpGet]
+ public async Task PreValidate(string domainHint)
+ {
+ try
{
- _schemeProvider = schemeProvider;
- _clientStore = clientStore;
- _interaction = interaction;
- _logger = logger;
- _organizationRepository = organizationRepository;
- _organizationUserRepository = organizationUserRepository;
- _organizationService = organizationService;
- _userRepository = userRepository;
- _ssoConfigRepository = ssoConfigRepository;
- _ssoUserRepository = ssoUserRepository;
- _policyRepository = policyRepository;
- _userService = userService;
- _i18nService = i18nService;
- _userManager = userManager;
- _eventService = eventService;
- _globalSettings = globalSettings;
- }
-
- [HttpGet]
- public async Task PreValidate(string domainHint)
- {
- IActionResult invalidJson(string errorMessageKey, Exception ex = null)
+ // Validate domain_hint provided
+ if (string.IsNullOrWhiteSpace(domainHint))
{
- Response.StatusCode = ex == null ? 400 : 500;
- return Json(new ErrorResponseModel(_i18nService.T(errorMessageKey))
- {
- ExceptionMessage = ex?.Message,
- ExceptionStackTrace = ex?.StackTrace,
- InnerExceptionMessage = ex?.InnerException?.Message,
- });
+ return InvalidJson("NoOrganizationIdentifierProvidedError");
}
+ // Validate organization exists from domain_hint
+ var organization = await _organizationRepository.GetByIdentifierAsync(domainHint);
+ if (organization == null)
+ {
+ return InvalidJson("OrganizationNotFoundByIdentifierError");
+ }
+ if (!organization.UseSso)
+ {
+ return InvalidJson("SsoNotAllowedForOrganizationError");
+ }
+
+ // Validate SsoConfig exists and is Enabled
+ var ssoConfig = await _ssoConfigRepository.GetByIdentifierAsync(domainHint);
+ if (ssoConfig == null)
+ {
+ return InvalidJson("SsoConfigurationNotFoundForOrganizationError");
+ }
+ if (!ssoConfig.Enabled)
+ {
+ return InvalidJson("SsoNotEnabledForOrganizationError");
+ }
+
+ // Validate Authentication Scheme exists and is loaded (cache)
+ var scheme = await _schemeProvider.GetSchemeAsync(organization.Id.ToString());
+ if (scheme == null || !(scheme is IDynamicAuthenticationScheme dynamicScheme))
+ {
+ return InvalidJson("NoSchemeOrHandlerForSsoConfigurationFoundError");
+ }
+
+ // Run scheme validation
try
{
- // Validate domain_hint provided
- if (string.IsNullOrWhiteSpace(domainHint))
- {
- return invalidJson("NoOrganizationIdentifierProvidedError");
- }
-
- // Validate organization exists from domain_hint
- var organization = await _organizationRepository.GetByIdentifierAsync(domainHint);
- if (organization == null)
- {
- return invalidJson("OrganizationNotFoundByIdentifierError");
- }
- if (!organization.UseSso)
- {
- return invalidJson("SsoNotAllowedForOrganizationError");
- }
-
- // Validate SsoConfig exists and is Enabled
- var ssoConfig = await _ssoConfigRepository.GetByIdentifierAsync(domainHint);
- if (ssoConfig == null)
- {
- return invalidJson("SsoConfigurationNotFoundForOrganizationError");
- }
- if (!ssoConfig.Enabled)
- {
- return invalidJson("SsoNotEnabledForOrganizationError");
- }
-
- // Validate Authentication Scheme exists and is loaded (cache)
- var scheme = await _schemeProvider.GetSchemeAsync(organization.Id.ToString());
- if (scheme == null || !(scheme is IDynamicAuthenticationScheme dynamicScheme))
- {
- return invalidJson("NoSchemeOrHandlerForSsoConfigurationFoundError");
- }
-
- // Run scheme validation
- try
- {
- await dynamicScheme.Validate();
- }
- catch (Exception ex)
- {
- var translatedException = _i18nService.GetLocalizedHtmlString(ex.Message);
- var errorKey = "InvalidSchemeConfigurationError";
- if (!translatedException.ResourceNotFound)
- {
- errorKey = ex.Message;
- }
- return invalidJson(errorKey, translatedException.ResourceNotFound ? ex : null);
- }
+ await dynamicScheme.Validate();
}
catch (Exception ex)
{
- return invalidJson("PreValidationError", ex);
- }
-
- // Everything is good!
- return new EmptyResult();
- }
-
- [HttpGet]
- public async Task Login(string returnUrl)
- {
- var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
- if (context.Parameters.AllKeys.Contains("domain_hint") &&
- !string.IsNullOrWhiteSpace(context.Parameters["domain_hint"]))
- {
- return RedirectToAction(nameof(ExternalChallenge), new
+ var translatedException = _i18nService.GetLocalizedHtmlString(ex.Message);
+ var errorKey = "InvalidSchemeConfigurationError";
+ if (!translatedException.ResourceNotFound)
{
- scheme = context.Parameters["domain_hint"],
- returnUrl,
- state = context.Parameters["state"],
- userIdentifier = context.Parameters["session_state"]
- });
- }
- else
- {
- throw new Exception(_i18nService.T("NoDomainHintProvided"));
- }
- }
-
- [HttpGet]
- public IActionResult ExternalChallenge(string scheme, string returnUrl, string state, string userIdentifier)
- {
- if (string.IsNullOrEmpty(returnUrl))
- {
- returnUrl = "~/";
- }
-
- if (!Url.IsLocalUrl(returnUrl) && !_interaction.IsValidReturnUrl(returnUrl))
- {
- throw new Exception(_i18nService.T("InvalidReturnUrl"));
- }
-
- var props = new AuthenticationProperties
- {
- RedirectUri = Url.Action(nameof(ExternalCallback)),
- Items =
- {
- // scheme will get serialized into `State` and returned back
- { "scheme", scheme },
- { "return_url", returnUrl },
- { "state", state },
- { "user_identifier", userIdentifier },
+ errorKey = ex.Message;
}
- };
+ return InvalidJson(errorKey, translatedException.ResourceNotFound ? ex : null);
+ }
- return Challenge(props, scheme);
+ var tokenable = new SsoTokenable(organization, _globalSettings.Sso.SsoTokenLifetimeInSeconds);
+ var token = _dataProtector.Protect(tokenable);
+
+ return new SsoPreValidateResponseModel(token);
}
-
- [HttpGet]
- public async Task ExternalCallback()
+ catch (Exception ex)
{
- // Read external identity from the temporary cookie
- var result = await HttpContext.AuthenticateAsync(
- AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
- if (result?.Succeeded != true)
- {
- throw new Exception(_i18nService.T("ExternalAuthenticationError"));
- }
-
- // Debugging
- var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}");
- _logger.LogDebug("External claims: {@claims}", externalClaims);
-
- // Lookup our user and external provider info
- var (user, provider, providerUserId, claims, ssoConfigData) = await FindUserFromExternalProviderAsync(result);
- if (user == null)
- {
- // This might be where you might initiate a custom workflow for user registration
- // in this sample we don't show how that would be done, as our sample implementation
- // simply auto-provisions new external user
- var userIdentifier = result.Properties.Items.Keys.Contains("user_identifier") ?
- result.Properties.Items["user_identifier"] : null;
- user = await AutoProvisionUserAsync(provider, providerUserId, claims, userIdentifier, ssoConfigData);
- }
-
- if (user != null)
- {
- // This allows us to collect any additional claims or properties
- // for the specific protocols used and store them in the local auth cookie.
- // this is typically used to store data needed for signout from those protocols.
- var additionalLocalClaims = new List();
- var localSignInProps = new AuthenticationProperties
- {
- IsPersistent = true,
- ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(1)
- };
- ProcessLoginCallback(result, additionalLocalClaims, localSignInProps);
-
- // Issue authentication cookie for user
- await HttpContext.SignInAsync(new IdentityServerUser(user.Id.ToString())
- {
- DisplayName = user.Email,
- IdentityProvider = provider,
- AdditionalClaims = additionalLocalClaims.ToArray()
- }, localSignInProps);
- }
-
- // Delete temporary cookie used during external authentication
- await HttpContext.SignOutAsync(AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
-
- // Retrieve return URL
- var returnUrl = result.Properties.Items["return_url"] ?? "~/";
-
- // Check if external login is in the context of an OIDC request
- var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
- if (context != null)
- {
- if (IsNativeClient(context))
- {
- // The client is native, so this change in how to
- // return the response is for better UX for the end user.
- HttpContext.Response.StatusCode = 200;
- HttpContext.Response.Headers["Location"] = string.Empty;
- return View("Redirect", new RedirectViewModel { RedirectUrl = returnUrl });
- }
- }
-
- return Redirect(returnUrl);
- }
-
- [HttpGet]
- public async Task Logout(string logoutId)
- {
- // Build a model so the logged out page knows what to display
- var (updatedLogoutId, redirectUri, externalAuthenticationScheme) = await GetLoggedOutDataAsync(logoutId);
-
- if (User?.Identity.IsAuthenticated == true)
- {
- // Delete local authentication cookie
- await HttpContext.SignOutAsync();
- }
-
- // HACK: Temporary workaroud for the time being that doesn't try to sign out of OneLogin schemes,
- // which doesnt support SLO
- if (externalAuthenticationScheme != null && !externalAuthenticationScheme.Contains("onelogin"))
- {
- // Build a return URL so the upstream provider will redirect back
- // to us after the user has logged out. this allows us to then
- // complete our single sign-out processing.
- var url = Url.Action("Logout", new { logoutId = updatedLogoutId });
-
- // This triggers a redirect to the external provider for sign-out
- return SignOut(new AuthenticationProperties { RedirectUri = url }, externalAuthenticationScheme);
- }
- if (redirectUri != null)
- {
- return View("Redirect", new RedirectViewModel { RedirectUrl = redirectUri });
- }
- else
- {
- return Redirect("~/");
- }
- }
-
- private async Task<(User user, string provider, string providerUserId, IEnumerable claims, SsoConfigurationData config)>
- FindUserFromExternalProviderAsync(AuthenticateResult result)
- {
- var provider = result.Properties.Items["scheme"];
- var orgId = new Guid(provider);
- var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(orgId);
- if (ssoConfig == null || !ssoConfig.Enabled)
- {
- throw new Exception(_i18nService.T("OrganizationOrSsoConfigNotFound"));
- }
-
- var ssoConfigData = ssoConfig.GetData();
- var externalUser = result.Principal;
-
- // Validate acr claim against expectation before going further
- if (!string.IsNullOrWhiteSpace(ssoConfigData.ExpectedReturnAcrValue))
- {
- var acrClaim = externalUser.FindFirst(JwtClaimTypes.AuthenticationContextClassReference);
- if (acrClaim?.Value != ssoConfigData.ExpectedReturnAcrValue)
- {
- throw new Exception(_i18nService.T("AcrMissingOrInvalid"));
- }
- }
-
- // Ensure the NameIdentifier used is not a transient name ID, if so, we need a different attribute
- // for the user identifier.
- static bool nameIdIsNotTransient(Claim c) => c.Type == ClaimTypes.NameIdentifier
- && (c.Properties == null
- || !c.Properties.ContainsKey(SamlPropertyKeys.ClaimFormat)
- || c.Properties[SamlPropertyKeys.ClaimFormat] != SamlNameIdFormats.Transient);
-
- // Try to determine the unique id of the external user (issued by the provider)
- // the most common claim type for that are the sub claim and the NameIdentifier
- // depending on the external provider, some other claim type might be used
- var customUserIdClaimTypes = ssoConfigData.GetAdditionalUserIdClaimTypes();
- var userIdClaim = externalUser.FindFirst(c => customUserIdClaimTypes.Contains(c.Type)) ??
- externalUser.FindFirst(JwtClaimTypes.Subject) ??
- externalUser.FindFirst(nameIdIsNotTransient) ??
- // Some SAML providers may use the `uid` attribute for this
- // where a transient NameID has been sent in the subject
- externalUser.FindFirst("uid") ??
- externalUser.FindFirst("upn") ??
- externalUser.FindFirst("eppn") ??
- throw new Exception(_i18nService.T("UnknownUserId"));
-
- // Remove the user id claim so we don't include it as an extra claim if/when we provision the user
- var claims = externalUser.Claims.ToList();
- claims.Remove(userIdClaim);
-
- // find external user
- var providerUserId = userIdClaim.Value;
-
- var user = await _userRepository.GetBySsoUserAsync(providerUserId, orgId);
-
- return (user, provider, providerUserId, claims, ssoConfigData);
- }
-
- private async Task AutoProvisionUserAsync(string provider, string providerUserId,
- IEnumerable claims, string userIdentifier, SsoConfigurationData config)
- {
- var name = GetName(claims, config.GetAdditionalNameClaimTypes());
- var email = GetEmailAddress(claims, config.GetAdditionalEmailClaimTypes());
- if (string.IsNullOrWhiteSpace(email) && providerUserId.Contains("@"))
- {
- email = providerUserId;
- }
-
- if (!Guid.TryParse(provider, out var orgId))
- {
- // TODO: support non-org (server-wide) SSO in the future?
- throw new Exception(_i18nService.T("SSOProviderIsNotAnOrgId", provider));
- }
-
- User existingUser = null;
- if (string.IsNullOrWhiteSpace(userIdentifier))
- {
- if (string.IsNullOrWhiteSpace(email))
- {
- throw new Exception(_i18nService.T("CannotFindEmailClaim"));
- }
- existingUser = await _userRepository.GetByEmailAsync(email);
- }
- else
- {
- var split = userIdentifier.Split(",");
- if (split.Length < 2)
- {
- throw new Exception(_i18nService.T("InvalidUserIdentifier"));
- }
- var userId = split[0];
- var token = split[1];
-
- var tokenOptions = new TokenOptions();
-
- var claimedUser = await _userService.GetUserByIdAsync(userId);
- if (claimedUser != null)
- {
- var tokenIsValid = await _userManager.VerifyUserTokenAsync(
- claimedUser, tokenOptions.PasswordResetTokenProvider, TokenPurposes.LinkSso, token);
- if (tokenIsValid)
- {
- existingUser = claimedUser;
- }
- else
- {
- throw new Exception(_i18nService.T("UserIdAndTokenMismatch"));
- }
- }
- }
-
- OrganizationUser orgUser = null;
- var organization = await _organizationRepository.GetByIdAsync(orgId);
- if (organization == null)
- {
- throw new Exception(_i18nService.T("CouldNotFindOrganization", orgId));
- }
-
- // Try to find OrgUser via existing User Id (accepted/confirmed user)
- if (existingUser != null)
- {
- var orgUsersByUserId = await _organizationUserRepository.GetManyByUserAsync(existingUser.Id);
- orgUser = orgUsersByUserId.SingleOrDefault(u => u.OrganizationId == orgId);
- }
-
- // If no Org User found by Existing User Id - search all organization users via email
- orgUser ??= await _organizationUserRepository.GetByOrganizationEmailAsync(orgId, email);
-
- // All Existing User flows handled below
- if (existingUser != null)
- {
- if (orgUser == null)
- {
- // Org User is not created - no invite has been sent
- throw new Exception(_i18nService.T("UserAlreadyExistsInviteProcess"));
- }
-
- if (orgUser.Status == OrganizationUserStatusType.Invited)
- {
- // Org User is invited - they must manually accept the invite via email and authenticate with MP
- throw new Exception(_i18nService.T("UserAlreadyInvited", email, organization.Name));
- }
-
- // Delete existing SsoUser (if any) - avoids error if providerId has changed and the sso link is stale
- await DeleteExistingSsoUserRecord(existingUser.Id, orgId, orgUser);
-
- // Accepted or Confirmed - create SSO link and return;
- await CreateSsoUserRecord(providerUserId, existingUser.Id, orgId);
- return existingUser;
- }
-
- // 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 initialSeatCount = organization.Seats.Value;
- var availableSeats = initialSeatCount - userCount;
- var prorationDate = DateTime.UtcNow;
- if (availableSeats < 1)
- {
- try
- {
- if (_globalSettings.SelfHosted)
- {
- throw new Exception("Cannot autoscale on self-hosted instance.");
- }
-
- await _organizationService.AutoAddSeatsAsync(organization, 1, prorationDate);
- }
- catch (Exception e)
- {
- if (organization.Seats.Value != initialSeatCount)
- {
- await _organizationService.AdjustSeatsAsync(orgId, initialSeatCount - organization.Seats.Value, prorationDate);
- }
- _logger.LogInformation(e, "SSO auto provisioning failed");
- throw new Exception(_i18nService.T("NoSeatsAvailable", organization.Name));
- }
- }
- }
-
- // Create user record - all existing user flows are handled above
- var user = new User
- {
- Name = name,
- Email = email,
- ApiKey = CoreHelpers.SecureRandomString(30)
- };
- await _userService.RegisterUserAsync(user);
-
- // If the organization has 2fa policy enabled, make sure to default jit user 2fa to email
- var twoFactorPolicy =
- await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.TwoFactorAuthentication);
- if (twoFactorPolicy != null && twoFactorPolicy.Enabled)
- {
- user.SetTwoFactorProviders(new Dictionary
- {
- [TwoFactorProviderType.Email] = new TwoFactorProvider
- {
- MetaData = new Dictionary { ["Email"] = user.Email.ToLowerInvariant() },
- Enabled = true
- }
- });
- await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Email);
- }
-
- // Create Org User if null or else update existing Org User
- if (orgUser == null)
- {
- orgUser = new OrganizationUser
- {
- OrganizationId = orgId,
- UserId = user.Id,
- Type = OrganizationUserType.User,
- Status = OrganizationUserStatusType.Invited
- };
- await _organizationUserRepository.CreateAsync(orgUser);
- }
- else
- {
- orgUser.UserId = user.Id;
- await _organizationUserRepository.ReplaceAsync(orgUser);
- }
-
- // Delete any stale user record to be safe
- await DeleteExistingSsoUserRecord(user.Id, orgId, orgUser);
-
- // Create sso user record
- await CreateSsoUserRecord(providerUserId, user.Id, orgId);
-
- return user;
- }
-
- private string GetEmailAddress(IEnumerable claims, IEnumerable additionalClaimTypes)
- {
- var filteredClaims = claims.Where(c => !string.IsNullOrWhiteSpace(c.Value) && c.Value.Contains("@"));
-
- var email = filteredClaims.GetFirstMatch(additionalClaimTypes.ToArray()) ??
- filteredClaims.GetFirstMatch(JwtClaimTypes.Email, ClaimTypes.Email,
- SamlClaimTypes.Email, "mail", "emailaddress");
- if (!string.IsNullOrWhiteSpace(email))
- {
- return email;
- }
-
- var username = filteredClaims.GetFirstMatch(JwtClaimTypes.PreferredUserName,
- SamlClaimTypes.UserId, "uid");
- if (!string.IsNullOrWhiteSpace(username))
- {
- return username;
- }
-
- return null;
- }
-
- private string GetName(IEnumerable claims, IEnumerable additionalClaimTypes)
- {
- var filteredClaims = claims.Where(c => !string.IsNullOrWhiteSpace(c.Value));
-
- var name = filteredClaims.GetFirstMatch(additionalClaimTypes.ToArray()) ??
- filteredClaims.GetFirstMatch(JwtClaimTypes.Name, ClaimTypes.Name,
- SamlClaimTypes.DisplayName, SamlClaimTypes.CommonName, "displayname", "cn");
- if (!string.IsNullOrWhiteSpace(name))
- {
- return name;
- }
-
- var givenName = filteredClaims.GetFirstMatch(SamlClaimTypes.GivenName, "givenname", "firstname",
- "fn", "fname", "nickname");
- var surname = filteredClaims.GetFirstMatch(SamlClaimTypes.Surname, "sn", "surname", "lastname");
- var nameParts = new[] { givenName, surname }.Where(p => !string.IsNullOrWhiteSpace(p));
- if (nameParts.Any())
- {
- return string.Join(' ', nameParts);
- }
-
- return null;
- }
-
- private async Task DeleteExistingSsoUserRecord(Guid userId, Guid orgId, OrganizationUser orgUser)
- {
- var existingSsoUser = await _ssoUserRepository.GetByUserIdOrganizationIdAsync(orgId, userId);
- if (existingSsoUser != null)
- {
- await _ssoUserRepository.DeleteAsync(userId, orgId);
- await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_ResetSsoLink);
- }
- }
-
- private async Task CreateSsoUserRecord(string providerUserId, Guid userId, Guid orgId)
- {
- var ssoUser = new SsoUser
- {
- ExternalId = providerUserId,
- UserId = userId,
- OrganizationId = orgId,
- };
- await _ssoUserRepository.CreateAsync(ssoUser);
- }
-
- private void ProcessLoginCallback(AuthenticateResult externalResult,
- List localClaims, AuthenticationProperties localSignInProps)
- {
- // If the external system sent a session id claim, copy it over
- // so we can use it for single sign-out
- var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId);
- if (sid != null)
- {
- localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value));
- }
-
- // If the external provider issued an idToken, we'll keep it for signout
- var idToken = externalResult.Properties.GetTokenValue("id_token");
- if (idToken != null)
- {
- localSignInProps.StoreTokens(
- new[] { new AuthenticationToken { Name = "id_token", Value = idToken } });
- }
- }
-
- private async Task GetProviderAsync(string returnUrl)
- {
- var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
- if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null)
- {
- return context.IdP;
- }
- var schemes = await _schemeProvider.GetAllSchemesAsync();
- var providers = schemes.Select(x => x.Name).ToList();
- return providers.FirstOrDefault();
- }
-
- private async Task<(string, string, string)> GetLoggedOutDataAsync(string logoutId)
- {
- // Get context information (client name, post logout redirect URI and iframe for federated signout)
- var logout = await _interaction.GetLogoutContextAsync(logoutId);
- string externalAuthenticationScheme = null;
- if (User?.Identity.IsAuthenticated == true)
- {
- var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value;
- if (idp != null && idp != IdentityServerConstants.LocalIdentityProvider)
- {
- var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp);
- if (providerSupportsSignout)
- {
- if (logoutId == null)
- {
- // If there's no current logout context, we need to create one
- // this captures necessary info from the current logged in user
- // before we signout and redirect away to the external IdP for signout
- logoutId = await _interaction.CreateLogoutContextAsync();
- }
-
- externalAuthenticationScheme = idp;
- }
- }
- }
-
- return (logoutId, logout?.PostLogoutRedirectUri, externalAuthenticationScheme);
- }
-
- public bool IsNativeClient(IdentityServer4.Models.AuthorizationRequest context)
- {
- return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
- && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
+ return InvalidJson("PreValidationError", ex);
}
}
+
+ [HttpGet]
+ public async Task Login(string returnUrl)
+ {
+ var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
+
+ if (!context.Parameters.AllKeys.Contains("domain_hint") ||
+ string.IsNullOrWhiteSpace(context.Parameters["domain_hint"]))
+ {
+ throw new Exception(_i18nService.T("NoDomainHintProvided"));
+ }
+
+ var ssoToken = context.Parameters[SsoTokenable.TokenIdentifier];
+
+ if (string.IsNullOrWhiteSpace(ssoToken))
+ {
+ return Unauthorized("A valid SSO token is required to continue with SSO login");
+ }
+
+ var domainHint = context.Parameters["domain_hint"];
+ var organization = await _organizationRepository.GetByIdentifierAsync(domainHint);
+
+ if (organization == null)
+ {
+ return InvalidJson("OrganizationNotFoundByIdentifierError");
+ }
+
+ var tokenable = _dataProtector.Unprotect(ssoToken);
+
+ if (!tokenable.TokenIsValid(organization))
+ {
+ return Unauthorized("The SSO token associated with your request is expired. A valid SSO token is required to continue.");
+ }
+
+ return RedirectToAction(nameof(ExternalChallenge), new
+ {
+ scheme = organization.Id.ToString(),
+ returnUrl,
+ state = context.Parameters["state"],
+ userIdentifier = context.Parameters["session_state"],
+ });
+ }
+
+ [HttpGet]
+ public IActionResult ExternalChallenge(string scheme, string returnUrl, string state, string userIdentifier)
+ {
+ if (string.IsNullOrEmpty(returnUrl))
+ {
+ returnUrl = "~/";
+ }
+
+ if (!Url.IsLocalUrl(returnUrl) && !_interaction.IsValidReturnUrl(returnUrl))
+ {
+ throw new Exception(_i18nService.T("InvalidReturnUrl"));
+ }
+
+ var props = new AuthenticationProperties
+ {
+ RedirectUri = Url.Action(nameof(ExternalCallback)),
+ Items =
+ {
+ // scheme will get serialized into `State` and returned back
+ { "scheme", scheme },
+ { "return_url", returnUrl },
+ { "state", state },
+ { "user_identifier", userIdentifier },
+ }
+ };
+
+ return Challenge(props, scheme);
+ }
+
+ [HttpGet]
+ public async Task ExternalCallback()
+ {
+ // Read external identity from the temporary cookie
+ var result = await HttpContext.AuthenticateAsync(
+ AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
+ if (result?.Succeeded != true)
+ {
+ throw new Exception(_i18nService.T("ExternalAuthenticationError"));
+ }
+
+ // Debugging
+ var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}");
+ _logger.LogDebug("External claims: {@claims}", externalClaims);
+
+ // Lookup our user and external provider info
+ var (user, provider, providerUserId, claims, ssoConfigData) = await FindUserFromExternalProviderAsync(result);
+ if (user == null)
+ {
+ // This might be where you might initiate a custom workflow for user registration
+ // in this sample we don't show how that would be done, as our sample implementation
+ // simply auto-provisions new external user
+ var userIdentifier = result.Properties.Items.Keys.Contains("user_identifier") ?
+ result.Properties.Items["user_identifier"] : null;
+ user = await AutoProvisionUserAsync(provider, providerUserId, claims, userIdentifier, ssoConfigData);
+ }
+
+ if (user != null)
+ {
+ // This allows us to collect any additional claims or properties
+ // for the specific protocols used and store them in the local auth cookie.
+ // this is typically used to store data needed for signout from those protocols.
+ var additionalLocalClaims = new List();
+ var localSignInProps = new AuthenticationProperties
+ {
+ IsPersistent = true,
+ ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(1)
+ };
+ ProcessLoginCallback(result, additionalLocalClaims, localSignInProps);
+
+ // Issue authentication cookie for user
+ await HttpContext.SignInAsync(new IdentityServerUser(user.Id.ToString())
+ {
+ DisplayName = user.Email,
+ IdentityProvider = provider,
+ AdditionalClaims = additionalLocalClaims.ToArray()
+ }, localSignInProps);
+ }
+
+ // Delete temporary cookie used during external authentication
+ await HttpContext.SignOutAsync(AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
+
+ // Retrieve return URL
+ var returnUrl = result.Properties.Items["return_url"] ?? "~/";
+
+ // Check if external login is in the context of an OIDC request
+ var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
+ if (context != null)
+ {
+ if (IsNativeClient(context))
+ {
+ // The client is native, so this change in how to
+ // return the response is for better UX for the end user.
+ HttpContext.Response.StatusCode = 200;
+ HttpContext.Response.Headers["Location"] = string.Empty;
+ return View("Redirect", new RedirectViewModel { RedirectUrl = returnUrl });
+ }
+ }
+
+ return Redirect(returnUrl);
+ }
+
+ [HttpGet]
+ public async Task Logout(string logoutId)
+ {
+ // Build a model so the logged out page knows what to display
+ var (updatedLogoutId, redirectUri, externalAuthenticationScheme) = await GetLoggedOutDataAsync(logoutId);
+
+ if (User?.Identity.IsAuthenticated == true)
+ {
+ // Delete local authentication cookie
+ await HttpContext.SignOutAsync();
+ }
+
+ // HACK: Temporary workaroud for the time being that doesn't try to sign out of OneLogin schemes,
+ // which doesnt support SLO
+ if (externalAuthenticationScheme != null && !externalAuthenticationScheme.Contains("onelogin"))
+ {
+ // Build a return URL so the upstream provider will redirect back
+ // to us after the user has logged out. this allows us to then
+ // complete our single sign-out processing.
+ var url = Url.Action("Logout", new { logoutId = updatedLogoutId });
+
+ // This triggers a redirect to the external provider for sign-out
+ return SignOut(new AuthenticationProperties { RedirectUri = url }, externalAuthenticationScheme);
+ }
+ if (redirectUri != null)
+ {
+ return View("Redirect", new RedirectViewModel { RedirectUrl = redirectUri });
+ }
+ else
+ {
+ return Redirect("~/");
+ }
+ }
+
+ private async Task<(User user, string provider, string providerUserId, IEnumerable claims, SsoConfigurationData config)>
+ FindUserFromExternalProviderAsync(AuthenticateResult result)
+ {
+ var provider = result.Properties.Items["scheme"];
+ var orgId = new Guid(provider);
+ var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(orgId);
+ if (ssoConfig == null || !ssoConfig.Enabled)
+ {
+ throw new Exception(_i18nService.T("OrganizationOrSsoConfigNotFound"));
+ }
+
+ var ssoConfigData = ssoConfig.GetData();
+ var externalUser = result.Principal;
+
+ // Validate acr claim against expectation before going further
+ if (!string.IsNullOrWhiteSpace(ssoConfigData.ExpectedReturnAcrValue))
+ {
+ var acrClaim = externalUser.FindFirst(JwtClaimTypes.AuthenticationContextClassReference);
+ if (acrClaim?.Value != ssoConfigData.ExpectedReturnAcrValue)
+ {
+ throw new Exception(_i18nService.T("AcrMissingOrInvalid"));
+ }
+ }
+
+ // Ensure the NameIdentifier used is not a transient name ID, if so, we need a different attribute
+ // for the user identifier.
+ static bool nameIdIsNotTransient(Claim c) => c.Type == ClaimTypes.NameIdentifier
+ && (c.Properties == null
+ || !c.Properties.ContainsKey(SamlPropertyKeys.ClaimFormat)
+ || c.Properties[SamlPropertyKeys.ClaimFormat] != SamlNameIdFormats.Transient);
+
+ // Try to determine the unique id of the external user (issued by the provider)
+ // the most common claim type for that are the sub claim and the NameIdentifier
+ // depending on the external provider, some other claim type might be used
+ var customUserIdClaimTypes = ssoConfigData.GetAdditionalUserIdClaimTypes();
+ var userIdClaim = externalUser.FindFirst(c => customUserIdClaimTypes.Contains(c.Type)) ??
+ externalUser.FindFirst(JwtClaimTypes.Subject) ??
+ externalUser.FindFirst(nameIdIsNotTransient) ??
+ // Some SAML providers may use the `uid` attribute for this
+ // where a transient NameID has been sent in the subject
+ externalUser.FindFirst("uid") ??
+ externalUser.FindFirst("upn") ??
+ externalUser.FindFirst("eppn") ??
+ throw new Exception(_i18nService.T("UnknownUserId"));
+
+ // Remove the user id claim so we don't include it as an extra claim if/when we provision the user
+ var claims = externalUser.Claims.ToList();
+ claims.Remove(userIdClaim);
+
+ // find external user
+ var providerUserId = userIdClaim.Value;
+
+ var user = await _userRepository.GetBySsoUserAsync(providerUserId, orgId);
+
+ return (user, provider, providerUserId, claims, ssoConfigData);
+ }
+
+ private async Task AutoProvisionUserAsync(string provider, string providerUserId,
+ IEnumerable claims, string userIdentifier, SsoConfigurationData config)
+ {
+ var name = GetName(claims, config.GetAdditionalNameClaimTypes());
+ var email = GetEmailAddress(claims, config.GetAdditionalEmailClaimTypes());
+ if (string.IsNullOrWhiteSpace(email) && providerUserId.Contains("@"))
+ {
+ email = providerUserId;
+ }
+
+ if (!Guid.TryParse(provider, out var orgId))
+ {
+ // TODO: support non-org (server-wide) SSO in the future?
+ throw new Exception(_i18nService.T("SSOProviderIsNotAnOrgId", provider));
+ }
+
+ User existingUser = null;
+ if (string.IsNullOrWhiteSpace(userIdentifier))
+ {
+ if (string.IsNullOrWhiteSpace(email))
+ {
+ throw new Exception(_i18nService.T("CannotFindEmailClaim"));
+ }
+ existingUser = await _userRepository.GetByEmailAsync(email);
+ }
+ else
+ {
+ var split = userIdentifier.Split(",");
+ if (split.Length < 2)
+ {
+ throw new Exception(_i18nService.T("InvalidUserIdentifier"));
+ }
+ var userId = split[0];
+ var token = split[1];
+
+ var tokenOptions = new TokenOptions();
+
+ var claimedUser = await _userService.GetUserByIdAsync(userId);
+ if (claimedUser != null)
+ {
+ var tokenIsValid = await _userManager.VerifyUserTokenAsync(
+ claimedUser, tokenOptions.PasswordResetTokenProvider, TokenPurposes.LinkSso, token);
+ if (tokenIsValid)
+ {
+ existingUser = claimedUser;
+ }
+ else
+ {
+ throw new Exception(_i18nService.T("UserIdAndTokenMismatch"));
+ }
+ }
+ }
+
+ OrganizationUser orgUser = null;
+ var organization = await _organizationRepository.GetByIdAsync(orgId);
+ if (organization == null)
+ {
+ throw new Exception(_i18nService.T("CouldNotFindOrganization", orgId));
+ }
+
+ // Try to find OrgUser via existing User Id (accepted/confirmed user)
+ if (existingUser != null)
+ {
+ var orgUsersByUserId = await _organizationUserRepository.GetManyByUserAsync(existingUser.Id);
+ orgUser = orgUsersByUserId.SingleOrDefault(u => u.OrganizationId == orgId);
+ }
+
+ // If no Org User found by Existing User Id - search all organization users via email
+ orgUser ??= await _organizationUserRepository.GetByOrganizationEmailAsync(orgId, email);
+
+ // All Existing User flows handled below
+ if (existingUser != null)
+ {
+ if (existingUser.UsesKeyConnector &&
+ (orgUser == null || orgUser.Status == OrganizationUserStatusType.Invited))
+ {
+ throw new Exception(_i18nService.T("UserAlreadyExistsKeyConnector"));
+ }
+
+ if (orgUser == null)
+ {
+ // Org User is not created - no invite has been sent
+ throw new Exception(_i18nService.T("UserAlreadyExistsInviteProcess"));
+ }
+
+ if (orgUser.Status == OrganizationUserStatusType.Invited)
+ {
+ // Org User is invited - they must manually accept the invite via email and authenticate with MP
+ throw new Exception(_i18nService.T("UserAlreadyInvited", email, organization.Name));
+ }
+
+ // Accepted or Confirmed - create SSO link and return;
+ await CreateSsoUserRecord(providerUserId, existingUser.Id, orgId, orgUser);
+ return existingUser;
+ }
+
+ // 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 occupiedSeats = await _organizationService.GetOccupiedSeatCount(organization);
+ var initialSeatCount = organization.Seats.Value;
+ var availableSeats = initialSeatCount - occupiedSeats;
+ var prorationDate = DateTime.UtcNow;
+ if (availableSeats < 1)
+ {
+ try
+ {
+ if (_globalSettings.SelfHosted)
+ {
+ throw new Exception("Cannot autoscale on self-hosted instance.");
+ }
+
+ await _organizationService.AutoAddSeatsAsync(organization, 1, prorationDate);
+ }
+ catch (Exception e)
+ {
+ if (organization.Seats.Value != initialSeatCount)
+ {
+ await _organizationService.AdjustSeatsAsync(orgId, initialSeatCount - organization.Seats.Value, prorationDate);
+ }
+ _logger.LogInformation(e, "SSO auto provisioning failed");
+ throw new Exception(_i18nService.T("NoSeatsAvailable", organization.Name));
+ }
+ }
+ }
+
+ // Create user record - all existing user flows are handled above
+ var user = new User
+ {
+ Name = name,
+ Email = email,
+ ApiKey = CoreHelpers.SecureRandomString(30)
+ };
+ await _userService.RegisterUserAsync(user);
+
+ // If the organization has 2fa policy enabled, make sure to default jit user 2fa to email
+ var twoFactorPolicy =
+ await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.TwoFactorAuthentication);
+ if (twoFactorPolicy != null && twoFactorPolicy.Enabled)
+ {
+ user.SetTwoFactorProviders(new Dictionary
+ {
+ [TwoFactorProviderType.Email] = new TwoFactorProvider
+ {
+ MetaData = new Dictionary { ["Email"] = user.Email.ToLowerInvariant() },
+ Enabled = true
+ }
+ });
+ await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Email);
+ }
+
+ // Create Org User if null or else update existing Org User
+ if (orgUser == null)
+ {
+ orgUser = new OrganizationUser
+ {
+ OrganizationId = orgId,
+ UserId = user.Id,
+ Type = OrganizationUserType.User,
+ Status = OrganizationUserStatusType.Invited
+ };
+ await _organizationUserRepository.CreateAsync(orgUser);
+ }
+ else
+ {
+ orgUser.UserId = user.Id;
+ await _organizationUserRepository.ReplaceAsync(orgUser);
+ }
+
+ // Create sso user record
+ await CreateSsoUserRecord(providerUserId, user.Id, orgId, orgUser);
+
+ return user;
+ }
+
+ private IActionResult InvalidJson(string errorMessageKey, Exception ex = null)
+ {
+ Response.StatusCode = ex == null ? 400 : 500;
+ return Json(new ErrorResponseModel(_i18nService.T(errorMessageKey))
+ {
+ ExceptionMessage = ex?.Message,
+ ExceptionStackTrace = ex?.StackTrace,
+ InnerExceptionMessage = ex?.InnerException?.Message,
+ });
+ }
+
+ private string GetEmailAddress(IEnumerable claims, IEnumerable additionalClaimTypes)
+ {
+ var filteredClaims = claims.Where(c => !string.IsNullOrWhiteSpace(c.Value) && c.Value.Contains("@"));
+
+ var email = filteredClaims.GetFirstMatch(additionalClaimTypes.ToArray()) ??
+ filteredClaims.GetFirstMatch(JwtClaimTypes.Email, ClaimTypes.Email,
+ SamlClaimTypes.Email, "mail", "emailaddress");
+ if (!string.IsNullOrWhiteSpace(email))
+ {
+ return email;
+ }
+
+ var username = filteredClaims.GetFirstMatch(JwtClaimTypes.PreferredUserName,
+ SamlClaimTypes.UserId, "uid");
+ if (!string.IsNullOrWhiteSpace(username))
+ {
+ return username;
+ }
+
+ return null;
+ }
+
+ private string GetName(IEnumerable claims, IEnumerable additionalClaimTypes)
+ {
+ var filteredClaims = claims.Where(c => !string.IsNullOrWhiteSpace(c.Value));
+
+ var name = filteredClaims.GetFirstMatch(additionalClaimTypes.ToArray()) ??
+ filteredClaims.GetFirstMatch(JwtClaimTypes.Name, ClaimTypes.Name,
+ SamlClaimTypes.DisplayName, SamlClaimTypes.CommonName, "displayname", "cn");
+ if (!string.IsNullOrWhiteSpace(name))
+ {
+ return name;
+ }
+
+ var givenName = filteredClaims.GetFirstMatch(SamlClaimTypes.GivenName, "givenname", "firstname",
+ "fn", "fname", "nickname");
+ var surname = filteredClaims.GetFirstMatch(SamlClaimTypes.Surname, "sn", "surname", "lastname");
+ var nameParts = new[] { givenName, surname }.Where(p => !string.IsNullOrWhiteSpace(p));
+ if (nameParts.Any())
+ {
+ return string.Join(' ', nameParts);
+ }
+
+ return null;
+ }
+
+ private async Task CreateSsoUserRecord(string providerUserId, Guid userId, Guid orgId, OrganizationUser orgUser)
+ {
+ // Delete existing SsoUser (if any) - avoids error if providerId has changed and the sso link is stale
+ var existingSsoUser = await _ssoUserRepository.GetByUserIdOrganizationIdAsync(orgId, userId);
+ if (existingSsoUser != null)
+ {
+ await _ssoUserRepository.DeleteAsync(userId, orgId);
+ await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_ResetSsoLink);
+ }
+ else
+ {
+ // If no stale user, this is the user's first Sso login ever
+ await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_FirstSsoLogin);
+ }
+
+ var ssoUser = new SsoUser
+ {
+ ExternalId = providerUserId,
+ UserId = userId,
+ OrganizationId = orgId,
+ };
+ await _ssoUserRepository.CreateAsync(ssoUser);
+ }
+
+ private void ProcessLoginCallback(AuthenticateResult externalResult,
+ List localClaims, AuthenticationProperties localSignInProps)
+ {
+ // If the external system sent a session id claim, copy it over
+ // so we can use it for single sign-out
+ var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId);
+ if (sid != null)
+ {
+ localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value));
+ }
+
+ // If the external provider issued an idToken, we'll keep it for signout
+ var idToken = externalResult.Properties.GetTokenValue("id_token");
+ if (idToken != null)
+ {
+ localSignInProps.StoreTokens(
+ new[] { new AuthenticationToken { Name = "id_token", Value = idToken } });
+ }
+ }
+
+ private async Task GetProviderAsync(string returnUrl)
+ {
+ var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
+ if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null)
+ {
+ return context.IdP;
+ }
+ var schemes = await _schemeProvider.GetAllSchemesAsync();
+ var providers = schemes.Select(x => x.Name).ToList();
+ return providers.FirstOrDefault();
+ }
+
+ private async Task<(string, string, string)> GetLoggedOutDataAsync(string logoutId)
+ {
+ // Get context information (client name, post logout redirect URI and iframe for federated signout)
+ var logout = await _interaction.GetLogoutContextAsync(logoutId);
+ string externalAuthenticationScheme = null;
+ if (User?.Identity.IsAuthenticated == true)
+ {
+ var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value;
+ if (idp != null && idp != IdentityServerConstants.LocalIdentityProvider)
+ {
+ var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp);
+ if (providerSupportsSignout)
+ {
+ if (logoutId == null)
+ {
+ // If there's no current logout context, we need to create one
+ // this captures necessary info from the current logged in user
+ // before we signout and redirect away to the external IdP for signout
+ logoutId = await _interaction.CreateLogoutContextAsync();
+ }
+
+ externalAuthenticationScheme = idp;
+ }
+ }
+ }
+
+ return (logoutId, logout?.PostLogoutRedirectUri, externalAuthenticationScheme);
+ }
+
+ public bool IsNativeClient(IdentityServer4.Models.AuthorizationRequest context)
+ {
+ return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
+ && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
+ }
}
diff --git a/bitwarden_license/src/Sso/Controllers/HomeController.cs b/bitwarden_license/src/Sso/Controllers/HomeController.cs
index 7536a1ca4..ee15fefc9 100644
--- a/bitwarden_license/src/Sso/Controllers/HomeController.cs
+++ b/bitwarden_license/src/Sso/Controllers/HomeController.cs
@@ -1,65 +1,54 @@
-using System;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Authorization;
-using IdentityServer4.Services;
-using System.Threading.Tasks;
+using System.Diagnostics;
using Bit.Sso.Models;
-using System.Diagnostics;
+using IdentityServer4.Services;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Diagnostics;
+using Microsoft.AspNetCore.Mvc;
-namespace Bit.Sso.Controllers
+namespace Bit.Sso.Controllers;
+
+public class HomeController : Controller
{
- public class HomeController : Controller
+ private readonly IIdentityServerInteractionService _interaction;
+
+ public HomeController(IIdentityServerInteractionService interaction)
{
- private readonly IIdentityServerInteractionService _interaction;
+ _interaction = interaction;
+ }
- public HomeController(IIdentityServerInteractionService interaction)
+ [Route("~/Error")]
+ [Route("~/Home/Error")]
+ [AllowAnonymous]
+ public async Task Error(string errorId)
+ {
+ var vm = new ErrorViewModel();
+
+ // retrieve error details from identityserver
+ var message = string.IsNullOrWhiteSpace(errorId) ? null :
+ await _interaction.GetErrorContextAsync(errorId);
+ if (message != null)
{
- _interaction = interaction;
+ vm.Error = message;
}
-
- [HttpGet("~/alive")]
- [HttpGet("~/now")]
- [AllowAnonymous]
- public DateTime GetAlive()
+ else
{
- return DateTime.UtcNow;
- }
-
- [Route("~/Error")]
- [Route("~/Home/Error")]
- [AllowAnonymous]
- public async Task