1
0
mirror of https://github.com/bitwarden/server synced 2025-12-18 09:13:19 +00:00

Merge remote-tracking branch 'origin/master' into feature/sm-billing

This commit is contained in:
Thomas Rittson
2023-07-04 14:44:02 +10:00
87 changed files with 1259 additions and 665 deletions

View File

@@ -14,7 +14,7 @@ jobs:
# Feature request
- if: github.event.label.name == 'feature-request'
name: Feature request
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
uses: peter-evans/close-issue@1373cadf1f0c96c1420bc000cfba2273ea307fd1 # v2.2.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.
@@ -25,7 +25,7 @@ jobs:
# Intended behavior
- if: github.event.label.name == 'intended-behavior'
name: Intended behaviour
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
uses: peter-evans/close-issue@1373cadf1f0c96c1420bc000cfba2273ea307fd1 # v2.2.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.
@@ -38,7 +38,7 @@ jobs:
# Customer support request
- if: github.event.label.name == 'customer-support'
name: Customer Support request
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
uses: peter-evans/close-issue@1373cadf1f0c96c1420bc000cfba2273ea307fd1 # v2.2.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.
@@ -49,14 +49,14 @@ jobs:
# Resolved
- if: github.event.label.name == 'resolved'
name: Resolved
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
uses: peter-evans/close-issue@1373cadf1f0c96c1420bc000cfba2273ea307fd1 # v2.2.0
with:
comment: |
Weve 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
uses: peter-evans/close-issue@1373cadf1f0c96c1420bc000cfba2273ea307fd1 # v2.2.0
with:
comment: |
As we havent heard from you about this problem in some time, this issue will now be closed.

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Install cloc
run: |
@@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Verify Format
run: dotnet format --verify-no-changes
@@ -43,7 +43,7 @@ jobs:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps:
- name: Set up dotnet
uses: actions/setup-dotnet@9211491ffb35dd6a6657ca4f45d43dfe6e97c829 # v2.0.0
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
with:
dotnet-version: "6.0.x"
@@ -55,7 +55,7 @@ jobs:
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Restore
run: dotnet restore --locked-mode
@@ -131,10 +131,10 @@ jobs:
dotnet: true
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Set up Node
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
cache: "npm"
cache-dependency-path: "**/package-lock.json"
@@ -178,7 +178,7 @@ jobs:
ls -atlh ../../../
- name: Upload project artifact
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: ${{ matrix.project_name }}.zip
path: ${{ matrix.base_path }}/${{ matrix.project_name }}/${{ matrix.project_name }}.zip
@@ -255,7 +255,7 @@ jobs:
dotnet: true
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Check Branch to Publish
env:
@@ -272,7 +272,7 @@ jobs:
########## ACRs ##########
- name: Login to Azure - QA Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
@@ -280,7 +280,7 @@ jobs:
run: az acr login -n bitwardenqa
- name: Login to Azure - PROD Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
@@ -288,13 +288,13 @@ jobs:
run: az acr login -n bitwardenprod
- name: Login to Azure - CI Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve github PAT secrets
id: retrieve-secret-pat
uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/get-keyvault-secrets@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
with:
keyvault: "bitwarden-ci"
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
@@ -302,7 +302,7 @@ jobs:
- name: Retrieve secrets
if: ${{ env.is_publish_branch == 'true' }}
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/get-keyvault-secrets@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
with:
keyvault: "bitwarden-ci"
secrets: "docker-password,
@@ -319,7 +319,7 @@ jobs:
- name: Setup Docker Content Trust (DCT)
if: ${{ env.is_publish_branch == 'true' }}
uses: bitwarden/gh-actions/setup-docker-trust@f955298c7a982b3fb5dbb73afd582c584fd5beec
uses: bitwarden/gh-actions/setup-docker-trust@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
with:
azure-creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
azure-keyvault-name: "bitwarden-ci"
@@ -351,7 +351,7 @@ jobs:
- name: Get build artifact
if: ${{ matrix.dotnet }}
uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 # v3.0.0
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: ${{ matrix.project_name }}.zip
@@ -363,7 +363,7 @@ jobs:
-d ${{ matrix.base_path }}/${{ matrix.project_name }}/obj/build-output/publish
- name: Build Docker image
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v3.2.0
uses: docker/build-push-action@1104d471370f9806843c095c1db02b5a90c5f8b6 # v3.3.1
with:
context: ${{ matrix.base_path }}/${{ matrix.project_name }}
file: ${{ matrix.base_path }}/${{ matrix.project_name }}/Dockerfile
@@ -393,12 +393,12 @@ jobs:
needs: build-docker
steps:
- name: Set up dotnet
uses: actions/setup-dotnet@9211491ffb35dd6a6657ca4f45d43dfe6e97c829 # v3.0.3
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
with:
dotnet-version: "6.0.x"
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Restore
run: dotnet tool restore
@@ -431,7 +431,7 @@ jobs:
- name: Upload Docker stub artifact
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: docker-stub.zip
path: docker-stub.zip
@@ -439,7 +439,7 @@ jobs:
- 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 # v3.0.0
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: docker-stub-sha256.txt
path: docker-stub-sha256.txt
@@ -465,7 +465,7 @@ jobs:
GLOBALSETTINGS__SQLSERVER__CONNECTIONSTRING: "placeholder"
- name: Upload Swagger artifact
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: swagger.json
path: swagger.json
@@ -490,7 +490,7 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Print environment
run: |
@@ -509,7 +509,7 @@ jobs:
- name: Upload project artifact Windows
if: ${{ contains(matrix.target, 'win') == true }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: MsSqlMigratorUtility-${{ matrix.target }}
path: util/MsSqlMigratorUtility/obj/build-output/publish/MsSqlMigratorUtility.exe
@@ -517,7 +517,7 @@ jobs:
- name: Upload project artifact
if: ${{ contains(matrix.target, 'win') == false }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: MsSqlMigratorUtility-${{ matrix.target }}
path: util/MsSqlMigratorUtility/obj/build-output/publish/MsSqlMigratorUtility
@@ -579,14 +579,14 @@ jobs:
fi
- name: Login to Azure - CI subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
if: failure()
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/get-keyvault-secrets@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
if: failure()
with:
keyvault: "bitwarden-ci"

View File

@@ -11,11 +11,11 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout repo
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
########## ACR ##########
- name: Login to Azure - QA Subscription
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a # v1.3.0
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
@@ -23,7 +23,7 @@ jobs:
run: az acr login -n bitwardenqa
- name: Login to Azure - PROD Subscription
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a # v1.3.0
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Login to Azure
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
@@ -85,14 +85,14 @@ jobs:
fi
- name: Login to Azure - CI subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
if: failure()
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/get-keyvault-secrets@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
if: failure()
with:
keyvault: "bitwarden-ci"

View File

@@ -25,10 +25,10 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.5.2
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Set up dotnet
uses: actions/setup-dotnet@9211491ffb35dd6a6657ca4f45d43dfe6e97c829 # v3.0.3
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
with:
dotnet-version: '6.0.x'
@@ -44,7 +44,7 @@ jobs:
shell: pwsh
- name: Upload DACPAC
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.1.2
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: sql.dacpac
path: Sql.dacpac
@@ -70,7 +70,7 @@ jobs:
shell: pwsh
- name: Upload Report
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.1.2
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: report.xml
path: |

View File

@@ -38,10 +38,10 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Set up dotnet
uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # v3.0.3
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
with:
dotnet-version: '6.0.x'

View File

@@ -30,7 +30,7 @@ jobs:
label: "DB-migrations-changed"
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
with:
fetch-depth: 2

View File

@@ -34,11 +34,11 @@ jobs:
fi
- name: Checkout repo
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Check Release Version
id: version
uses: bitwarden/gh-actions/release-version-check@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/release-version-check@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
with:
release-type: ${{ github.event.inputs.release_type }}
project-type: dotnet
@@ -87,7 +87,7 @@ jobs:
- name: Download latest Release ${{ matrix.name }} asset
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/download-artifacts@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
with:
workflow: build.yml
workflow_conclusion: success
@@ -96,7 +96,7 @@ jobs:
- name: Download latest Release ${{ matrix.name }} asset
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/download-artifacts@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
with:
workflow: build.yml
workflow_conclusion: success
@@ -104,7 +104,7 @@ jobs:
artifacts: ${{ matrix.name }}.zip
- name: Login to Azure - CI subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
@@ -129,12 +129,12 @@ jobs:
echo "publish-profile=$publish_profile" >> $GITHUB_OUTPUT
- name: Login to Azure
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Deploy App
uses: azure/webapps-deploy@016bdd3f9b7cec60310bcf9da98f671628795644 # v2.2.4
uses: azure/webapps-deploy@fb8292eb575db1bb18a90627e8959cd51dbb355c # v2.2.10
with:
app-name: ${{ steps.retrieve-secrets.outputs.webapp-name }}
publish-profile: ${{ steps.retrieve-secrets.outputs.publish-profile }}
@@ -229,7 +229,7 @@ jobs:
echo "Github Release Option: $RELEASE_OPTION"
- name: Checkout repo
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Setup project name
id: setup
@@ -243,7 +243,7 @@ jobs:
- name: Setup DCT
id: setup-dct
if: matrix.origin_docker_repo == 'bitwarden'
uses: bitwarden/gh-actions/setup-docker-trust@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/setup-docker-trust@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
with:
azure-creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
azure-keyvault-name: "bitwarden-ci"
@@ -286,7 +286,7 @@ jobs:
########## ACR PROD ##########
- name: Login to Azure - PROD Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
@@ -340,7 +340,7 @@ jobs:
steps:
- name: Download latest Release docker-stub
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/download-artifacts@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
with:
workflow: build.yml
workflow_conclusion: success
@@ -351,7 +351,7 @@ jobs:
- name: Download latest Release docker-stub
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/download-artifacts@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
with:
workflow: build.yml
workflow_conclusion: success

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: 'Run stale action'
uses: actions/stale@3cc123766321e9f15a6676375c154ccffb12a358 # v5.0.0
uses: actions/stale@f7176fd3007623b69d27091f9b9d4ab7995f0a06 # v5.2.1
with:
stale-issue-label: 'needs-reply'
stale-pr-label: 'needs-changes'

View File

@@ -29,7 +29,7 @@ jobs:
echo "name_lower=$NAME_LOWER" >> $GITHUB_OUTPUT
- name: Login to Azure - CI Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
@@ -47,7 +47,7 @@ jobs:
echo "webapp-name=$webapp_name" >> $GITHUB_OUTPUT
- name: Login to Azure
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}

View File

@@ -14,22 +14,22 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout Branch
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Login to Azure - CI Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/get-keyvault-secrets@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
with:
keyvault: "bitwarden-ci"
secrets: "github-gpg-private-key, github-gpg-private-key-passphrase"
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@111c56156bcc6918c056dbef52164cfa583dc549 # v5.2.0
uses: crazy-max/ghaction-import-gpg@72b6676b71ab476b77e676928516f6982eef7a41 # v5.3.0
with:
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
@@ -40,7 +40,7 @@ jobs:
run: git switch -c version_bump_${{ github.event.inputs.version_number }}
- name: Bump Version - Props
uses: bitwarden/gh-actions/version-bump@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/version-bump@74f4ac01c9abe0a7331c9a5de822a558fd4a4710
with:
version: ${{ github.event.inputs.version_number }}
file_path: "Directory.Build.props"

View File

@@ -8,4 +8,4 @@ on:
jobs:
call-workflow:
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@74f4ac01c9abe0a7331c9a5de822a558fd4a4710

View File

@@ -52,7 +52,7 @@ public class ProjectAuthorizationHandler : AuthorizationHandler<ProjectOperation
{
AccessClientType.NoAccessCheck => true,
AccessClientType.User => true,
AccessClientType.ServiceAccount => false,
AccessClientType.ServiceAccount => true,
_ => false,
};
@@ -67,10 +67,6 @@ public class ProjectAuthorizationHandler : AuthorizationHandler<ProjectOperation
{
var (accessClient, userId) =
await _accessClientQuery.GetAccessClientAsync(context.User, resource.OrganizationId);
if (accessClient == AccessClientType.ServiceAccount)
{
return;
}
var access = await _projectRepository.AccessToProjectAsync(resource.Id, userId, accessClient);

View File

@@ -74,7 +74,8 @@ public class SecretAuthorizationHandler : AuthorizationHandler<SecretOperationRe
AccessClientType.NoAccessCheck => true,
AccessClientType.User => (await _projectRepository.AccessToProjectAsync(project!.Id, userId, accessClient))
.Write,
AccessClientType.ServiceAccount => false,
AccessClientType.ServiceAccount => (await _projectRepository.AccessToProjectAsync(project!.Id, userId, accessClient))
.Write,
_ => false,
};
@@ -84,6 +85,7 @@ public class SecretAuthorizationHandler : AuthorizationHandler<SecretOperationRe
}
}
private async Task CanUpdateSecretAsync(AuthorizationHandlerContext context,
SecretOperationRequirement requirement, Secret resource)
{
@@ -106,12 +108,10 @@ public class SecretAuthorizationHandler : AuthorizationHandler<SecretOperationRe
hasAccess = true;
break;
case AccessClientType.User:
var newProject = resource.Projects?.FirstOrDefault();
var access = (await _secretRepository.AccessToSecretAsync(resource.Id, userId, accessClient)).Write;
var accessToNew = newProject != null &&
(await _projectRepository.AccessToProjectAsync(newProject.Id, userId, accessClient))
.Write;
hasAccess = access && accessToNew;
hasAccess = await GetAccessToUpdateSecretAsync(resource, userId, accessClient);
break;
case AccessClientType.ServiceAccount:
hasAccess = await GetAccessToUpdateSecretAsync(resource, userId, accessClient);
break;
default:
hasAccess = false;
@@ -129,11 +129,6 @@ public class SecretAuthorizationHandler : AuthorizationHandler<SecretOperationRe
{
var (accessClient, userId) = await _accessClientQuery.GetAccessClientAsync(context.User, resource.OrganizationId);
if (accessClient == AccessClientType.ServiceAccount)
{
return;
}
var access = await _secretRepository.AccessToSecretAsync(resource.Id, userId, accessClient);
if (access.Write)
@@ -141,4 +136,14 @@ public class SecretAuthorizationHandler : AuthorizationHandler<SecretOperationRe
context.Succeed(requirement);
}
}
private async Task<bool> GetAccessToUpdateSecretAsync(Secret resource, Guid userId, AccessClientType accessClient)
{
var newProject = resource.Projects?.FirstOrDefault();
var access = (await _secretRepository.AccessToSecretAsync(resource.Id, userId, accessClient)).Write;
var accessToNew = newProject != null &&
(await _projectRepository.AccessToProjectAsync(newProject.Id, userId, accessClient))
.Write;
return access && accessToNew;
}
}

