mirror of
https://github.com/bitwarden/server.git
synced 2024-12-01 13:43:23 +01:00
Merge branch 'main' into ac/pm-10338/leave-endpoint-to-log-organizationuser_left
This commit is contained in:
commit
2bd8c64c2f
@ -30,7 +30,7 @@ jobs:
|
|||||||
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
||||||
|
|
||||||
- name: Check out branch
|
- name: Check out branch
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
token: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ jobs:
|
|||||||
if: ${{ needs.setup.outputs.copy_finalization_scripts == 'true' }}
|
if: ${{ needs.setup.outputs.copy_finalization_scripts == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
42
.github/workflows/build.yml
vendored
42
.github/workflows/build.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Set up .NET
|
- name: Set up .NET
|
||||||
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
||||||
@ -68,7 +68,7 @@ jobs:
|
|||||||
node: true
|
node: true
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Set up .NET
|
- name: Set up .NET
|
||||||
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
||||||
@ -110,7 +110,7 @@ jobs:
|
|||||||
ls -atlh ../../../
|
ls -atlh ../../../
|
||||||
|
|
||||||
- name: Upload project artifact
|
- name: Upload project artifact
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.project_name }}.zip
|
name: ${{ matrix.project_name }}.zip
|
||||||
path: ${{ matrix.base_path }}/${{ matrix.project_name }}/${{ matrix.project_name }}.zip
|
path: ${{ matrix.base_path }}/${{ matrix.project_name }}/${{ matrix.project_name }}.zip
|
||||||
@ -173,7 +173,7 @@ jobs:
|
|||||||
dotnet: true
|
dotnet: true
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Check branch to publish
|
- name: Check branch to publish
|
||||||
env:
|
env:
|
||||||
@ -263,7 +263,7 @@ jobs:
|
|||||||
-d ${{ matrix.base_path }}/${{ matrix.project_name }}/obj/build-output/publish
|
-d ${{ matrix.base_path }}/${{ matrix.project_name }}/obj/build-output/publish
|
||||||
|
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
uses: docker/build-push-action@32945a339266b759abcbdc89316275140b0fc960 # v6.8.0
|
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
|
||||||
with:
|
with:
|
||||||
context: ${{ matrix.base_path }}/${{ matrix.project_name }}
|
context: ${{ matrix.base_path }}/${{ matrix.project_name }}
|
||||||
file: ${{ matrix.base_path }}/${{ matrix.project_name }}/Dockerfile
|
file: ${{ matrix.base_path }}/${{ matrix.project_name }}/Dockerfile
|
||||||
@ -275,14 +275,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Scan Docker image
|
- name: Scan Docker image
|
||||||
id: container-scan
|
id: container-scan
|
||||||
uses: anchore/scan-action@64a33b277ea7a1215a3c142735a1091341939ff5 # v4.1.2
|
uses: anchore/scan-action@49e50b215b647c5ec97abb66f69af73c46a4ca08 # v5.0.1
|
||||||
with:
|
with:
|
||||||
image: ${{ steps.image-tags.outputs.primary_tag }}
|
image: ${{ steps.image-tags.outputs.primary_tag }}
|
||||||
fail-build: false
|
fail-build: false
|
||||||
output-format: sarif
|
output-format: sarif
|
||||||
|
|
||||||
- name: Upload Grype results to GitHub
|
- name: Upload Grype results to GitHub
|
||||||
uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9
|
uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
|
||||||
with:
|
with:
|
||||||
sarif_file: ${{ steps.container-scan.outputs.sarif }}
|
sarif_file: ${{ steps.container-scan.outputs.sarif }}
|
||||||
|
|
||||||
@ -292,7 +292,7 @@ jobs:
|
|||||||
needs: build-docker
|
needs: build-docker
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Set up .NET
|
- name: Set up .NET
|
||||||
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
||||||
@ -311,7 +311,7 @@ jobs:
|
|||||||
github.ref == 'refs/heads/hotfix-rc'
|
github.ref == 'refs/heads/hotfix-rc'
|
||||||
run: |
|
run: |
|
||||||
# Set proper setup image based on branch
|
# Set proper setup image based on branch
|
||||||
case "${{ github.ref }}" in
|
case "$GITHUB_REF" in
|
||||||
"refs/heads/main")
|
"refs/heads/main")
|
||||||
SETUP_IMAGE="$_AZ_REGISTRY/setup:dev"
|
SETUP_IMAGE="$_AZ_REGISTRY/setup:dev"
|
||||||
;;
|
;;
|
||||||
@ -355,7 +355,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Docker stub US artifact
|
- name: Upload Docker stub US artifact
|
||||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: docker-stub-US.zip
|
name: docker-stub-US.zip
|
||||||
path: docker-stub-US.zip
|
path: docker-stub-US.zip
|
||||||
@ -363,7 +363,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Docker stub EU artifact
|
- name: Upload Docker stub EU artifact
|
||||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: docker-stub-EU.zip
|
name: docker-stub-EU.zip
|
||||||
path: docker-stub-EU.zip
|
path: docker-stub-EU.zip
|
||||||
@ -371,7 +371,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Docker stub US checksum artifact
|
- name: Upload Docker stub US checksum artifact
|
||||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: docker-stub-US-sha256.txt
|
name: docker-stub-US-sha256.txt
|
||||||
path: docker-stub-US-sha256.txt
|
path: docker-stub-US-sha256.txt
|
||||||
@ -379,7 +379,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Docker stub EU checksum artifact
|
- name: Upload Docker stub EU checksum artifact
|
||||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: docker-stub-EU-sha256.txt
|
name: docker-stub-EU-sha256.txt
|
||||||
path: docker-stub-EU-sha256.txt
|
path: docker-stub-EU-sha256.txt
|
||||||
@ -403,7 +403,7 @@ jobs:
|
|||||||
GLOBALSETTINGS__SQLSERVER__CONNECTIONSTRING: "placeholder"
|
GLOBALSETTINGS__SQLSERVER__CONNECTIONSTRING: "placeholder"
|
||||||
|
|
||||||
- name: Upload Public API Swagger artifact
|
- name: Upload Public API Swagger artifact
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: swagger.json
|
name: swagger.json
|
||||||
path: swagger.json
|
path: swagger.json
|
||||||
@ -437,14 +437,14 @@ jobs:
|
|||||||
GLOBALSETTINGS__SQLSERVER__CONNECTIONSTRING: "placeholder"
|
GLOBALSETTINGS__SQLSERVER__CONNECTIONSTRING: "placeholder"
|
||||||
|
|
||||||
- name: Upload Internal API Swagger artifact
|
- name: Upload Internal API Swagger artifact
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: internal.json
|
name: internal.json
|
||||||
path: internal.json
|
path: internal.json
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload Identity Swagger artifact
|
- name: Upload Identity Swagger artifact
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: identity.json
|
name: identity.json
|
||||||
path: identity.json
|
path: identity.json
|
||||||
@ -467,7 +467,7 @@ jobs:
|
|||||||
- win-x64
|
- win-x64
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Set up .NET
|
- name: Set up .NET
|
||||||
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
||||||
@ -486,7 +486,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload project artifact for Windows
|
- name: Upload project artifact for Windows
|
||||||
if: ${{ contains(matrix.target, 'win') == true }}
|
if: ${{ contains(matrix.target, 'win') == true }}
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: MsSqlMigratorUtility-${{ matrix.target }}
|
name: MsSqlMigratorUtility-${{ matrix.target }}
|
||||||
path: util/MsSqlMigratorUtility/obj/build-output/publish/MsSqlMigratorUtility.exe
|
path: util/MsSqlMigratorUtility/obj/build-output/publish/MsSqlMigratorUtility.exe
|
||||||
@ -494,7 +494,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload project artifact
|
- name: Upload project artifact
|
||||||
if: ${{ contains(matrix.target, 'win') == false }}
|
if: ${{ contains(matrix.target, 'win') == false }}
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: MsSqlMigratorUtility-${{ matrix.target }}
|
name: MsSqlMigratorUtility-${{ matrix.target }}
|
||||||
path: util/MsSqlMigratorUtility/obj/build-output/publish/MsSqlMigratorUtility
|
path: util/MsSqlMigratorUtility/obj/build-output/publish/MsSqlMigratorUtility
|
||||||
@ -528,9 +528,9 @@ jobs:
|
|||||||
workflow_id: 'build-unified.yml',
|
workflow_id: 'build-unified.yml',
|
||||||
ref: 'main',
|
ref: 'main',
|
||||||
inputs: {
|
inputs: {
|
||||||
server_branch: '${{ github.ref }}'
|
server_branch: process.env.GITHUB_REF
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
trigger-k8s-deploy:
|
trigger-k8s-deploy:
|
||||||
name: Trigger k8s deploy
|
name: Trigger k8s deploy
|
||||||
|
2
.github/workflows/cleanup-rc-branch.yml
vendored
2
.github/workflows/cleanup-rc-branch.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
||||||
|
|
||||||
- name: Checkout main
|
- name: Checkout main
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
token: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
token: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
||||||
|
2
.github/workflows/code-references.yml
vendored
2
.github/workflows/code-references.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repository
|
- name: Check out repository
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Collect
|
- name: Collect
|
||||||
id: collect
|
id: collect
|
||||||
|
2
.github/workflows/protect-files.yml
vendored
2
.github/workflows/protect-files.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
|||||||
label: "DB-migrations-changed"
|
label: "DB-migrations-changed"
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
|
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@ -99,7 +99,7 @@ jobs:
|
|||||||
echo "Github Release Option: $RELEASE_OPTION"
|
echo "Github Release Option: $RELEASE_OPTION"
|
||||||
|
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Set up project name
|
- name: Set up project name
|
||||||
id: setup
|
id: setup
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Check release version
|
- name: Check release version
|
||||||
id: version
|
id: version
|
||||||
|
6
.github/workflows/repository-management.yml
vendored
6
.github/workflows/repository-management.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Check out target ref
|
- name: Check out target ref
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.target_ref }}
|
ref: ${{ inputs.target_ref }}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ jobs:
|
|||||||
version: ${{ inputs.version_number_override }}
|
version: ${{ inputs.version_number_override }}
|
||||||
|
|
||||||
- name: Check out branch
|
- name: Check out branch
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ jobs:
|
|||||||
needs: bump_version
|
needs: bump_version
|
||||||
steps:
|
steps:
|
||||||
- name: Check out main branch
|
- name: Check out main branch
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
|
8
.github/workflows/scan.yml
vendored
8
.github/workflows/scan.yml
vendored
@ -26,12 +26,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Scan with Checkmarx
|
- name: Scan with Checkmarx
|
||||||
uses: checkmarx/ast-github-action@9fda5a4a2c297608117a5a56af424502a9192e57 # 2.0.34
|
uses: checkmarx/ast-github-action@f0869bd1a37fddc06499a096101e6c900e815d81 # 2.0.36
|
||||||
env:
|
env:
|
||||||
INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}"
|
INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}"
|
||||||
with:
|
with:
|
||||||
@ -46,7 +46,7 @@ jobs:
|
|||||||
--output-path . ${{ env.INCREMENTAL }}
|
--output-path . ${{ env.INCREMENTAL }}
|
||||||
|
|
||||||
- name: Upload Checkmarx results to GitHub
|
- name: Upload Checkmarx results to GitHub
|
||||||
uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9
|
uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
|
||||||
with:
|
with:
|
||||||
sarif_file: cx_result.sarif
|
sarif_file: cx_result.sarif
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ jobs:
|
|||||||
distribution: "zulu"
|
distribution: "zulu"
|
||||||
|
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
8
.github/workflows/test-database.yml
vendored
8
.github/workflows/test-database.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Set up .NET
|
- name: Set up .NET
|
||||||
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
||||||
@ -147,7 +147,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Set up .NET
|
- name: Set up .NET
|
||||||
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
||||||
@ -164,7 +164,7 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Upload DACPAC
|
- name: Upload DACPAC
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: sql.dacpac
|
name: sql.dacpac
|
||||||
path: Sql.dacpac
|
path: Sql.dacpac
|
||||||
@ -190,7 +190,7 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Report validation results
|
- name: Report validation results
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: report.xml
|
name: report.xml
|
||||||
path: |
|
path: |
|
||||||
|
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Set up .NET
|
- name: Set up .NET
|
||||||
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
|
||||||
@ -77,7 +77,7 @@ jobs:
|
|||||||
fail-on-error: true
|
fail-on-error: true
|
||||||
|
|
||||||
- name: Upload to codecov.io
|
- name: Upload to codecov.io
|
||||||
uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0
|
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
|
||||||
if: ${{ needs.check-test-secrets.outputs.available == 'true' }}
|
if: ${{ needs.check-test-secrets.outputs.available == 'true' }}
|
||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
@ -124,7 +124,11 @@ public class OrganizationsController : Controller
|
|||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var organizations = await _organizationUserRepository.GetManyDetailsByUserAsync(userId,
|
var organizations = await _organizationUserRepository.GetManyDetailsByUserAsync(userId,
|
||||||
OrganizationUserStatusType.Confirmed);
|
OrganizationUserStatusType.Confirmed);
|
||||||
var responses = organizations.Select(o => new ProfileOrganizationResponseModel(o));
|
|
||||||
|
var organizationManagingActiveUser = await _userService.GetOrganizationsManagingUserAsync(userId);
|
||||||
|
var organizationIdsManagingActiveUser = organizationManagingActiveUser.Select(o => o.Id);
|
||||||
|
|
||||||
|
var responses = organizations.Select(o => new ProfileOrganizationResponseModel(o, organizationIdsManagingActiveUser));
|
||||||
return new ListResponseModel<ProfileOrganizationResponseModel>(responses);
|
return new ListResponseModel<ProfileOrganizationResponseModel>(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,10 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
{
|
{
|
||||||
public ProfileOrganizationResponseModel(string str) : base(str) { }
|
public ProfileOrganizationResponseModel(string str) : base(str) { }
|
||||||
|
|
||||||
public ProfileOrganizationResponseModel(OrganizationUserOrganizationDetails organization) : this("profileOrganization")
|
public ProfileOrganizationResponseModel(
|
||||||
|
OrganizationUserOrganizationDetails organization,
|
||||||
|
IEnumerable<Guid> organizationIdsManagingUser)
|
||||||
|
: this("profileOrganization")
|
||||||
{
|
{
|
||||||
Id = organization.OrganizationId;
|
Id = organization.OrganizationId;
|
||||||
Name = organization.Name;
|
Name = organization.Name;
|
||||||
@ -64,6 +67,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
AccessSecretsManager = organization.AccessSecretsManager;
|
AccessSecretsManager = organization.AccessSecretsManager;
|
||||||
LimitCollectionCreationDeletion = organization.LimitCollectionCreationDeletion;
|
LimitCollectionCreationDeletion = organization.LimitCollectionCreationDeletion;
|
||||||
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
|
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
|
||||||
|
UserIsManagedByOrganization = organizationIdsManagingUser.Contains(organization.OrganizationId);
|
||||||
|
|
||||||
if (organization.SsoConfig != null)
|
if (organization.SsoConfig != null)
|
||||||
{
|
{
|
||||||
@ -122,4 +126,15 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
public bool AccessSecretsManager { get; set; }
|
public bool AccessSecretsManager { get; set; }
|
||||||
public bool LimitCollectionCreationDeletion { get; set; }
|
public bool LimitCollectionCreationDeletion { get; set; }
|
||||||
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates if the organization manages the user.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// An organization manages a user if the user's email domain is verified by the organization and the user is a member of it.
|
||||||
|
/// The organization must be enabled and able to have verified domains.
|
||||||
|
/// </remarks>
|
||||||
|
/// <returns>
|
||||||
|
/// False if the Account Deprovisioning feature flag is disabled.
|
||||||
|
/// </returns>
|
||||||
|
public bool UserIsManagedByOrganization { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -443,11 +443,11 @@ public class AccountsController : Controller
|
|||||||
|
|
||||||
var twoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
var twoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
||||||
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
|
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
|
||||||
var managedByOrganizationId = await GetManagedByOrganizationIdAsync(user);
|
var organizationIdsManagingActiveUser = await GetOrganizationIdsManagingUserAsync(user.Id);
|
||||||
|
|
||||||
var response = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails,
|
var response = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails,
|
||||||
providerUserOrganizationDetails, twoFactorEnabled,
|
providerUserOrganizationDetails, twoFactorEnabled,
|
||||||
hasPremiumFromOrg, managedByOrganizationId);
|
hasPremiumFromOrg, organizationIdsManagingActiveUser);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,7 +457,9 @@ public class AccountsController : Controller
|
|||||||
var userId = _userService.GetProperUserId(User);
|
var userId = _userService.GetProperUserId(User);
|
||||||
var organizationUserDetails = await _organizationUserRepository.GetManyDetailsByUserAsync(userId.Value,
|
var organizationUserDetails = await _organizationUserRepository.GetManyDetailsByUserAsync(userId.Value,
|
||||||
OrganizationUserStatusType.Confirmed);
|
OrganizationUserStatusType.Confirmed);
|
||||||
var responseData = organizationUserDetails.Select(o => new ProfileOrganizationResponseModel(o));
|
var organizationIdsManagingActiveUser = await GetOrganizationIdsManagingUserAsync(userId.Value);
|
||||||
|
|
||||||
|
var responseData = organizationUserDetails.Select(o => new ProfileOrganizationResponseModel(o, organizationIdsManagingActiveUser));
|
||||||
return new ListResponseModel<ProfileOrganizationResponseModel>(responseData);
|
return new ListResponseModel<ProfileOrganizationResponseModel>(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,9 +477,9 @@ public class AccountsController : Controller
|
|||||||
|
|
||||||
var twoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
var twoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
||||||
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
|
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
|
||||||
var managedByOrganizationId = await GetManagedByOrganizationIdAsync(user);
|
var organizationIdsManagingActiveUser = await GetOrganizationIdsManagingUserAsync(user.Id);
|
||||||
|
|
||||||
var response = new ProfileResponseModel(user, null, null, null, twoFactorEnabled, hasPremiumFromOrg, managedByOrganizationId);
|
var response = new ProfileResponseModel(user, null, null, null, twoFactorEnabled, hasPremiumFromOrg, organizationIdsManagingActiveUser);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,9 +496,9 @@ public class AccountsController : Controller
|
|||||||
|
|
||||||
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
||||||
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
|
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
|
||||||
var managedByOrganizationId = await GetManagedByOrganizationIdAsync(user);
|
var organizationIdsManagingActiveUser = await GetOrganizationIdsManagingUserAsync(user.Id);
|
||||||
|
|
||||||
var response = new ProfileResponseModel(user, null, null, null, userTwoFactorEnabled, userHasPremiumFromOrganization, managedByOrganizationId);
|
var response = new ProfileResponseModel(user, null, null, null, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationIdsManagingActiveUser);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,9 +649,9 @@ public class AccountsController : Controller
|
|||||||
|
|
||||||
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
||||||
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
|
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
|
||||||
var managedByOrganizationId = await GetManagedByOrganizationIdAsync(user);
|
var organizationIdsManagingActiveUser = await GetOrganizationIdsManagingUserAsync(user.Id);
|
||||||
|
|
||||||
var profile = new ProfileResponseModel(user, null, null, null, userTwoFactorEnabled, userHasPremiumFromOrganization, managedByOrganizationId);
|
var profile = new ProfileResponseModel(user, null, null, null, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationIdsManagingActiveUser);
|
||||||
return new PaymentResponseModel
|
return new PaymentResponseModel
|
||||||
{
|
{
|
||||||
UserProfile = profile,
|
UserProfile = profile,
|
||||||
@ -937,14 +939,9 @@ public class AccountsController : Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Guid?> GetManagedByOrganizationIdAsync(User user)
|
private async Task<IEnumerable<Guid>> GetOrganizationIdsManagingUserAsync(Guid userId)
|
||||||
{
|
{
|
||||||
if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
var organizationManagingUser = await _userService.GetOrganizationsManagingUserAsync(userId);
|
||||||
{
|
return organizationManagingUser.Select(o => o.Id);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var organizationManagingUser = await _userService.GetOrganizationManagingUserAsync(user.Id);
|
|
||||||
return organizationManagingUser?.Id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,10 @@ public class OrganizationsController(
|
|||||||
var organizationDetails = await organizationUserRepository.GetDetailsByUserAsync(userId, organization.Id,
|
var organizationDetails = await organizationUserRepository.GetDetailsByUserAsync(userId, organization.Id,
|
||||||
OrganizationUserStatusType.Confirmed);
|
OrganizationUserStatusType.Confirmed);
|
||||||
|
|
||||||
return new ProfileOrganizationResponseModel(organizationDetails);
|
var organizationManagingActiveUser = await userService.GetOrganizationsManagingUserAsync(userId);
|
||||||
|
var organizationIdsManagingActiveUser = organizationManagingActiveUser.Select(o => o.Id);
|
||||||
|
|
||||||
|
return new ProfileOrganizationResponseModel(organizationDetails, organizationIdsManagingActiveUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id:guid}/seat")]
|
[HttpPost("{id:guid}/seat")]
|
||||||
|
@ -15,7 +15,7 @@ public class ProfileResponseModel : ResponseModel
|
|||||||
IEnumerable<ProviderUserOrganizationDetails> providerUserOrganizationDetails,
|
IEnumerable<ProviderUserOrganizationDetails> providerUserOrganizationDetails,
|
||||||
bool twoFactorEnabled,
|
bool twoFactorEnabled,
|
||||||
bool premiumFromOrganization,
|
bool premiumFromOrganization,
|
||||||
Guid? managedByOrganizationId) : base("profile")
|
IEnumerable<Guid> organizationIdsManagingUser) : base("profile")
|
||||||
{
|
{
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
@ -37,11 +37,10 @@ public class ProfileResponseModel : ResponseModel
|
|||||||
UsesKeyConnector = user.UsesKeyConnector;
|
UsesKeyConnector = user.UsesKeyConnector;
|
||||||
AvatarColor = user.AvatarColor;
|
AvatarColor = user.AvatarColor;
|
||||||
CreationDate = user.CreationDate;
|
CreationDate = user.CreationDate;
|
||||||
Organizations = organizationsUserDetails?.Select(o => new ProfileOrganizationResponseModel(o));
|
Organizations = organizationsUserDetails?.Select(o => new ProfileOrganizationResponseModel(o, organizationIdsManagingUser));
|
||||||
Providers = providerUserDetails?.Select(p => new ProfileProviderResponseModel(p));
|
Providers = providerUserDetails?.Select(p => new ProfileProviderResponseModel(p));
|
||||||
ProviderOrganizations =
|
ProviderOrganizations =
|
||||||
providerUserOrganizationDetails?.Select(po => new ProfileProviderOrganizationResponseModel(po));
|
providerUserOrganizationDetails?.Select(po => new ProfileProviderOrganizationResponseModel(po));
|
||||||
ManagedByOrganizationId = managedByOrganizationId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProfileResponseModel() : base("profile")
|
public ProfileResponseModel() : base("profile")
|
||||||
@ -63,7 +62,6 @@ public class ProfileResponseModel : ResponseModel
|
|||||||
public bool UsesKeyConnector { get; set; }
|
public bool UsesKeyConnector { get; set; }
|
||||||
public string AvatarColor { get; set; }
|
public string AvatarColor { get; set; }
|
||||||
public DateTime CreationDate { get; set; }
|
public DateTime CreationDate { get; set; }
|
||||||
public Guid? ManagedByOrganizationId { get; set; }
|
|
||||||
public IEnumerable<ProfileOrganizationResponseModel> Organizations { get; set; }
|
public IEnumerable<ProfileOrganizationResponseModel> Organizations { get; set; }
|
||||||
public IEnumerable<ProfileProviderResponseModel> Providers { get; set; }
|
public IEnumerable<ProfileProviderResponseModel> Providers { get; set; }
|
||||||
public IEnumerable<ProfileProviderOrganizationResponseModel> ProviderOrganizations { get; set; }
|
public IEnumerable<ProfileProviderOrganizationResponseModel> ProviderOrganizations { get; set; }
|
||||||
|
@ -910,6 +910,13 @@ public class CiphersController : Controller
|
|||||||
throw new BadRequestException(ModelState);
|
throw new BadRequestException(ModelState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If Account Deprovisioning is enabled, we need to check if the user is managed by any organization.
|
||||||
|
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||||
|
&& await _userService.IsManagedByAnyOrganizationAsync(user.Id))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Cannot purge accounts owned by an organization. Contact your organization administrator for additional details.");
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(organizationId))
|
if (string.IsNullOrWhiteSpace(organizationId))
|
||||||
{
|
{
|
||||||
await _cipherRepository.DeleteByUserIdAsync(user.Id);
|
await _cipherRepository.DeleteByUserIdAsync(user.Id);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Bit.Api.Vault.Models.Response;
|
using Bit.Api.Vault.Models.Response;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
@ -7,7 +6,6 @@ using Bit.Core.Entities;
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
@ -95,23 +93,12 @@ public class SyncController : Controller
|
|||||||
|
|
||||||
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
||||||
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
|
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
|
||||||
var managedByOrganizationId = await GetManagedByOrganizationIdAsync(user, organizationUserDetails);
|
var organizationManagingActiveUser = await _userService.GetOrganizationsManagingUserAsync(user.Id);
|
||||||
|
var organizationIdsManagingActiveUser = organizationManagingActiveUser.Select(o => o.Id);
|
||||||
|
|
||||||
var response = new SyncResponseModel(_globalSettings, user, userTwoFactorEnabled, userHasPremiumFromOrganization,
|
var response = new SyncResponseModel(_globalSettings, user, userTwoFactorEnabled, userHasPremiumFromOrganization,
|
||||||
managedByOrganizationId, organizationUserDetails, providerUserDetails, providerUserOrganizationDetails,
|
organizationIdsManagingActiveUser, organizationUserDetails, providerUserDetails, providerUserOrganizationDetails,
|
||||||
folders, collections, ciphers, collectionCiphersGroupDict, excludeDomains, policies, sends);
|
folders, collections, ciphers, collectionCiphersGroupDict, excludeDomains, policies, sends);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Guid?> GetManagedByOrganizationIdAsync(User user, IEnumerable<OrganizationUserOrganizationDetails> organizationUserDetails)
|
|
||||||
{
|
|
||||||
if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) ||
|
|
||||||
!organizationUserDetails.Any(o => o.Enabled && o.UseSso))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var organizationManagingUser = await _userService.GetOrganizationManagingUserAsync(user.Id);
|
|
||||||
return organizationManagingUser?.Id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ public class SyncResponseModel : ResponseModel
|
|||||||
User user,
|
User user,
|
||||||
bool userTwoFactorEnabled,
|
bool userTwoFactorEnabled,
|
||||||
bool userHasPremiumFromOrganization,
|
bool userHasPremiumFromOrganization,
|
||||||
Guid? managedByOrganizationId,
|
IEnumerable<Guid> organizationIdsManagingUser,
|
||||||
IEnumerable<OrganizationUserOrganizationDetails> organizationUserDetails,
|
IEnumerable<OrganizationUserOrganizationDetails> organizationUserDetails,
|
||||||
IEnumerable<ProviderUserProviderDetails> providerUserDetails,
|
IEnumerable<ProviderUserProviderDetails> providerUserDetails,
|
||||||
IEnumerable<ProviderUserOrganizationDetails> providerUserOrganizationDetails,
|
IEnumerable<ProviderUserOrganizationDetails> providerUserOrganizationDetails,
|
||||||
@ -35,7 +35,7 @@ public class SyncResponseModel : ResponseModel
|
|||||||
: base("sync")
|
: base("sync")
|
||||||
{
|
{
|
||||||
Profile = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails,
|
Profile = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails,
|
||||||
providerUserOrganizationDetails, userTwoFactorEnabled, userHasPremiumFromOrganization, managedByOrganizationId);
|
providerUserOrganizationDetails, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationIdsManagingUser);
|
||||||
Folders = folders.Select(f => new FolderResponseModel(f));
|
Folders = folders.Select(f => new FolderResponseModel(f));
|
||||||
Ciphers = ciphers.Select(c => new CipherDetailsResponseModel(c, globalSettings, collectionCiphersDict));
|
Ciphers = ciphers.Select(c => new CipherDetailsResponseModel(c, globalSettings, collectionCiphersDict));
|
||||||
Collections = collections?.Select(
|
Collections = collections?.Select(
|
||||||
|
@ -19,7 +19,7 @@ public interface IOrganizationRepository : IRepository<Organization, Guid>
|
|||||||
Task<IEnumerable<string>> GetOwnerEmailAddressesById(Guid organizationId);
|
Task<IEnumerable<string>> GetOwnerEmailAddressesById(Guid organizationId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the organization that has a claimed domain matching the user's email domain.
|
/// Gets the organizations that have a verified domain matching the user's email domain.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<Organization> GetByClaimedUserDomainAsync(Guid userId);
|
Task<ICollection<Organization>> GetByVerifiedUserEmailDomainAsync(Guid userId);
|
||||||
}
|
}
|
||||||
|
@ -90,14 +90,20 @@ public interface IUserService
|
|||||||
/// Indicates if the user is managed by any organization.
|
/// Indicates if the user is managed by any organization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// A managed user is a user whose email domain matches one of the Organization's verified domains.
|
/// A user is considered managed by an organization if their email domain matches one of the verified domains of that organization, and the user is a member of it.
|
||||||
/// The organization must be enabled and be on an Enterprise plan.
|
/// The organization must be enabled and able to have verified domains.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
/// <returns>
|
||||||
|
/// False if the Account Deprovisioning feature flag is disabled.
|
||||||
|
/// </returns>
|
||||||
Task<bool> IsManagedByAnyOrganizationAsync(Guid userId);
|
Task<bool> IsManagedByAnyOrganizationAsync(Guid userId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the organization that manages the user.
|
/// Gets the organizations that manage the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// An empty collection if the Account Deprovisioning feature flag is disabled.
|
||||||
|
/// </returns>
|
||||||
/// <inheritdoc cref="IsManagedByAnyOrganizationAsync(Guid)"/>
|
/// <inheritdoc cref="IsManagedByAnyOrganizationAsync(Guid)"/>
|
||||||
Task<Organization> GetOrganizationManagingUserAsync(Guid userId);
|
Task<IEnumerable<Organization>> GetOrganizationsManagingUserAsync(Guid userId);
|
||||||
}
|
}
|
||||||
|
@ -1267,18 +1267,24 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
|
|
||||||
public async Task<bool> IsManagedByAnyOrganizationAsync(Guid userId)
|
public async Task<bool> IsManagedByAnyOrganizationAsync(Guid userId)
|
||||||
{
|
{
|
||||||
var managingOrganization = await GetOrganizationManagingUserAsync(userId);
|
var managingOrganizations = await GetOrganizationsManagingUserAsync(userId);
|
||||||
return managingOrganization != null;
|
return managingOrganizations.Any();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Organization> GetOrganizationManagingUserAsync(Guid userId)
|
public async Task<IEnumerable<Organization>> GetOrganizationsManagingUserAsync(Guid userId)
|
||||||
{
|
{
|
||||||
// Users can only be managed by an Organization that is enabled and can have organization domains
|
if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
||||||
var organization = await _organizationRepository.GetByClaimedUserDomainAsync(userId);
|
{
|
||||||
|
return Enumerable.Empty<Organization>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all organizations that have verified the user's email domain.
|
||||||
|
var organizationsWithVerifiedUserEmailDomain = await _organizationRepository.GetByVerifiedUserEmailDomainAsync(userId);
|
||||||
|
|
||||||
|
// Organizations must be enabled and able to have verified domains.
|
||||||
// TODO: Replace "UseSso" with a new organization ability like "UseOrganizationDomains" (PM-11622).
|
// TODO: Replace "UseSso" with a new organization ability like "UseOrganizationDomains" (PM-11622).
|
||||||
// Verified domains were tied to SSO, so we currently check the "UseSso" organization ability.
|
// Verified domains were tied to SSO, so we currently check the "UseSso" organization ability.
|
||||||
return (organization is { Enabled: true, UseSso: true }) ? organization : null;
|
return organizationsWithVerifiedUserEmailDomain.Where(organization => organization is { Enabled: true, UseSso: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IsLegacyUser(string)"/>
|
/// <inheritdoc cref="IsLegacyUser(string)"/>
|
||||||
|
@ -12,8 +12,4 @@
|
|||||||
<ProjectReference Include="..\Core\Core.csproj" />
|
<ProjectReference Include="..\Core\Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="MessagePack" Version="2.5.172" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -168,7 +168,7 @@ public class OrganizationRepository : Repository<Organization, Guid>, IOrganizat
|
|||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Organization> GetByClaimedUserDomainAsync(Guid userId)
|
public async Task<ICollection<Organization>> GetByVerifiedUserEmailDomainAsync(Guid userId)
|
||||||
{
|
{
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
using (var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
@ -177,7 +177,7 @@ public class OrganizationRepository : Repository<Organization, Guid>, IOrganizat
|
|||||||
new { UserId = userId },
|
new { UserId = userId },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
return result.SingleOrDefault();
|
return result.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,7 +276,7 @@ public class OrganizationRepository : Repository<Core.AdminConsole.Entities.Orga
|
|||||||
return await query.ToListAsync();
|
return await query.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Core.AdminConsole.Entities.Organization> GetByClaimedUserDomainAsync(Guid userId)
|
public async Task<ICollection<Core.AdminConsole.Entities.Organization>> GetByVerifiedUserEmailDomainAsync(Guid userId)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
@ -291,7 +291,7 @@ public class OrganizationRepository : Repository<Core.AdminConsole.Entities.Orga
|
|||||||
&& u.Email.ToLower().EndsWith("@" + od.DomainName.ToLower())
|
&& u.Email.ToLower().EndsWith("@" + od.DomainName.ToLower())
|
||||||
select o;
|
select o;
|
||||||
|
|
||||||
return await query.FirstOrDefaultAsync();
|
return await query.ToArrayAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ using Microsoft.AspNetCore.Identity;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using NSubstitute.ReceivedExtensions;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Bit.Core.Test.Services;
|
namespace Bit.Core.Test.Services;
|
||||||
@ -282,45 +281,69 @@ public class UserServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task IsManagedByAnyOrganizationAsync_WithManagingEnabledOrganization_ReturnsTrue(
|
public async Task IsManagedByAnyOrganizationAsync_WithAccountDeprovisioningDisabled_ReturnsFalse(
|
||||||
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
SutProvider<UserService> sutProvider, Guid userId)
|
||||||
{
|
{
|
||||||
organization.Enabled = true;
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
organization.UseSso = true;
|
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||||
|
.Returns(false);
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
|
||||||
.GetByClaimedUserDomainAsync(userId)
|
|
||||||
.Returns(organization);
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.IsManagedByAnyOrganizationAsync(userId);
|
|
||||||
Assert.True(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task IsManagedByAnyOrganizationAsync_WithManagingDisabledOrganization_ReturnsFalse(
|
|
||||||
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
|
||||||
{
|
|
||||||
organization.Enabled = false;
|
|
||||||
organization.UseSso = true;
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
|
||||||
.GetByClaimedUserDomainAsync(userId)
|
|
||||||
.Returns(organization);
|
|
||||||
|
|
||||||
var result = await sutProvider.Sut.IsManagedByAnyOrganizationAsync(userId);
|
var result = await sutProvider.Sut.IsManagedByAnyOrganizationAsync(userId);
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task IsManagedByAnyOrganizationAsync_WithOrganizationUseSsoFalse_ReturnsFalse(
|
public async Task IsManagedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithManagingEnabledOrganization_ReturnsTrue(
|
||||||
|
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
||||||
|
{
|
||||||
|
organization.Enabled = true;
|
||||||
|
organization.UseSso = true;
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
|
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByVerifiedUserEmailDomainAsync(userId)
|
||||||
|
.Returns(new[] { organization });
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.IsManagedByAnyOrganizationAsync(userId);
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task IsManagedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithManagingDisabledOrganization_ReturnsFalse(
|
||||||
|
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
||||||
|
{
|
||||||
|
organization.Enabled = false;
|
||||||
|
organization.UseSso = true;
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
|
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByVerifiedUserEmailDomainAsync(userId)
|
||||||
|
.Returns(new[] { organization });
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.IsManagedByAnyOrganizationAsync(userId);
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task IsManagedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithOrganizationUseSsoFalse_ReturnsFalse(
|
||||||
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
SutProvider<UserService> sutProvider, Guid userId, Organization organization)
|
||||||
{
|
{
|
||||||
organization.Enabled = true;
|
organization.Enabled = true;
|
||||||
organization.UseSso = false;
|
organization.UseSso = false;
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
|
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
.GetByClaimedUserDomainAsync(userId)
|
.GetByVerifiedUserEmailDomainAsync(userId)
|
||||||
.Returns(organization);
|
.Returns(new[] { organization });
|
||||||
|
|
||||||
var result = await sutProvider.Sut.IsManagedByAnyOrganizationAsync(userId);
|
var result = await sutProvider.Sut.IsManagedByAnyOrganizationAsync(userId);
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
|
@ -97,13 +97,160 @@ public class OrganizationRepositoryTests
|
|||||||
ResetPasswordKey = "resetpasswordkey1",
|
ResetPasswordKey = "resetpasswordkey1",
|
||||||
});
|
});
|
||||||
|
|
||||||
var user1Response = await organizationRepository.GetByClaimedUserDomainAsync(user1.Id);
|
var user1Response = await organizationRepository.GetByVerifiedUserEmailDomainAsync(user1.Id);
|
||||||
var user2Response = await organizationRepository.GetByClaimedUserDomainAsync(user2.Id);
|
var user2Response = await organizationRepository.GetByVerifiedUserEmailDomainAsync(user2.Id);
|
||||||
var user3Response = await organizationRepository.GetByClaimedUserDomainAsync(user3.Id);
|
var user3Response = await organizationRepository.GetByVerifiedUserEmailDomainAsync(user3.Id);
|
||||||
|
|
||||||
Assert.NotNull(user1Response);
|
Assert.NotEmpty(user1Response);
|
||||||
Assert.Equal(organization.Id, user1Response.Id);
|
Assert.Equal(organization.Id, user1Response.First().Id);
|
||||||
Assert.Null(user2Response);
|
Assert.Empty(user2Response);
|
||||||
Assert.Null(user3Response);
|
Assert.Empty(user3Response);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DatabaseTheory, DatabaseData]
|
||||||
|
public async Task GetByVerifiedUserEmailDomainAsync_WithUnverifiedDomains_ReturnsEmpty(
|
||||||
|
IUserRepository userRepository,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
IOrganizationDomainRepository organizationDomainRepository)
|
||||||
|
{
|
||||||
|
var id = Guid.NewGuid();
|
||||||
|
var domainName = $"{id}.example.com";
|
||||||
|
|
||||||
|
var user = await userRepository.CreateAsync(new User
|
||||||
|
{
|
||||||
|
Name = "Test User",
|
||||||
|
Email = $"test+{id}@{domainName}",
|
||||||
|
ApiKey = "TEST",
|
||||||
|
SecurityStamp = "stamp",
|
||||||
|
Kdf = KdfType.PBKDF2_SHA256,
|
||||||
|
KdfIterations = 1,
|
||||||
|
KdfMemory = 2,
|
||||||
|
KdfParallelism = 3
|
||||||
|
});
|
||||||
|
|
||||||
|
var organization = await organizationRepository.CreateAsync(new Organization
|
||||||
|
{
|
||||||
|
Name = $"Test Org {id}",
|
||||||
|
BillingEmail = user.Email,
|
||||||
|
Plan = "Test",
|
||||||
|
PrivateKey = "privatekey",
|
||||||
|
});
|
||||||
|
|
||||||
|
var organizationDomain = new OrganizationDomain
|
||||||
|
{
|
||||||
|
OrganizationId = organization.Id,
|
||||||
|
DomainName = domainName,
|
||||||
|
Txt = "btw+12345",
|
||||||
|
};
|
||||||
|
organizationDomain.SetNextRunDate(12);
|
||||||
|
organizationDomain.SetJobRunCount();
|
||||||
|
await organizationDomainRepository.CreateAsync(organizationDomain);
|
||||||
|
|
||||||
|
await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||||
|
{
|
||||||
|
OrganizationId = organization.Id,
|
||||||
|
UserId = user.Id,
|
||||||
|
Status = OrganizationUserStatusType.Confirmed,
|
||||||
|
ResetPasswordKey = "resetpasswordkey",
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await organizationRepository.GetByVerifiedUserEmailDomainAsync(user.Id);
|
||||||
|
|
||||||
|
Assert.Empty(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DatabaseTheory, DatabaseData]
|
||||||
|
public async Task GetByVerifiedUserEmailDomainAsync_WithMultipleVerifiedDomains_ReturnsAllMatchingOrganizations(
|
||||||
|
IUserRepository userRepository,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
IOrganizationDomainRepository organizationDomainRepository)
|
||||||
|
{
|
||||||
|
var id = Guid.NewGuid();
|
||||||
|
var domainName = $"{id}.example.com";
|
||||||
|
|
||||||
|
var user = await userRepository.CreateAsync(new User
|
||||||
|
{
|
||||||
|
Name = "Test User",
|
||||||
|
Email = $"test+{id}@{domainName}",
|
||||||
|
ApiKey = "TEST",
|
||||||
|
SecurityStamp = "stamp",
|
||||||
|
Kdf = KdfType.PBKDF2_SHA256,
|
||||||
|
KdfIterations = 1,
|
||||||
|
KdfMemory = 2,
|
||||||
|
KdfParallelism = 3
|
||||||
|
});
|
||||||
|
|
||||||
|
var organization1 = await organizationRepository.CreateAsync(new Organization
|
||||||
|
{
|
||||||
|
Name = $"Test Org 1 {id}",
|
||||||
|
BillingEmail = user.Email,
|
||||||
|
Plan = "Test",
|
||||||
|
PrivateKey = "privatekey1",
|
||||||
|
});
|
||||||
|
|
||||||
|
var organization2 = await organizationRepository.CreateAsync(new Organization
|
||||||
|
{
|
||||||
|
Name = $"Test Org 2 {id}",
|
||||||
|
BillingEmail = user.Email,
|
||||||
|
Plan = "Test",
|
||||||
|
PrivateKey = "privatekey2",
|
||||||
|
});
|
||||||
|
|
||||||
|
var organizationDomain1 = new OrganizationDomain
|
||||||
|
{
|
||||||
|
OrganizationId = organization1.Id,
|
||||||
|
DomainName = domainName,
|
||||||
|
Txt = "btw+12345",
|
||||||
|
};
|
||||||
|
organizationDomain1.SetNextRunDate(12);
|
||||||
|
organizationDomain1.SetJobRunCount();
|
||||||
|
organizationDomain1.SetVerifiedDate();
|
||||||
|
await organizationDomainRepository.CreateAsync(organizationDomain1);
|
||||||
|
|
||||||
|
var organizationDomain2 = new OrganizationDomain
|
||||||
|
{
|
||||||
|
OrganizationId = organization2.Id,
|
||||||
|
DomainName = domainName,
|
||||||
|
Txt = "btw+67890",
|
||||||
|
};
|
||||||
|
organizationDomain2.SetNextRunDate(12);
|
||||||
|
organizationDomain2.SetJobRunCount();
|
||||||
|
organizationDomain2.SetVerifiedDate();
|
||||||
|
await organizationDomainRepository.CreateAsync(organizationDomain2);
|
||||||
|
|
||||||
|
await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||||
|
{
|
||||||
|
OrganizationId = organization1.Id,
|
||||||
|
UserId = user.Id,
|
||||||
|
Status = OrganizationUserStatusType.Confirmed,
|
||||||
|
ResetPasswordKey = "resetpasswordkey1",
|
||||||
|
});
|
||||||
|
|
||||||
|
await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||||
|
{
|
||||||
|
OrganizationId = organization2.Id,
|
||||||
|
UserId = user.Id,
|
||||||
|
Status = OrganizationUserStatusType.Confirmed,
|
||||||
|
ResetPasswordKey = "resetpasswordkey2",
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await organizationRepository.GetByVerifiedUserEmailDomainAsync(user.Id);
|
||||||
|
|
||||||
|
Assert.Equal(2, result.Count);
|
||||||
|
Assert.Contains(result, org => org.Id == organization1.Id);
|
||||||
|
Assert.Contains(result, org => org.Id == organization2.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DatabaseTheory, DatabaseData]
|
||||||
|
public async Task GetByVerifiedUserEmailDomainAsync_WithNonExistentUser_ReturnsEmpty(
|
||||||
|
IOrganizationRepository organizationRepository)
|
||||||
|
{
|
||||||
|
var nonExistentUserId = Guid.NewGuid();
|
||||||
|
|
||||||
|
var result = await organizationRepository.GetByVerifiedUserEmailDomainAsync(nonExistentUserId);
|
||||||
|
|
||||||
|
Assert.Empty(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user