View File

@@ -1,4 +1,7 @@
using Bit.Core.Repositories;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Identity;
using Bit.Core.Repositories;
using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.SecretsManager.Repositories;
@@ -10,23 +13,34 @@ public class CreateProjectCommand : ICreateProjectCommand
private readonly IAccessPolicyRepository _accessPolicyRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IProjectRepository _projectRepository;
private readonly ICurrentContext _currentContext;
public CreateProjectCommand(
IAccessPolicyRepository accessPolicyRepository,
IOrganizationUserRepository organizationUserRepository,
IProjectRepository projectRepository)
IProjectRepository projectRepository,
ICurrentContext currentContext)
{
_accessPolicyRepository = accessPolicyRepository;
_organizationUserRepository = organizationUserRepository;
_projectRepository = projectRepository;
_currentContext = currentContext;
}
public async Task<Project> CreateAsync(Project project, Guid userId)
public async Task<Project> CreateAsync(Project project, Guid id, ClientType clientType)
{
if (clientType != ClientType.User && clientType != ClientType.ServiceAccount)
{
throw new NotFoundException();
}
var createdProject = await _projectRepository.CreateAsync(project);
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(createdProject.OrganizationId,
userId);
if (clientType == ClientType.User)
{
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(createdProject.OrganizationId, id);
var accessPolicy = new UserProjectAccessPolicy()
{
OrganizationUserId = orgUser.Id,
@@ -34,7 +48,23 @@ public class CreateProjectCommand : ICreateProjectCommand
Read = true,
Write = true,
};
await _accessPolicyRepository.CreateManyAsync(new List<BaseAccessPolicy> { accessPolicy });
}
else if (clientType == ClientType.ServiceAccount)
{
var serviceAccountProjectAccessPolicy = new ServiceAccountProjectAccessPolicy()
{
ServiceAccountId = id,
GrantedProjectId = createdProject.Id,
Read = true,
Write = true,
};
await _accessPolicyRepository.CreateManyAsync(new List<BaseAccessPolicy> { serviceAccountProjectAccessPolicy });
}
return createdProject;
}
}

View File

@@ -55,7 +55,7 @@ public class DeleteProjectCommand : IDeleteProjectCommand
foreach (var project in projects)
{
var access = await _projectRepository.AccessToProjectAsync(project.Id, userId, accessClient);
if (!access.Write || accessClient == AccessClientType.ServiceAccount)
if (!access.Write)
{
results.Add(new Tuple<Project, string>(project, "access denied"));
}

View File

@@ -2783,7 +2783,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",

View File

@@ -2786,7 +2786,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2794,7 +2794,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -2806,9 +2806,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -7,6 +7,9 @@
<PropertyGroup Condition=" '$(RunConfiguration)' == 'Sso' " />
<PropertyGroup Condition=" '$(RunConfiguration)' == 'Sso-SelfHost' " />
<ItemGroup>
<!-- This is a transitive dependency to Sustainsys.Saml2.AspNetCore2 -->
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.1.22" />
<PackageReference Include="Sustainsys.Saml2.AspNetCore2" Version="2.9.0" />
</ItemGroup>

View File

@@ -17,7 +17,7 @@
"jquery": "3.5.1",
"merge-stream": "2.0.0",
"popper.js": "1.16.1",
"sass": "^1.49.7"
"sass": "1.49.7"
}
},
"node_modules/@nodelib/fs.scandir": {
@@ -3924,9 +3924,9 @@
}
},
"node_modules/sass/node_modules/anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
@@ -8158,9 +8158,9 @@
},
"dependencies": {
"anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"requires": {
"normalize-path": "^3.0.0",

View File

@@ -16,6 +16,6 @@
"jquery": "3.5.1",
"merge-stream": "2.0.0",
"popper.js": "1.16.1",
"sass": "^1.49.7"
"sass": "1.49.7"
}
}

View File

@@ -2,6 +2,19 @@
"version": 1,
"dependencies": {
"net6.0": {
"Microsoft.AspNetCore.Http": {
"type": "Direct",
"requested": "[2.1.22, )",
"resolved": "2.1.22",
"contentHash": "+Blk++1JWqghbl8+3azQmKhiNZA5wAepL9dY2I6KVmu2Ri07MAcvAVC888qUvO7yd7xgRgZOMfihezKg14O/2A==",
"dependencies": {
"Microsoft.AspNetCore.Http.Abstractions": "2.1.1",
"Microsoft.AspNetCore.WebUtilities": "2.1.1",
"Microsoft.Extensions.ObjectPool": "2.1.1",
"Microsoft.Extensions.Options": "2.1.1",
"Microsoft.Net.Http.Headers": "2.1.1"
}
},
"Sustainsys.Saml2.AspNetCore2": {
"type": "Direct",
"requested": "[2.9.0, )",
@@ -418,18 +431,6 @@
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
},
"Microsoft.AspNetCore.Http": {
"type": "Transitive",
"resolved": "2.1.1",
"contentHash": "pPDcCW8spnyibK3krpxrOpaFHf5fjV6k1Hsl6gfh77N/8gRYlLU7MOQDUnjpEwdlHmtxwJKQJNxZqVQOmJGRUw==",
"dependencies": {
"Microsoft.AspNetCore.Http.Abstractions": "2.1.1",
"Microsoft.AspNetCore.WebUtilities": "2.1.1",
"Microsoft.Extensions.ObjectPool": "2.1.1",
"Microsoft.Extensions.Options": "2.1.1",
"Microsoft.Net.Http.Headers": "2.1.1"
}
},
"Microsoft.AspNetCore.Http.Abstractions": {
"type": "Transitive",
"resolved": "2.1.1",
@@ -2945,7 +2946,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2953,7 +2954,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -2965,9 +2966,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -39,6 +39,11 @@ public class ProjectAuthorizationHandlerTests
.ReturnsForAnyArgs(
(AccessClientType.User, userId));
break;
case PermissionType.RunAsServiceAccountWithPermission:
sutProvider.GetDependency<IAccessClientQuery>().GetAccessClientAsync(default, organizationId)
.ReturnsForAnyArgs(
(AccessClientType.ServiceAccount, userId));
break;
default:
throw new ArgumentOutOfRangeException(nameof(permissionType), permissionType, null);
}
@@ -103,7 +108,6 @@ public class ProjectAuthorizationHandlerTests
}
[Theory]
[BitAutoData(AccessClientType.ServiceAccount)]
[BitAutoData(AccessClientType.Organization)]
public async Task CanCreateProject_NotSupportedClientTypes_DoesNotSucceed(AccessClientType clientType,
SutProvider<ProjectAuthorizationHandler> sutProvider, Project project, ClaimsPrincipal claimsPrincipal)
@@ -125,6 +129,7 @@ public class ProjectAuthorizationHandlerTests
[Theory]
[BitAutoData(PermissionType.RunAsAdmin)]
[BitAutoData(PermissionType.RunAsUserWithPermission)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission)]
public async Task CanCreateProject_Success(PermissionType permissionType,
SutProvider<ProjectAuthorizationHandler> sutProvider, Project project, ClaimsPrincipal claimsPrincipal)
{
@@ -199,6 +204,8 @@ public class ProjectAuthorizationHandlerTests
[Theory]
[BitAutoData(PermissionType.RunAsUserWithPermission, true, false)]
[BitAutoData(PermissionType.RunAsUserWithPermission, false, false)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, false)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, false, false)]
public async Task CanUpdateProject_ShouldNotSucceed(PermissionType permissionType, bool read, bool write,
SutProvider<ProjectAuthorizationHandler> sutProvider, Project project, ClaimsPrincipal claimsPrincipal,
Guid userId)
@@ -221,6 +228,8 @@ public class ProjectAuthorizationHandlerTests
[BitAutoData(PermissionType.RunAsAdmin, false, true)]
[BitAutoData(PermissionType.RunAsUserWithPermission, true, true)]
[BitAutoData(PermissionType.RunAsUserWithPermission, false, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, false, true)]
public async Task CanUpdateProject_Success(PermissionType permissionType, bool read, bool write,
SutProvider<ProjectAuthorizationHandler> sutProvider, Project project, ClaimsPrincipal claimsPrincipal,
Guid userId)

View File

@@ -41,6 +41,10 @@ public class SecretAuthorizationHandlerTests
sutProvider.GetDependency<IAccessClientQuery>().GetAccessClientAsync(default, organizationId).ReturnsForAnyArgs(
(clientType, userId));
break;
case PermissionType.RunAsServiceAccountWithPermission:
sutProvider.GetDependency<IAccessClientQuery>().GetAccessClientAsync(default, organizationId).ReturnsForAnyArgs(
(AccessClientType.ServiceAccount, userId));
break;
default:
throw new ArgumentOutOfRangeException(nameof(permissionType), permissionType, null);
}
@@ -105,7 +109,6 @@ public class SecretAuthorizationHandlerTests
}
[Theory]
[BitAutoData(AccessClientType.ServiceAccount)]
[BitAutoData(AccessClientType.Organization)]
public async Task CanCreateSecret_NotSupportedClientTypes_DoesNotSucceed(AccessClientType clientType,
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret, Guid userId,
@@ -114,7 +117,7 @@ public class SecretAuthorizationHandlerTests
var requirement = SecretOperations.Create;
SetupPermission(sutProvider, PermissionType.RunAsUserWithPermission, secret.OrganizationId, userId, clientType);
sutProvider.GetDependency<IProjectRepository>()
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, default).Returns(
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, Arg.Any<AccessClientType>()).Returns(
(true, true));
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, secret);
@@ -182,6 +185,8 @@ public class SecretAuthorizationHandlerTests
[Theory]
[BitAutoData(PermissionType.RunAsUserWithPermission, true, false)]
[BitAutoData(PermissionType.RunAsUserWithPermission, false, false)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, false)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, false, false)]
public async Task CanCreateSecret_DoesNotSucceed(PermissionType permissionType, bool read, bool write,
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret,
Guid userId,
@@ -190,7 +195,7 @@ public class SecretAuthorizationHandlerTests
var requirement = SecretOperations.Create;
SetupPermission(sutProvider, permissionType, secret.OrganizationId, userId);
sutProvider.GetDependency<IProjectRepository>()
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, default).Returns(
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, Arg.Any<AccessClientType>()).ReturnsForAnyArgs(
(read, write));
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, secret);
@@ -207,6 +212,8 @@ public class SecretAuthorizationHandlerTests
[BitAutoData(PermissionType.RunAsAdmin, false, false)]
[BitAutoData(PermissionType.RunAsUserWithPermission, true, true)]
[BitAutoData(PermissionType.RunAsUserWithPermission, false, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, false, true)]
public async Task CanCreateSecret_Success(PermissionType permissionType, bool read, bool write,
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret,
Guid userId,
@@ -215,7 +222,7 @@ public class SecretAuthorizationHandlerTests
var requirement = SecretOperations.Create;
SetupPermission(sutProvider, permissionType, secret.OrganizationId, userId);
sutProvider.GetDependency<IProjectRepository>()
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, default).Returns(
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, Arg.Any<AccessClientType>()).ReturnsForAnyArgs(
(read, write));
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, secret);
@@ -243,7 +250,6 @@ public class SecretAuthorizationHandlerTests
}
[Theory]
[BitAutoData(AccessClientType.ServiceAccount)]
[BitAutoData(AccessClientType.Organization)]
public async Task CanUpdateSecret_NotSupportedClientTypes_DoesNotSucceed(AccessClientType clientType,
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret, Guid userId,
@@ -252,7 +258,7 @@ public class SecretAuthorizationHandlerTests
var requirement = SecretOperations.Update;
SetupPermission(sutProvider, PermissionType.RunAsUserWithPermission, secret.OrganizationId, userId, clientType);
sutProvider.GetDependency<IProjectRepository>()
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, default).Returns(
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, Arg.Any<AccessClientType>()).Returns(
(true, true));
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, secret);
@@ -327,6 +333,15 @@ public class SecretAuthorizationHandlerTests
[BitAutoData(PermissionType.RunAsUserWithPermission, true, false, true, true)]
[BitAutoData(PermissionType.RunAsUserWithPermission, true, false, false, true)]
[BitAutoData(PermissionType.RunAsUserWithPermission, true, false, false, false)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, true, true, false)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, true, false, false)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, false, true, true, false)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, false, true, false, false)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, false, false, true, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, false, false, false, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, false, true, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, false, false, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, false, false, false)]
public async Task CanUpdateSecret_DoesNotSucceed(PermissionType permissionType, bool read, bool write,
bool projectRead, bool projectWrite,
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret,
@@ -335,10 +350,10 @@ public class SecretAuthorizationHandlerTests
{
var requirement = SecretOperations.Update;
SetupPermission(sutProvider, permissionType, secret.OrganizationId, userId);
sutProvider.GetDependency<ISecretRepository>().AccessToSecretAsync(secret.Id, userId, default).Returns(
sutProvider.GetDependency<ISecretRepository>().AccessToSecretAsync(secret.Id, userId, Arg.Any<AccessClientType>()).Returns(
(read, write));
sutProvider.GetDependency<IProjectRepository>()
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, default).Returns(
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, Arg.Any<AccessClientType>()).Returns(
(projectRead, projectWrite));
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, secret);
@@ -355,6 +370,8 @@ public class SecretAuthorizationHandlerTests
[BitAutoData(PermissionType.RunAsAdmin, false, false)]
[BitAutoData(PermissionType.RunAsUserWithPermission, true, true)]
[BitAutoData(PermissionType.RunAsUserWithPermission, false, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, false, true)]
public async Task CanUpdateSecret_Success(PermissionType permissionType, bool read, bool write,
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret,
Guid userId,
@@ -362,10 +379,10 @@ public class SecretAuthorizationHandlerTests
{
var requirement = SecretOperations.Update;
SetupPermission(sutProvider, permissionType, secret.OrganizationId, userId);
sutProvider.GetDependency<ISecretRepository>().AccessToSecretAsync(secret.Id, userId, default).Returns(
sutProvider.GetDependency<ISecretRepository>().AccessToSecretAsync(secret.Id, userId, Arg.Any<AccessClientType>()).Returns(
(read, write));
sutProvider.GetDependency<IProjectRepository>()
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, default).Returns(
.AccessToProjectAsync(secret.Projects!.FirstOrDefault()!.Id, userId, Arg.Any<AccessClientType>()).Returns(
(read, write));
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, secret);
@@ -409,32 +426,16 @@ public class SecretAuthorizationHandlerTests
Assert.False(authzContext.HasSucceeded);
}
[Theory]
[BitAutoData]
public async Task CanDeleteSecret_ServiceAccountClient_DoesNotSucceed(
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret, Guid userId,
ClaimsPrincipal claimsPrincipal)
{
var requirement = SecretOperations.Delete;
SetupPermission(sutProvider, PermissionType.RunAsUserWithPermission, secret.OrganizationId, userId,
AccessClientType.ServiceAccount);
sutProvider.GetDependency<ISecretRepository>()
.AccessToSecretAsync(secret.Id, userId, Arg.Any<AccessClientType>())
.Returns((true, true));
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, secret);
await sutProvider.Sut.HandleAsync(authzContext);
Assert.False(authzContext.HasSucceeded);
}
[Theory]
[BitAutoData(PermissionType.RunAsAdmin, true, true, true)]
[BitAutoData(PermissionType.RunAsUserWithPermission, false, false, false)]
[BitAutoData(PermissionType.RunAsUserWithPermission, false, true, true)]
[BitAutoData(PermissionType.RunAsUserWithPermission, true, false, false)]
[BitAutoData(PermissionType.RunAsUserWithPermission, true, true, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, false, false, false)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, false, true, true)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, false, false)]
[BitAutoData(PermissionType.RunAsServiceAccountWithPermission, true, true, true)]
public async Task CanDeleteProject_AccessCheck(PermissionType permissionType, bool read, bool write,
bool expected,
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret,

View File

@@ -1,4 +1,5 @@
using Bit.Commercial.Core.SecretsManager.Commands.Projects;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.SecretsManager.Entities;
@@ -29,7 +30,7 @@ public class CreateProjectCommandTests
.CreateAsync(Arg.Any<Project>())
.Returns(data);
await sutProvider.Sut.CreateAsync(data, userId);
await sutProvider.Sut.CreateAsync(data, userId, sutProvider.GetDependency<ICurrentContext>().ClientType);
await sutProvider.GetDependency<IProjectRepository>().Received(1)
.CreateAsync(Arg.Is(data));

View File

@@ -4,4 +4,5 @@ public enum PermissionType
{
RunAsAdmin,
RunAsUserWithPermission,
RunAsServiceAccountWithPermission
}

View File

@@ -2818,7 +2818,7 @@
"commercial.core": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3"
"Core": "2023.5.1"
}
},
"common": {
@@ -2826,7 +2826,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"NSubstitute": "4.3.0",
@@ -2880,8 +2880,8 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Common": "2023.4.3",
"Core": "2023.4.3",
"Common": "2023.5.1",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"Moq": "4.17.2",

View File

@@ -3144,7 +3144,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"NSubstitute": "4.3.0",
@@ -3196,15 +3196,15 @@
"identity": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"SharedWeb": "2023.4.3",
"Core": "2023.5.1",
"SharedWeb": "2023.5.1",
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -3212,7 +3212,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -3224,8 +3224,8 @@
"integrationtestcommon": {
"type": "Project",
"dependencies": {
"Common": "2023.4.3",
"Identity": "2023.4.3",
"Common": "2023.5.1",
"Identity": "2023.5.1",
"Microsoft.AspNetCore.Mvc.Testing": "6.0.5",
"Microsoft.EntityFrameworkCore.InMemory": "6.0.5",
"Microsoft.Extensions.Configuration": "6.0.1"
@@ -3234,16 +3234,16 @@
"scim": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"SharedWeb": "2023.4.3"
"Core": "2023.5.1",
"SharedWeb": "2023.5.1"
}
},
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -2989,7 +2989,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"NSubstitute": "4.3.0",
@@ -3041,7 +3041,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -3049,7 +3049,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -3061,16 +3061,16 @@
"scim": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"SharedWeb": "2023.4.3"
"Core": "2023.5.1",
"SharedWeb": "2023.5.1"
}
},
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Script for generating and installing the Bitwarden development certificates on Linux.
IDENTITY_SERVER_KEY=identity_server_dev.key
IDENTITY_SERVER_CERT=identity_server_dev.crt
IDENTITY_SERVER_CN="Bitwarden Identity Server Dev"
DATA_PROTECTION_KEY=data_protection_dev.key
DATA_PROTECTION_CERT=data_protection_dev.crt
DATA_PROTECTION_CN="Bitwarden Data Protection Dev"
# Detect management command to trust generated certificates.
if [ -x "$(command -v update-ca-certificates)" ]; then
# Debian based
CA_CERT_DIR=/usr/local/share/ca-certificates/
UPDATE_CA_CMD=update-ca-certificates
elif [ -x "$(command -v update-ca-trust)" ]; then
# Redhat based
CA_CERT_DIR=/etc/pki/ca-trust/source/anchors/
UPDATE_CA_CMD=update-ca-trust
else
echo 'Error: Update manager for CA certificates not found!'
exit 1
fi
openssl req -x509 -newkey rsa:4096 -sha256 -nodes -days 3650 \
-keyout $IDENTITY_SERVER_KEY \
-out $IDENTITY_SERVER_CERT \
-subj "/CN=$IDENTITY_SERVER_CN"
sudo cp $IDENTITY_SERVER_CERT $CA_CERT_DIR
openssl req -x509 -newkey rsa:4096 -sha256 -nodes -days 3650 \
-keyout $DATA_PROTECTION_KEY \
-out $DATA_PROTECTION_CERT \
-subj "/CN=$DATA_PROTECTION_CN"
sudo cp $DATA_PROTECTION_CERT $CA_CERT_DIR
sudo $UPDATE_CA_CMD
identity=($(openssl x509 -in $IDENTITY_SERVER_CERT -outform der | sha1sum | tr a-z A-Z))
data=($(openssl x509 -in $DATA_PROTECTION_CERT -outform der | sha1sum | tr a-z A-Z))
echo "Certificate fingerprints:"
echo "Identity Server Dev: ${identity}"
echo "Data Protection Dev: ${data}"

View File

@@ -15,7 +15,7 @@
"connectionString": "Server=localhost;Database=vault_dev;User Id=SA;Password=SET_A_PASSWORD_HERE_123;Encrypt=True;TrustServerCertificate=True"
},
"postgreSql": {
"connectionString": "Host=localhost;Username=postgres;Password=SET_A_PASSWORD_HERE_123;Database=vault_dev;Include Error Detail=true",
"connectionString": "Host=localhost;Username=postgres;Password=SET_A_PASSWORD_HERE_123;Database=vault_dev;Include Error Detail=true"
},
"mySql": {
"connectionString": "server=localhost;uid=root;pwd=SET_A_PASSWORD_HERE_123;database=vault_dev"

View File

@@ -15,15 +15,35 @@ export const options = {
name: "Config",
},
},
scenarios: {
constant_load: {
executor: "constant-arrival-rate",
rate: 1,
timeUnit: "1s", // 1 request / second
duration: "10m",
preAllocatedVUs: 5,
},
ramping_load: {
executor: "ramping-arrival-rate",
startRate: 60,
timeUnit: "1m", // 1 request / second to start
stages: [
{ duration: "30s", target: 10 },
{ duration: "1m", target: 20 },
{ duration: "2m", target: 25 },
{ duration: "30s", target: 60 },
{ duration: "2m", target: 150 },
{ duration: "1m", target: 90 },
{ duration: "2m", target: 200 },
{ duration: "2m", target: 120 },
{ duration: "1m", target: 180 },
{ duration: "30s", target: 250 },
{ duration: "30s", target: 90 },
{ duration: "30s", target: 0 },
],
preAllocatedVUs: 40,
},
},
thresholds: {
http_req_failed: ["rate<0.01"],
http_req_duration: ["p(95)<1000"],
http_req_duration: ["p(95)<750"],
},
};

View File

@@ -16,15 +16,35 @@ export const options = {
name: "Groups",
},
},
scenarios: {
constant_load: {
executor: "constant-arrival-rate",
rate: 30,
timeUnit: "1m", // 0.5 requests / second
duration: "10m",
preAllocatedVUs: 5,
},
ramping_load: {
executor: "ramping-arrival-rate",
startRate: 30,
timeUnit: "1m", // 0.5 requests / second to start
stages: [
{ duration: "30s", target: 10 },
{ duration: "1m", target: 20 },
{ duration: "2m", target: 25 },
{ duration: "30s", target: 30 },
{ duration: "2m", target: 75 },
{ duration: "1m", target: 60 },
{ duration: "2m", target: 100 },
{ duration: "2m", target: 90 },
{ duration: "1m", target: 120 },
{ duration: "30s", target: 150 },
{ duration: "30s", target: 60 },
{ duration: "30s", target: 0 },
],
preAllocatedVUs: 20,
},
},
thresholds: {
http_req_failed: ["rate<0.01"],
http_req_duration: ["p(95)<1500"],
http_req_duration: ["p(95)<900"],
},
};

View File

@@ -12,15 +12,33 @@ export const options = {
name: "Login",
},
},
scenarios: {
constant_load: {
executor: "constant-arrival-rate",
rate: 2,
timeUnit: "1s", // 2 requests / second
duration: "10m",
preAllocatedVUs: 10,
},
ramping_load: {
executor: "ramping-arrival-rate",
startRate: 60,
timeUnit: "1m", // 1 request / second to start
stages: [
{ duration: "30s", target: 10 },
{ duration: "1m", target: 20 },
{ duration: "2m", target: 25 },
{ duration: "30s", target: 60 },
{ duration: "5m", target: 120 },
{ duration: "2m", target: 150 },
{ duration: "1m", target: 180 },
{ duration: "30s", target: 200 },
{ duration: "30s", target: 90 },
{ duration: "30s", target: 0 },
],
preAllocatedVUs: 25,
},
},
thresholds: {
http_req_failed: ["rate<0.01"],
http_req_duration: ["p(95)<3000"],
http_req_duration: ["p(95)<1500"],
},
};

View File

@@ -25,7 +25,7 @@ docker_build() {
docker build -t bitwarden/$project_name_lower:$docker_tag $project_dir
if [ "$docker_push" == "1" ]; then
docker push bitwarden/$project_name_lower:$docker_tag $project_dir
docker push bitwarden/$project_name_lower:$docker_tag
fi
}

View File

@@ -1,6 +1,7 @@
using Bit.Core;
using Bit.Core.Jobs;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Quartz;
namespace Bit.Admin.Auth.Jobs;
@@ -8,20 +9,26 @@ namespace Bit.Admin.Auth.Jobs;
public class DeleteAuthRequestsJob : BaseJob
{
private readonly IAuthRequestRepository _authRepo;
private readonly IGlobalSettings _globalSettings;
public DeleteAuthRequestsJob(
IAuthRequestRepository authrepo,
IGlobalSettings globalSettings,
ILogger<DeleteAuthRequestsJob> logger)
: base(logger)
{
_authRepo = authrepo;
_globalSettings = globalSettings;
}
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
{
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteAuthRequestsJob: Start");
var count = await _authRepo.DeleteExpiredAsync();
_logger.LogInformation(Constants.BypassFiltersEventId, $"{count} records deleted from AuthRequests.");
var count = await _authRepo.DeleteExpiredAsync(
_globalSettings.PasswordlessAuth.UserRequestExpiration,
_globalSettings.PasswordlessAuth.AdminRequestExpiration,
_globalSettings.PasswordlessAuth.AfterAdminApprovalExpiration);
_logger.LogInformation(Constants.BypassFiltersEventId, "{Count} records deleted from AuthRequests.", count);
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteAuthRequestsJob: End");
}
}

View File

@@ -17,8 +17,8 @@
"jquery": "3.5.1",
"merge-stream": "2.0.0",
"popper.js": "1.16.1",
"sass": "^1.49.7",
"toastr": "^2.1.4"
"sass": "1.49.7",
"toastr": "2.1.4"
}
},
"node_modules/@nodelib/fs.scandir": {
@@ -3925,9 +3925,9 @@
}
},
"node_modules/sass/node_modules/anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
@@ -4694,7 +4694,7 @@
"node_modules/toastr": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/toastr/-/toastr-2.1.4.tgz",
"integrity": "sha1-i0O+ZPudDEFIcURvLbjoyk6V8YE=",
"integrity": "sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA==",
"dev": true,
"dependencies": {
"jquery": ">=1.12.0"
@@ -8168,9 +8168,9 @@
},
"dependencies": {
"anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"requires": {
"normalize-path": "^3.0.0",
@@ -8774,7 +8774,7 @@
"toastr": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/toastr/-/toastr-2.1.4.tgz",
"integrity": "sha1-i0O+ZPudDEFIcURvLbjoyk6V8YE=",
"integrity": "sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA==",
"dev": true,
"requires": {
"jquery": ">=1.12.0"

View File

@@ -16,7 +16,7 @@
"jquery": "3.5.1",
"merge-stream": "2.0.0",
"popper.js": "1.16.1",
"sass": "^1.49.7",
"toastr": "^2.1.4"
"sass": "1.49.7",
"toastr": "2.1.4"
}
}

View File

@@ -2834,7 +2834,7 @@
"commercial.core": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3"
"Core": "2023.5.1"
}
},
"core": {
@@ -2882,7 +2882,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2890,7 +2890,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -2902,7 +2902,7 @@
"migrator": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.Extensions.Logging": "6.0.0",
"dbup-sqlserver": "5.0.8"
}
@@ -2910,30 +2910,30 @@
"mysqlmigrations": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
},
"postgresmigrations": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
},
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
},
"sqlitemigrations": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -9,6 +9,7 @@ using Bit.Core.Models.Business;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Models.Data.Organizations.Policies;
using Bit.Core.Repositories;
using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces;
using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -19,6 +20,7 @@ namespace Bit.Api.Controllers;
[Authorize("Application")]
public class OrganizationUsersController : Controller
{
private readonly IEnableAccessSecretsManagerCommand _enableAccessSecretsManagerCommand;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IOrganizationService _organizationService;
@@ -29,6 +31,7 @@ public class OrganizationUsersController : Controller
private readonly ICurrentContext _currentContext;
public OrganizationUsersController(
IEnableAccessSecretsManagerCommand enableAccessSecretsManagerCommand,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IOrganizationService organizationService,
@@ -38,6 +41,7 @@ public class OrganizationUsersController : Controller
IPolicyRepository policyRepository,
ICurrentContext currentContext)
{
_enableAccessSecretsManagerCommand = enableAccessSecretsManagerCommand;
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
_organizationService = organizationService;
@@ -420,6 +424,29 @@ public class OrganizationUsersController : Controller
return await RestoreOrRevokeUsersAsync(orgId, model, (orgId, orgUserIds, restoringUserId) => _organizationService.RestoreUsersAsync(orgId, orgUserIds, restoringUserId, _userService));
}
[HttpPatch("enable-secrets-manager")]
[HttpPut("enable-secrets-manager")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkEnableSecretsManagerAsync(Guid orgId,
[FromBody] OrganizationUserBulkRequestModel model)
{
if (!await _currentContext.ManageUsers(orgId))
{
throw new NotFoundException();
}
var orgUsers = (await _organizationUserRepository.GetManyAsync(model.Ids))
.Where(ou => ou.OrganizationId == orgId).ToList();
if (orgUsers.Count == 0)
{
throw new BadRequestException("Users invalid.");
}
var results = await _enableAccessSecretsManagerCommand.EnableUsersAsync(orgUsers);
return new ListResponseModel<OrganizationUserBulkResponseModel>(results.Select(r =>
new OrganizationUserBulkResponseModel(r.organizationUser.Id, r.error)));
}
private async Task RestoreOrRevokeUserAsync(
Guid orgId,
Guid id,

View File

@@ -28,6 +28,7 @@ public class ConfigResponseModel : ResponseModel
GitHash = AssemblyHelpers.GetGitHash();
Environment = new EnvironmentConfigResponseModel
{
CloudRegion = globalSettings.BaseServiceUri.CloudRegion,
Vault = globalSettings.BaseServiceUri.Vault,
Api = globalSettings.BaseServiceUri.Api,
Identity = globalSettings.BaseServiceUri.Identity,
@@ -46,6 +47,7 @@ public class ServerConfigResponseModel
public class EnvironmentConfigResponseModel
{
public string CloudRegion { get; set; }
public string Vault { get; set; }
public string Api { get; set; }
public string Identity { get; set; }

View File

@@ -73,9 +73,8 @@ public class ProjectsController : Controller
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User).Value;
var result = await _createProjectCommand.CreateAsync(project, userId);
var result = await _createProjectCommand.CreateAsync(project, userId, _currentContext.ClientType);
// Creating a project means you have read & write permission.
return new ProjectResponseModel(result, true, true);

View File

@@ -136,7 +136,10 @@ public class Startup
services.AddCoreLocalizationServices();
//health check
if (!globalSettings.SelfHosted)
{
services.AddHealthChecks(globalSettings);
}
#if OSS
services.AddOosServices();
@@ -215,12 +218,15 @@ public class Startup
{
endpoints.MapDefaultControllerRoute();
if (!globalSettings.SelfHosted)
{
endpoints.MapHealthChecks("/healthz");
endpoints.MapHealthChecks("/healthz/extended", new HealthCheckOptions
{
ResponseWriter = HealthCheckServiceExtensions.WriteResponse
});
}
});
// Add Swagger

View File

@@ -1,5 +1,6 @@
using Bit.Core.IdentityServer;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Bit.SharedWeb.Health;
using Microsoft.OpenApi.Models;
@@ -80,35 +81,35 @@ public static class ServiceCollectionExtensions
builder.AddUrlGroup(identityUri, "identity");
if (!string.IsNullOrEmpty(globalSettings.SqlServer.ConnectionString))
if (CoreHelpers.SettingHasValue(globalSettings.SqlServer.ConnectionString))
{
builder.AddSqlServer(globalSettings.SqlServer.ConnectionString);
}
if (!string.IsNullOrEmpty(globalSettings.Redis.ConnectionString))
if (CoreHelpers.SettingHasValue(globalSettings.Redis.ConnectionString))
{
builder.AddRedis(globalSettings.Redis.ConnectionString);
}
if (!string.IsNullOrEmpty(globalSettings.Storage.ConnectionString))
if (CoreHelpers.SettingHasValue(globalSettings.Storage.ConnectionString))
{
builder.AddAzureQueueStorage(globalSettings.Storage.ConnectionString, name: "storage_queue")
.AddAzureQueueStorage(globalSettings.Events.ConnectionString, name: "events_queue");
}
if (!string.IsNullOrEmpty(globalSettings.Notifications.ConnectionString))
if (CoreHelpers.SettingHasValue(globalSettings.Notifications.ConnectionString))
{
builder.AddAzureQueueStorage(globalSettings.Notifications.ConnectionString,
name: "notifications_queue");
}
if (!string.IsNullOrEmpty(globalSettings.ServiceBus.ConnectionString))
if (CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ConnectionString))
{
builder.AddAzureServiceBusTopic(_ => globalSettings.ServiceBus.ConnectionString,
_ => globalSettings.ServiceBus.ApplicationCacheTopicName, name: "service_bus");
}
if (!string.IsNullOrEmpty(globalSettings.Mail.SendGridApiKey))
if (CoreHelpers.SettingHasValue(globalSettings.Mail.SendGridApiKey))
{
builder.AddSendGrid(globalSettings.Mail.SendGridApiKey);
}

View File

@@ -2907,85 +2907,85 @@
"commercial.core": {
"type": "Project",
"dependencies": {
"Core": "[2023.5.0, )"
"Core": "2023.5.1"
}
},
"commercial.infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.5.0, )",
"Infrastructure.EntityFramework": "[2023.5.0, )"
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.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.3.2, )",
"Azure.Storage.Blobs": "[12.14.1, )",
"Azure.Storage.Queues": "[12.12.0, )",
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.12.0, )",
"DnsClient": "[1.7.0, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[7.0.0, )",
"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, )"
"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.3.2",
"Azure.Storage.Blobs": "12.14.1",
"Azure.Storage.Queues": "12.12.0",
"BitPay.Light": "1.0.1907",
"Braintree": "5.12.0",
"DnsClient": "1.7.0",
"Fido2.AspNet": "3.0.1",
"Handlebars.Net": "2.1.2",
"IdentityServer4": "4.1.2",
"IdentityServer4.AccessTokenValidation": "3.0.1",
"LaunchDarkly.ServerSdk": "7.0.0",
"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.5.0, )",
"Dapper": "[2.0.123, )"
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
"infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.5.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, )"
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.5.1",
"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.5.0, )",
"Infrastructure.Dapper": "[2023.5.0, )",
"Infrastructure.EntityFramework": "[2023.5.0, )"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -2786,7 +2786,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2794,7 +2794,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -2806,9 +2806,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -5,7 +5,7 @@ namespace Bit.Core.Repositories;
public interface IAuthRequestRepository : IRepository<AuthRequest, Guid>
{
Task<int> DeleteExpiredAsync();
Task<int> DeleteExpiredAsync(TimeSpan userRequestExpiration, TimeSpan adminRequestExpiration, TimeSpan afterAdminApprovalExpiration);
Task<ICollection<AuthRequest>> GetManyByUserIdAsync(Guid userId);
Task<ICollection<OrganizationAdminAuthRequest>> GetManyPendingByOrganizationIdAsync(Guid organizationId);
Task<ICollection<OrganizationAdminAuthRequest>> GetManyAdminApprovalRequestsByManyIdsAsync(Guid organizationId, IEnumerable<Guid> ids);

View File

@@ -3,4 +3,7 @@
public interface IPasswordlessAuthSettings
{
bool KnownDevicesOnly { get; set; }
TimeSpan UserRequestExpiration { get; set; }
TimeSpan AdminRequestExpiration { get; set; }
TimeSpan AfterAdminApprovalExpiration { get; set; }
}

View File

@@ -15,6 +15,8 @@ using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterpri
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted;
using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager;
using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tokens;
@@ -29,6 +31,7 @@ public static class OrganizationServiceCollectionExtensions
public static void AddOrganizationServices(this IServiceCollection services, IGlobalSettings globalSettings)
{
services.AddScoped<IOrganizationService, OrganizationService>();
services.AddScoped<IEnableAccessSecretsManagerCommand, EnableAccessSecretsManagerCommand>();
services.AddTokenizers();
services.AddOrganizationGroupCommands();
services.AddOrganizationConnectionCommands();

View File

@@ -0,0 +1,43 @@
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces;
namespace Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager;
public class EnableAccessSecretsManagerCommand : IEnableAccessSecretsManagerCommand
{
private readonly IOrganizationUserRepository _organizationUserRepository;
public EnableAccessSecretsManagerCommand(IOrganizationUserRepository organizationUserRepository)
{
_organizationUserRepository = organizationUserRepository;
}
public async Task<List<(OrganizationUser organizationUser, string error)>> EnableUsersAsync(
IEnumerable<OrganizationUser> organizationUsers)
{
var results = new List<(OrganizationUser organizationUser, string error)>();
var usersToEnable = new List<OrganizationUser>();
foreach (var orgUser in organizationUsers)
{
if (orgUser.AccessSecretsManager)
{
results.Add((orgUser, "User already has access to Secrets Manager"));
}
else
{
orgUser.AccessSecretsManager = true;
usersToEnable.Add(orgUser);
results.Add((orgUser, ""));
}
}
if (usersToEnable.Any())
{
await _organizationUserRepository.ReplaceManyAsync(usersToEnable);
}
return results;
}
}

View File

@@ -0,0 +1,9 @@
using Bit.Core.Entities;
namespace Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces;
public interface IEnableAccessSecretsManagerCommand
{
Task<List<(OrganizationUser organizationUser, string error)>> EnableUsersAsync(
IEnumerable<OrganizationUser> organizationUsers);
}

View File

@@ -1,8 +1,9 @@
using Bit.Core.SecretsManager.Entities;
using Bit.Core.Identity;
using Bit.Core.SecretsManager.Entities;
namespace Bit.Core.SecretsManager.Commands.Projects.Interfaces;
public interface ICreateProjectCommand
{
Task<Project> CreateAsync(Project project, Guid userId);
Task<Project> CreateAsync(Project project, Guid userId, ClientType clientType);
}

View File

@@ -143,6 +143,7 @@ public class GlobalSettings : IGlobalSettings
_globalSettings = globalSettings;
}
public string CloudRegion { get; set; }
public string Vault { get; set; }
public string VaultWithHash => $"{Vault}/#";
@@ -534,6 +535,9 @@ public class GlobalSettings : IGlobalSettings
public class PasswordlessAuthSettings : IPasswordlessAuthSettings
{
public bool KnownDevicesOnly { get; set; } = true;
public TimeSpan UserRequestExpiration { get; set; } = TimeSpan.FromMinutes(15);
public TimeSpan AdminRequestExpiration { get; set; } = TimeSpan.FromDays(7);
public TimeSpan AfterAdminApprovalExpiration { get; set; } = TimeSpan.FromHours(12);
}
public class DomainVerificationSettings : IDomainVerificationSettings

View File

@@ -3,6 +3,7 @@ namespace Bit.Core.Settings;
public interface IBaseServiceUriSettings
{
string CloudRegion { get; set; }
string Vault { get; set; }
string VaultWithHash { get; }
string Api { get; set; }

View File

@@ -2786,7 +2786,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2794,7 +2794,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -2806,9 +2806,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -2786,7 +2786,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2794,7 +2794,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -2806,9 +2806,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -2796,7 +2796,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2804,7 +2804,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -2816,9 +2816,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -2808,7 +2808,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2816,7 +2816,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -2828,9 +2828,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -19,13 +19,19 @@ public class AuthRequestRepository : Repository<AuthRequest, Guid>, IAuthRequest
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<int> DeleteExpiredAsync()
public async Task<int> DeleteExpiredAsync(
TimeSpan userRequestExpiration, TimeSpan adminRequestExpiration, TimeSpan afterAdminApprovalExpiration)
{
using (var connection = new SqlConnection(ConnectionString))
{
return await connection.ExecuteAsync(
$"[{Schema}].[AuthRequest_DeleteIfExpired]",
null,
new
{
UserExpirationSeconds = (int)userRequestExpiration.TotalSeconds,
AdminExpirationSeconds = (int)adminRequestExpiration.TotalSeconds,
AdminApprovalExpirationSeconds = (int)afterAdminApprovalExpiration.TotalSeconds,
},
commandType: CommandType.StoredProcedure);
}
}

View File

@@ -15,15 +15,20 @@ public class AuthRequestRepository : Repository<Core.Auth.Entities.AuthRequest,
public AuthRequestRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.AuthRequests)
{ }
public async Task<int> DeleteExpiredAsync()
public async Task<int> DeleteExpiredAsync(
TimeSpan userRequestExpiration, TimeSpan adminRequestExpiration, TimeSpan afterAdminApprovalExpiration)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var expiredRequests = await dbContext.AuthRequests.Where(a => a.CreationDate < DateTime.Now.AddMinutes(-15)).ToListAsync();
var expiredRequests = await dbContext.AuthRequests
.Where(a => (a.Type != AuthRequestType.AdminApproval && a.CreationDate.AddSeconds(userRequestExpiration.TotalSeconds) < DateTime.UtcNow)
|| (a.Type == AuthRequestType.AdminApproval && a.Approved != true && a.CreationDate.AddSeconds(adminRequestExpiration.TotalSeconds) < DateTime.UtcNow)
|| (a.Type == AuthRequestType.AdminApproval && a.Approved == true && a.ResponseDate.Value.AddSeconds(afterAdminApprovalExpiration.TotalSeconds) < DateTime.UtcNow))
.ToListAsync();
dbContext.AuthRequests.RemoveRange(expiredRequests);
await dbContext.SaveChangesAsync();
return 1;
return await dbContext.SaveChangesAsync();
}
}

View File

@@ -50,7 +50,7 @@ public class UserCipherDetailsQuery : IQuery<CipherDetails>
where ou.AccessAll || cu.CollectionId != null || g.AccessAll || cg.CollectionId != null
select new { c, ou, o, cc, cu, gu, g, cg }.c;
select c;
var query2 = from c in dbContext.Ciphers
where c.UserId == _userId
@@ -78,6 +78,8 @@ public class UserCipherDetailsQuery : IQuery<CipherDetails>
}
private static Guid? GetFolderId(Guid? userId, Cipher cipher)
{
try
{
if (userId.HasValue && !string.IsNullOrWhiteSpace(cipher.Folders))
{
@@ -87,6 +89,13 @@ public class UserCipherDetailsQuery : IQuery<CipherDetails>
return folder;
}
}
return null;
}
catch
{
// Some Folders might be in an invalid format like: '{ "", "<ValidGuid>" }'
return null;
}
}
}

View File

@@ -1,4 +1,6 @@
using AutoMapper;
using System.Text.Json;
using System.Text.Json.Nodes;
using AutoMapper;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Bit.Core.Vault.Enums;
@@ -13,8 +15,8 @@ using Bit.Infrastructure.EntityFramework.Vault.Repositories.Queries;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NS = Newtonsoft.Json;
using NSL = Newtonsoft.Json.Linq;
using User = Bit.Core.Entities.User;
namespace Bit.Infrastructure.EntityFramework.Vault.Repositories;
@@ -198,9 +200,9 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
{
var dbContext = GetDatabaseContext(scope);
var cipher = await dbContext.Ciphers.FindAsync(cipherId);
var attachmentsJson = JObject.Parse(cipher.Attachments);
var attachmentsJson = NSL.JObject.Parse(cipher.Attachments);
attachmentsJson.Remove(attachmentId);
cipher.Attachments = JsonConvert.SerializeObject(attachmentsJson);
cipher.Attachments = NS.JsonConvert.SerializeObject(attachmentsJson);
await dbContext.SaveChangesAsync();
if (cipher.OrganizationId.HasValue)
@@ -396,8 +398,8 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
await idsToMove.ForEachAsync(cipher =>
{
var foldersJson = string.IsNullOrWhiteSpace(cipher.Folders) ?
new JObject() :
JObject.Parse(cipher.Folders);
new NSL.JObject() :
NSL.JObject.Parse(cipher.Folders);
if (folderId.HasValue)
{
@@ -409,7 +411,7 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
foldersJson.Remove(userId.ToString());
}
dbContext.Attach(cipher);
cipher.Folders = JsonConvert.SerializeObject(foldersJson);
cipher.Folders = NS.JsonConvert.SerializeObject(foldersJson);
});
await dbContext.UserBumpAccountRevisionDateAsync(userId);
await dbContext.SaveChangesAsync();
@@ -418,27 +420,27 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
public async Task ReplaceAsync(CipherDetails cipher)
{
cipher.UserId = cipher.OrganizationId.HasValue ?
null :
cipher.UserId;
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var entity = await dbContext.Ciphers.FindAsync(cipher.Id);
if (entity != null)
{
var userIdKey = $"\"{cipher.UserId}\"";
if (cipher.Favorite)
{
if (cipher.Favorites == null)
{
cipher.Favorites = $"{{{userIdKey}:true}}";
var jsonObject = new JsonObject(new[]
{
new KeyValuePair<string, JsonNode>(cipher.UserId.Value.ToString(), true),
});
cipher.Favorites = JsonSerializer.Serialize(jsonObject);
}
else
{
var favorites = CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, bool>>(cipher.Favorites);
favorites.Add(cipher.UserId.Value, true);
cipher.Favorites = JsonConvert.SerializeObject(favorites);
cipher.Favorites = JsonSerializer.Serialize(favorites);
}
}
else
@@ -447,32 +449,45 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
{
var favorites = CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, bool>>(cipher.Favorites);
favorites.Remove(cipher.UserId.Value);
cipher.Favorites = JsonConvert.SerializeObject(favorites);
cipher.Favorites = JsonSerializer.Serialize(favorites);
}
}
if (cipher.FolderId.HasValue)
{
if (cipher.Folders == null)
{
cipher.Folders = $"{{{userIdKey}:\"{cipher.FolderId}\"}}";
var jsonObject = new JsonObject(new[]
{
new KeyValuePair<string, JsonNode>(cipher.UserId.Value.ToString(), cipher.FolderId),
});
cipher.Folders = JsonSerializer.Serialize(jsonObject);
}
else
{
var folders = CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, Guid>>(cipher.Folders);
folders.Add(cipher.UserId.Value, cipher.FolderId.Value);
cipher.Folders = JsonConvert.SerializeObject(folders);
cipher.Folders = JsonSerializer.Serialize(folders);
}
}
else
{
if (cipher.Folders != null && cipher.Folders.Contains(cipher.UserId.Value.ToString()))
{
var folders = CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, bool>>(cipher.Favorites);
var folders = CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, Guid>>(cipher.Folders);
folders.Remove(cipher.UserId.Value);
cipher.Favorites = JsonConvert.SerializeObject(folders);
cipher.Folders = JsonSerializer.Serialize(folders);
}
}
var mappedEntity = Mapper.Map<Cipher>((Core.Vault.Entities.Cipher)cipher);
// Check if this cipher is a part of an organization, and if so do
// not save the UserId into the database. This must be done after we
// set the user specific data like Folders and Favorites because
// the UserId key is used for that
cipher.UserId = cipher.OrganizationId.HasValue ?
null :
cipher.UserId;
var mappedEntity = Mapper.Map<Cipher>(cipher);
dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity);
if (cipher.OrganizationId.HasValue)
@@ -701,10 +716,10 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
var cipher = await dbContext.Ciphers.FindAsync(attachment.Id);
var attachments = string.IsNullOrWhiteSpace(cipher.Attachments) ?
new Dictionary<string, CipherAttachment.MetaData>() :
JsonConvert.DeserializeObject<Dictionary<string, CipherAttachment.MetaData>>(cipher.Attachments);
var metaData = JsonConvert.DeserializeObject<CipherAttachment.MetaData>(attachment.AttachmentData);
NS.JsonConvert.DeserializeObject<Dictionary<string, CipherAttachment.MetaData>>(cipher.Attachments);
var metaData = NS.JsonConvert.DeserializeObject<CipherAttachment.MetaData>(attachment.AttachmentData);
attachments[attachment.AttachmentId] = metaData;
cipher.Attachments = JsonConvert.SerializeObject(attachments);
cipher.Attachments = NS.JsonConvert.SerializeObject(attachments);
await dbContext.SaveChangesAsync();
if (attachment.OrganizationId.HasValue)
@@ -744,7 +759,7 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
var dbContext = GetDatabaseContext(scope);
var cipher = await dbContext.Ciphers.FindAsync(id);
var foldersJson = JObject.Parse(cipher.Folders);
var foldersJson = NSL.JObject.Parse(cipher.Folders);
if (foldersJson == null && folderId.HasValue)
{
foldersJson.Add(userId.ToString(), folderId.Value);
@@ -758,7 +773,7 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
foldersJson.Remove(userId.ToString());
}
var favoritesJson = JObject.Parse(cipher.Favorites);
var favoritesJson = NSL.JObject.Parse(cipher.Favorites);
if (favorite)
{
favoritesJson.Add(userId.ToString(), favorite);

View File

@@ -2836,7 +2836,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2844,7 +2844,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -2856,9 +2856,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -2786,7 +2786,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2794,7 +2794,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",

View File

@@ -1,6 +1,19 @@
CREATE PROCEDURE [dbo].[AuthRequest_DeleteIfExpired]
-- UserExpirationSeconds to 15 minutes (15 * 60)
-- AdminExpirationSeconds to 7 days (7 * 24 * 60 * 60)
-- AdminApprovalExpirationSeconds to 12 hour (12 * 60 * 60)
CREATE PROCEDURE [dbo].[AuthRequest_DeleteIfExpired]
@UserExpirationSeconds INT = 900,
@AdminExpirationSeconds INT = 604800,
@AdminApprovalExpirationSeconds INT = 43200
AS
BEGIN
SET NOCOUNT OFF
DELETE FROM [dbo].[AuthRequest] WHERE [CreationDate] < DATEADD(minute, -15, GETUTCDATE());
DELETE FROM [dbo].[AuthRequest]
-- User requests expire after 15 minutes (by default) of their creation
WHERE ([Type] != 2 AND DATEADD(second, @UserExpirationSeconds, [CreationDate]) < GETUTCDATE())
-- Admin requests expire after 7 days (by default) of their creation if they have not been approved
OR ([Type] = 2 AND ([Approved] IS NULL OR [Approved] = 0) AND DATEADD(second, @AdminExpirationSeconds,[CreationDate]) < GETUTCDATE())
-- Admin requests expire after 12 hours (by default) of their approval
OR ([Type] = 2 AND [Approved] = 1 AND DATEADD(second, @AdminApprovalExpirationSeconds, [ResponseDate]) < GETUTCDATE());
END

View File

@@ -3273,133 +3273,133 @@
"api": {
"type": "Project",
"dependencies": {
"AspNetCore.HealthChecks.AzureServiceBus": "[6.1.0, )",
"AspNetCore.HealthChecks.AzureStorage": "[6.1.2, )",
"AspNetCore.HealthChecks.Network": "[6.0.4, )",
"AspNetCore.HealthChecks.Redis": "[6.0.4, )",
"AspNetCore.HealthChecks.SendGrid": "[6.0.2, )",
"AspNetCore.HealthChecks.SqlServer": "[6.0.2, )",
"AspNetCore.HealthChecks.Uris": "[6.0.3, )",
"Azure.Messaging.EventGrid": "[4.10.0, )",
"Commercial.Core": "[2023.5.0, )",
"Commercial.Infrastructure.EntityFramework": "[2023.5.0, )",
"Core": "[2023.5.0, )",
"SharedWeb": "[2023.5.0, )",
"Swashbuckle.AspNetCore": "[6.5.0, )"
"AspNetCore.HealthChecks.AzureServiceBus": "6.1.0",
"AspNetCore.HealthChecks.AzureStorage": "6.1.2",
"AspNetCore.HealthChecks.Network": "6.0.4",
"AspNetCore.HealthChecks.Redis": "6.0.4",
"AspNetCore.HealthChecks.SendGrid": "6.0.2",
"AspNetCore.HealthChecks.SqlServer": "6.0.2",
"AspNetCore.HealthChecks.Uris": "6.0.3",
"Azure.Messaging.EventGrid": "4.10.0",
"Commercial.Core": "2023.5.1",
"Commercial.Infrastructure.EntityFramework": "2023.5.1",
"Core": "2023.5.1",
"SharedWeb": "2023.5.1",
"Swashbuckle.AspNetCore": "6.5.0"
}
},
"commercial.core": {
"type": "Project",
"dependencies": {
"Core": "[2023.5.0, )"
"Core": "2023.5.1"
}
},
"commercial.infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.5.0, )",
"Infrastructure.EntityFramework": "[2023.5.0, )"
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
},
"common": {
"type": "Project",
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.5.0, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
"xunit": "[2.4.1, )"
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"NSubstitute": "4.3.0",
"xunit": "2.4.1"
}
},
"core": {
"type": "Project",
"dependencies": {
"AWSSDK.SQS": "[3.7.2.47, )",
"AWSSDK.SimpleEmail": "[3.7.0.150, )",
"AspNetCoreRateLimit": "[4.0.2, )",
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.3.2, )",
"Azure.Storage.Blobs": "[12.14.1, )",
"Azure.Storage.Queues": "[12.12.0, )",
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.12.0, )",
"DnsClient": "[1.7.0, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[7.0.0, )",
"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, )"
"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.3.2",
"Azure.Storage.Blobs": "12.14.1",
"Azure.Storage.Queues": "12.12.0",
"BitPay.Light": "1.0.1907",
"Braintree": "5.12.0",
"DnsClient": "1.7.0",
"Fido2.AspNet": "3.0.1",
"Handlebars.Net": "2.1.2",
"IdentityServer4": "4.1.2",
"IdentityServer4.AccessTokenValidation": "3.0.1",
"LaunchDarkly.ServerSdk": "7.0.0",
"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"
}
},
"identity": {
"type": "Project",
"dependencies": {
"Core": "[2023.5.0, )",
"SharedWeb": "[2023.5.0, )",
"Swashbuckle.AspNetCore.SwaggerGen": "[6.5.0, )"
"Core": "2023.5.1",
"SharedWeb": "2023.5.1",
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.5.0, )",
"Dapper": "[2.0.123, )"
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
"infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.5.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, )"
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.5.1",
"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"
}
},
"integrationtestcommon": {
"type": "Project",
"dependencies": {
"Common": "[2023.5.0, )",
"Identity": "[2023.5.0, )",
"Microsoft.AspNetCore.Mvc.Testing": "[6.0.5, )",
"Microsoft.EntityFrameworkCore.InMemory": "[6.0.5, )",
"Microsoft.Extensions.Configuration": "[6.0.1, )"
"Common": "2023.5.1",
"Identity": "2023.5.1",
"Microsoft.AspNetCore.Mvc.Testing": "6.0.5",
"Microsoft.EntityFrameworkCore.InMemory": "6.0.5",
"Microsoft.Extensions.Configuration": "6.0.1"
}
},
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "[2023.5.0, )",
"Infrastructure.Dapper": "[2023.5.0, )",
"Infrastructure.EntityFramework": "[2023.5.0, )"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -114,12 +114,12 @@ public class ProjectsControllerTests
var resultProject = data.ToProject(orgId);
sutProvider.GetDependency<ICreateProjectCommand>().CreateAsync(default, default)
sutProvider.GetDependency<ICreateProjectCommand>().CreateAsync(default, default, sutProvider.GetDependency<ICurrentContext>().ClientType)
.ReturnsForAnyArgs(resultProject);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.CreateAsync(orgId, data));
await sutProvider.GetDependency<ICreateProjectCommand>().DidNotReceiveWithAnyArgs()
.CreateAsync(Arg.Any<Project>(), Arg.Any<Guid>());
.CreateAsync(Arg.Any<Project>(), Arg.Any<Guid>(), sutProvider.GetDependency<ICurrentContext>().ClientType);
}
[Theory]
@@ -134,13 +134,13 @@ public class ProjectsControllerTests
var resultProject = data.ToProject(orgId);
sutProvider.GetDependency<ICreateProjectCommand>().CreateAsync(default, default)
sutProvider.GetDependency<ICreateProjectCommand>().CreateAsync(default, default, sutProvider.GetDependency<ICurrentContext>().ClientType)
.ReturnsForAnyArgs(resultProject);
await sutProvider.Sut.CreateAsync(orgId, data);
await sutProvider.GetDependency<ICreateProjectCommand>().Received(1)
.CreateAsync(Arg.Any<Project>(), Arg.Any<Guid>());
.CreateAsync(Arg.Any<Project>(), Arg.Any<Guid>(), sutProvider.GetDependency<ICurrentContext>().ClientType);
}
[Theory]

View File

@@ -3151,129 +3151,129 @@
"api": {
"type": "Project",
"dependencies": {
"AspNetCore.HealthChecks.AzureServiceBus": "[6.1.0, )",
"AspNetCore.HealthChecks.AzureStorage": "[6.1.2, )",
"AspNetCore.HealthChecks.Network": "[6.0.4, )",
"AspNetCore.HealthChecks.Redis": "[6.0.4, )",
"AspNetCore.HealthChecks.SendGrid": "[6.0.2, )",
"AspNetCore.HealthChecks.SqlServer": "[6.0.2, )",
"AspNetCore.HealthChecks.Uris": "[6.0.3, )",
"Azure.Messaging.EventGrid": "[4.10.0, )",
"Commercial.Core": "[2023.5.0, )",
"Commercial.Infrastructure.EntityFramework": "[2023.5.0, )",
"Core": "[2023.5.0, )",
"SharedWeb": "[2023.5.0, )",
"Swashbuckle.AspNetCore": "[6.5.0, )"
"AspNetCore.HealthChecks.AzureServiceBus": "6.1.0",
"AspNetCore.HealthChecks.AzureStorage": "6.1.2",
"AspNetCore.HealthChecks.Network": "6.0.4",
"AspNetCore.HealthChecks.Redis": "6.0.4",
"AspNetCore.HealthChecks.SendGrid": "6.0.2",
"AspNetCore.HealthChecks.SqlServer": "6.0.2",
"AspNetCore.HealthChecks.Uris": "6.0.3",
"Azure.Messaging.EventGrid": "4.10.0",
"Commercial.Core": "2023.5.1",
"Commercial.Infrastructure.EntityFramework": "2023.5.1",
"Core": "2023.5.1",
"SharedWeb": "2023.5.1",
"Swashbuckle.AspNetCore": "6.5.0"
}
},
"commercial.core": {
"type": "Project",
"dependencies": {
"Core": "[2023.5.0, )"
"Core": "2023.5.1"
}
},
"commercial.infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.5.0, )",
"Infrastructure.EntityFramework": "[2023.5.0, )"
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
},
"common": {
"type": "Project",
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.5.0, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
"xunit": "[2.4.1, )"
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"NSubstitute": "4.3.0",
"xunit": "2.4.1"
}
},
"core": {
"type": "Project",
"dependencies": {
"AWSSDK.SQS": "[3.7.2.47, )",
"AWSSDK.SimpleEmail": "[3.7.0.150, )",
"AspNetCoreRateLimit": "[4.0.2, )",
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.3.2, )",
"Azure.Storage.Blobs": "[12.14.1, )",
"Azure.Storage.Queues": "[12.12.0, )",
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.12.0, )",
"DnsClient": "[1.7.0, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[7.0.0, )",
"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, )"
"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.3.2",
"Azure.Storage.Blobs": "12.14.1",
"Azure.Storage.Queues": "12.12.0",
"BitPay.Light": "1.0.1907",
"Braintree": "5.12.0",
"DnsClient": "1.7.0",
"Fido2.AspNet": "3.0.1",
"Handlebars.Net": "2.1.2",
"IdentityServer4": "4.1.2",
"IdentityServer4.AccessTokenValidation": "3.0.1",
"LaunchDarkly.ServerSdk": "7.0.0",
"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"
}
},
"core.test": {
"type": "Project",
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Common": "[2023.5.0, )",
"Core": "[2023.5.0, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"Moq": "[4.17.2, )",
"NSubstitute": "[4.3.0, )",
"xunit": "[2.4.1, )"
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Common": "2023.5.1",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"Moq": "4.17.2",
"NSubstitute": "4.3.0",
"xunit": "2.4.1"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.5.0, )",
"Dapper": "[2.0.123, )"
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
"infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.5.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, )"
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.5.1",
"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.5.0, )",
"Infrastructure.Dapper": "[2023.5.0, )",
"Infrastructure.EntityFramework": "[2023.5.0, )"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -2988,8 +2988,8 @@
"billing": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"SharedWeb": "2023.4.3"
"Core": "2023.5.1",
"SharedWeb": "2023.5.1"
}
},
"common": {
@@ -2997,7 +2997,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"NSubstitute": "4.3.0",
@@ -3049,7 +3049,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -3057,7 +3057,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -3069,9 +3069,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -0,0 +1,81 @@
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Helpers;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.SecretsManager.Commands.EnableAccessSecretsManager;
[SutProviderCustomize]
public class EnableAccessSecretsManagerCommandTests
{
[Theory]
[BitAutoData]
public async Task EnableUsers_UsersAlreadyEnabled_DoesNotCallRepository(
SutProvider<EnableAccessSecretsManagerCommand> sutProvider, ICollection<OrganizationUser> data)
{
foreach (var item in data)
{
item.AccessSecretsManager = true;
}
var result = await sutProvider.Sut.EnableUsersAsync(data);
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
.ReplaceManyAsync(default);
Assert.Equal(data.Count, result.Count);
Assert.Equal(data.Count,
result.Where(x => x.error == "User already has access to Secrets Manager").ToList().Count);
}
[Theory]
[BitAutoData]
public async Task EnableUsers_OneUserNotEnabled_CallsRepositoryForOne(
SutProvider<EnableAccessSecretsManagerCommand> sutProvider, ICollection<OrganizationUser> data)
{
var firstUser = new List<OrganizationUser>();
foreach (var item in data)
{
if (item == data.First())
{
item.AccessSecretsManager = false;
firstUser.Add(item);
}
else
{
item.AccessSecretsManager = true;
}
}
var result = await sutProvider.Sut.EnableUsersAsync(data);
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
.ReplaceManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(firstUser)));
Assert.Equal(data.Count, result.Count);
Assert.Equal(data.Count - 1,
result.Where(x => x.error == "User already has access to Secrets Manager").ToList().Count);
}
[Theory]
[BitAutoData]
public async Task EnableUsers_Success(
SutProvider<EnableAccessSecretsManagerCommand> sutProvider, ICollection<OrganizationUser> data)
{
foreach (var item in data)
{
item.AccessSecretsManager = false;
}
var result = await sutProvider.Sut.EnableUsersAsync(data);
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
.ReplaceManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data)));
Assert.Equal(data.Count, result.Count);
}
}

View File

@@ -2825,7 +2825,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"NSubstitute": "4.3.0",

View File

@@ -2972,14 +2972,14 @@
"type": "Project",
"dependencies": {
"AngleSharp": "0.16.1",
"Core": "2023.4.3",
"SharedWeb": "2023.4.3"
"Core": "2023.5.1",
"SharedWeb": "2023.5.1"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2987,7 +2987,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -2999,9 +2999,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -3144,7 +3144,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"NSubstitute": "4.3.0",
@@ -3196,15 +3196,15 @@
"identity": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"SharedWeb": "2023.4.3",
"Core": "2023.5.1",
"SharedWeb": "2023.5.1",
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -3212,7 +3212,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -3224,8 +3224,8 @@
"integrationtestcommon": {
"type": "Project",
"dependencies": {
"Common": "2023.4.3",
"Identity": "2023.4.3",
"Common": "2023.5.1",
"Identity": "2023.5.1",
"Microsoft.AspNetCore.Mvc.Testing": "6.0.5",
"Microsoft.EntityFrameworkCore.InMemory": "6.0.5",
"Microsoft.Extensions.Configuration": "6.0.1"
@@ -3234,9 +3234,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -3011,7 +3011,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"NSubstitute": "4.3.0",
@@ -3063,15 +3063,15 @@
"identity": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"SharedWeb": "2023.4.3",
"Core": "2023.5.1",
"SharedWeb": "2023.5.1",
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -3079,7 +3079,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -3091,9 +3091,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -3001,7 +3001,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"NSubstitute": "4.3.0",
@@ -3055,8 +3055,8 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Common": "2023.4.3",
"Core": "2023.4.3",
"Common": "2023.5.1",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"Moq": "4.17.2",
@@ -3067,7 +3067,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -3075,7 +3075,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",

View File

@@ -0,0 +1,99 @@
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Enums;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Xunit;
namespace Bit.Infrastructure.IntegrationTest.Auth.Repositories;
public class AuthRequestRepositoryTests
{
private readonly static TimeSpan _userRequestExpiration = TimeSpan.FromMinutes(15);
private readonly static TimeSpan _adminRequestExpiration = TimeSpan.FromDays(6);
private readonly static TimeSpan _afterAdminApprovalExpiration = TimeSpan.FromHours(12);
[DatabaseTheory, DatabaseData]
public async Task DeleteExpiredAsync_Works(
IAuthRequestRepository authRequestRepository,
IUserRepository userRepository,
ITestDatabaseHelper helper)
{
var user = await userRepository.CreateAsync(new User
{
Name = "Test User",
Email = $"test+{Guid.NewGuid()}@email.com",
ApiKey = "TEST",
SecurityStamp = "stamp",
});
// A user auth request type that has passed it's expiration time, should be deleted.
var userExpiredAuthRequest = await authRequestRepository.CreateAsync(
CreateAuthRequest(user.Id, AuthRequestType.AuthenticateAndUnlock, CreateExpiredDate(_userRequestExpiration)));
// An AdminApproval request that hasn't had any action taken on it and has passed it's expiration time, should be deleted.
var adminApprovalExpiredAuthRequest = await authRequestRepository.CreateAsync(
CreateAuthRequest(user.Id, AuthRequestType.AdminApproval, CreateExpiredDate(_adminRequestExpiration)));
// An AdminApproval request that was approved before it expired but the user has been approved for too long, should be deleted.
var adminApprovedExpiredAuthRequest = await authRequestRepository.CreateAsync(
CreateAuthRequest(user.Id, AuthRequestType.AdminApproval, DateTime.UtcNow.AddDays(-6), true, CreateExpiredDate(_afterAdminApprovalExpiration)));
// An AdminApproval request that was rejected within it's allowed lifetime but has no gone past it's expiration time, should be deleted.
var adminRejectedExpiredAuthRequest = await authRequestRepository.CreateAsync(
CreateAuthRequest(user.Id, AuthRequestType.AdminApproval, CreateExpiredDate(_adminRequestExpiration), false, DateTime.UtcNow.AddHours(-1)));
// A User AuthRequest that was created just a minute ago.
var notExpiredUserAuthRequest = await authRequestRepository.CreateAsync(
CreateAuthRequest(user.Id, AuthRequestType.Unlock, DateTime.UtcNow.AddMinutes(-1)));
// An AdminApproval AuthRequest that was create 6 days 23 hours 59 minutes 59 seconds ago which is right on the edge of still being valid
var notExpiredAdminApprovalRequest = await authRequestRepository.CreateAsync(
CreateAuthRequest(user.Id, AuthRequestType.AdminApproval, DateTime.UtcNow.Add(new TimeSpan(days: 6, hours: 23, minutes: 59, seconds: 59))));
// An AdminApproval AuthRequest that was created a week ago but just approved 11 hours ago.
var notExpiredApprovedAdminApprovalRequest = await authRequestRepository.CreateAsync(
CreateAuthRequest(user.Id, AuthRequestType.AdminApproval, DateTime.UtcNow.AddDays(7), true, DateTime.UtcNow.AddHours(11)));
helper.ClearTracker();
var numberOfDeleted = await authRequestRepository.DeleteExpiredAsync(_userRequestExpiration, _adminRequestExpiration, _afterAdminApprovalExpiration);
// Ensure all the AuthRequests that should have been deleted, have been deleted.
Assert.Null(await authRequestRepository.GetByIdAsync(userExpiredAuthRequest.Id));
Assert.Null(await authRequestRepository.GetByIdAsync(adminApprovalExpiredAuthRequest.Id));
Assert.Null(await authRequestRepository.GetByIdAsync(adminApprovedExpiredAuthRequest.Id));
Assert.Null(await authRequestRepository.GetByIdAsync(adminRejectedExpiredAuthRequest.Id));
// Ensure that all the AuthRequests that should have been left alone, were.
Assert.NotNull(await authRequestRepository.GetByIdAsync(notExpiredUserAuthRequest.Id));
Assert.NotNull(await authRequestRepository.GetByIdAsync(notExpiredAdminApprovalRequest.Id));
Assert.NotNull(await authRequestRepository.GetByIdAsync(notExpiredApprovedAdminApprovalRequest.Id));
// Ensure the repository responds with the amount of items it deleted and it deleted the right amount.
// NOTE: On local development this might fail on it's first run because the developer could have expired AuthRequests
// on their machine but aren't running the job that would delete them. The second run of this test should succeed.
Assert.Equal(4, numberOfDeleted);
}
private static AuthRequest CreateAuthRequest(Guid userId, AuthRequestType authRequestType, DateTime creationDate, bool? approved = null, DateTime? responseDate = null)
{
return new AuthRequest
{
UserId = userId,
Type = authRequestType,
Approved = approved,
RequestDeviceIdentifier = "something", // TODO: EF Doesn't enforce this as not null
RequestIpAddress = "1.1.1.1", // TODO: EF Doesn't enforce this as not null
AccessCode = "test_access_code", // TODO: EF Doesn't enforce this as not null
PublicKey = "test_public_key", // TODO: EF Doesn't enforce this as not null
CreationDate = creationDate,
ResponseDate = responseDate,
};
}
private static DateTime CreateExpiredDate(TimeSpan expirationPeriod)
{
var exp = expirationPeriod + TimeSpan.FromMinutes(1);
return DateTime.UtcNow.Add(exp.Negate());
}
}

View File

@@ -1,4 +1,5 @@
using Bit.Core.Entities;
using System.Text.Json;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
@@ -121,4 +122,84 @@ public class CipherRepositoryTests
var collectionCiphers = await collectionCipherRepository.GetManyByOrganizationIdAsync(organization.Id);
Assert.NotEmpty(collectionCiphers);
}
[DatabaseTheory, DatabaseData]
public async Task ReplaceAsync_SuccessfullyMovesCipherToOrganization(IUserRepository userRepository,
ICipherRepository cipherRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IFolderRepository folderRepository,
ITestDatabaseHelper helper)
{
// This tests what happens when a cipher is moved into an organizations
var user = await userRepository.CreateAsync(new User
{
Name = "Test User",
Email = $"test+{Guid.NewGuid()}@email.com",
ApiKey = "TEST",
SecurityStamp = "stamp",
});
user = await userRepository.GetByIdAsync(user.Id);
// Create cipher in personal vault
var createdCipher = await cipherRepository.CreateAsync(new Cipher
{
UserId = user.Id,
Data = "", // TODO: EF does not enforce this as NOT NULL
});
var organization = await organizationRepository.CreateAsync(new Organization
{
Name = "Test Organization",
BillingEmail = user.Email,
Plan = "Test" // TODO: EF does not enforce this as NOT NULL
});
_ = await organizationUserRepository.CreateAsync(new OrganizationUser
{
UserId = user.Id,
OrganizationId = organization.Id,
Status = OrganizationUserStatusType.Confirmed,
Type = OrganizationUserType.Owner,
});
var folder = await folderRepository.CreateAsync(new Folder
{
Name = "FolderName",
UserId = user.Id,
});
helper.ClearTracker();
// Move cipher to organization vault
await cipherRepository.ReplaceAsync(new CipherDetails
{
Id = createdCipher.Id,
UserId = user.Id,
OrganizationId = organization.Id,
FolderId = folder.Id,
Data = "", // TODO: EF does not enforce this as NOT NULL
});
var updatedCipher = await cipherRepository.GetByIdAsync(createdCipher.Id);
Assert.Null(updatedCipher.UserId);
Assert.Equal(organization.Id, updatedCipher.OrganizationId);
Assert.NotNull(updatedCipher.Folders);
using var foldersJsonDocument = JsonDocument.Parse(updatedCipher.Folders);
var foldersJsonElement = foldersJsonDocument.RootElement;
Assert.Equal(JsonValueKind.Object, foldersJsonElement.ValueKind);
// TODO: Should we force similar casing for guids across DB's
// I'd rather we only interact with them as the actual Guid type
var userProperty = foldersJsonElement
.EnumerateObject()
.FirstOrDefault(jp => string.Equals(jp.Name, user.Id.ToString(), StringComparison.OrdinalIgnoreCase));
Assert.NotEqual(default, userProperty);
Assert.Equal(folder.Id, userProperty.Value.GetGuid());
}
}

View File

@@ -2901,7 +2901,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -2909,7 +2909,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",

View File

@@ -3130,7 +3130,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "4.17.0",
"AutoFixture.Xunit2": "4.17.0",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "17.1.0",
"NSubstitute": "4.3.0",
@@ -3182,15 +3182,15 @@
"identity": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"SharedWeb": "2023.4.3",
"Core": "2023.5.1",
"SharedWeb": "2023.5.1",
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
@@ -3198,7 +3198,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
@@ -3210,9 +3210,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Infrastructure.Dapper": "2023.4.3",
"Infrastructure.EntityFramework": "2023.4.3"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -0,0 +1,25 @@
IF OBJECT_ID('[dbo].[AuthRequest_DeleteIfExpired]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[AuthRequest_DeleteIfExpired]
END
GO
-- UserExpirationSeconds to 15 minutes (15 * 60)
-- AdminExpirationSeconds to 7 days (7 * 24 * 60 * 60)
-- AdminApprovalExpirationSeconds to 12 hour (12 * 60 * 60)
CREATE PROCEDURE [dbo].[AuthRequest_DeleteIfExpired]
@UserExpirationSeconds INT = 900,
@AdminExpirationSeconds INT = 604800,
@AdminApprovalExpirationSeconds INT = 43200
AS
BEGIN
SET NOCOUNT OFF
DELETE FROM [dbo].[AuthRequest]
-- User requests expire after 15 minutes (by default) of their creation
WHERE ([Type] != 2 AND DATEADD(second, @UserExpirationSeconds, [CreationDate]) < GETUTCDATE())
-- Admin requests expire after 7 days (by default) of their creation if they have not been approved
OR ([Type] = 2 AND ([Approved] IS NULL OR [Approved] = 0) AND DATEADD(second, @AdminExpirationSeconds,[CreationDate]) < GETUTCDATE())
-- Admin requests expire after 12 hours (by default) of their approval
OR ([Type] = 2 AND [Approved] = 1 AND DATEADD(second, @AdminApprovalExpirationSeconds, [ResponseDate]) < GETUTCDATE());
END

View File

@@ -2729,7 +2729,7 @@
"migrator": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.Extensions.Logging": "6.0.0",
"dbup-sqlserver": "5.0.8"
}

View File

@@ -2797,7 +2797,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",

View File

@@ -2797,7 +2797,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",

View File

@@ -73,6 +73,7 @@ public class EnvironmentFileBuilder
_globalOverrideValues = new Dictionary<string, string>
{
["globalSettings__baseServiceUri__vault"] = _context.Config.Url,
["globalSettings__baseServiceUri__cloudVaultRegion"] = "US",
["globalSettings__sqlServer__connectionString"] = $"\"{dbConnectionString.Replace("\"", "\\\"")}\"",
["globalSettings__identityServer__certificatePassword"] = _context.Install?.IdentityCertPassword,
["globalSettings__internalIdentityKey"] = _context.Stub ? "RANDOM_IDENTITY_KEY" :

View File

@@ -2706,7 +2706,7 @@
"migrator": {
"type": "Project",
"dependencies": {
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.Extensions.Logging": "6.0.0",
"dbup-sqlserver": "5.0.8"
}

View File

@@ -2913,103 +2913,103 @@
"api": {
"type": "Project",
"dependencies": {
"AspNetCore.HealthChecks.AzureServiceBus": "[6.1.0, )",
"AspNetCore.HealthChecks.AzureStorage": "[6.1.2, )",
"AspNetCore.HealthChecks.Network": "[6.0.4, )",
"AspNetCore.HealthChecks.Redis": "[6.0.4, )",
"AspNetCore.HealthChecks.SendGrid": "[6.0.2, )",
"AspNetCore.HealthChecks.SqlServer": "[6.0.2, )",
"AspNetCore.HealthChecks.Uris": "[6.0.3, )",
"Azure.Messaging.EventGrid": "[4.10.0, )",
"Commercial.Core": "[2023.5.0, )",
"Commercial.Infrastructure.EntityFramework": "[2023.5.0, )",
"Core": "[2023.5.0, )",
"SharedWeb": "[2023.5.0, )",
"Swashbuckle.AspNetCore": "[6.5.0, )"
"AspNetCore.HealthChecks.AzureServiceBus": "6.1.0",
"AspNetCore.HealthChecks.AzureStorage": "6.1.2",
"AspNetCore.HealthChecks.Network": "6.0.4",
"AspNetCore.HealthChecks.Redis": "6.0.4",
"AspNetCore.HealthChecks.SendGrid": "6.0.2",
"AspNetCore.HealthChecks.SqlServer": "6.0.2",
"AspNetCore.HealthChecks.Uris": "6.0.3",
"Azure.Messaging.EventGrid": "4.10.0",
"Commercial.Core": "2023.5.1",
"Commercial.Infrastructure.EntityFramework": "2023.5.1",
"Core": "2023.5.1",
"SharedWeb": "2023.5.1",
"Swashbuckle.AspNetCore": "6.5.0"
}
},
"commercial.core": {
"type": "Project",
"dependencies": {
"Core": "[2023.5.0, )"
"Core": "2023.5.1"
}
},
"commercial.infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.5.0, )",
"Infrastructure.EntityFramework": "[2023.5.0, )"
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.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.3.2, )",
"Azure.Storage.Blobs": "[12.14.1, )",
"Azure.Storage.Queues": "[12.12.0, )",
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.12.0, )",
"DnsClient": "[1.7.0, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[7.0.0, )",
"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, )"
"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.3.2",
"Azure.Storage.Blobs": "12.14.1",
"Azure.Storage.Queues": "12.12.0",
"BitPay.Light": "1.0.1907",
"Braintree": "5.12.0",
"DnsClient": "1.7.0",
"Fido2.AspNet": "3.0.1",
"Handlebars.Net": "2.1.2",
"IdentityServer4": "4.1.2",
"IdentityServer4.AccessTokenValidation": "3.0.1",
"LaunchDarkly.ServerSdk": "7.0.0",
"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.5.0, )",
"Dapper": "[2.0.123, )"
"Core": "2023.5.1",
"Dapper": "2.0.123"
}
},
"infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.5.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, )"
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.5.1",
"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.5.0, )",
"Infrastructure.Dapper": "[2023.5.0, )",
"Infrastructure.EntityFramework": "[2023.5.0, )"
"Core": "2023.5.1",
"Infrastructure.Dapper": "2023.5.1",
"Infrastructure.EntityFramework": "2023.5.1"
}
}
}

View File

@@ -2797,7 +2797,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
"Core": "2023.4.3",
"Core": "2023.5.1",
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",