mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 16:12:49 -05:00
Merge branch 'master' into feature/billing-obfuscation
This commit is contained in:
14
.github/workflows/build-migrator-cli.yml
vendored
Normal file
14
.github/workflows/build-migrator-cli.yml
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: Build Migrator CLI
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
stub:
|
||||||
|
name: Stub
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Stub
|
||||||
|
run: echo "Stub"
|
24
.github/workflows/build-self-host.yml
vendored
24
.github/workflows/build-self-host.yml
vendored
@ -9,6 +9,12 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- ".github/workflows/**"
|
- ".github/workflows/**"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
branches-ignore:
|
||||||
|
- "l10n_master"
|
||||||
|
- "gh-pages"
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/build-self-host.yml"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-docker:
|
build-docker:
|
||||||
@ -48,11 +54,21 @@ jobs:
|
|||||||
run: az acr login -n bitwardenqa
|
run: az acr login -n bitwardenqa
|
||||||
|
|
||||||
- name: Login to Azure - Prod Subscription
|
- name: Login to Azure - Prod Subscription
|
||||||
if: ${{ env.is_publish_branch == 'true' }}
|
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Login to Azure ACR
|
||||||
|
run: az acr login -n bitwardenprod
|
||||||
|
|
||||||
|
|
||||||
|
- name: Retrieve github PAT secrets
|
||||||
|
id: retrieve-secret-pat
|
||||||
|
uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af
|
||||||
|
with:
|
||||||
|
keyvault: "bitwarden-prod-kv"
|
||||||
|
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
||||||
|
|
||||||
- name: Retrieve secrets
|
- name: Retrieve secrets
|
||||||
if: ${{ env.is_publish_branch == 'true' }}
|
if: ${{ env.is_publish_branch == 'true' }}
|
||||||
id: retrieve-secrets
|
id: retrieve-secrets
|
||||||
@ -102,9 +118,9 @@ jobs:
|
|||||||
IMAGE_TAG: ${{ steps.tag.outputs.image_tag }}
|
IMAGE_TAG: ${{ steps.tag.outputs.image_tag }}
|
||||||
run: |
|
run: |
|
||||||
if [ "$IMAGE_TAG" = "dev" ] || [ "$IMAGE_TAG" = "beta" ]; then
|
if [ "$IMAGE_TAG" = "dev" ] || [ "$IMAGE_TAG" = "beta" ]; then
|
||||||
echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG},bitwarden/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT
|
echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG},bitwardenprod.azurecr.io/self-host:${IMAGE_TAG},bitwarden/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT
|
echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG},bitwardenprod.azurecr.io/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
@ -118,6 +134,8 @@ jobs:
|
|||||||
linux/arm64/v8
|
linux/arm64/v8
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.tag-list.outputs.tags }}
|
tags: ${{ steps.tag-list.outputs.tags }}
|
||||||
|
secrets: |
|
||||||
|
"GH_PAT=${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}"
|
||||||
|
|
||||||
- name: Log out of Docker and disable Docker Notary
|
- name: Log out of Docker and disable Docker Notary
|
||||||
if: ${{ env.is_publish_branch == 'true' }}
|
if: ${{ env.is_publish_branch == 'true' }}
|
||||||
|
241
.github/workflows/build.yml
vendored
241
.github/workflows/build.yml
vendored
@ -191,74 +191,145 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- project_name: Admin
|
- project_name: Admin
|
||||||
base_path: ./src
|
base_path: ./src
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: Api
|
- project_name: Api
|
||||||
base_path: ./src
|
base_path: ./src
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: Attachments
|
- project_name: Attachments
|
||||||
base_path: ./util
|
base_path: ./util
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
- project_name: Events
|
- project_name: Events
|
||||||
base_path: ./src
|
base_path: ./src
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: EventsProcessor
|
- project_name: EventsProcessor
|
||||||
base_path: ./src
|
base_path: ./src
|
||||||
docker_repos: [bitwardenqa.azurecr.io]
|
docker_repos: [bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: Icons
|
- project_name: Icons
|
||||||
base_path: ./src
|
base_path: ./src
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: Identity
|
- project_name: Identity
|
||||||
base_path: ./src
|
base_path: ./src
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: MsSql
|
- project_name: MsSql
|
||||||
base_path: ./util
|
base_path: ./util
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
- project_name: Nginx
|
- project_name: Nginx
|
||||||
base_path: ./util
|
base_path: ./util
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
- project_name: Notifications
|
- project_name: Notifications
|
||||||
base_path: ./src
|
base_path: ./src
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: Server
|
- project_name: Server
|
||||||
base_path: ./util
|
base_path: ./util
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: Setup
|
- project_name: Setup
|
||||||
base_path: ./util
|
base_path: ./util
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: Sso
|
- project_name: Sso
|
||||||
base_path: ./bitwarden_license/src
|
base_path: ./bitwarden_license/src
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: Scim
|
- project_name: Scim
|
||||||
base_path: ./bitwarden_license/src
|
base_path: ./bitwarden_license/src
|
||||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: Billing
|
- project_name: Billing
|
||||||
base_path: ./src
|
base_path: ./src
|
||||||
docker_repos: [bitwardenqa.azurecr.io]
|
docker_repos: [bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
|
||||||
dotnet: true
|
dotnet: true
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||||
|
|
||||||
- name: Set up image tag
|
- name: Check Branch to Publish
|
||||||
|
env:
|
||||||
|
PUBLISH_BRANCHES: "master,rc,hotfix-rc"
|
||||||
|
id: publish-branch-check
|
||||||
|
run: |
|
||||||
|
IFS="," read -a publish_branches <<< $PUBLISH_BRANCHES
|
||||||
|
|
||||||
|
if [[ " ${publish_branches[*]} " =~ " ${GITHUB_REF:11} " ]]; then
|
||||||
|
echo "is_publish_branch=true" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "is_publish_branch=false" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
########## ACRs ##########
|
||||||
|
- name: Login to Azure - QA Subscription
|
||||||
|
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||||
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Login to QA ACR
|
||||||
|
run: az acr login -n bitwardenqa
|
||||||
|
|
||||||
|
- name: Login to Azure - PROD Subscription
|
||||||
|
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||||
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Login to PROD ACR
|
||||||
|
run: az acr login -n bitwardenprod
|
||||||
|
|
||||||
|
- name: Retrieve github PAT secrets
|
||||||
|
id: retrieve-secret-pat
|
||||||
|
uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af
|
||||||
|
with:
|
||||||
|
keyvault: "bitwarden-prod-kv"
|
||||||
|
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
||||||
|
|
||||||
|
- name: Retrieve secrets
|
||||||
|
if: ${{ env.is_publish_branch == 'true' }}
|
||||||
|
id: retrieve-secrets
|
||||||
|
uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af
|
||||||
|
with:
|
||||||
|
keyvault: "bitwarden-prod-kv"
|
||||||
|
secrets: "docker-password,
|
||||||
|
docker-username,
|
||||||
|
dct-delegate-2-repo-passphrase,
|
||||||
|
dct-delegate-2-key"
|
||||||
|
|
||||||
|
- name: Log into Docker
|
||||||
|
if: ${{ env.is_publish_branch == 'true' }}
|
||||||
|
env:
|
||||||
|
DOCKER_USERNAME: ${{ steps.retrieve-secrets.outputs.docker-username }}
|
||||||
|
DOCKER_PASSWORD: ${{ steps.retrieve-secrets.outputs.docker-password }}
|
||||||
|
run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||||
|
|
||||||
|
- name: Setup Docker Trust
|
||||||
|
if: ${{ env.is_publish_branch == 'true' }}
|
||||||
|
env:
|
||||||
|
DCT_DELEGATION_KEY_ID: "c9bde8ec820701516491e5e03d3a6354e7bd66d05fa3df2b0062f68b116dc59c"
|
||||||
|
DCT_DELEGATE_KEY: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-key }}
|
||||||
|
DCT_REPO_PASSPHRASE: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-repo-passphrase }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.docker/trust/private
|
||||||
|
echo "$DCT_DELEGATE_KEY" > ~/.docker/trust/private/$DCT_DELEGATION_KEY_ID.key
|
||||||
|
echo "DOCKER_CONTENT_TRUST=1" >> $GITHUB_ENV
|
||||||
|
echo "DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$DCT_REPO_PASSPHRASE" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
########## Generate image tag and build Docker image ##########
|
||||||
|
- name: Generate Docker image tag
|
||||||
|
id: tag
|
||||||
run: |
|
run: |
|
||||||
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
||||||
if [[ "$IMAGE_TAG" == "master" ]]; then
|
if [[ "$IMAGE_TAG" == "master" ]]; then
|
||||||
IMAGE_TAG=dev
|
IMAGE_TAG=dev
|
||||||
|
elif [[ "$IMAGE_TAG" == "rc" ]] || [[ "$IMAGE_TAG" == "hotfix-rc" ]]; then
|
||||||
|
IMAGE_TAG=beta
|
||||||
fi
|
fi
|
||||||
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
########## Build Docker Image ##########
|
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Setup project name
|
- name: Setup project name
|
||||||
id: setup
|
id: setup
|
||||||
run: |
|
run: |
|
||||||
@ -267,6 +338,13 @@ jobs:
|
|||||||
echo "PROJECT_NAME: $PROJECT_NAME"
|
echo "PROJECT_NAME: $PROJECT_NAME"
|
||||||
echo "project_name=$PROJECT_NAME" >> $GITHUB_OUTPUT
|
echo "project_name=$PROJECT_NAME" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Generate tag list
|
||||||
|
id: tag-list
|
||||||
|
env:
|
||||||
|
IMAGE_TAG: ${{ steps.tag.outputs.image_tag }}
|
||||||
|
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
||||||
|
run: echo "tags=bitwardenqa.azurecr.io/${PROJECT_NAME}:${IMAGE_TAG},bitwardenprod.azurecr.io/${PROJECT_NAME}:${IMAGE_TAG}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Get build artifact
|
- name: Get build artifact
|
||||||
if: ${{ matrix.dotnet }}
|
if: ${{ matrix.dotnet }}
|
||||||
uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741
|
uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741
|
||||||
@ -281,125 +359,26 @@ 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
|
||||||
env:
|
uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5
|
||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
|
||||||
run: docker build -t $PROJECT_NAME ${{ matrix.base_path }}/${{ matrix.project_name }}
|
|
||||||
|
|
||||||
########## QA ACR ##########
|
|
||||||
- name: Login to Azure - QA Subscription
|
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
|
context: ${{ matrix.base_path }}/${{ matrix.project_name }}
|
||||||
|
file: ${{ matrix.base_path }}/${{ matrix.project_name }}/Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.tag-list.outputs.tags }}
|
||||||
|
secrets: |
|
||||||
|
"GH_PAT=${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}"
|
||||||
|
|
||||||
- name: Login to QA ACR
|
- name: Push to DockerHub
|
||||||
run: az acr login -n bitwardenqa
|
if: contains(matrix.docker_repos, 'bitwarden') && env.is_publish_branch == 'true'
|
||||||
|
|
||||||
- name: Tag and push image to QA ACR
|
|
||||||
env:
|
env:
|
||||||
|
IMAGE_TAG: ${{ steps.tag.outputs.image_tag }}
|
||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
||||||
REGISTRY: bitwardenqa.azurecr.io
|
|
||||||
run: |
|
run: |
|
||||||
docker tag $PROJECT_NAME \
|
docker tag bitwardenprod.azurecr.io/$PROJECT_NAME:$IMAGE_TAG bitwarden/$PROJECT_NAME:$IMAGE_TAG
|
||||||
$REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
docker push bitwarden/$PROJECT_NAME:$IMAGE_TAG
|
||||||
docker push $REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
|
||||||
|
|
||||||
- name: Log out of Docker
|
- name: Log out of Docker
|
||||||
run: docker logout
|
|
||||||
|
|
||||||
########## PROD ACR ##########
|
|
||||||
- name: Login to Azure - PROD Subscription
|
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
|
||||||
|
|
||||||
- name: Login to PROD ACR
|
|
||||||
run: az acr login -n bitwardenprod
|
|
||||||
|
|
||||||
- name: Tag and push image to PROD ACR
|
|
||||||
env:
|
|
||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
|
||||||
REGISTRY: bitwardenprod.azurecr.io
|
|
||||||
run: |
|
|
||||||
docker tag $PROJECT_NAME \
|
|
||||||
$REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
|
||||||
docker push $REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
|
||||||
|
|
||||||
- name: Log out of Docker
|
|
||||||
run: docker logout
|
|
||||||
|
|
||||||
########## DockerHub ##########
|
|
||||||
- name: Login to Azure - Prod Subscription
|
|
||||||
if: |
|
|
||||||
contains(matrix.docker_repos, 'bitwarden')
|
|
||||||
&& (github.ref == 'refs/heads/master' ||
|
|
||||||
github.ref == 'refs/heads/rc' ||
|
|
||||||
github.ref == 'refs/heads/hotfix-rc')
|
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
|
||||||
|
|
||||||
- name: Retrieve secrets
|
|
||||||
if: |
|
|
||||||
contains(matrix.docker_repos, 'bitwarden')
|
|
||||||
&& (github.ref == 'refs/heads/master' ||
|
|
||||||
github.ref == 'refs/heads/rc' ||
|
|
||||||
github.ref == 'refs/heads/hotfix-rc')
|
|
||||||
id: retrieve-secrets
|
|
||||||
uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af
|
|
||||||
with:
|
|
||||||
keyvault: "bitwarden-prod-kv"
|
|
||||||
secrets: "docker-password,
|
|
||||||
docker-username,
|
|
||||||
dct-delegate-2-repo-passphrase,
|
|
||||||
dct-delegate-2-key"
|
|
||||||
|
|
||||||
- name: Log into Docker
|
|
||||||
if: |
|
|
||||||
contains(matrix.docker_repos, 'bitwarden')
|
|
||||||
&& (github.ref == 'refs/heads/master' ||
|
|
||||||
github.ref == 'refs/heads/rc' ||
|
|
||||||
github.ref == 'refs/heads/hotfix-rc')
|
|
||||||
env:
|
|
||||||
DOCKER_USERNAME: ${{ steps.retrieve-secrets.outputs.docker-username }}
|
|
||||||
DOCKER_PASSWORD: ${{ steps.retrieve-secrets.outputs.docker-password }}
|
|
||||||
run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
|
||||||
|
|
||||||
- name: Setup Docker Trust
|
|
||||||
if: |
|
|
||||||
contains(matrix.docker_repos, 'bitwarden')
|
|
||||||
&& (github.ref == 'refs/heads/master' ||
|
|
||||||
github.ref == 'refs/heads/rc' ||
|
|
||||||
github.ref == 'refs/heads/hotfix-rc')
|
|
||||||
env:
|
|
||||||
DCT_DELEGATION_KEY_ID: "c9bde8ec820701516491e5e03d3a6354e7bd66d05fa3df2b0062f68b116dc59c"
|
|
||||||
DCT_DELEGATE_KEY: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-key }}
|
|
||||||
DCT_REPO_PASSPHRASE: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-repo-passphrase }}
|
|
||||||
run: |
|
|
||||||
mkdir -p ~/.docker/trust/private
|
|
||||||
echo "$DCT_DELEGATE_KEY" > ~/.docker/trust/private/$DCT_DELEGATION_KEY_ID.key
|
|
||||||
echo "DOCKER_CONTENT_TRUST=1" >> $GITHUB_ENV
|
|
||||||
echo "DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$DCT_REPO_PASSPHRASE" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Tag and Push RC to Docker Hub
|
|
||||||
if: |
|
|
||||||
contains(matrix.docker_repos, 'bitwarden')
|
|
||||||
&& (github.ref == 'refs/heads/master' ||
|
|
||||||
github.ref == 'refs/heads/rc' ||
|
|
||||||
github.ref == 'refs/heads/hotfix-rc')
|
|
||||||
env:
|
|
||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
|
||||||
REGISTRY: bitwarden
|
|
||||||
run: |
|
|
||||||
docker tag $PROJECT_NAME \
|
|
||||||
$REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
|
||||||
docker push $REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
|
||||||
|
|
||||||
- name: Log out of Docker and disable Docker Notary
|
|
||||||
if: |
|
|
||||||
contains(matrix.docker_repos, 'bitwarden')
|
|
||||||
&& (github.ref == 'refs/heads/master' ||
|
|
||||||
github.ref == 'refs/heads/rc' ||
|
|
||||||
github.ref == 'refs/heads/hotfix-rc')
|
|
||||||
run: |
|
run: |
|
||||||
docker logout
|
docker logout
|
||||||
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
|
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
|
||||||
|
42
.github/workflows/cleanup-after-pr.yml
vendored
42
.github/workflows/cleanup-after-pr.yml
vendored
@ -22,10 +22,21 @@ jobs:
|
|||||||
- name: Login to Azure ACR
|
- name: Login to Azure ACR
|
||||||
run: az acr login -n bitwardenqa
|
run: az acr login -n bitwardenqa
|
||||||
|
|
||||||
|
- name: Login to Azure - PROD Subscription
|
||||||
|
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||||
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Login to Azure ACR
|
||||||
|
run: az acr login -n bitwardenprod
|
||||||
|
|
||||||
########## Remove Docker images ##########
|
########## Remove Docker images ##########
|
||||||
- name: Remove the docker image from ACR
|
- name: Remove the docker image from ACR
|
||||||
env:
|
env:
|
||||||
REGISTRY_NAME: bitwardenqa
|
REGISTRIES: |
|
||||||
|
registries:
|
||||||
|
- bitwardenprod
|
||||||
|
- bitwardenqa
|
||||||
SERVICES: |
|
SERVICES: |
|
||||||
services:
|
services:
|
||||||
- Admin
|
- Admin
|
||||||
@ -45,21 +56,24 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
for SERVICE in $(echo "${{ env.SERVICES }}" | yq e ".services[]" - )
|
for SERVICE in $(echo "${{ env.SERVICES }}" | yq e ".services[]" - )
|
||||||
do
|
do
|
||||||
SERVICE_NAME=$(echo $SERVICE | awk '{print tolower($0)}')
|
for REGISTRY in $( echo "${{ env.REGISTRIES }}" | yq e ".registries[]" - )
|
||||||
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
do
|
||||||
|
SERVICE_NAME=$(echo $SERVICE | awk '{print tolower($0)}')
|
||||||
|
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
||||||
|
|
||||||
echo "[*] Checking if remote exists: $REGISTRY_NAME.azurecr.io/$SERVICE_NAME:$IMAGE_TAG"
|
echo "[*] Checking if remote exists: $REGISTRY.azurecr.io/$SERVICE_NAME:$IMAGE_TAG"
|
||||||
TAG_EXISTS=$(
|
TAG_EXISTS=$(
|
||||||
az acr repository show-tags --name $REGISTRY_NAME --repository $SERVICE_NAME \
|
az acr repository show-tags --name $REGISTRY --repository $SERVICE_NAME \
|
||||||
| jq --arg $TAG "$IMAGE_TAG" -e '. | any(. == "$TAG")'
|
| jq --arg $TAG "$IMAGE_TAG" -e '. | any(. == "$TAG")'
|
||||||
)
|
)
|
||||||
|
|
||||||
if [[ "$TAG_EXISTS" == "true" ]]; then
|
if [[ "$TAG_EXISTS" == "true" ]]; then
|
||||||
echo "[*] Tag exists. Removing tag"
|
echo "[*] Tag exists. Removing tag"
|
||||||
az acr repository delete --name $REGISTRY_NAME --image $SERVICE_NAME:$IMAGE_TAG --yes
|
az acr repository delete --name $REGISTRY --image $SERVICE_NAME:$IMAGE_TAG --yes
|
||||||
else
|
else
|
||||||
echo "[*] Tag does not exist. No action needed"
|
echo "[*] Tag does not exist. No action needed"
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Log out of Docker
|
- name: Log out of Docker
|
||||||
|
15
.github/workflows/container-registry-purge.yml
vendored
15
.github/workflows/container-registry-purge.yml
vendored
@ -11,28 +11,15 @@ jobs:
|
|||||||
purge:
|
purge:
|
||||||
name: Purge old images
|
name: Purge old images
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- name: bitwardenqa
|
|
||||||
- name: bitwardenprod
|
|
||||||
steps:
|
steps:
|
||||||
- name: Login to Azure
|
- name: Login to Azure
|
||||||
if: matrix.name == 'bitwardenprod'
|
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
- name: Login to Azure
|
|
||||||
if: matrix.name == 'bitwardenqa'
|
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
|
|
||||||
|
|
||||||
- name: Purge images
|
- name: Purge images
|
||||||
env:
|
env:
|
||||||
REGISTRY: ${{ matrix.name }}
|
REGISTRY: bitwardenprod
|
||||||
AGO_DUR_VER: "180d"
|
AGO_DUR_VER: "180d"
|
||||||
AGO_DUR: "30d"
|
AGO_DUR: "30d"
|
||||||
run: |
|
run: |
|
||||||
|
8
.github/workflows/database.yml
vendored
8
.github/workflows/database.yml
vendored
@ -83,11 +83,17 @@ jobs:
|
|||||||
run: /usr/local/sqlpackage/sqlpackage /action:DeployReport /SourceFile:"Sql.dacpac" /TargetConnectionString:"Server=localhost;Database=vault_dev;User Id=SA;Password=SET_A_PASSWORD_HERE_123;Encrypt=True;TrustServerCertificate=True;" /OutputPath:"report.xml" /p:IgnoreColumnOrder=True /p:IgnoreComments=True
|
run: /usr/local/sqlpackage/sqlpackage /action:DeployReport /SourceFile:"Sql.dacpac" /TargetConnectionString:"Server=localhost;Database=vault_dev;User Id=SA;Password=SET_A_PASSWORD_HERE_123;Encrypt=True;TrustServerCertificate=True;" /OutputPath:"report.xml" /p:IgnoreColumnOrder=True /p:IgnoreComments=True
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Generate SQL file
|
||||||
|
run: /usr/local/sqlpackage/sqlpackage /action:Script /SourceFile:"Sql.dacpac" /TargetConnectionString:"Server=localhost;Database=vault_dev;User Id=SA;Password=SET_A_PASSWORD_HERE_123;Encrypt=True;TrustServerCertificate=True;" /OutputPath:"diff.sql" /p:IgnoreColumnOrder=True /p:IgnoreComments=True
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
- name: Upload Report
|
- name: Upload Report
|
||||||
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
||||||
with:
|
with:
|
||||||
name: report.xml
|
name: report.xml
|
||||||
path: report.xml
|
path: |
|
||||||
|
report.xml
|
||||||
|
diff.sql
|
||||||
|
|
||||||
- name: Validate XML
|
- name: Validate XML
|
||||||
run: |
|
run: |
|
||||||
|
64
.github/workflows/release.yml
vendored
64
.github/workflows/release.yml
vendored
@ -188,7 +188,7 @@ jobs:
|
|||||||
origin_docker_repo: bitwarden
|
origin_docker_repo: bitwarden
|
||||||
- project_name: EventsProcessor
|
- project_name: EventsProcessor
|
||||||
prod_acr: true
|
prod_acr: true
|
||||||
origin_docker_repo: bitwardenqa.azurecr.io
|
origin_docker_repo: bitwardenprod.azurecr.io
|
||||||
- project_name: Icons
|
- project_name: Icons
|
||||||
origin_docker_repo: bitwarden
|
origin_docker_repo: bitwarden
|
||||||
prod_acr: true
|
prod_acr: true
|
||||||
@ -209,7 +209,7 @@ jobs:
|
|||||||
- project_name: Scim
|
- project_name: Scim
|
||||||
origin_docker_repo: bitwarden
|
origin_docker_repo: bitwarden
|
||||||
- project_name: Billing
|
- project_name: Billing
|
||||||
origin_docker_repo: bitwardenqa.azurecr.io
|
origin_docker_repo: bitwardenprod.azurecr.io
|
||||||
steps:
|
steps:
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
env:
|
env:
|
||||||
@ -277,31 +277,19 @@ jobs:
|
|||||||
docker logout
|
docker logout
|
||||||
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
|
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
|
||||||
|
|
||||||
########## ACR QA ##########
|
########## ACR PROD ##########
|
||||||
- name: Login to Azure - QA Subscription
|
- name: Login to Azure - PROD Subscription
|
||||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
- name: Login to Azure ACR
|
- name: Login to Azure ACR
|
||||||
run: az acr login -n bitwardenqa
|
run: az acr login -n bitwardenprod
|
||||||
|
|
||||||
- name: Pull latest project image
|
|
||||||
if: matrix.origin_docker_repo == 'bitwardenqa.azurecr.io'
|
|
||||||
env:
|
|
||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
|
||||||
REGISTRY: bitwardenqa.azurecr.io
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
|
|
||||||
docker pull $REGISTRY/$PROJECT_NAME:latest
|
|
||||||
else
|
|
||||||
docker pull $REGISTRY/$PROJECT_NAME:$_BRANCH_NAME
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Tag version and latest
|
- name: Tag version and latest
|
||||||
env:
|
env:
|
||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
||||||
REGISTRY: bitwardenqa.azurecr.io
|
REGISTRY: bitwardenprod.azurecr.io
|
||||||
ORIGIN_REGISTRY: ${{ matrix.origin_docker_repo }}
|
ORIGIN_REGISTRY: ${{ matrix.origin_docker_repo }}
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
|
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
|
||||||
@ -313,43 +301,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Push version and latest image
|
- name: Push version and latest image
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||||
env:
|
|
||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
|
||||||
REGISTRY: bitwardenqa.azurecr.io
|
|
||||||
run: |
|
|
||||||
docker push $REGISTRY/$PROJECT_NAME:latest
|
|
||||||
docker push $REGISTRY/$PROJECT_NAME:$_RELEASE_VERSION
|
|
||||||
|
|
||||||
- name: Log out of Docker
|
|
||||||
run: docker logout
|
|
||||||
|
|
||||||
########## ACR PROD ##########
|
|
||||||
- name: Login to Azure - PROD Subscription
|
|
||||||
if: matrix.prod_acr == true
|
|
||||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
|
||||||
|
|
||||||
- name: Login to Azure ACR
|
|
||||||
if: matrix.prod_acr == true
|
|
||||||
run: az acr login -n bitwardenprod
|
|
||||||
|
|
||||||
- name: Tag version and latest
|
|
||||||
if: matrix.prod_acr == true
|
|
||||||
env:
|
|
||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
|
||||||
REGISTRY: bitwardenprod.azurecr.io
|
|
||||||
ORIGIN_REGISTRY: ${{ matrix.origin_docker_repo }}
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
|
|
||||||
docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:latest $REGISTRY/$PROJECT_NAME:dryrun
|
|
||||||
else
|
|
||||||
docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:$_BRANCH_NAME $REGISTRY/$PROJECT_NAME:$_RELEASE_VERSION
|
|
||||||
docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:$_BRANCH_NAME $REGISTRY/$PROJECT_NAME:latest
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Push version and latest image
|
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' && matrix.prod_acr == true }}
|
|
||||||
env:
|
env:
|
||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
||||||
REGISTRY: bitwardenprod.azurecr.io
|
REGISTRY: bitwardenprod.azurecr.io
|
||||||
@ -358,7 +309,6 @@ jobs:
|
|||||||
docker push $REGISTRY/$PROJECT_NAME:latest
|
docker push $REGISTRY/$PROJECT_NAME:latest
|
||||||
|
|
||||||
- name: Log out of Docker
|
- name: Log out of Docker
|
||||||
if: matrix.prod_acr == true
|
|
||||||
run: docker logout
|
run: docker logout
|
||||||
|
|
||||||
release:
|
release:
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<!--2022.6.2-->
|
<!--2022.6.2-->
|
||||||
<Version>2023.1.0</Version>
|
<Version>2023.2.1</Version>
|
||||||
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
||||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
13
README.md
13
README.md
@ -80,16 +80,3 @@ Consider installing our git pre-commit hook for automatic formatting.
|
|||||||
```bash
|
```bash
|
||||||
git config --local core.hooksPath .git-hooks
|
git config --local core.hooksPath .git-hooks
|
||||||
```
|
```
|
||||||
|
|
||||||
### File Scoped Namespaces
|
|
||||||
|
|
||||||
We recently migrated to using file scoped namespaces to save some horizontal space. All previous branches will need to update to avoid large merge conflicts using the following steps:
|
|
||||||
|
|
||||||
1. Check out your local Branch
|
|
||||||
2. Run `git merge 9b7aef0763ad14e229b337c3b5b27cb411009792`
|
|
||||||
3. Resolve any merge conflicts, commit.
|
|
||||||
4. Run `dotnet format`
|
|
||||||
5. Commit
|
|
||||||
6. Run `git merge -Xours 7f5f010e1eea400300c47f776604ecf46c4b4f2d`
|
|
||||||
7. Fix Merge conflicts
|
|
||||||
8. Push
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
@ -8,13 +9,36 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
|||||||
public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
|
public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
|
||||||
{
|
{
|
||||||
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
||||||
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
|
|
||||||
public CreateAccessPoliciesCommand(IAccessPolicyRepository accessPolicyRepository)
|
public CreateAccessPoliciesCommand(
|
||||||
|
IAccessPolicyRepository accessPolicyRepository,
|
||||||
|
IProjectRepository projectRepository,
|
||||||
|
IServiceAccountRepository serviceAccountRepository)
|
||||||
{
|
{
|
||||||
_accessPolicyRepository = accessPolicyRepository;
|
_accessPolicyRepository = accessPolicyRepository;
|
||||||
|
_projectRepository = projectRepository;
|
||||||
|
_serviceAccountRepository = serviceAccountRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<BaseAccessPolicy>> CreateAsync(List<BaseAccessPolicy> accessPolicies)
|
private static IEnumerable<Guid?> GetDistinctGrantedProjectIds(List<BaseAccessPolicy> accessPolicies)
|
||||||
|
{
|
||||||
|
var userGrantedIds = accessPolicies.OfType<UserProjectAccessPolicy>().Select(ap => ap.GrantedProjectId);
|
||||||
|
var groupGrantedIds = accessPolicies.OfType<GroupProjectAccessPolicy>().Select(ap => ap.GrantedProjectId);
|
||||||
|
var saGrantedIds = accessPolicies.OfType<ServiceAccountProjectAccessPolicy>().Select(ap => ap.GrantedProjectId);
|
||||||
|
return userGrantedIds.Concat(groupGrantedIds).Concat(saGrantedIds).Distinct();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<Guid?> GetDistinctGrantedServiceAccountIds(List<BaseAccessPolicy> accessPolicies)
|
||||||
|
{
|
||||||
|
var userGrantedIds = accessPolicies.OfType<UserServiceAccountAccessPolicy>().Select(ap => ap.GrantedServiceAccountId);
|
||||||
|
var groupGrantedIds = accessPolicies.OfType<GroupServiceAccountAccessPolicy>()
|
||||||
|
.Select(ap => ap.GrantedServiceAccountId);
|
||||||
|
return userGrantedIds.Concat(groupGrantedIds).Distinct();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CheckForDistinctAccessPolicies(IReadOnlyCollection<BaseAccessPolicy> accessPolicies)
|
||||||
{
|
{
|
||||||
var distinctAccessPolicies = accessPolicies.DistinctBy(baseAccessPolicy =>
|
var distinctAccessPolicies = accessPolicies.DistinctBy(baseAccessPolicy =>
|
||||||
{
|
{
|
||||||
@ -22,8 +46,12 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
|
|||||||
{
|
{
|
||||||
UserProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.OrganizationUserId, ap.GrantedProjectId),
|
UserProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.OrganizationUserId, ap.GrantedProjectId),
|
||||||
GroupProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.GroupId, ap.GrantedProjectId),
|
GroupProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.GroupId, ap.GrantedProjectId),
|
||||||
ServiceAccountProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.ServiceAccountId, ap.GrantedProjectId),
|
ServiceAccountProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.ServiceAccountId,
|
||||||
_ => throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy))
|
ap.GrantedProjectId),
|
||||||
|
UserServiceAccountAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.OrganizationUserId,
|
||||||
|
ap.GrantedServiceAccountId),
|
||||||
|
GroupServiceAccountAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.GroupId, ap.GrantedServiceAccountId),
|
||||||
|
_ => throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy)),
|
||||||
};
|
};
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
@ -31,7 +59,44 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
|
|||||||
{
|
{
|
||||||
throw new BadRequestException("Resources must be unique");
|
throw new BadRequestException("Resources must be unique");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<BaseAccessPolicy>> CreateManyAsync(List<BaseAccessPolicy> accessPolicies, Guid userId, AccessClientType accessType)
|
||||||
|
{
|
||||||
|
CheckForDistinctAccessPolicies(accessPolicies);
|
||||||
|
await CheckAccessPoliciesDoNotExistAsync(accessPolicies);
|
||||||
|
await CheckCanCreateAsync(accessPolicies, userId, accessType);
|
||||||
|
return await _accessPolicyRepository.CreateManyAsync(accessPolicies);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckCanCreateAsync(List<BaseAccessPolicy> accessPolicies, Guid userId, AccessClientType accessType)
|
||||||
|
{
|
||||||
|
var projectIds = GetDistinctGrantedProjectIds(accessPolicies).ToList();
|
||||||
|
var serviceAccountIds = GetDistinctGrantedServiceAccountIds(accessPolicies).ToList();
|
||||||
|
|
||||||
|
if (projectIds.Any())
|
||||||
|
{
|
||||||
|
foreach (var projectId in projectIds)
|
||||||
|
{
|
||||||
|
await CheckPermissionAsync(accessType, userId, projectId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (serviceAccountIds.Any())
|
||||||
|
{
|
||||||
|
foreach (var serviceAccountId in serviceAccountIds)
|
||||||
|
{
|
||||||
|
await CheckPermissionAsync(accessType, userId, serviceAccountIdToCheck: serviceAccountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!projectIds.Any() && !serviceAccountIds.Any())
|
||||||
|
{
|
||||||
|
throw new BadRequestException("No granted IDs specified");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckAccessPoliciesDoNotExistAsync(List<BaseAccessPolicy> accessPolicies)
|
||||||
|
{
|
||||||
foreach (var accessPolicy in accessPolicies)
|
foreach (var accessPolicy in accessPolicies)
|
||||||
{
|
{
|
||||||
if (await _accessPolicyRepository.AccessPolicyExists(accessPolicy))
|
if (await _accessPolicyRepository.AccessPolicyExists(accessPolicy))
|
||||||
@ -39,7 +104,42 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
|
|||||||
throw new BadRequestException("Resource already exists");
|
throw new BadRequestException("Resource already exists");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return await _accessPolicyRepository.CreateManyAsync(accessPolicies);
|
private async Task CheckPermissionAsync(AccessClientType accessClient, Guid userId, Guid? projectIdToCheck = null,
|
||||||
|
Guid? serviceAccountIdToCheck = null)
|
||||||
|
{
|
||||||
|
bool hasAccess;
|
||||||
|
switch (accessClient)
|
||||||
|
{
|
||||||
|
case AccessClientType.NoAccessCheck:
|
||||||
|
hasAccess = true;
|
||||||
|
break;
|
||||||
|
case AccessClientType.User:
|
||||||
|
if (projectIdToCheck.HasValue)
|
||||||
|
{
|
||||||
|
hasAccess = await _projectRepository.UserHasWriteAccessToProject(projectIdToCheck.Value, userId);
|
||||||
|
}
|
||||||
|
else if (serviceAccountIdToCheck.HasValue)
|
||||||
|
{
|
||||||
|
hasAccess =
|
||||||
|
await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(
|
||||||
|
serviceAccountIdToCheck.Value, userId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("No ID to check provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hasAccess = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasAccess)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
||||||
@ -7,14 +10,23 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
|||||||
public class DeleteAccessPolicyCommand : IDeleteAccessPolicyCommand
|
public class DeleteAccessPolicyCommand : IDeleteAccessPolicyCommand
|
||||||
{
|
{
|
||||||
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
|
|
||||||
public DeleteAccessPolicyCommand(IAccessPolicyRepository accessPolicyRepository)
|
public DeleteAccessPolicyCommand(
|
||||||
|
IAccessPolicyRepository accessPolicyRepository,
|
||||||
|
ICurrentContext currentContext,
|
||||||
|
IProjectRepository projectRepository,
|
||||||
|
IServiceAccountRepository serviceAccountRepository)
|
||||||
{
|
{
|
||||||
|
_projectRepository = projectRepository;
|
||||||
|
_serviceAccountRepository = serviceAccountRepository;
|
||||||
_accessPolicyRepository = accessPolicyRepository;
|
_accessPolicyRepository = accessPolicyRepository;
|
||||||
|
_currentContext = currentContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(Guid id, Guid userId)
|
||||||
public async Task DeleteAsync(Guid id)
|
|
||||||
{
|
{
|
||||||
var accessPolicy = await _accessPolicyRepository.GetByIdAsync(id);
|
var accessPolicy = await _accessPolicyRepository.GetByIdAsync(id);
|
||||||
if (accessPolicy == null)
|
if (accessPolicy == null)
|
||||||
@ -22,6 +34,74 @@ public class DeleteAccessPolicyCommand : IDeleteAccessPolicyCommand
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!await IsAllowedToDeleteAsync(accessPolicy, userId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
await _accessPolicyRepository.DeleteAsync(id);
|
await _accessPolicyRepository.DeleteAsync(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<bool> IsAllowedToDeleteAsync(BaseAccessPolicy baseAccessPolicy, Guid userId) =>
|
||||||
|
baseAccessPolicy switch
|
||||||
|
{
|
||||||
|
UserProjectAccessPolicy ap => await HasPermissionAsync(ap.GrantedProject!.OrganizationId, userId,
|
||||||
|
ap.GrantedProjectId),
|
||||||
|
GroupProjectAccessPolicy ap => await HasPermissionAsync(ap.GrantedProject!.OrganizationId, userId,
|
||||||
|
ap.GrantedProjectId),
|
||||||
|
ServiceAccountProjectAccessPolicy ap => await HasPermissionAsync(ap.GrantedProject!.OrganizationId,
|
||||||
|
userId, ap.GrantedProjectId),
|
||||||
|
UserServiceAccountAccessPolicy ap => await HasPermissionAsync(
|
||||||
|
ap.GrantedServiceAccount!.OrganizationId,
|
||||||
|
userId, serviceAccountIdToCheck: ap.GrantedServiceAccountId),
|
||||||
|
GroupServiceAccountAccessPolicy ap => await HasPermissionAsync(
|
||||||
|
ap.GrantedServiceAccount!.OrganizationId,
|
||||||
|
userId, serviceAccountIdToCheck: ap.GrantedServiceAccountId),
|
||||||
|
_ => throw new ArgumentException("Unsupported access policy type provided."),
|
||||||
|
};
|
||||||
|
|
||||||
|
private async Task<bool> HasPermissionAsync(
|
||||||
|
Guid organizationId,
|
||||||
|
Guid userId,
|
||||||
|
Guid? projectIdToCheck = null,
|
||||||
|
Guid? serviceAccountIdToCheck = null)
|
||||||
|
{
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||||
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
|
bool hasAccess;
|
||||||
|
switch (accessClient)
|
||||||
|
{
|
||||||
|
case AccessClientType.NoAccessCheck:
|
||||||
|
hasAccess = true;
|
||||||
|
break;
|
||||||
|
case AccessClientType.User:
|
||||||
|
if (projectIdToCheck.HasValue)
|
||||||
|
{
|
||||||
|
hasAccess = await _projectRepository.UserHasWriteAccessToProject(projectIdToCheck.Value, userId);
|
||||||
|
}
|
||||||
|
else if (serviceAccountIdToCheck.HasValue)
|
||||||
|
{
|
||||||
|
hasAccess =
|
||||||
|
await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(
|
||||||
|
serviceAccountIdToCheck.Value, userId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("No ID to check provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hasAccess = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasAccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
@ -8,13 +10,23 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
|||||||
public class UpdateAccessPolicyCommand : IUpdateAccessPolicyCommand
|
public class UpdateAccessPolicyCommand : IUpdateAccessPolicyCommand
|
||||||
{
|
{
|
||||||
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
|
|
||||||
public UpdateAccessPolicyCommand(IAccessPolicyRepository accessPolicyRepository)
|
public UpdateAccessPolicyCommand(
|
||||||
|
IAccessPolicyRepository accessPolicyRepository,
|
||||||
|
ICurrentContext currentContext,
|
||||||
|
IProjectRepository projectRepository,
|
||||||
|
IServiceAccountRepository serviceAccountRepository)
|
||||||
{
|
{
|
||||||
_accessPolicyRepository = accessPolicyRepository;
|
_accessPolicyRepository = accessPolicyRepository;
|
||||||
|
_currentContext = currentContext;
|
||||||
|
_projectRepository = projectRepository;
|
||||||
|
_serviceAccountRepository = serviceAccountRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<BaseAccessPolicy> UpdateAsync(Guid id, bool read, bool write)
|
public async Task<BaseAccessPolicy> UpdateAsync(Guid id, bool read, bool write, Guid userId)
|
||||||
{
|
{
|
||||||
var accessPolicy = await _accessPolicyRepository.GetByIdAsync(id);
|
var accessPolicy = await _accessPolicyRepository.GetByIdAsync(id);
|
||||||
if (accessPolicy == null)
|
if (accessPolicy == null)
|
||||||
@ -22,11 +34,78 @@ public class UpdateAccessPolicyCommand : IUpdateAccessPolicyCommand
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!await IsAllowedToUpdateAsync(accessPolicy, userId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
accessPolicy.Read = read;
|
accessPolicy.Read = read;
|
||||||
accessPolicy.Write = write;
|
accessPolicy.Write = write;
|
||||||
accessPolicy.RevisionDate = DateTime.UtcNow;
|
accessPolicy.RevisionDate = DateTime.UtcNow;
|
||||||
|
|
||||||
await _accessPolicyRepository.ReplaceAsync(accessPolicy);
|
await _accessPolicyRepository.ReplaceAsync(accessPolicy);
|
||||||
return accessPolicy;
|
return accessPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<bool> IsAllowedToUpdateAsync(BaseAccessPolicy baseAccessPolicy, Guid userId) =>
|
||||||
|
baseAccessPolicy switch
|
||||||
|
{
|
||||||
|
UserProjectAccessPolicy ap => await HasPermissionsAsync(ap.GrantedProject!.OrganizationId, userId,
|
||||||
|
ap.GrantedProjectId),
|
||||||
|
GroupProjectAccessPolicy ap => await HasPermissionsAsync(ap.GrantedProject!.OrganizationId, userId,
|
||||||
|
ap.GrantedProjectId),
|
||||||
|
ServiceAccountProjectAccessPolicy ap => await HasPermissionsAsync(ap.GrantedProject!.OrganizationId,
|
||||||
|
userId, ap.GrantedProjectId),
|
||||||
|
UserServiceAccountAccessPolicy ap => await HasPermissionsAsync(
|
||||||
|
ap.GrantedServiceAccount!.OrganizationId,
|
||||||
|
userId, serviceAccountIdToCheck: ap.GrantedServiceAccountId),
|
||||||
|
GroupServiceAccountAccessPolicy ap => await HasPermissionsAsync(
|
||||||
|
ap.GrantedServiceAccount!.OrganizationId,
|
||||||
|
userId, serviceAccountIdToCheck: ap.GrantedServiceAccountId),
|
||||||
|
_ => throw new ArgumentException("Unsupported access policy type provided."),
|
||||||
|
};
|
||||||
|
|
||||||
|
private async Task<bool> HasPermissionsAsync(
|
||||||
|
Guid organizationId,
|
||||||
|
Guid userId,
|
||||||
|
Guid? projectIdToCheck = null,
|
||||||
|
Guid? serviceAccountIdToCheck = null)
|
||||||
|
{
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||||
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
|
bool hasAccess;
|
||||||
|
switch (accessClient)
|
||||||
|
{
|
||||||
|
case AccessClientType.NoAccessCheck:
|
||||||
|
hasAccess = true;
|
||||||
|
break;
|
||||||
|
case AccessClientType.User:
|
||||||
|
if (projectIdToCheck.HasValue)
|
||||||
|
{
|
||||||
|
hasAccess = await _projectRepository.UserHasWriteAccessToProject(projectIdToCheck.Value, userId);
|
||||||
|
}
|
||||||
|
else if (serviceAccountIdToCheck.HasValue)
|
||||||
|
{
|
||||||
|
hasAccess =
|
||||||
|
await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(
|
||||||
|
serviceAccountIdToCheck.Value, userId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("No ID to check provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hasAccess = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasAccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
using Bit.Core.SecretsManager.Commands.Porting;
|
||||||
|
using Bit.Core.SecretsManager.Commands.Porting.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Commercial.Core.SecretsManager.Commands.Porting;
|
||||||
|
|
||||||
|
public class ImportCommand : IImportCommand
|
||||||
|
{
|
||||||
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
private readonly ISecretRepository _secretRepository;
|
||||||
|
|
||||||
|
public ImportCommand(IProjectRepository projectRepository, ISecretRepository secretRepository)
|
||||||
|
{
|
||||||
|
_projectRepository = projectRepository;
|
||||||
|
_secretRepository = secretRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ImportAsync(Guid organizationId, SMImport import)
|
||||||
|
{
|
||||||
|
var importedProjects = new List<Guid>();
|
||||||
|
var importedSecrets = new List<Guid>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
import = AssignNewIds(import);
|
||||||
|
|
||||||
|
if (import.Projects.Any())
|
||||||
|
{
|
||||||
|
importedProjects = (await _projectRepository.ImportAsync(import.Projects.Select(p => new Project
|
||||||
|
{
|
||||||
|
Id = p.Id,
|
||||||
|
OrganizationId = organizationId,
|
||||||
|
Name = p.Name,
|
||||||
|
}))).Select(p => p.Id).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.Secrets != null && import.Secrets.Any())
|
||||||
|
{
|
||||||
|
importedSecrets = (await _secretRepository.ImportAsync(import.Secrets.Select(s => new Secret
|
||||||
|
{
|
||||||
|
Id = s.Id,
|
||||||
|
OrganizationId = organizationId,
|
||||||
|
Key = s.Key,
|
||||||
|
Value = s.Value,
|
||||||
|
Note = s.Note,
|
||||||
|
Projects = s.ProjectIds?.Select(id => new Project { Id = id }).ToList(),
|
||||||
|
}))).Select(s => s.Id).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
if (importedProjects.Any())
|
||||||
|
{
|
||||||
|
await _projectRepository.DeleteManyByIdAsync(importedProjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (importedSecrets.Any())
|
||||||
|
{
|
||||||
|
await _secretRepository.HardDeleteManyByIdAsync(importedSecrets);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Error attempting import");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SMImport AssignNewIds(SMImport import)
|
||||||
|
{
|
||||||
|
var projects = new Dictionary<Guid, SMImport.InnerProject>();
|
||||||
|
var secrets = new List<SMImport.InnerSecret>();
|
||||||
|
|
||||||
|
if (import.Projects != null && import.Projects.Any())
|
||||||
|
{
|
||||||
|
projects = import.Projects.ToDictionary(
|
||||||
|
p => p.Id,
|
||||||
|
p => new SMImport.InnerProject { Id = Guid.NewGuid(), Name = p.Name }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.Secrets != null && import.Secrets.Any())
|
||||||
|
{
|
||||||
|
foreach (var secret in import.Secrets)
|
||||||
|
{
|
||||||
|
secrets.Add(new SMImport.InnerSecret
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Key = secret.Key,
|
||||||
|
Value = secret.Value,
|
||||||
|
Note = secret.Note,
|
||||||
|
ProjectIds = secret.ProjectIds?.Select(id => projects[id].Id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SMImport
|
||||||
|
{
|
||||||
|
Projects = projects.Values,
|
||||||
|
Secrets = secrets,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
|
||||||
@ -6,15 +7,35 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.Projects;
|
|||||||
|
|
||||||
public class CreateProjectCommand : ICreateProjectCommand
|
public class CreateProjectCommand : ICreateProjectCommand
|
||||||
{
|
{
|
||||||
|
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
||||||
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IProjectRepository _projectRepository;
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
|
||||||
public CreateProjectCommand(IProjectRepository projectRepository)
|
public CreateProjectCommand(
|
||||||
|
IAccessPolicyRepository accessPolicyRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
IProjectRepository projectRepository
|
||||||
|
)
|
||||||
{
|
{
|
||||||
|
_accessPolicyRepository = accessPolicyRepository;
|
||||||
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_projectRepository = projectRepository;
|
_projectRepository = projectRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Project> CreateAsync(Project project)
|
public async Task<Project> CreateAsync(Project project, Guid userId)
|
||||||
{
|
{
|
||||||
return await _projectRepository.CreateAsync(project);
|
var createdProject = await _projectRepository.CreateAsync(project);
|
||||||
|
|
||||||
|
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(createdProject.OrganizationId,
|
||||||
|
userId);
|
||||||
|
var accessPolicy = new UserProjectAccessPolicy()
|
||||||
|
{
|
||||||
|
OrganizationUserId = orgUser.Id,
|
||||||
|
GrantedProjectId = createdProject.Id,
|
||||||
|
Read = true,
|
||||||
|
Write = true,
|
||||||
|
};
|
||||||
|
await _accessPolicyRepository.CreateManyAsync(new List<BaseAccessPolicy> { accessPolicy });
|
||||||
|
return createdProject;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
|
||||||
@ -7,14 +10,34 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
|||||||
public class CreateSecretCommand : ICreateSecretCommand
|
public class CreateSecretCommand : ICreateSecretCommand
|
||||||
{
|
{
|
||||||
private readonly ISecretRepository _secretRepository;
|
private readonly ISecretRepository _secretRepository;
|
||||||
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
|
|
||||||
public CreateSecretCommand(ISecretRepository secretRepository)
|
public CreateSecretCommand(ISecretRepository secretRepository, IProjectRepository projectRepository, ICurrentContext currentContext)
|
||||||
{
|
{
|
||||||
_secretRepository = secretRepository;
|
_secretRepository = secretRepository;
|
||||||
|
_projectRepository = projectRepository;
|
||||||
|
_currentContext = currentContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Secret> CreateAsync(Secret secret)
|
public async Task<Secret> CreateAsync(Secret secret, Guid userId)
|
||||||
{
|
{
|
||||||
|
var orgAdmin = await _currentContext.OrganizationAdmin(secret.OrganizationId);
|
||||||
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
var project = secret.Projects?.FirstOrDefault();
|
||||||
|
|
||||||
|
var hasAccess = accessClient switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => true,
|
||||||
|
AccessClientType.User => project != null && await _projectRepository.UserHasWriteAccessToProject(project.Id, userId),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!hasAccess)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
return await _secretRepository.CreateAsync(secret);
|
return await _secretRepository.CreateAsync(secret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
@ -10,25 +11,27 @@ public class DeleteSecretCommand : IDeleteSecretCommand
|
|||||||
{
|
{
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly ISecretRepository _secretRepository;
|
private readonly ISecretRepository _secretRepository;
|
||||||
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
|
||||||
public DeleteSecretCommand(ICurrentContext currentContext, ISecretRepository secretRepository)
|
public DeleteSecretCommand(ISecretRepository secretRepository, IProjectRepository projectRepository, ICurrentContext currentContext)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_secretRepository = secretRepository;
|
_secretRepository = secretRepository;
|
||||||
|
_projectRepository = projectRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<Tuple<Secret, string>>> DeleteSecrets(List<Guid> ids)
|
public async Task<List<Tuple<Secret, string>>> DeleteSecrets(List<Guid> ids, Guid userId)
|
||||||
{
|
{
|
||||||
var secrets = await _secretRepository.GetManyByIds(ids);
|
var secrets = (await _secretRepository.GetManyByIds(ids)).ToList();
|
||||||
|
|
||||||
if (secrets?.Any() != true)
|
if (secrets.Any() != true)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure all secrets belongs to the same organization
|
// Ensure all secrets belongs to the same organization
|
||||||
var organizationId = secrets.First().OrganizationId;
|
var organizationId = secrets.First().OrganizationId;
|
||||||
if (secrets.Any(p => p.OrganizationId != organizationId))
|
if (secrets.Any(secret => secret.OrganizationId != organizationId))
|
||||||
{
|
{
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
}
|
}
|
||||||
@ -38,21 +41,46 @@ public class DeleteSecretCommand : IDeleteSecretCommand
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var results = ids.Select(id =>
|
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||||
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
|
var results = new List<Tuple<Secret, string>>();
|
||||||
|
var deleteIds = new List<Guid>();
|
||||||
|
|
||||||
|
foreach (var secret in secrets)
|
||||||
{
|
{
|
||||||
var secret = secrets.FirstOrDefault(secret => secret.Id == id);
|
var hasAccess = orgAdmin;
|
||||||
if (secret == null)
|
|
||||||
|
if (secret.Projects != null && secret.Projects?.Count > 0)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
var projectId = secret.Projects.First().Id;
|
||||||
|
|
||||||
|
hasAccess = accessClient switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => true,
|
||||||
|
AccessClientType.User => await _projectRepository.UserHasWriteAccessToProject(projectId, userId),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasAccess)
|
||||||
|
{
|
||||||
|
results.Add(new Tuple<Secret, string>(secret, "access denied"));
|
||||||
}
|
}
|
||||||
// TODO Once permissions are implemented add check for each secret here.
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new Tuple<Secret, string>(secret, "");
|
deleteIds.Add(secret.Id);
|
||||||
|
results.Add(new Tuple<Secret, string>(secret, ""));
|
||||||
}
|
}
|
||||||
}).ToList();
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (deleteIds.Count > 0)
|
||||||
|
{
|
||||||
|
await _secretRepository.SoftDeleteManyByIdAsync(deleteIds);
|
||||||
|
}
|
||||||
|
|
||||||
await _secretRepository.SoftDeleteManyByIdAsync(ids);
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
@ -8,23 +10,45 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
|||||||
public class UpdateSecretCommand : IUpdateSecretCommand
|
public class UpdateSecretCommand : IUpdateSecretCommand
|
||||||
{
|
{
|
||||||
private readonly ISecretRepository _secretRepository;
|
private readonly ISecretRepository _secretRepository;
|
||||||
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
|
|
||||||
public UpdateSecretCommand(ISecretRepository secretRepository)
|
public UpdateSecretCommand(ISecretRepository secretRepository, IProjectRepository projectRepository, ICurrentContext currentContext)
|
||||||
{
|
{
|
||||||
_secretRepository = secretRepository;
|
_secretRepository = secretRepository;
|
||||||
|
_projectRepository = projectRepository;
|
||||||
|
_currentContext = currentContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Secret> UpdateAsync(Secret secret)
|
public async Task<Secret> UpdateAsync(Secret updatedSecret, Guid userId)
|
||||||
{
|
{
|
||||||
var existingSecret = await _secretRepository.GetByIdAsync(secret.Id);
|
var secret = await _secretRepository.GetByIdAsync(updatedSecret.Id);
|
||||||
if (existingSecret == null)
|
if (secret == null || !_currentContext.AccessSecretsManager(secret.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
secret.OrganizationId = existingSecret.OrganizationId;
|
var orgAdmin = await _currentContext.OrganizationAdmin(secret.OrganizationId);
|
||||||
secret.CreationDate = existingSecret.CreationDate;
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
secret.DeletedDate = existingSecret.DeletedDate;
|
|
||||||
|
var project = updatedSecret.Projects?.FirstOrDefault();
|
||||||
|
|
||||||
|
var hasAccess = accessClient switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => true,
|
||||||
|
AccessClientType.User => project != null && await _projectRepository.UserHasWriteAccessToProject(project.Id, userId),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!hasAccess)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
secret.Key = updatedSecret.Key;
|
||||||
|
secret.Value = updatedSecret.Value;
|
||||||
|
secret.Note = updatedSecret.Note;
|
||||||
|
secret.Projects = updatedSecret.Projects;
|
||||||
secret.RevisionDate = DateTime.UtcNow;
|
secret.RevisionDate = DateTime.UtcNow;
|
||||||
|
|
||||||
await _secretRepository.UpdateAsync(secret);
|
await _secretRepository.UpdateAsync(secret);
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
|
||||||
|
|
||||||
|
public class RevokeAccessTokensCommand : IRevokeAccessTokensCommand
|
||||||
|
{
|
||||||
|
private readonly IApiKeyRepository _apiKeyRepository;
|
||||||
|
|
||||||
|
public RevokeAccessTokensCommand(IApiKeyRepository apiKeyRepository)
|
||||||
|
{
|
||||||
|
_apiKeyRepository = apiKeyRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RevokeAsync(ServiceAccount serviceAccount, IEnumerable<Guid> Ids)
|
||||||
|
{
|
||||||
|
var accessTokens = await _apiKeyRepository.GetManyByServiceAccountIdAsync(serviceAccount.Id);
|
||||||
|
|
||||||
|
var tokensToDelete = accessTokens.Where(at => Ids.Contains(at.Id));
|
||||||
|
|
||||||
|
await _apiKeyRepository.DeleteManyAsync(tokensToDelete);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.SecretsManager.Commands.Trash.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Commercial.Core.SecretsManager.Commands.Trash;
|
||||||
|
|
||||||
|
public class EmptyTrashCommand : IEmptyTrashCommand
|
||||||
|
{
|
||||||
|
private readonly ISecretRepository _secretRepository;
|
||||||
|
|
||||||
|
public EmptyTrashCommand(ISecretRepository secretRepository)
|
||||||
|
{
|
||||||
|
_secretRepository = secretRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task EmptyTrash(Guid organizationId, List<Guid> ids)
|
||||||
|
{
|
||||||
|
var secrets = await _secretRepository.GetManyByOrganizationIdInTrashByIdsAsync(organizationId, ids);
|
||||||
|
|
||||||
|
var missingId = ids.Except(secrets.Select(_ => _.Id)).Any();
|
||||||
|
if (missingId)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
await _secretRepository.HardDeleteManyByIdAsync(ids);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.SecretsManager.Commands.Trash.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Commercial.Core.SecretsManager.Commands.Trash;
|
||||||
|
|
||||||
|
public class RestoreTrashCommand : IRestoreTrashCommand
|
||||||
|
{
|
||||||
|
private readonly ISecretRepository _secretRepository;
|
||||||
|
|
||||||
|
public RestoreTrashCommand(ISecretRepository secretRepository)
|
||||||
|
{
|
||||||
|
_secretRepository = secretRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RestoreTrash(Guid organizationId, List<Guid> ids)
|
||||||
|
{
|
||||||
|
var secrets = await _secretRepository.GetManyByOrganizationIdInTrashByIdsAsync(organizationId, ids);
|
||||||
|
|
||||||
|
var missingId = ids.Except(secrets.Select(_ => _.Id)).Any();
|
||||||
|
if (missingId)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
await _secretRepository.RestoreManyByIdAsync(ids);
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,17 @@
|
|||||||
using Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
using Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
||||||
using Bit.Commercial.Core.SecretsManager.Commands.AccessTokens;
|
using Bit.Commercial.Core.SecretsManager.Commands.AccessTokens;
|
||||||
|
using Bit.Commercial.Core.SecretsManager.Commands.Porting;
|
||||||
using Bit.Commercial.Core.SecretsManager.Commands.Projects;
|
using Bit.Commercial.Core.SecretsManager.Commands.Projects;
|
||||||
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
||||||
using Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
|
using Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
|
||||||
|
using Bit.Commercial.Core.SecretsManager.Commands.Trash;
|
||||||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
|
using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Commands.Porting.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
|
using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Commands.Trash.Interfaces;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.SecretsManager;
|
namespace Bit.Commercial.Core.SecretsManager;
|
||||||
@ -24,9 +28,13 @@ public static class SecretsManagerCollectionExtensions
|
|||||||
services.AddScoped<IDeleteProjectCommand, DeleteProjectCommand>();
|
services.AddScoped<IDeleteProjectCommand, DeleteProjectCommand>();
|
||||||
services.AddScoped<ICreateServiceAccountCommand, CreateServiceAccountCommand>();
|
services.AddScoped<ICreateServiceAccountCommand, CreateServiceAccountCommand>();
|
||||||
services.AddScoped<IUpdateServiceAccountCommand, UpdateServiceAccountCommand>();
|
services.AddScoped<IUpdateServiceAccountCommand, UpdateServiceAccountCommand>();
|
||||||
|
services.AddScoped<IRevokeAccessTokensCommand, RevokeAccessTokensCommand>();
|
||||||
services.AddScoped<ICreateAccessTokenCommand, CreateAccessTokenCommand>();
|
services.AddScoped<ICreateAccessTokenCommand, CreateAccessTokenCommand>();
|
||||||
services.AddScoped<ICreateAccessPoliciesCommand, CreateAccessPoliciesCommand>();
|
services.AddScoped<ICreateAccessPoliciesCommand, CreateAccessPoliciesCommand>();
|
||||||
services.AddScoped<IUpdateAccessPolicyCommand, UpdateAccessPolicyCommand>();
|
services.AddScoped<IUpdateAccessPolicyCommand, UpdateAccessPolicyCommand>();
|
||||||
services.AddScoped<IDeleteAccessPolicyCommand, DeleteAccessPolicyCommand>();
|
services.AddScoped<IDeleteAccessPolicyCommand, DeleteAccessPolicyCommand>();
|
||||||
|
services.AddScoped<IImportCommand, ImportCommand>();
|
||||||
|
services.AddScoped<IEmptyTrashCommand, EmptyTrashCommand>();
|
||||||
|
services.AddScoped<IRestoreTrashCommand, RestoreTrashCommand>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,8 +45,8 @@
|
|||||||
},
|
},
|
||||||
"Azure.Core": {
|
"Azure.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.24.0",
|
"resolved": "1.25.0",
|
||||||
"contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
|
"contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
@ -83,28 +83,28 @@
|
|||||||
},
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.11.0",
|
"resolved": "12.14.1",
|
||||||
"contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==",
|
"contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Common": {
|
"Azure.Storage.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.10.0",
|
"resolved": "12.13.0",
|
||||||
"contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==",
|
"contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Core": "1.22.0",
|
"Azure.Core": "1.25.0",
|
||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.9.0",
|
"resolved": "12.12.0",
|
||||||
"contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==",
|
"contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Memory.Data": "1.0.2",
|
"System.Memory.Data": "1.0.2",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
@ -126,6 +126,14 @@
|
|||||||
"System.Xml.XPath.XmlDocument": "4.3.0"
|
"System.Xml.XPath.XmlDocument": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"DnsClient": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.0",
|
||||||
|
"contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Win32.Registry": "5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Fido2": {
|
"Fido2": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "3.0.1",
|
"resolved": "3.0.1",
|
||||||
@ -2549,10 +2557,11 @@
|
|||||||
"AspNetCoreRateLimit": "[4.0.2, )",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
||||||
"Azure.Storage.Blobs": "[12.11.0, )",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "[12.9.0, )",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "[1.0.1907, )",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "[5.12.0, )",
|
"Braintree": "[5.12.0, )",
|
||||||
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "[3.0.1, )",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
using AutoMapper;
|
using System.Linq.Expressions;
|
||||||
|
using AutoMapper;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
using Bit.Infrastructure.EntityFramework.SecretsManager.Models;
|
using Bit.Infrastructure.EntityFramework.SecretsManager.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
|
||||||
namespace Bit.Commercial.Infrastructure.EntityFramework.SecretsManager.Repositories;
|
namespace Bit.Commercial.Infrastructure.EntityFramework.SecretsManager.Repositories;
|
||||||
|
|
||||||
public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPolicyRepository
|
public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPolicyRepository
|
||||||
@ -14,147 +17,160 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Expression<Func<ServiceAccountProjectAccessPolicy, bool>> UserHasWriteAccessToProject(Guid userId) =>
|
||||||
|
policy =>
|
||||||
|
policy.GrantedProject.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Write) ||
|
||||||
|
policy.GrantedProject.GroupAccessPolicies.Any(ap =>
|
||||||
|
ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Write));
|
||||||
|
|
||||||
public async Task<List<Core.SecretsManager.Entities.BaseAccessPolicy>> CreateManyAsync(List<Core.SecretsManager.Entities.BaseAccessPolicy> baseAccessPolicies)
|
public async Task<List<Core.SecretsManager.Entities.BaseAccessPolicy>> CreateManyAsync(List<Core.SecretsManager.Entities.BaseAccessPolicy> baseAccessPolicies)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
foreach (var baseAccessPolicy in baseAccessPolicies)
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
baseAccessPolicy.SetNewId();
|
||||||
foreach (var baseAccessPolicy in baseAccessPolicies)
|
|
||||||
{
|
|
||||||
baseAccessPolicy.SetNewId();
|
|
||||||
switch (baseAccessPolicy)
|
|
||||||
{
|
|
||||||
case Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy:
|
|
||||||
{
|
|
||||||
var entity =
|
|
||||||
Mapper.Map<UserProjectAccessPolicy>(accessPolicy);
|
|
||||||
await dbContext.AddAsync(entity);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Core.SecretsManager.Entities.UserServiceAccountAccessPolicy accessPolicy:
|
|
||||||
{
|
|
||||||
var entity =
|
|
||||||
Mapper.Map<UserServiceAccountAccessPolicy>(accessPolicy);
|
|
||||||
await dbContext.AddAsync(entity);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy:
|
|
||||||
{
|
|
||||||
var entity = Mapper.Map<GroupProjectAccessPolicy>(accessPolicy);
|
|
||||||
await dbContext.AddAsync(entity);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy accessPolicy:
|
|
||||||
{
|
|
||||||
var entity = Mapper.Map<GroupServiceAccountAccessPolicy>(accessPolicy);
|
|
||||||
await dbContext.AddAsync(entity);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy:
|
|
||||||
{
|
|
||||||
var entity = Mapper.Map<ServiceAccountProjectAccessPolicy>(accessPolicy);
|
|
||||||
await dbContext.AddAsync(entity);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
return baseAccessPolicies;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> AccessPolicyExists(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
var dbContext = GetDatabaseContext(scope);
|
|
||||||
switch (baseAccessPolicy)
|
switch (baseAccessPolicy)
|
||||||
{
|
{
|
||||||
case Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy:
|
case Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy:
|
||||||
{
|
{
|
||||||
var policy = await dbContext.UserProjectAccessPolicy
|
var entity =
|
||||||
.Where(c => c.OrganizationUserId == accessPolicy.OrganizationUserId &&
|
Mapper.Map<UserProjectAccessPolicy>(accessPolicy);
|
||||||
c.GrantedProjectId == accessPolicy.GrantedProjectId)
|
await dbContext.AddAsync(entity);
|
||||||
.FirstOrDefaultAsync();
|
break;
|
||||||
return policy != null;
|
}
|
||||||
|
case Core.SecretsManager.Entities.UserServiceAccountAccessPolicy accessPolicy:
|
||||||
|
{
|
||||||
|
var entity =
|
||||||
|
Mapper.Map<UserServiceAccountAccessPolicy>(accessPolicy);
|
||||||
|
await dbContext.AddAsync(entity);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy:
|
case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy:
|
||||||
{
|
{
|
||||||
var policy = await dbContext.GroupProjectAccessPolicy
|
var entity = Mapper.Map<GroupProjectAccessPolicy>(accessPolicy);
|
||||||
.Where(c => c.GroupId == accessPolicy.GroupId &&
|
await dbContext.AddAsync(entity);
|
||||||
c.GrantedProjectId == accessPolicy.GrantedProjectId)
|
break;
|
||||||
.FirstOrDefaultAsync();
|
}
|
||||||
return policy != null;
|
case Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy accessPolicy:
|
||||||
|
{
|
||||||
|
var entity = Mapper.Map<GroupServiceAccountAccessPolicy>(accessPolicy);
|
||||||
|
await dbContext.AddAsync(entity);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy:
|
case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy:
|
||||||
{
|
{
|
||||||
var policy = await dbContext.ServiceAccountProjectAccessPolicy
|
var entity = Mapper.Map<ServiceAccountProjectAccessPolicy>(accessPolicy);
|
||||||
.Where(c => c.ServiceAccountId == accessPolicy.ServiceAccountId &&
|
await dbContext.AddAsync(entity);
|
||||||
c.GrantedProjectId == accessPolicy.GrantedProjectId)
|
break;
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
return policy != null;
|
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
return baseAccessPolicies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> AccessPolicyExists(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy)
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
switch (baseAccessPolicy)
|
||||||
|
{
|
||||||
|
case Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy:
|
||||||
|
{
|
||||||
|
var policy = await dbContext.UserProjectAccessPolicy
|
||||||
|
.Where(c => c.OrganizationUserId == accessPolicy.OrganizationUserId &&
|
||||||
|
c.GrantedProjectId == accessPolicy.GrantedProjectId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
return policy != null;
|
||||||
|
}
|
||||||
|
case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy:
|
||||||
|
{
|
||||||
|
var policy = await dbContext.GroupProjectAccessPolicy
|
||||||
|
.Where(c => c.GroupId == accessPolicy.GroupId &&
|
||||||
|
c.GrantedProjectId == accessPolicy.GrantedProjectId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
return policy != null;
|
||||||
|
}
|
||||||
|
case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy:
|
||||||
|
{
|
||||||
|
var policy = await dbContext.ServiceAccountProjectAccessPolicy
|
||||||
|
.Where(c => c.ServiceAccountId == accessPolicy.ServiceAccountId &&
|
||||||
|
c.GrantedProjectId == accessPolicy.GrantedProjectId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
return policy != null;
|
||||||
|
}
|
||||||
|
case Core.SecretsManager.Entities.UserServiceAccountAccessPolicy accessPolicy:
|
||||||
|
{
|
||||||
|
var policy = await dbContext.UserServiceAccountAccessPolicy
|
||||||
|
.Where(c => c.OrganizationUserId == accessPolicy.OrganizationUserId &&
|
||||||
|
c.GrantedServiceAccountId == accessPolicy.GrantedServiceAccountId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
return policy != null;
|
||||||
|
}
|
||||||
|
case Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy accessPolicy:
|
||||||
|
{
|
||||||
|
var policy = await dbContext.GroupServiceAccountAccessPolicy
|
||||||
|
.Where(c => c.GroupId == accessPolicy.GroupId &&
|
||||||
|
c.GrantedServiceAccountId == accessPolicy.GrantedServiceAccountId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
return policy != null;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Core.SecretsManager.Entities.BaseAccessPolicy?> GetByIdAsync(Guid id)
|
public async Task<Core.SecretsManager.Entities.BaseAccessPolicy?> GetByIdAsync(Guid id)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
{
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var entity = await dbContext.AccessPolicies.Where(ap => ap.Id == id)
|
||||||
var entity = await dbContext.AccessPolicies.Where(ap => ap.Id == id)
|
.Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
|
||||||
.Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
|
.Include(ap => ((UserProjectAccessPolicy)ap).GrantedProject)
|
||||||
.Include(ap => ((GroupProjectAccessPolicy)ap).Group)
|
.Include(ap => ((GroupProjectAccessPolicy)ap).Group)
|
||||||
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
|
.Include(ap => ((GroupProjectAccessPolicy)ap).GrantedProject)
|
||||||
.FirstOrDefaultAsync();
|
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
|
||||||
|
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).GrantedProject)
|
||||||
|
.Include(ap => ((UserServiceAccountAccessPolicy)ap).OrganizationUser.User)
|
||||||
|
.Include(ap => ((UserServiceAccountAccessPolicy)ap).GrantedServiceAccount)
|
||||||
|
.Include(ap => ((GroupServiceAccountAccessPolicy)ap).Group)
|
||||||
|
.Include(ap => ((GroupServiceAccountAccessPolicy)ap).GrantedServiceAccount)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
if (entity == null)
|
return entity == null ? null : MapToCore(entity);
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MapToCore(entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReplaceAsync(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy)
|
public async Task ReplaceAsync(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var entity = await dbContext.AccessPolicies.FindAsync(baseAccessPolicy.Id);
|
||||||
|
if (entity != null)
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
dbContext.AccessPolicies.Attach(entity);
|
||||||
var entity = await dbContext.AccessPolicies.FindAsync(baseAccessPolicy.Id);
|
entity.Write = baseAccessPolicy.Write;
|
||||||
if (entity != null)
|
entity.Read = baseAccessPolicy.Read;
|
||||||
{
|
entity.RevisionDate = baseAccessPolicy.RevisionDate;
|
||||||
dbContext.AccessPolicies.Attach(entity);
|
await dbContext.SaveChangesAsync();
|
||||||
entity.Write = baseAccessPolicy.Write;
|
|
||||||
entity.Read = baseAccessPolicy.Read;
|
|
||||||
entity.RevisionDate = baseAccessPolicy.RevisionDate;
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id)
|
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
{
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var dbContext = GetDatabaseContext(scope);
|
|
||||||
|
|
||||||
var entities = await dbContext.AccessPolicies.Where(ap =>
|
var entities = await dbContext.AccessPolicies.Where(ap =>
|
||||||
((UserProjectAccessPolicy)ap).GrantedProjectId == id ||
|
((UserProjectAccessPolicy)ap).GrantedProjectId == id ||
|
||||||
((GroupProjectAccessPolicy)ap).GrantedProjectId == id ||
|
((GroupProjectAccessPolicy)ap).GrantedProjectId == id ||
|
||||||
((ServiceAccountProjectAccessPolicy)ap).GrantedProjectId == id)
|
((ServiceAccountProjectAccessPolicy)ap).GrantedProjectId == id)
|
||||||
.Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
|
.Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
|
||||||
.Include(ap => ((GroupProjectAccessPolicy)ap).Group)
|
.Include(ap => ((GroupProjectAccessPolicy)ap).Group)
|
||||||
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
|
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
return entities.Select(MapToCore);
|
||||||
return entities.Select(MapToCore);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id)
|
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id)
|
||||||
@ -174,28 +190,51 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
|||||||
|
|
||||||
public async Task DeleteAsync(Guid id)
|
public async Task DeleteAsync(Guid id)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var entity = await dbContext.AccessPolicies.FindAsync(id);
|
||||||
|
if (entity != null)
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
dbContext.Remove(entity);
|
||||||
var entity = await dbContext.AccessPolicies.FindAsync(id);
|
await dbContext.SaveChangesAsync();
|
||||||
if (entity != null)
|
|
||||||
{
|
|
||||||
dbContext.Remove(entity);
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Core.SecretsManager.Entities.BaseAccessPolicy MapToCore(BaseAccessPolicy baseAccessPolicyEntity)
|
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByServiceAccountIdAsync(Guid id, Guid userId,
|
||||||
|
AccessClientType accessType)
|
||||||
{
|
{
|
||||||
return baseAccessPolicyEntity switch
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var query = dbContext.ServiceAccountProjectAccessPolicy.Where(ap =>
|
||||||
|
ap.ServiceAccountId == id);
|
||||||
|
|
||||||
|
query = accessType switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => query,
|
||||||
|
AccessClientType.User => query.Where(UserHasWriteAccessToProject(userId)),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(accessType), accessType, null),
|
||||||
|
};
|
||||||
|
|
||||||
|
var entities = await query
|
||||||
|
.Include(ap => ap.ServiceAccount)
|
||||||
|
.Include(ap => ap.GrantedProject)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return entities.Select(MapToCore);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Core.SecretsManager.Entities.BaseAccessPolicy MapToCore(
|
||||||
|
BaseAccessPolicy baseAccessPolicyEntity) =>
|
||||||
|
baseAccessPolicyEntity switch
|
||||||
{
|
{
|
||||||
UserProjectAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.UserProjectAccessPolicy>(ap),
|
UserProjectAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.UserProjectAccessPolicy>(ap),
|
||||||
GroupProjectAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.GroupProjectAccessPolicy>(ap),
|
GroupProjectAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.GroupProjectAccessPolicy>(ap),
|
||||||
ServiceAccountProjectAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy>(ap),
|
ServiceAccountProjectAccessPolicy ap => Mapper
|
||||||
UserServiceAccountAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.UserServiceAccountAccessPolicy>(ap),
|
.Map<Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy>(ap),
|
||||||
GroupServiceAccountAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy>(ap),
|
UserServiceAccountAccessPolicy ap =>
|
||||||
_ => throw new ArgumentException("Unsupported access policy type")
|
Mapper.Map<Core.SecretsManager.Entities.UserServiceAccountAccessPolicy>(ap),
|
||||||
|
GroupServiceAccountAccessPolicy ap => Mapper
|
||||||
|
.Map<Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy>(ap),
|
||||||
|
_ => throw new ArgumentException("Unsupported access policy type"),
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,24 @@ public class ProjectRepository : Repository<Core.SecretsManager.Entities.Project
|
|||||||
return Mapper.Map<List<Core.SecretsManager.Entities.Project>>(projects);
|
return Mapper.Map<List<Core.SecretsManager.Entities.Project>>(projects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Core.SecretsManager.Entities.Project>> GetManyByOrganizationIdWriteAccessAsync(
|
||||||
|
Guid organizationId, Guid userId, AccessClientType accessType)
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var query = dbContext.Project.Where(p => p.OrganizationId == organizationId && p.DeletedDate == null);
|
||||||
|
|
||||||
|
query = accessType switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => query,
|
||||||
|
AccessClientType.User => query.Where(UserHasWriteAccessToProject(userId)),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(accessType), accessType, null),
|
||||||
|
};
|
||||||
|
|
||||||
|
var projects = await query.OrderBy(p => p.RevisionDate).ToListAsync();
|
||||||
|
return Mapper.Map<List<Core.SecretsManager.Entities.Project>>(projects);
|
||||||
|
}
|
||||||
|
|
||||||
private static Expression<Func<Project, bool>> UserHasReadAccessToProject(Guid userId) => p =>
|
private static Expression<Func<Project, bool>> UserHasReadAccessToProject(Guid userId) => p =>
|
||||||
p.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) ||
|
p.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) ||
|
||||||
p.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read));
|
p.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read));
|
||||||
@ -56,6 +74,9 @@ public class ProjectRepository : Repository<Core.SecretsManager.Entities.Project
|
|||||||
private static Expression<Func<Project, bool>> ServiceAccountHasReadAccessToProject(Guid serviceAccountId) => p =>
|
private static Expression<Func<Project, bool>> ServiceAccountHasReadAccessToProject(Guid serviceAccountId) => p =>
|
||||||
p.ServiceAccountAccessPolicies.Any(ap => ap.ServiceAccount.Id == serviceAccountId && ap.Read);
|
p.ServiceAccountAccessPolicies.Any(ap => ap.ServiceAccount.Id == serviceAccountId && ap.Read);
|
||||||
|
|
||||||
|
private static Expression<Func<Project, bool>> ServiceAccountHasWriteAccessToProject(Guid serviceAccountId) => p =>
|
||||||
|
p.ServiceAccountAccessPolicies.Any(ap => ap.ServiceAccount.Id == serviceAccountId && ap.Write);
|
||||||
|
|
||||||
public async Task DeleteManyByIdAsync(IEnumerable<Guid> ids)
|
public async Task DeleteManyByIdAsync(IEnumerable<Guid> ids)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
@ -82,6 +103,28 @@ public class ProjectRepository : Repository<Core.SecretsManager.Entities.Project
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ServiceAccountHasReadAccessToProject(Guid id, Guid userId)
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var query = dbContext.Project
|
||||||
|
.Where(p => p.Id == id)
|
||||||
|
.Where(ServiceAccountHasReadAccessToProject(userId));
|
||||||
|
|
||||||
|
return await query.AnyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ServiceAccountHasWriteAccessToProject(Guid id, Guid userId)
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var query = dbContext.Project
|
||||||
|
.Where(p => p.Id == id)
|
||||||
|
.Where(ServiceAccountHasWriteAccessToProject(userId));
|
||||||
|
|
||||||
|
return await query.AnyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> UserHasReadAccessToProject(Guid id, Guid userId)
|
public async Task<bool> UserHasReadAccessToProject(Guid id, Guid userId)
|
||||||
{
|
{
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
@ -103,4 +146,14 @@ public class ProjectRepository : Repository<Core.SecretsManager.Entities.Project
|
|||||||
|
|
||||||
return await query.AnyAsync();
|
return await query.AnyAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Core.SecretsManager.Entities.Project>> ImportAsync(IEnumerable<Core.SecretsManager.Entities.Project> projects)
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var entities = projects.Select(p => Mapper.Map<Project>(p));
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
await GetDbSet(dbContext).AddRangeAsync(entities);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
return projects;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using AutoMapper;
|
using System.Linq.Expressions;
|
||||||
|
using AutoMapper;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Infrastructure.EntityFramework;
|
using Bit.Infrastructure.EntityFramework;
|
||||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
@ -34,18 +36,48 @@ public class SecretRepository : Repository<Core.SecretsManager.Entities.Secret,
|
|||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var secrets = await dbContext.Secret
|
var secrets = await dbContext.Secret
|
||||||
.Where(c => ids.Contains(c.Id) && c.DeletedDate == null)
|
.Where(c => ids.Contains(c.Id) && c.DeletedDate == null)
|
||||||
|
.Include(c => c.Projects)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
return Mapper.Map<List<Core.SecretsManager.Entities.Secret>>(secrets);
|
return Mapper.Map<List<Core.SecretsManager.Entities.Secret>>(secrets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Core.SecretsManager.Entities.Secret>> GetManyByOrganizationIdAsync(Guid organizationId)
|
private static Expression<Func<Secret, bool>> ServiceAccountHasReadAccessToSecret(Guid serviceAccountId) => s =>
|
||||||
|
s.Projects.Any(p =>
|
||||||
|
p.ServiceAccountAccessPolicies.Any(ap => ap.ServiceAccount.Id == serviceAccountId && ap.Read));
|
||||||
|
|
||||||
|
private static Expression<Func<Secret, bool>> UserHasReadAccessToSecret(Guid userId) => s =>
|
||||||
|
s.Projects.Any(p =>
|
||||||
|
p.UserAccessPolicies.Any(ap => ap.OrganizationUser.UserId == userId && ap.Read) ||
|
||||||
|
p.GroupAccessPolicies.Any(ap =>
|
||||||
|
ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.UserId == userId && ap.Read)));
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Core.SecretsManager.Entities.Secret>> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId, AccessClientType accessType)
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var query = dbContext.Secret.Include(c => c.Projects).Where(c => c.OrganizationId == organizationId && c.DeletedDate == null);
|
||||||
|
|
||||||
|
query = accessType switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => query,
|
||||||
|
AccessClientType.User => query.Where(UserHasReadAccessToSecret(userId)),
|
||||||
|
AccessClientType.ServiceAccount => query.Where(ServiceAccountHasReadAccessToSecret(userId)),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(accessType), accessType, null),
|
||||||
|
};
|
||||||
|
|
||||||
|
var secrets = await query.OrderBy(c => c.RevisionDate).ToListAsync();
|
||||||
|
return Mapper.Map<List<Core.SecretsManager.Entities.Secret>>(secrets);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Core.SecretsManager.Entities.Secret>> GetManyByOrganizationIdInTrashByIdsAsync(Guid organizationId, IEnumerable<Guid> ids)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var secrets = await dbContext.Secret
|
var secrets = await dbContext.Secret
|
||||||
.Where(c => c.OrganizationId == organizationId && c.DeletedDate == null)
|
.Where(s => ids.Contains(s.Id) && s.OrganizationId == organizationId && s.DeletedDate != null)
|
||||||
.Include("Projects")
|
.Include("Projects")
|
||||||
.OrderBy(c => c.RevisionDate)
|
.OrderBy(c => c.RevisionDate)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
@ -54,19 +86,42 @@ public class SecretRepository : Repository<Core.SecretsManager.Entities.Secret,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Core.SecretsManager.Entities.Secret>> GetManyByProjectIdAsync(Guid projectId)
|
public async Task<IEnumerable<Core.SecretsManager.Entities.Secret>> GetManyByOrganizationIdInTrashAsync(Guid organizationId)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var secrets = await dbContext.Secret
|
var secrets = await dbContext.Secret
|
||||||
.Where(s => s.Projects.Any(p => p.Id == projectId) && s.DeletedDate == null).Include("Projects")
|
.Where(c => c.OrganizationId == organizationId && c.DeletedDate != null)
|
||||||
.OrderBy(s => s.RevisionDate).ToListAsync();
|
.Include("Projects")
|
||||||
|
.OrderBy(c => c.RevisionDate)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
return Mapper.Map<List<Core.SecretsManager.Entities.Secret>>(secrets);
|
return Mapper.Map<List<Core.SecretsManager.Entities.Secret>>(secrets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Core.SecretsManager.Entities.Secret>> GetManyByProjectIdAsync(Guid projectId, Guid userId, AccessClientType accessType)
|
||||||
|
{
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var query = dbContext.Secret.Include(s => s.Projects)
|
||||||
|
.Where(s => s.Projects.Any(p => p.Id == projectId) && s.DeletedDate == null);
|
||||||
|
|
||||||
|
query = accessType switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => query,
|
||||||
|
AccessClientType.User => query.Where(UserHasReadAccessToSecret(userId)),
|
||||||
|
AccessClientType.ServiceAccount => query.Where(ServiceAccountHasReadAccessToSecret(userId)),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(accessType), accessType, null),
|
||||||
|
};
|
||||||
|
|
||||||
|
var secrets = await query.OrderBy(s => s.RevisionDate).ToListAsync();
|
||||||
|
return Mapper.Map<List<Core.SecretsManager.Entities.Secret>>(secrets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override async Task<Core.SecretsManager.Entities.Secret> CreateAsync(Core.SecretsManager.Entities.Secret secret)
|
public override async Task<Core.SecretsManager.Entities.Secret> CreateAsync(Core.SecretsManager.Entities.Secret secret)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
@ -92,11 +147,11 @@ public class SecretRepository : Repository<Core.SecretsManager.Entities.Secret,
|
|||||||
|
|
||||||
public async Task<Core.SecretsManager.Entities.Secret> UpdateAsync(Core.SecretsManager.Entities.Secret secret)
|
public async Task<Core.SecretsManager.Entities.Secret> UpdateAsync(Core.SecretsManager.Entities.Secret secret)
|
||||||
{
|
{
|
||||||
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var mappedEntity = Mapper.Map<Secret>(secret);
|
var mappedEntity = Mapper.Map<Secret>(secret);
|
||||||
|
|
||||||
var entity = await dbContext.Secret
|
var entity = await dbContext.Secret
|
||||||
.Include("Projects")
|
.Include("Projects")
|
||||||
.FirstAsync(s => s.Id == secret.Id);
|
.FirstAsync(s => s.Id == secret.Id);
|
||||||
@ -136,4 +191,65 @@ public class SecretRepository : Repository<Core.SecretsManager.Entities.Secret,
|
|||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task HardDeleteManyByIdAsync(IEnumerable<Guid> ids)
|
||||||
|
{
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var secrets = dbContext.Secret.Where(c => ids.Contains(c.Id));
|
||||||
|
await secrets.ForEachAsync(secret =>
|
||||||
|
{
|
||||||
|
dbContext.Attach(secret);
|
||||||
|
dbContext.Remove(secret);
|
||||||
|
});
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RestoreManyByIdAsync(IEnumerable<Guid> ids)
|
||||||
|
{
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var secrets = dbContext.Secret.Where(c => ids.Contains(c.Id));
|
||||||
|
await secrets.ForEachAsync(secret =>
|
||||||
|
{
|
||||||
|
dbContext.Attach(secret);
|
||||||
|
secret.DeletedDate = null;
|
||||||
|
});
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Core.SecretsManager.Entities.Secret>> ImportAsync(IEnumerable<Core.SecretsManager.Entities.Secret> secrets)
|
||||||
|
{
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var entities = new List<Secret>();
|
||||||
|
var projects = secrets
|
||||||
|
.SelectMany(s => s.Projects ?? Enumerable.Empty<Core.SecretsManager.Entities.Project>())
|
||||||
|
.DistinctBy(p => p.Id)
|
||||||
|
.Select(p => Mapper.Map<Project>(p))
|
||||||
|
.ToDictionary(p => p.Id, p => p);
|
||||||
|
|
||||||
|
dbContext.AttachRange(projects.Values);
|
||||||
|
|
||||||
|
foreach (var s in secrets)
|
||||||
|
{
|
||||||
|
var entity = Mapper.Map<Secret>(s);
|
||||||
|
|
||||||
|
if (s.Projects?.Count > 0)
|
||||||
|
{
|
||||||
|
entity.Projects = s.Projects.Select(p => projects[p.Id]).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
entities.Add(entity);
|
||||||
|
}
|
||||||
|
await GetDbSet(dbContext).AddRangeAsync(entities);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
return secrets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,23 @@ public class ServiceAccountRepository : Repository<Core.SecretsManager.Entities.
|
|||||||
return await query.AnyAsync();
|
return await query.AnyAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Core.SecretsManager.Entities.ServiceAccount>> GetManyByOrganizationIdWriteAccessAsync(Guid organizationId, Guid userId, AccessClientType accessType)
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var query = dbContext.ServiceAccount.Where(c => c.OrganizationId == organizationId);
|
||||||
|
|
||||||
|
query = accessType switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => query,
|
||||||
|
AccessClientType.User => query.Where(UserHasWriteAccessToServiceAccount(userId)),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(accessType), accessType, null),
|
||||||
|
};
|
||||||
|
|
||||||
|
var serviceAccounts = await query.OrderBy(c => c.RevisionDate).ToListAsync();
|
||||||
|
return Mapper.Map<List<Core.SecretsManager.Entities.ServiceAccount>>(serviceAccounts);
|
||||||
|
}
|
||||||
|
|
||||||
private static Expression<Func<ServiceAccount, bool>> UserHasReadAccessToServiceAccount(Guid userId) => sa =>
|
private static Expression<Func<ServiceAccount, bool>> UserHasReadAccessToServiceAccount(Guid userId) => sa =>
|
||||||
sa.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) ||
|
sa.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) ||
|
||||||
sa.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read));
|
sa.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read));
|
||||||
|
@ -63,8 +63,8 @@
|
|||||||
},
|
},
|
||||||
"Azure.Core": {
|
"Azure.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.24.0",
|
"resolved": "1.25.0",
|
||||||
"contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
|
"contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
@ -101,28 +101,28 @@
|
|||||||
},
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.11.0",
|
"resolved": "12.14.1",
|
||||||
"contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==",
|
"contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Common": {
|
"Azure.Storage.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.10.0",
|
"resolved": "12.13.0",
|
||||||
"contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==",
|
"contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Core": "1.22.0",
|
"Azure.Core": "1.25.0",
|
||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.9.0",
|
"resolved": "12.12.0",
|
||||||
"contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==",
|
"contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Memory.Data": "1.0.2",
|
"System.Memory.Data": "1.0.2",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
@ -144,6 +144,14 @@
|
|||||||
"System.Xml.XPath.XmlDocument": "4.3.0"
|
"System.Xml.XPath.XmlDocument": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"DnsClient": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.0",
|
||||||
|
"contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Win32.Registry": "5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Fido2": {
|
"Fido2": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "3.0.1",
|
"resolved": "3.0.1",
|
||||||
@ -2722,10 +2730,11 @@
|
|||||||
"AspNetCoreRateLimit": "[4.0.2, )",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
||||||
"Azure.Storage.Blobs": "[12.11.0, )",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "[12.9.0, )",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "[1.0.1907, )",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "[5.12.0, )",
|
"Braintree": "[5.12.0, )",
|
||||||
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "[3.0.1, )",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
|
@ -97,11 +97,11 @@ public class UsersController : Controller
|
|||||||
|
|
||||||
if (model.Active && orgUser.Status == OrganizationUserStatusType.Revoked)
|
if (model.Active && orgUser.Status == OrganizationUserStatusType.Revoked)
|
||||||
{
|
{
|
||||||
await _organizationService.RestoreUserAsync(orgUser, null, _userService);
|
await _organizationService.RestoreUserAsync(orgUser, EventSystemUser.SCIM, _userService);
|
||||||
}
|
}
|
||||||
else if (!model.Active && orgUser.Status != OrganizationUserStatusType.Revoked)
|
else if (!model.Active && orgUser.Status != OrganizationUserStatusType.Revoked)
|
||||||
{
|
{
|
||||||
await _organizationService.RevokeUserAsync(orgUser, null);
|
await _organizationService.RevokeUserAsync(orgUser, EventSystemUser.SCIM);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Have to get full details object for response model
|
// Have to get full details object for response model
|
||||||
|
@ -71,8 +71,8 @@
|
|||||||
},
|
},
|
||||||
"Azure.Core": {
|
"Azure.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.24.0",
|
"resolved": "1.25.0",
|
||||||
"contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
|
"contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
@ -109,28 +109,28 @@
|
|||||||
},
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.11.0",
|
"resolved": "12.14.1",
|
||||||
"contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==",
|
"contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Common": {
|
"Azure.Storage.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.10.0",
|
"resolved": "12.13.0",
|
||||||
"contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==",
|
"contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Core": "1.22.0",
|
"Azure.Core": "1.25.0",
|
||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.9.0",
|
"resolved": "12.12.0",
|
||||||
"contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==",
|
"contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Memory.Data": "1.0.2",
|
"System.Memory.Data": "1.0.2",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
@ -157,6 +157,14 @@
|
|||||||
"resolved": "2.0.123",
|
"resolved": "2.0.123",
|
||||||
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
||||||
},
|
},
|
||||||
|
"DnsClient": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.0",
|
||||||
|
"contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Win32.Registry": "5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Fido2": {
|
"Fido2": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "3.0.1",
|
"resolved": "3.0.1",
|
||||||
@ -2998,10 +3006,11 @@
|
|||||||
"AspNetCoreRateLimit": "[4.0.2, )",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
||||||
"Azure.Storage.Blobs": "[12.11.0, )",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "[12.9.0, )",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "[1.0.1907, )",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "[5.12.0, )",
|
"Braintree": "[5.12.0, )",
|
||||||
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "[3.0.1, )",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
|
@ -483,7 +483,7 @@ public class AccountController : Controller
|
|||||||
// Before any user creation - if Org User doesn't exist at this point - make sure there are enough seats to add one
|
// Before any user creation - if Org User doesn't exist at this point - make sure there are enough seats to add one
|
||||||
if (orgUser == null && organization.Seats.HasValue)
|
if (orgUser == null && organization.Seats.HasValue)
|
||||||
{
|
{
|
||||||
var occupiedSeats = await _organizationService.GetOccupiedSeatCount(organization);
|
var occupiedSeats = await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
||||||
var initialSeatCount = organization.Seats.Value;
|
var initialSeatCount = organization.Seats.Value;
|
||||||
var availableSeats = initialSeatCount - occupiedSeats;
|
var availableSeats = initialSeatCount - occupiedSeats;
|
||||||
var prorationDate = DateTime.UtcNow;
|
var prorationDate = DateTime.UtcNow;
|
||||||
|
@ -74,8 +74,8 @@
|
|||||||
},
|
},
|
||||||
"Azure.Core": {
|
"Azure.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.24.0",
|
"resolved": "1.25.0",
|
||||||
"contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
|
"contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
@ -112,28 +112,28 @@
|
|||||||
},
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.11.0",
|
"resolved": "12.14.1",
|
||||||
"contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==",
|
"contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Common": {
|
"Azure.Storage.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.10.0",
|
"resolved": "12.13.0",
|
||||||
"contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==",
|
"contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Core": "1.22.0",
|
"Azure.Core": "1.25.0",
|
||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.9.0",
|
"resolved": "12.12.0",
|
||||||
"contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==",
|
"contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Memory.Data": "1.0.2",
|
"System.Memory.Data": "1.0.2",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
@ -160,6 +160,14 @@
|
|||||||
"resolved": "2.0.123",
|
"resolved": "2.0.123",
|
||||||
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
||||||
},
|
},
|
||||||
|
"DnsClient": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.0",
|
||||||
|
"contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Win32.Registry": "5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Fido2": {
|
"Fido2": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "3.0.1",
|
"resolved": "3.0.1",
|
||||||
@ -2868,10 +2876,11 @@
|
|||||||
"AspNetCoreRateLimit": "[4.0.2, )",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
||||||
"Azure.Storage.Blobs": "[12.11.0, )",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "[12.9.0, )",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "[1.0.1907, )",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "[5.12.0, )",
|
"Braintree": "[5.12.0, )",
|
||||||
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "[3.0.1, )",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
using Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
using Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
||||||
|
using Bit.Commercial.Core.Test.SecretsManager.Enums;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using Bit.Test.Common.Helpers;
|
using Bit.Test.Common.Helpers;
|
||||||
@ -11,21 +14,233 @@ using Xunit;
|
|||||||
namespace Bit.Commercial.Core.Test.SecretsManager.AccessPolicies;
|
namespace Bit.Commercial.Core.Test.SecretsManager.AccessPolicies;
|
||||||
|
|
||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
|
[ProjectCustomize]
|
||||||
public class CreateAccessPoliciesCommandTests
|
public class CreateAccessPoliciesCommandTests
|
||||||
{
|
{
|
||||||
[Theory]
|
private static List<BaseAccessPolicy> MakeGrantedProjectAccessPolicies(Guid grantedProjectId, List<UserProjectAccessPolicy> userProjectAccessPolicies,
|
||||||
[BitAutoData]
|
|
||||||
public async Task CreateAsync_CallsCreate(List<UserProjectAccessPolicy> userProjectAccessPolicies,
|
|
||||||
List<GroupProjectAccessPolicy> groupProjectAccessPolicies,
|
List<GroupProjectAccessPolicy> groupProjectAccessPolicies,
|
||||||
List<ServiceAccountProjectAccessPolicy> serviceAccountProjectAccessPolicies,
|
List<ServiceAccountProjectAccessPolicy> serviceAccountProjectAccessPolicies)
|
||||||
SutProvider<CreateAccessPoliciesCommand> sutProvider)
|
|
||||||
{
|
{
|
||||||
var data = new List<BaseAccessPolicy>();
|
var data = new List<BaseAccessPolicy>();
|
||||||
|
foreach (var ap in userProjectAccessPolicies)
|
||||||
|
{
|
||||||
|
ap.GrantedProjectId = grantedProjectId;
|
||||||
|
}
|
||||||
|
foreach (var ap in groupProjectAccessPolicies)
|
||||||
|
{
|
||||||
|
ap.GrantedProjectId = grantedProjectId;
|
||||||
|
}
|
||||||
|
foreach (var ap in serviceAccountProjectAccessPolicies)
|
||||||
|
{
|
||||||
|
ap.GrantedProjectId = grantedProjectId;
|
||||||
|
}
|
||||||
data.AddRange(userProjectAccessPolicies);
|
data.AddRange(userProjectAccessPolicies);
|
||||||
data.AddRange(groupProjectAccessPolicies);
|
data.AddRange(groupProjectAccessPolicies);
|
||||||
data.AddRange(serviceAccountProjectAccessPolicies);
|
data.AddRange(serviceAccountProjectAccessPolicies);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
await sutProvider.Sut.CreateAsync(data);
|
private static List<BaseAccessPolicy> MakeGrantedServiceAccountAccessPolicies(Guid grantedServiceAccountId, List<UserServiceAccountAccessPolicy> userServiceAccountAccessPolicies,
|
||||||
|
List<GroupServiceAccountAccessPolicy> groupServiceAccountAccessPolicies)
|
||||||
|
{
|
||||||
|
var data = new List<BaseAccessPolicy>();
|
||||||
|
foreach (var ap in userServiceAccountAccessPolicies)
|
||||||
|
{
|
||||||
|
ap.GrantedServiceAccountId = grantedServiceAccountId;
|
||||||
|
}
|
||||||
|
foreach (var ap in groupServiceAccountAccessPolicies)
|
||||||
|
{
|
||||||
|
ap.GrantedServiceAccountId = grantedServiceAccountId;
|
||||||
|
}
|
||||||
|
data.AddRange(userServiceAccountAccessPolicies);
|
||||||
|
data.AddRange(groupServiceAccountAccessPolicies);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<BaseAccessPolicy> MakeDuplicate(List<BaseAccessPolicy> data, AccessPolicyType accessPolicyType)
|
||||||
|
{
|
||||||
|
switch (accessPolicyType)
|
||||||
|
{
|
||||||
|
case AccessPolicyType.UserProjectAccessPolicy:
|
||||||
|
{
|
||||||
|
var mockAccessPolicy = new UserProjectAccessPolicy
|
||||||
|
{
|
||||||
|
OrganizationUserId = Guid.NewGuid(),
|
||||||
|
GrantedProjectId = Guid.NewGuid(),
|
||||||
|
};
|
||||||
|
data.Add(mockAccessPolicy);
|
||||||
|
|
||||||
|
// Add a duplicate policy
|
||||||
|
data.Add(mockAccessPolicy);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AccessPolicyType.GroupProjectAccessPolicy:
|
||||||
|
{
|
||||||
|
var mockAccessPolicy = new GroupProjectAccessPolicy
|
||||||
|
{
|
||||||
|
GroupId = Guid.NewGuid(),
|
||||||
|
GrantedProjectId = Guid.NewGuid(),
|
||||||
|
};
|
||||||
|
data.Add(mockAccessPolicy);
|
||||||
|
|
||||||
|
// Add a duplicate policy
|
||||||
|
data.Add(mockAccessPolicy);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AccessPolicyType.ServiceAccountProjectAccessPolicy:
|
||||||
|
{
|
||||||
|
var mockAccessPolicy = new ServiceAccountProjectAccessPolicy
|
||||||
|
{
|
||||||
|
ServiceAccountId = Guid.NewGuid(),
|
||||||
|
GrantedProjectId = Guid.NewGuid(),
|
||||||
|
};
|
||||||
|
data.Add(mockAccessPolicy);
|
||||||
|
|
||||||
|
// Add a duplicate policy
|
||||||
|
data.Add(mockAccessPolicy);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AccessPolicyType.UserServiceAccountAccessPolicy:
|
||||||
|
{
|
||||||
|
var mockAccessPolicy = new UserServiceAccountAccessPolicy
|
||||||
|
{
|
||||||
|
OrganizationUserId = Guid.NewGuid(),
|
||||||
|
GrantedServiceAccountId = Guid.NewGuid(),
|
||||||
|
};
|
||||||
|
data.Add(mockAccessPolicy);
|
||||||
|
|
||||||
|
// Add a duplicate policy
|
||||||
|
data.Add(mockAccessPolicy);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AccessPolicyType.GroupServiceAccountAccessPolicy:
|
||||||
|
{
|
||||||
|
var mockAccessPolicy = new GroupServiceAccountAccessPolicy
|
||||||
|
{
|
||||||
|
GroupId = Guid.NewGuid(),
|
||||||
|
GrantedServiceAccountId = Guid.NewGuid(),
|
||||||
|
};
|
||||||
|
data.Add(mockAccessPolicy);
|
||||||
|
|
||||||
|
// Add a duplicate policy
|
||||||
|
data.Add(mockAccessPolicy);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetupPermission(SutProvider<CreateAccessPoliciesCommand> sutProvider,
|
||||||
|
PermissionType permissionType, Project project, Guid userId)
|
||||||
|
{
|
||||||
|
if (permissionType == PermissionType.RunAsUserWithPermission)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(project.Id, userId)
|
||||||
|
.Returns(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetupPermission(SutProvider<CreateAccessPoliciesCommand> sutProvider,
|
||||||
|
PermissionType permissionType, ServiceAccount serviceAccount, Guid userId)
|
||||||
|
{
|
||||||
|
if (permissionType == PermissionType.RunAsUserWithPermission)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IServiceAccountRepository>()
|
||||||
|
.UserHasWriteAccessToServiceAccount(serviceAccount.Id, userId).Returns(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task CreateMany_AlreadyExists_Throws_BadRequestException(
|
||||||
|
Guid userId,
|
||||||
|
Project project,
|
||||||
|
ServiceAccount serviceAccount,
|
||||||
|
List<UserProjectAccessPolicy> userProjectAccessPolicies,
|
||||||
|
List<GroupProjectAccessPolicy> groupProjectAccessPolicies,
|
||||||
|
List<ServiceAccountProjectAccessPolicy> serviceAccountProjectAccessPolicies,
|
||||||
|
List<UserServiceAccountAccessPolicy> userServiceAccountAccessPolicies,
|
||||||
|
List<GroupServiceAccountAccessPolicy> groupServiceAccountAccessPolicies,
|
||||||
|
SutProvider<CreateAccessPoliciesCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var data = MakeGrantedProjectAccessPolicies(project.Id, userProjectAccessPolicies, groupProjectAccessPolicies,
|
||||||
|
serviceAccountProjectAccessPolicies);
|
||||||
|
var saData = MakeGrantedServiceAccountAccessPolicies(serviceAccount.Id, userServiceAccountAccessPolicies, groupServiceAccountAccessPolicies);
|
||||||
|
data = data.Concat(saData).ToList();
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IAccessPolicyRepository>().AccessPolicyExists(Arg.Any<BaseAccessPolicy>())
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||||
|
sutProvider.Sut.CreateManyAsync(data, userId, AccessClientType.NoAccessCheck));
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().CreateManyAsync(default!);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(AccessPolicyType.UserProjectAccessPolicy)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupProjectAccessPolicy)]
|
||||||
|
[BitAutoData(AccessPolicyType.ServiceAccountProjectAccessPolicy)]
|
||||||
|
[BitAutoData(AccessPolicyType.UserServiceAccountAccessPolicy)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupServiceAccountAccessPolicy)]
|
||||||
|
public async Task CreateMany_NotUnique_ThrowsException(
|
||||||
|
AccessPolicyType accessPolicyType,
|
||||||
|
Guid userId,
|
||||||
|
Project project,
|
||||||
|
ServiceAccount serviceAccount,
|
||||||
|
List<UserProjectAccessPolicy> userProjectAccessPolicies,
|
||||||
|
List<GroupProjectAccessPolicy> groupProjectAccessPolicies,
|
||||||
|
List<ServiceAccountProjectAccessPolicy> serviceAccountProjectAccessPolicies,
|
||||||
|
List<UserServiceAccountAccessPolicy> userServiceAccountAccessPolicies,
|
||||||
|
List<GroupServiceAccountAccessPolicy> groupServiceAccountAccessPolicies,
|
||||||
|
SutProvider<CreateAccessPoliciesCommand> sutProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var data = MakeGrantedProjectAccessPolicies(project.Id, userProjectAccessPolicies, groupProjectAccessPolicies,
|
||||||
|
serviceAccountProjectAccessPolicies);
|
||||||
|
var saData = MakeGrantedServiceAccountAccessPolicies(serviceAccount.Id, userServiceAccountAccessPolicies, groupServiceAccountAccessPolicies);
|
||||||
|
data = data.Concat(saData).ToList();
|
||||||
|
data = MakeDuplicate(data, accessPolicyType);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||||
|
sutProvider.Sut.CreateManyAsync(data, userId, AccessClientType.NoAccessCheck));
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||||
|
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(PermissionType.RunAsAdmin)]
|
||||||
|
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
||||||
|
public async Task CreateMany_Success(
|
||||||
|
PermissionType permissionType,
|
||||||
|
Guid userId,
|
||||||
|
Project project,
|
||||||
|
ServiceAccount serviceAccount,
|
||||||
|
List<UserProjectAccessPolicy> userProjectAccessPolicies,
|
||||||
|
List<GroupProjectAccessPolicy> groupProjectAccessPolicies,
|
||||||
|
List<ServiceAccountProjectAccessPolicy> serviceAccountProjectAccessPolicies,
|
||||||
|
List<UserServiceAccountAccessPolicy> userServiceAccountAccessPolicies,
|
||||||
|
List<GroupServiceAccountAccessPolicy> groupServiceAccountAccessPolicies,
|
||||||
|
SutProvider<CreateAccessPoliciesCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var data = MakeGrantedProjectAccessPolicies(project.Id, userProjectAccessPolicies, groupProjectAccessPolicies,
|
||||||
|
serviceAccountProjectAccessPolicies);
|
||||||
|
var saData = MakeGrantedServiceAccountAccessPolicies(serviceAccount.Id, userServiceAccountAccessPolicies, groupServiceAccountAccessPolicies);
|
||||||
|
data = data.Concat(saData).ToList();
|
||||||
|
|
||||||
|
SetupPermission(sutProvider, permissionType, serviceAccount, userId);
|
||||||
|
SetupPermission(sutProvider, permissionType, project, userId);
|
||||||
|
|
||||||
|
if (permissionType == PermissionType.RunAsAdmin)
|
||||||
|
{
|
||||||
|
await sutProvider.Sut.CreateManyAsync(data, userId, AccessClientType.NoAccessCheck);
|
||||||
|
}
|
||||||
|
else if (permissionType == PermissionType.RunAsUserWithPermission)
|
||||||
|
{
|
||||||
|
await sutProvider.Sut.CreateManyAsync(data, userId, AccessClientType.User);
|
||||||
|
}
|
||||||
|
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||||
.CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data)));
|
.CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data)));
|
||||||
@ -33,94 +248,26 @@ public class CreateAccessPoliciesCommandTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task CreateAsync_AlreadyExists_Throws_BadRequestException(
|
public async Task CreateMany_UserWithoutPermission_Throws(
|
||||||
|
Guid userId,
|
||||||
|
Project project,
|
||||||
|
ServiceAccount serviceAccount,
|
||||||
List<UserProjectAccessPolicy> userProjectAccessPolicies,
|
List<UserProjectAccessPolicy> userProjectAccessPolicies,
|
||||||
List<GroupProjectAccessPolicy> groupProjectAccessPolicies,
|
List<GroupProjectAccessPolicy> groupProjectAccessPolicies,
|
||||||
List<ServiceAccountProjectAccessPolicy> serviceAccountProjectAccessPolicies,
|
List<ServiceAccountProjectAccessPolicy> serviceAccountProjectAccessPolicies,
|
||||||
|
List<UserServiceAccountAccessPolicy> userServiceAccountAccessPolicies,
|
||||||
|
List<GroupServiceAccountAccessPolicy> groupServiceAccountAccessPolicies,
|
||||||
SutProvider<CreateAccessPoliciesCommand> sutProvider)
|
SutProvider<CreateAccessPoliciesCommand> sutProvider)
|
||||||
{
|
{
|
||||||
var data = new List<BaseAccessPolicy>();
|
var data = MakeGrantedProjectAccessPolicies(project.Id, userProjectAccessPolicies, groupProjectAccessPolicies,
|
||||||
data.AddRange(userProjectAccessPolicies);
|
serviceAccountProjectAccessPolicies);
|
||||||
data.AddRange(groupProjectAccessPolicies);
|
var saData = MakeGrantedServiceAccountAccessPolicies(serviceAccount.Id, userServiceAccountAccessPolicies, groupServiceAccountAccessPolicies);
|
||||||
data.AddRange(serviceAccountProjectAccessPolicies);
|
data = data.Concat(saData).ToList();
|
||||||
|
|
||||||
sutProvider.GetDependency<IAccessPolicyRepository>().AccessPolicyExists(Arg.Any<BaseAccessPolicy>())
|
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||||
.Returns(true);
|
sutProvider.Sut.CreateManyAsync(data, userId, AccessClientType.User));
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.CreateAsync(data));
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||||
|
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().CreateManyAsync(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(true, false, false)]
|
|
||||||
[BitAutoData(false, true, false)]
|
|
||||||
[BitAutoData(true, true, false)]
|
|
||||||
[BitAutoData(false, false, true)]
|
|
||||||
[BitAutoData(true, false, true)]
|
|
||||||
[BitAutoData(false, true, true)]
|
|
||||||
[BitAutoData(true, true, true)]
|
|
||||||
public async Task CreateAsync_NotUnique_ThrowsException(
|
|
||||||
bool testUserPolicies,
|
|
||||||
bool testGroupPolicies,
|
|
||||||
bool testServiceAccountPolicies,
|
|
||||||
List<UserProjectAccessPolicy> userProjectAccessPolicies,
|
|
||||||
List<GroupProjectAccessPolicy> groupProjectAccessPolicies,
|
|
||||||
List<ServiceAccountProjectAccessPolicy> serviceAccountProjectAccessPolicies,
|
|
||||||
SutProvider<CreateAccessPoliciesCommand> sutProvider
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var data = new List<BaseAccessPolicy>();
|
|
||||||
data.AddRange(userProjectAccessPolicies);
|
|
||||||
data.AddRange(groupProjectAccessPolicies);
|
|
||||||
data.AddRange(serviceAccountProjectAccessPolicies);
|
|
||||||
|
|
||||||
if (testUserPolicies)
|
|
||||||
{
|
|
||||||
var mockUserPolicy = new UserProjectAccessPolicy
|
|
||||||
{
|
|
||||||
OrganizationUserId = Guid.NewGuid(),
|
|
||||||
GrantedProjectId = Guid.NewGuid()
|
|
||||||
};
|
|
||||||
data.Add(mockUserPolicy);
|
|
||||||
|
|
||||||
// Add a duplicate policy
|
|
||||||
data.Add(mockUserPolicy);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (testGroupPolicies)
|
|
||||||
{
|
|
||||||
var mockGroupPolicy = new GroupProjectAccessPolicy
|
|
||||||
{
|
|
||||||
GroupId = Guid.NewGuid(),
|
|
||||||
GrantedProjectId = Guid.NewGuid()
|
|
||||||
};
|
|
||||||
data.Add(mockGroupPolicy);
|
|
||||||
|
|
||||||
// Add a duplicate policy
|
|
||||||
data.Add(mockGroupPolicy);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (testServiceAccountPolicies)
|
|
||||||
{
|
|
||||||
var mockServiceAccountPolicy = new ServiceAccountProjectAccessPolicy
|
|
||||||
{
|
|
||||||
ServiceAccountId = Guid.NewGuid(),
|
|
||||||
GrantedProjectId = Guid.NewGuid()
|
|
||||||
};
|
|
||||||
data.Add(mockServiceAccountPolicy);
|
|
||||||
|
|
||||||
// Add a duplicate policy
|
|
||||||
data.Add(mockServiceAccountPolicy);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IAccessPolicyRepository>().AccessPolicyExists(Arg.Any<BaseAccessPolicy>())
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.CreateAsync(data));
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().CreateManyAsync(default);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
using Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
using Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
||||||
|
using Bit.Commercial.Core.Test.SecretsManager.Enums;
|
||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
@ -11,28 +15,216 @@ using Xunit;
|
|||||||
namespace Bit.Commercial.Core.Test.SecretsManager.AccessPolicies;
|
namespace Bit.Commercial.Core.Test.SecretsManager.AccessPolicies;
|
||||||
|
|
||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
|
[ProjectCustomize]
|
||||||
public class DeleteAccessPolicyCommandTests
|
public class DeleteAccessPolicyCommandTests
|
||||||
{
|
{
|
||||||
|
private static void SetupPermission(SutProvider<DeleteAccessPolicyCommand> sutProvider,
|
||||||
|
PermissionType permissionType, Project grantedProject, Guid userId)
|
||||||
|
{
|
||||||
|
switch (permissionType)
|
||||||
|
{
|
||||||
|
case PermissionType.RunAsAdmin:
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(grantedProject.OrganizationId)
|
||||||
|
.Returns(true);
|
||||||
|
break;
|
||||||
|
case PermissionType.RunAsUserWithPermission:
|
||||||
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(grantedProject.Id, userId)
|
||||||
|
.Returns(true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(permissionType), permissionType, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetupPermission(SutProvider<DeleteAccessPolicyCommand> sutProvider,
|
||||||
|
PermissionType permissionType, ServiceAccount grantedServiceAccount, Guid userId)
|
||||||
|
{
|
||||||
|
switch (permissionType)
|
||||||
|
{
|
||||||
|
case PermissionType.RunAsAdmin:
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(grantedServiceAccount.OrganizationId)
|
||||||
|
.Returns(true);
|
||||||
|
break;
|
||||||
|
case PermissionType.RunAsUserWithPermission:
|
||||||
|
sutProvider.GetDependency<IServiceAccountRepository>()
|
||||||
|
.UserHasWriteAccessToServiceAccount(grantedServiceAccount.Id, userId)
|
||||||
|
.Returns(true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(permissionType), permissionType, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BaseAccessPolicy CreatePolicyToReturn(AccessPolicyType accessPolicyType, Guid data,
|
||||||
|
Project grantedProject, Group mockGroup, ServiceAccount mockServiceAccount) =>
|
||||||
|
accessPolicyType switch
|
||||||
|
{
|
||||||
|
AccessPolicyType.UserProjectAccessPolicy => new UserProjectAccessPolicy
|
||||||
|
{
|
||||||
|
Id = data,
|
||||||
|
GrantedProjectId = grantedProject.Id,
|
||||||
|
GrantedProject = grantedProject,
|
||||||
|
},
|
||||||
|
AccessPolicyType.GroupProjectAccessPolicy => new GroupProjectAccessPolicy
|
||||||
|
{
|
||||||
|
Id = data,
|
||||||
|
GrantedProjectId = grantedProject.Id,
|
||||||
|
Group = mockGroup,
|
||||||
|
GrantedProject = grantedProject,
|
||||||
|
},
|
||||||
|
AccessPolicyType.ServiceAccountProjectAccessPolicy => new ServiceAccountProjectAccessPolicy
|
||||||
|
{
|
||||||
|
Id = data,
|
||||||
|
GrantedProjectId = grantedProject.Id,
|
||||||
|
ServiceAccount = mockServiceAccount,
|
||||||
|
GrantedProject = grantedProject,
|
||||||
|
},
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static BaseAccessPolicy CreatePolicyToReturn(AccessPolicyType accessPolicyType, Guid data,
|
||||||
|
ServiceAccount grantedServiceAccount, Group mockGroup) =>
|
||||||
|
accessPolicyType switch
|
||||||
|
{
|
||||||
|
AccessPolicyType.UserServiceAccountAccessPolicy => new UserServiceAccountAccessPolicy
|
||||||
|
{
|
||||||
|
Id = data,
|
||||||
|
GrantedServiceAccountId = grantedServiceAccount.Id,
|
||||||
|
GrantedServiceAccount = grantedServiceAccount,
|
||||||
|
},
|
||||||
|
AccessPolicyType.GroupServiceAccountAccessPolicy => new GroupServiceAccountAccessPolicy
|
||||||
|
{
|
||||||
|
Id = data,
|
||||||
|
GrantedServiceAccountId = grantedServiceAccount.Id,
|
||||||
|
Group = mockGroup,
|
||||||
|
GrantedServiceAccount = grantedServiceAccount,
|
||||||
|
},
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task DeleteAccessPolicy_Throws_NotFoundException(Guid data,
|
public async Task DeleteAccessPolicy_Throws_NotFoundException(Guid data, Guid userId,
|
||||||
SutProvider<DeleteAccessPolicyCommand> sutProvider)
|
SutProvider<DeleteAccessPolicyCommand> sutProvider)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
|
||||||
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data).ReturnsNull();
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data).ReturnsNull();
|
||||||
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAsync(data));
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAsync(data, userId));
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().DeleteAsync(default);
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().DeleteAsync(default);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task DeleteAccessPolicy_Success(Guid data,
|
public async Task DeleteAccessPolicy_SmNotEnabled_Throws_NotFoundException(Guid data, Guid userId,
|
||||||
SutProvider<DeleteAccessPolicyCommand> sutProvider)
|
SutProvider<DeleteAccessPolicyCommand> sutProvider)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data)
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(false);
|
||||||
.Returns(new UserProjectAccessPolicy { Id = data });
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data).ReturnsNull();
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteAsync(data, userId));
|
||||||
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().DeleteAsync(default);
|
||||||
|
}
|
||||||
|
|
||||||
await sutProvider.Sut.DeleteAsync(data);
|
[Theory]
|
||||||
|
[BitAutoData(AccessPolicyType.UserProjectAccessPolicy, PermissionType.RunAsAdmin)]
|
||||||
|
[BitAutoData(AccessPolicyType.UserProjectAccessPolicy, PermissionType.RunAsUserWithPermission)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupProjectAccessPolicy, PermissionType.RunAsAdmin)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupProjectAccessPolicy, PermissionType.RunAsUserWithPermission)]
|
||||||
|
[BitAutoData(AccessPolicyType.ServiceAccountProjectAccessPolicy, PermissionType.RunAsAdmin)]
|
||||||
|
[BitAutoData(AccessPolicyType.ServiceAccountProjectAccessPolicy, PermissionType.RunAsUserWithPermission)]
|
||||||
|
public async Task DeleteAccessPolicy_ProjectGrants_PermissionsCheck_Success(
|
||||||
|
AccessPolicyType accessPolicyType,
|
||||||
|
PermissionType permissionType,
|
||||||
|
Guid data,
|
||||||
|
Guid userId,
|
||||||
|
Project grantedProject,
|
||||||
|
Group mockGroup,
|
||||||
|
ServiceAccount mockServiceAccount,
|
||||||
|
SutProvider<DeleteAccessPolicyCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
|
||||||
|
var policyToReturn =
|
||||||
|
CreatePolicyToReturn(accessPolicyType, data, grantedProject, mockGroup, mockServiceAccount);
|
||||||
|
SetupPermission(sutProvider, permissionType, grantedProject, userId);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data)
|
||||||
|
.Returns(policyToReturn);
|
||||||
|
|
||||||
|
await sutProvider.Sut.DeleteAsync(data, userId);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1).DeleteAsync(Arg.Is(data));
|
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1).DeleteAsync(Arg.Is(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(AccessPolicyType.UserProjectAccessPolicy)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupProjectAccessPolicy)]
|
||||||
|
[BitAutoData(AccessPolicyType.ServiceAccountProjectAccessPolicy)]
|
||||||
|
public async Task DeleteAccessPolicy_UserProjectAccessPolicy_PermissionsCheck_ThrowsNotAuthorized(
|
||||||
|
AccessPolicyType accessPolicyType,
|
||||||
|
Guid data,
|
||||||
|
Guid userId,
|
||||||
|
Group mockGroup,
|
||||||
|
ServiceAccount mockServiceAccount,
|
||||||
|
Project grantedProject,
|
||||||
|
SutProvider<DeleteAccessPolicyCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
|
||||||
|
var policyToReturn =
|
||||||
|
CreatePolicyToReturn(accessPolicyType, data, grantedProject, mockGroup, mockServiceAccount);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data)
|
||||||
|
.Returns(policyToReturn);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||||
|
sutProvider.Sut.DeleteAsync(data, userId));
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().DeleteAsync(default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(AccessPolicyType.UserServiceAccountAccessPolicy, PermissionType.RunAsAdmin)]
|
||||||
|
[BitAutoData(AccessPolicyType.UserServiceAccountAccessPolicy, PermissionType.RunAsUserWithPermission)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupServiceAccountAccessPolicy, PermissionType.RunAsAdmin)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupServiceAccountAccessPolicy, PermissionType.RunAsUserWithPermission)]
|
||||||
|
public async Task DeleteAccessPolicy_ServiceAccountGrants_PermissionsCheck_Success(
|
||||||
|
AccessPolicyType accessPolicyType,
|
||||||
|
PermissionType permissionType,
|
||||||
|
Guid data,
|
||||||
|
Guid userId,
|
||||||
|
ServiceAccount grantedServiceAccount,
|
||||||
|
Group mockGroup,
|
||||||
|
SutProvider<DeleteAccessPolicyCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
|
||||||
|
var policyToReturn = CreatePolicyToReturn(accessPolicyType, data, grantedServiceAccount, mockGroup);
|
||||||
|
SetupPermission(sutProvider, permissionType, grantedServiceAccount, userId);
|
||||||
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data)
|
||||||
|
.Returns(policyToReturn);
|
||||||
|
|
||||||
|
await sutProvider.Sut.DeleteAsync(data, userId);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1).DeleteAsync(Arg.Is(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(AccessPolicyType.UserServiceAccountAccessPolicy)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupServiceAccountAccessPolicy)]
|
||||||
|
public async Task DeleteAccessPolicy_ServiceAccountGrants_PermissionsCheck_Throws(
|
||||||
|
AccessPolicyType accessPolicyType,
|
||||||
|
Guid data,
|
||||||
|
Guid userId,
|
||||||
|
ServiceAccount grantedServiceAccount,
|
||||||
|
Group mockGroup,
|
||||||
|
SutProvider<DeleteAccessPolicyCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
|
||||||
|
var policyToReturn = CreatePolicyToReturn(accessPolicyType, data, grantedServiceAccount, mockGroup);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data)
|
||||||
|
.Returns(policyToReturn);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||||
|
sutProvider.Sut.DeleteAsync(data, userId));
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().DeleteAsync(default);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
using Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
using Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
||||||
|
using Bit.Commercial.Core.Test.SecretsManager.Enums;
|
||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using Bit.Test.Common.Helpers;
|
using Bit.Test.Common.Helpers;
|
||||||
@ -11,30 +15,246 @@ using Xunit;
|
|||||||
namespace Bit.Commercial.Core.Test.SecretsManager.AccessPolicies;
|
namespace Bit.Commercial.Core.Test.SecretsManager.AccessPolicies;
|
||||||
|
|
||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
|
[ProjectCustomize]
|
||||||
public class UpdateAccessPolicyCommandTests
|
public class UpdateAccessPolicyCommandTests
|
||||||
{
|
{
|
||||||
|
private static void SetupPermission(SutProvider<UpdateAccessPolicyCommand> sutProvider,
|
||||||
|
PermissionType permissionType, Project grantedProject, Guid userId)
|
||||||
|
{
|
||||||
|
switch (permissionType)
|
||||||
|
{
|
||||||
|
case PermissionType.RunAsAdmin:
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(grantedProject.OrganizationId)
|
||||||
|
.Returns(true);
|
||||||
|
break;
|
||||||
|
case PermissionType.RunAsUserWithPermission:
|
||||||
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(grantedProject.Id, userId)
|
||||||
|
.Returns(true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(permissionType), permissionType, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetupPermission(SutProvider<UpdateAccessPolicyCommand> sutProvider,
|
||||||
|
PermissionType permissionType, ServiceAccount grantedServiceAccount, Guid userId)
|
||||||
|
{
|
||||||
|
switch (permissionType)
|
||||||
|
{
|
||||||
|
case PermissionType.RunAsAdmin:
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(grantedServiceAccount.OrganizationId)
|
||||||
|
.Returns(true);
|
||||||
|
break;
|
||||||
|
case PermissionType.RunAsUserWithPermission:
|
||||||
|
sutProvider.GetDependency<IServiceAccountRepository>()
|
||||||
|
.UserHasWriteAccessToServiceAccount(grantedServiceAccount.Id, userId)
|
||||||
|
.Returns(true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(permissionType), permissionType, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BaseAccessPolicy CreatePolicyToReturn(AccessPolicyType accessPolicyType,
|
||||||
|
ServiceAccount grantedServiceAccount, Guid data, Group mockGroup)
|
||||||
|
{
|
||||||
|
switch (accessPolicyType)
|
||||||
|
{
|
||||||
|
case AccessPolicyType.UserServiceAccountAccessPolicy:
|
||||||
|
return
|
||||||
|
new UserServiceAccountAccessPolicy
|
||||||
|
{
|
||||||
|
Id = data,
|
||||||
|
Read = true,
|
||||||
|
Write = true,
|
||||||
|
GrantedServiceAccountId = grantedServiceAccount.Id,
|
||||||
|
GrantedServiceAccount = grantedServiceAccount,
|
||||||
|
};
|
||||||
|
case AccessPolicyType.GroupServiceAccountAccessPolicy:
|
||||||
|
mockGroup.OrganizationId = grantedServiceAccount.OrganizationId;
|
||||||
|
return new GroupServiceAccountAccessPolicy
|
||||||
|
{
|
||||||
|
Id = data,
|
||||||
|
GrantedServiceAccountId = grantedServiceAccount.Id,
|
||||||
|
GrantedServiceAccount = grantedServiceAccount,
|
||||||
|
Read = true,
|
||||||
|
Write = true,
|
||||||
|
Group = mockGroup,
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(accessPolicyType), accessPolicyType, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BaseAccessPolicy CreatePolicyToReturn(AccessPolicyType accessPolicyType, Guid data,
|
||||||
|
Project grantedProject, Group mockGroup, ServiceAccount mockServiceAccount)
|
||||||
|
{
|
||||||
|
switch (accessPolicyType)
|
||||||
|
{
|
||||||
|
case AccessPolicyType.UserProjectAccessPolicy:
|
||||||
|
return
|
||||||
|
new UserProjectAccessPolicy
|
||||||
|
{
|
||||||
|
Id = data,
|
||||||
|
Read = true,
|
||||||
|
Write = true,
|
||||||
|
GrantedProjectId = grantedProject.Id,
|
||||||
|
GrantedProject = grantedProject,
|
||||||
|
};
|
||||||
|
case AccessPolicyType.GroupProjectAccessPolicy:
|
||||||
|
mockGroup.OrganizationId = grantedProject.OrganizationId;
|
||||||
|
return
|
||||||
|
new GroupProjectAccessPolicy
|
||||||
|
{
|
||||||
|
Id = data,
|
||||||
|
GrantedProjectId = grantedProject.Id,
|
||||||
|
Read = true,
|
||||||
|
Write = true,
|
||||||
|
Group = mockGroup,
|
||||||
|
GrantedProject = grantedProject,
|
||||||
|
};
|
||||||
|
case AccessPolicyType.ServiceAccountProjectAccessPolicy:
|
||||||
|
mockServiceAccount.OrganizationId = grantedProject.OrganizationId;
|
||||||
|
return new ServiceAccountProjectAccessPolicy
|
||||||
|
{
|
||||||
|
Id = data,
|
||||||
|
GrantedProjectId = grantedProject.Id,
|
||||||
|
Read = true,
|
||||||
|
Write = true,
|
||||||
|
ServiceAccount = mockServiceAccount,
|
||||||
|
GrantedProject = grantedProject,
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(accessPolicyType), accessPolicyType, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_Throws_NotFoundException(Guid data, bool read, bool write,
|
public async Task UpdateAsync_Throws_NotFoundException(Guid data, bool read, bool write, Guid userId,
|
||||||
SutProvider<UpdateAccessPolicyCommand> sutProvider)
|
SutProvider<UpdateAccessPolicyCommand> sutProvider)
|
||||||
{
|
{
|
||||||
var exception =
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
|
||||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(data, read, write));
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(data, read, write, userId));
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_Calls_Replace(Guid data, bool read, bool write,
|
public async Task UpdateAsync_SmNotEnabled_Throws_NotFoundException(Guid data, bool read, bool write, Guid userId,
|
||||||
SutProvider<UpdateAccessPolicyCommand> sutProvider)
|
SutProvider<UpdateAccessPolicyCommand> sutProvider)
|
||||||
{
|
{
|
||||||
var existingPolicy = new UserProjectAccessPolicy { Id = data, Read = true, Write = true };
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(false);
|
||||||
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data).Returns(existingPolicy);
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(data, read, write, userId));
|
||||||
var result = await sutProvider.Sut.UpdateAsync(data, read, write);
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1).ReplaceAsync(existingPolicy);
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(AccessPolicyType.UserProjectAccessPolicy, PermissionType.RunAsAdmin)]
|
||||||
|
[BitAutoData(AccessPolicyType.UserProjectAccessPolicy, PermissionType.RunAsUserWithPermission)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupProjectAccessPolicy, PermissionType.RunAsAdmin)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupProjectAccessPolicy, PermissionType.RunAsUserWithPermission)]
|
||||||
|
[BitAutoData(AccessPolicyType.ServiceAccountProjectAccessPolicy, PermissionType.RunAsAdmin)]
|
||||||
|
[BitAutoData(AccessPolicyType.ServiceAccountProjectAccessPolicy, PermissionType.RunAsUserWithPermission)]
|
||||||
|
public async Task UpdateAsync_ProjectGrants_PermissionsCheck_Success(
|
||||||
|
AccessPolicyType accessPolicyType,
|
||||||
|
PermissionType permissionType,
|
||||||
|
Guid data,
|
||||||
|
bool read,
|
||||||
|
bool write,
|
||||||
|
Guid userId,
|
||||||
|
Project grantedProject,
|
||||||
|
Group mockGroup,
|
||||||
|
ServiceAccount mockServiceAccount,
|
||||||
|
SutProvider<UpdateAccessPolicyCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
|
||||||
|
var policyToReturn =
|
||||||
|
CreatePolicyToReturn(accessPolicyType, data, grantedProject, mockGroup, mockServiceAccount);
|
||||||
|
SetupPermission(sutProvider, permissionType, grantedProject, userId);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data).Returns(policyToReturn);
|
||||||
|
var result = await sutProvider.Sut.UpdateAsync(data, read, write, userId);
|
||||||
|
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1).ReplaceAsync(policyToReturn);
|
||||||
|
|
||||||
AssertHelper.AssertRecent(result.RevisionDate);
|
AssertHelper.AssertRecent(result.RevisionDate);
|
||||||
Assert.Equal(read, result.Read);
|
Assert.Equal(read, result.Read);
|
||||||
Assert.Equal(write, result.Write);
|
Assert.Equal(write, result.Write);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(AccessPolicyType.UserProjectAccessPolicy)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupProjectAccessPolicy)]
|
||||||
|
[BitAutoData(AccessPolicyType.ServiceAccountProjectAccessPolicy)]
|
||||||
|
public async Task UpdateAsync_ProjectGrants_PermissionsCheck_Throws(
|
||||||
|
AccessPolicyType accessPolicyType,
|
||||||
|
Guid data,
|
||||||
|
bool read,
|
||||||
|
bool write,
|
||||||
|
Guid userId,
|
||||||
|
Project grantedProject,
|
||||||
|
Group mockGroup,
|
||||||
|
ServiceAccount mockServiceAccount,
|
||||||
|
SutProvider<UpdateAccessPolicyCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
|
||||||
|
var policyToReturn =
|
||||||
|
CreatePolicyToReturn(accessPolicyType, data, grantedProject, mockGroup, mockServiceAccount);
|
||||||
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data).Returns(policyToReturn);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||||
|
sutProvider.Sut.UpdateAsync(data, read, write, userId));
|
||||||
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(AccessPolicyType.UserServiceAccountAccessPolicy, PermissionType.RunAsAdmin)]
|
||||||
|
[BitAutoData(AccessPolicyType.UserServiceAccountAccessPolicy, PermissionType.RunAsUserWithPermission)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupServiceAccountAccessPolicy, PermissionType.RunAsAdmin)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupServiceAccountAccessPolicy, PermissionType.RunAsUserWithPermission)]
|
||||||
|
public async Task UpdateAsync_ServiceAccountGrants_PermissionsCheck_Success(
|
||||||
|
AccessPolicyType accessPolicyType,
|
||||||
|
PermissionType permissionType,
|
||||||
|
Guid data,
|
||||||
|
bool read,
|
||||||
|
bool write,
|
||||||
|
Guid userId,
|
||||||
|
ServiceAccount grantedServiceAccount,
|
||||||
|
Group mockGroup,
|
||||||
|
SutProvider<UpdateAccessPolicyCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
|
||||||
|
var policyToReturn = CreatePolicyToReturn(accessPolicyType, grantedServiceAccount, data, mockGroup);
|
||||||
|
SetupPermission(sutProvider, permissionType, grantedServiceAccount, userId);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data).Returns(policyToReturn);
|
||||||
|
var result = await sutProvider.Sut.UpdateAsync(data, read, write, userId);
|
||||||
|
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1).ReplaceAsync(policyToReturn);
|
||||||
|
|
||||||
|
AssertHelper.AssertRecent(result.RevisionDate);
|
||||||
|
Assert.Equal(read, result.Read);
|
||||||
|
Assert.Equal(write, result.Write);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(AccessPolicyType.UserServiceAccountAccessPolicy)]
|
||||||
|
[BitAutoData(AccessPolicyType.GroupServiceAccountAccessPolicy)]
|
||||||
|
public async Task UpdateAsync_ServiceAccountGrants_PermissionsCheck_Throws(
|
||||||
|
AccessPolicyType accessPolicyType,
|
||||||
|
Guid data,
|
||||||
|
bool read,
|
||||||
|
bool write,
|
||||||
|
Guid userId,
|
||||||
|
ServiceAccount grantedServiceAccount,
|
||||||
|
Group mockGroup,
|
||||||
|
SutProvider<UpdateAccessPolicyCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
|
||||||
|
var policyToReturn = CreatePolicyToReturn(accessPolicyType, grantedServiceAccount, data, mockGroup);
|
||||||
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetByIdAsync(data).Returns(policyToReturn);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||||
|
sutProvider.Sut.UpdateAsync(data, read, write, userId));
|
||||||
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
namespace Bit.Commercial.Core.Test.SecretsManager.Enums;
|
||||||
|
|
||||||
|
public enum AccessPolicyType
|
||||||
|
{
|
||||||
|
UserProjectAccessPolicy,
|
||||||
|
GroupProjectAccessPolicy,
|
||||||
|
ServiceAccountProjectAccessPolicy,
|
||||||
|
UserServiceAccountAccessPolicy,
|
||||||
|
GroupServiceAccountAccessPolicy,
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Bit.Commercial.Core.Test.SecretsManager.Enums;
|
||||||
|
|
||||||
|
public enum PermissionType
|
||||||
|
{
|
||||||
|
RunAsAdmin,
|
||||||
|
RunAsUserWithPermission,
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
using Bit.Commercial.Core.SecretsManager.Commands.Projects;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using NSubstitute;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Commercial.Core.Test.SecretsManager.Projects;
|
||||||
|
|
||||||
|
[SutProviderCustomize]
|
||||||
|
[ProjectCustomize]
|
||||||
|
public class CreateProjectCommandTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task CreateAsync_CallsCreate(Project data,
|
||||||
|
Guid userId,
|
||||||
|
SutProvider<CreateProjectCommand> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetByOrganizationAsync(Arg.Any<Guid>(), Arg.Any<Guid>())
|
||||||
|
.Returns(new OrganizationUser() { Id = userId });
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IProjectRepository>()
|
||||||
|
.CreateAsync(Arg.Any<Project>())
|
||||||
|
.Returns(data);
|
||||||
|
|
||||||
|
await sutProvider.Sut.CreateAsync(data, userId);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IProjectRepository>().Received(1)
|
||||||
|
.CreateAsync(Arg.Is(data));
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||||
|
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,7 @@
|
|||||||
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
||||||
|
using Bit.Commercial.Core.Test.SecretsManager.Enums;
|
||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Test.SecretsManager.AutoFixture.SecretsFixture;
|
using Bit.Core.Test.SecretsManager.AutoFixture.SecretsFixture;
|
||||||
@ -14,14 +17,54 @@ namespace Bit.Commercial.Core.Test.SecretsManager.Secrets;
|
|||||||
public class CreateSecretCommandTests
|
public class CreateSecretCommandTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData(PermissionType.RunAsAdmin)]
|
||||||
public async Task CreateAsync_CallsCreate(Secret data,
|
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
||||||
SutProvider<CreateSecretCommand> sutProvider)
|
public async Task CreateAsync_Success(PermissionType permissionType, Secret data,
|
||||||
|
SutProvider<CreateSecretCommand> sutProvider, Guid userId, Project mockProject)
|
||||||
{
|
{
|
||||||
await sutProvider.Sut.CreateAsync(data);
|
data.Projects = new List<Project>() { mockProject };
|
||||||
|
|
||||||
|
if (permissionType == PermissionType.RunAsAdmin)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(data.OrganizationId).Returns(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(data.OrganizationId).Returns(false);
|
||||||
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject((Guid)(data.Projects?.First().Id), userId).Returns(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
await sutProvider.Sut.CreateAsync(data, userId);
|
||||||
|
|
||||||
await sutProvider.GetDependency<ISecretRepository>().Received(1)
|
await sutProvider.GetDependency<ISecretRepository>().Received(1)
|
||||||
.CreateAsync(data);
|
.CreateAsync(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task CreateAsync_UserWithoutPermission_ThrowsNotFound(Secret data,
|
||||||
|
SutProvider<CreateSecretCommand> sutProvider, Guid userId, Project mockProject)
|
||||||
|
{
|
||||||
|
data.Projects = new List<Project>() { mockProject };
|
||||||
|
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(data.OrganizationId).Returns(false);
|
||||||
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject((Guid)(data.Projects?.First().Id), userId).Returns(false);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||||
|
sutProvider.Sut.CreateAsync(data, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task CreateAsync_NoProjects_User_ThrowsNotFound(Secret data,
|
||||||
|
SutProvider<CreateSecretCommand> sutProvider, Guid userId)
|
||||||
|
{
|
||||||
|
data.Projects = null;
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(data.OrganizationId).Returns(false);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||||
|
sutProvider.Sut.CreateAsync(data, userId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
||||||
|
using Bit.Commercial.Core.Test.SecretsManager.Enums;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using Bit.Test.Common.Helpers;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.Test.SecretsManager.Secrets;
|
namespace Bit.Commercial.Core.Test.SecretsManager.Secrets;
|
||||||
|
|
||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
|
[ProjectCustomize]
|
||||||
public class DeleteSecretCommandTests
|
public class DeleteSecretCommandTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
@ -20,7 +24,7 @@ public class DeleteSecretCommandTests
|
|||||||
{
|
{
|
||||||
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(data).Returns(new List<Secret>());
|
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(data).Returns(new List<Secret>());
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteSecrets(data));
|
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteSecrets(data, default));
|
||||||
|
|
||||||
await sutProvider.GetDependency<ISecretRepository>().DidNotReceiveWithAnyArgs().SoftDeleteManyByIdAsync(default);
|
await sutProvider.GetDependency<ISecretRepository>().DidNotReceiveWithAnyArgs().SoftDeleteManyByIdAsync(default);
|
||||||
}
|
}
|
||||||
@ -36,22 +40,39 @@ public class DeleteSecretCommandTests
|
|||||||
};
|
};
|
||||||
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(data).Returns(new List<Secret>() { secret });
|
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(data).Returns(new List<Secret>() { secret });
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteSecrets(data));
|
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteSecrets(data, default));
|
||||||
|
|
||||||
await sutProvider.GetDependency<ISecretRepository>().DidNotReceiveWithAnyArgs().SoftDeleteManyByIdAsync(default);
|
await sutProvider.GetDependency<ISecretRepository>().DidNotReceiveWithAnyArgs().SoftDeleteManyByIdAsync(default);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData(PermissionType.RunAsAdmin)]
|
||||||
public async Task DeleteSecrets_Success(List<Guid> data,
|
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
||||||
SutProvider<DeleteSecretCommand> sutProvider)
|
public async Task DeleteSecrets_Success(PermissionType permissionType, List<Guid> data,
|
||||||
|
SutProvider<DeleteSecretCommand> sutProvider, Guid userId, Guid organizationId, Project mockProject)
|
||||||
{
|
{
|
||||||
|
List<Project> projects = null;
|
||||||
|
|
||||||
|
if (permissionType == PermissionType.RunAsAdmin)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(organizationId).Returns(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(organizationId).Returns(false);
|
||||||
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(mockProject.Id, userId).Returns(true);
|
||||||
|
projects = new List<Project>() { mockProject };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var secrets = new List<Secret>();
|
var secrets = new List<Secret>();
|
||||||
foreach (Guid id in data)
|
foreach (Guid id in data)
|
||||||
{
|
{
|
||||||
var secret = new Secret()
|
var secret = new Secret()
|
||||||
{
|
{
|
||||||
Id = id
|
Id = id,
|
||||||
|
OrganizationId = organizationId,
|
||||||
|
Projects = projects
|
||||||
};
|
};
|
||||||
secrets.Add(secret);
|
secrets.Add(secret);
|
||||||
}
|
}
|
||||||
@ -59,9 +80,9 @@ public class DeleteSecretCommandTests
|
|||||||
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(data).Returns(secrets);
|
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(data).Returns(secrets);
|
||||||
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(default).ReturnsForAnyArgs(true);
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(default).ReturnsForAnyArgs(true);
|
||||||
|
|
||||||
var results = await sutProvider.Sut.DeleteSecrets(data);
|
var results = await sutProvider.Sut.DeleteSecrets(data, userId);
|
||||||
|
await sutProvider.GetDependency<ISecretRepository>().Received(1).SoftDeleteManyByIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data)));
|
||||||
|
|
||||||
await sutProvider.GetDependency<ISecretRepository>().Received(1).SoftDeleteManyByIdAsync(Arg.Is(data));
|
|
||||||
foreach (var result in results)
|
foreach (var result in results)
|
||||||
{
|
{
|
||||||
Assert.Equal("", result.Item2);
|
Assert.Equal("", result.Item2);
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
||||||
|
using Bit.Commercial.Core.Test.SecretsManager.Enums;
|
||||||
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||||
using Bit.Core.Test.SecretsManager.AutoFixture.SecretsFixture;
|
using Bit.Core.Test.SecretsManager.AutoFixture.SecretsFixture;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
@ -13,23 +16,38 @@ namespace Bit.Commercial.Core.Test.SecretsManager.Secrets;
|
|||||||
|
|
||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
[SecretCustomize]
|
[SecretCustomize]
|
||||||
|
[ProjectCustomize]
|
||||||
public class UpdateSecretCommandTests
|
public class UpdateSecretCommandTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_SecretDoesNotExist_ThrowsNotFound(Secret data, SutProvider<UpdateSecretCommand> sutProvider)
|
public async Task UpdateAsync_SecretDoesNotExist_ThrowsNotFound(Secret data, SutProvider<UpdateSecretCommand> sutProvider)
|
||||||
{
|
{
|
||||||
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(data));
|
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(data, default));
|
||||||
|
|
||||||
await sutProvider.GetDependency<ISecretRepository>().DidNotReceiveWithAnyArgs().UpdateAsync(default);
|
await sutProvider.GetDependency<ISecretRepository>().DidNotReceiveWithAnyArgs().UpdateAsync(default);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData(PermissionType.RunAsAdmin)]
|
||||||
public async Task UpdateAsync_CallsReplaceAsync(Secret data, SutProvider<UpdateSecretCommand> sutProvider)
|
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
||||||
|
public async Task UpdateAsync_Success(PermissionType permissionType, Secret data, SutProvider<UpdateSecretCommand> sutProvider, Guid userId, Project mockProject)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(data.OrganizationId).Returns(true);
|
||||||
|
|
||||||
|
if (permissionType == PermissionType.RunAsAdmin)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(data.OrganizationId).Returns(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data.Projects = new List<Project>() { mockProject };
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(data.OrganizationId).Returns(false);
|
||||||
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject((Guid)(data.Projects?.First().Id), userId).Returns(true);
|
||||||
|
}
|
||||||
|
|
||||||
sutProvider.GetDependency<ISecretRepository>().GetByIdAsync(data.Id).Returns(data);
|
sutProvider.GetDependency<ISecretRepository>().GetByIdAsync(data.Id).Returns(data);
|
||||||
await sutProvider.Sut.UpdateAsync(data);
|
await sutProvider.Sut.UpdateAsync(data, userId);
|
||||||
|
|
||||||
await sutProvider.GetDependency<ISecretRepository>().Received(1)
|
await sutProvider.GetDependency<ISecretRepository>().Received(1)
|
||||||
.UpdateAsync(data);
|
.UpdateAsync(data);
|
||||||
@ -37,11 +55,14 @@ public class UpdateSecretCommandTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_DoesNotModifyOrganizationId(Secret existingSecret, SutProvider<UpdateSecretCommand> sutProvider)
|
public async Task UpdateAsync_DoesNotModifyOrganizationId(Secret existingSecret, SutProvider<UpdateSecretCommand> sutProvider, Guid userId)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<ISecretRepository>().GetByIdAsync(existingSecret.Id).Returns(existingSecret);
|
|
||||||
|
|
||||||
var updatedOrgId = Guid.NewGuid();
|
var updatedOrgId = Guid.NewGuid();
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(existingSecret.OrganizationId).Returns(true);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(existingSecret.OrganizationId).Returns(true);
|
||||||
|
sutProvider.GetDependency<ISecretRepository>().GetByIdAsync(existingSecret.Id).Returns(existingSecret);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(updatedOrgId).Returns(true);
|
||||||
|
|
||||||
var secretUpdate = new Secret()
|
var secretUpdate = new Secret()
|
||||||
{
|
{
|
||||||
OrganizationId = updatedOrgId,
|
OrganizationId = updatedOrgId,
|
||||||
@ -49,7 +70,7 @@ public class UpdateSecretCommandTests
|
|||||||
Key = existingSecret.Key,
|
Key = existingSecret.Key,
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await sutProvider.Sut.UpdateAsync(secretUpdate);
|
var result = await sutProvider.Sut.UpdateAsync(secretUpdate, userId);
|
||||||
|
|
||||||
Assert.Equal(existingSecret.OrganizationId, result.OrganizationId);
|
Assert.Equal(existingSecret.OrganizationId, result.OrganizationId);
|
||||||
Assert.NotEqual(existingSecret.OrganizationId, updatedOrgId);
|
Assert.NotEqual(existingSecret.OrganizationId, updatedOrgId);
|
||||||
@ -57,9 +78,11 @@ public class UpdateSecretCommandTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_DoesNotModifyCreationDate(Secret existingSecret, SutProvider<UpdateSecretCommand> sutProvider)
|
public async Task UpdateAsync_DoesNotModifyCreationDate(Secret existingSecret, SutProvider<UpdateSecretCommand> sutProvider, Guid userId)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(existingSecret.OrganizationId).Returns(true);
|
||||||
sutProvider.GetDependency<ISecretRepository>().GetByIdAsync(existingSecret.Id).Returns(existingSecret);
|
sutProvider.GetDependency<ISecretRepository>().GetByIdAsync(existingSecret.Id).Returns(existingSecret);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(existingSecret.OrganizationId).Returns(true);
|
||||||
|
|
||||||
var updatedCreationDate = DateTime.UtcNow;
|
var updatedCreationDate = DateTime.UtcNow;
|
||||||
var secretUpdate = new Secret()
|
var secretUpdate = new Secret()
|
||||||
@ -67,9 +90,10 @@ public class UpdateSecretCommandTests
|
|||||||
CreationDate = updatedCreationDate,
|
CreationDate = updatedCreationDate,
|
||||||
Id = existingSecret.Id,
|
Id = existingSecret.Id,
|
||||||
Key = existingSecret.Key,
|
Key = existingSecret.Key,
|
||||||
|
OrganizationId = existingSecret.OrganizationId
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await sutProvider.Sut.UpdateAsync(secretUpdate);
|
var result = await sutProvider.Sut.UpdateAsync(secretUpdate, userId);
|
||||||
|
|
||||||
Assert.Equal(existingSecret.CreationDate, result.CreationDate);
|
Assert.Equal(existingSecret.CreationDate, result.CreationDate);
|
||||||
Assert.NotEqual(existingSecret.CreationDate, updatedCreationDate);
|
Assert.NotEqual(existingSecret.CreationDate, updatedCreationDate);
|
||||||
@ -77,9 +101,11 @@ public class UpdateSecretCommandTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_DoesNotModifyDeletionDate(Secret existingSecret, SutProvider<UpdateSecretCommand> sutProvider)
|
public async Task UpdateAsync_DoesNotModifyDeletionDate(Secret existingSecret, SutProvider<UpdateSecretCommand> sutProvider, Guid userId)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(existingSecret.OrganizationId).Returns(true);
|
||||||
sutProvider.GetDependency<ISecretRepository>().GetByIdAsync(existingSecret.Id).Returns(existingSecret);
|
sutProvider.GetDependency<ISecretRepository>().GetByIdAsync(existingSecret.Id).Returns(existingSecret);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(existingSecret.OrganizationId).Returns(true);
|
||||||
|
|
||||||
var updatedDeletionDate = DateTime.UtcNow;
|
var updatedDeletionDate = DateTime.UtcNow;
|
||||||
var secretUpdate = new Secret()
|
var secretUpdate = new Secret()
|
||||||
@ -87,9 +113,10 @@ public class UpdateSecretCommandTests
|
|||||||
DeletedDate = updatedDeletionDate,
|
DeletedDate = updatedDeletionDate,
|
||||||
Id = existingSecret.Id,
|
Id = existingSecret.Id,
|
||||||
Key = existingSecret.Key,
|
Key = existingSecret.Key,
|
||||||
|
OrganizationId = existingSecret.OrganizationId
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await sutProvider.Sut.UpdateAsync(secretUpdate);
|
var result = await sutProvider.Sut.UpdateAsync(secretUpdate, userId);
|
||||||
|
|
||||||
Assert.Equal(existingSecret.DeletedDate, result.DeletedDate);
|
Assert.Equal(existingSecret.DeletedDate, result.DeletedDate);
|
||||||
Assert.NotEqual(existingSecret.DeletedDate, updatedDeletionDate);
|
Assert.NotEqual(existingSecret.DeletedDate, updatedDeletionDate);
|
||||||
@ -98,9 +125,12 @@ public class UpdateSecretCommandTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_RevisionDateIsUpdatedToUtcNow(Secret existingSecret, SutProvider<UpdateSecretCommand> sutProvider)
|
public async Task UpdateAsync_RevisionDateIsUpdatedToUtcNow(Secret existingSecret, SutProvider<UpdateSecretCommand> sutProvider, Guid userId)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(existingSecret.OrganizationId).Returns(true);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(existingSecret.OrganizationId).Returns(true);
|
||||||
sutProvider.GetDependency<ISecretRepository>().GetByIdAsync(existingSecret.Id).Returns(existingSecret);
|
sutProvider.GetDependency<ISecretRepository>().GetByIdAsync(existingSecret.Id).Returns(existingSecret);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(existingSecret.OrganizationId).Returns(true);
|
||||||
|
|
||||||
var updatedRevisionDate = DateTime.UtcNow.AddDays(10);
|
var updatedRevisionDate = DateTime.UtcNow.AddDays(10);
|
||||||
var secretUpdate = new Secret()
|
var secretUpdate = new Secret()
|
||||||
@ -108,11 +138,12 @@ public class UpdateSecretCommandTests
|
|||||||
RevisionDate = updatedRevisionDate,
|
RevisionDate = updatedRevisionDate,
|
||||||
Id = existingSecret.Id,
|
Id = existingSecret.Id,
|
||||||
Key = existingSecret.Key,
|
Key = existingSecret.Key,
|
||||||
|
OrganizationId = existingSecret.OrganizationId
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await sutProvider.Sut.UpdateAsync(secretUpdate);
|
var result = await sutProvider.Sut.UpdateAsync(secretUpdate, userId);
|
||||||
|
|
||||||
Assert.NotEqual(existingSecret.RevisionDate, result.RevisionDate);
|
Assert.NotEqual(secretUpdate.RevisionDate, result.RevisionDate);
|
||||||
AssertHelper.AssertRecent(result.RevisionDate);
|
AssertHelper.AssertRecent(result.RevisionDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
using Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using NSubstitute;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Commercial.Core.Test.SecretsManager.ServiceAccounts;
|
||||||
|
|
||||||
|
[SutProviderCustomize]
|
||||||
|
public class RevokeAccessTokenCommandTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task RevokeAsyncAsync_Success(ServiceAccount serviceAccount, SutProvider<RevokeAccessTokensCommand> sutProvider)
|
||||||
|
{
|
||||||
|
var apiKey1 = new ApiKey
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
ServiceAccountId = serviceAccount.Id
|
||||||
|
};
|
||||||
|
|
||||||
|
var apiKey2 = new ApiKey
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
ServiceAccountId = serviceAccount.Id
|
||||||
|
};
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IApiKeyRepository>()
|
||||||
|
.GetManyByServiceAccountIdAsync(serviceAccount.Id)
|
||||||
|
.Returns(new List<ApiKey> { apiKey1, apiKey2 });
|
||||||
|
|
||||||
|
await sutProvider.Sut.RevokeAsync(serviceAccount, new List<Guid> { apiKey1.Id });
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IApiKeyRepository>().Received(1)
|
||||||
|
.DeleteManyAsync(Arg.Is<IEnumerable<ApiKey>>(arg => arg.SequenceEqual(new List<ApiKey> { apiKey1 })));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
using Bit.Commercial.Core.SecretsManager.Commands.Trash;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using NSubstitute;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Commercial.Core.Test.SecretsManager.Trash;
|
||||||
|
|
||||||
|
[SutProviderCustomize]
|
||||||
|
[ProjectCustomize]
|
||||||
|
public class EmptyTrashCommandTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task EmptyTrash_Throws_NotFoundException(Guid orgId, Secret s1, Secret s2, SutProvider<EmptyTrashCommand> sutProvider)
|
||||||
|
{
|
||||||
|
s1.DeletedDate = DateTime.Now;
|
||||||
|
|
||||||
|
var ids = new List<Guid> { s1.Id, s2.Id };
|
||||||
|
sutProvider.GetDependency<ISecretRepository>()
|
||||||
|
.GetManyByOrganizationIdInTrashByIdsAsync(orgId, ids)
|
||||||
|
.Returns(new List<Secret> { s1 });
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.EmptyTrash(orgId, ids));
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<ISecretRepository>().DidNotReceiveWithAnyArgs().RestoreManyByIdAsync(default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task EmptyTrash_Success(Guid orgId, Secret s1, Secret s2, SutProvider<EmptyTrashCommand> sutProvider)
|
||||||
|
{
|
||||||
|
s1.DeletedDate = DateTime.Now;
|
||||||
|
|
||||||
|
var ids = new List<Guid> { s1.Id, s2.Id };
|
||||||
|
sutProvider.GetDependency<ISecretRepository>()
|
||||||
|
.GetManyByOrganizationIdInTrashByIdsAsync(orgId, ids)
|
||||||
|
.Returns(new List<Secret> { s1, s2 });
|
||||||
|
|
||||||
|
await sutProvider.Sut.EmptyTrash(orgId, ids);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<ISecretRepository>().Received(1).HardDeleteManyByIdAsync(ids);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
using Bit.Commercial.Core.SecretsManager.Commands.Trash;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using NSubstitute;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Commercial.Core.Test.SecretsManager.Trash;
|
||||||
|
|
||||||
|
[SutProviderCustomize]
|
||||||
|
[ProjectCustomize]
|
||||||
|
public class RestoreTrashCommandTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task RestoreTrash_Throws_NotFoundException(Guid orgId, Secret s1, Secret s2, SutProvider<RestoreTrashCommand> sutProvider)
|
||||||
|
{
|
||||||
|
s1.DeletedDate = DateTime.Now;
|
||||||
|
|
||||||
|
var ids = new List<Guid> { s1.Id, s2.Id };
|
||||||
|
sutProvider.GetDependency<ISecretRepository>()
|
||||||
|
.GetManyByOrganizationIdInTrashByIdsAsync(orgId, ids)
|
||||||
|
.Returns(new List<Secret> { s1 });
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.RestoreTrash(orgId, ids));
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<ISecretRepository>().DidNotReceiveWithAnyArgs().RestoreManyByIdAsync(default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task RestoreTrash_Success(Guid orgId, Secret s1, Secret s2, SutProvider<RestoreTrashCommand> sutProvider)
|
||||||
|
{
|
||||||
|
s1.DeletedDate = DateTime.Now;
|
||||||
|
|
||||||
|
var ids = new List<Guid> { s1.Id, s2.Id };
|
||||||
|
sutProvider.GetDependency<ISecretRepository>()
|
||||||
|
.GetManyByOrganizationIdInTrashByIdsAsync(orgId, ids)
|
||||||
|
.Returns(new List<Secret> { s1, s2 });
|
||||||
|
|
||||||
|
await sutProvider.Sut.RestoreTrash(orgId, ids);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<ISecretRepository>().Received(1).RestoreManyByIdAsync(ids);
|
||||||
|
}
|
||||||
|
}
|
@ -105,8 +105,8 @@
|
|||||||
},
|
},
|
||||||
"Azure.Core": {
|
"Azure.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.24.0",
|
"resolved": "1.25.0",
|
||||||
"contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
|
"contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
@ -143,28 +143,28 @@
|
|||||||
},
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.11.0",
|
"resolved": "12.14.1",
|
||||||
"contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==",
|
"contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Common": {
|
"Azure.Storage.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.10.0",
|
"resolved": "12.13.0",
|
||||||
"contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==",
|
"contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Core": "1.22.0",
|
"Azure.Core": "1.25.0",
|
||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.9.0",
|
"resolved": "12.12.0",
|
||||||
"contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==",
|
"contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Memory.Data": "1.0.2",
|
"System.Memory.Data": "1.0.2",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
@ -203,6 +203,14 @@
|
|||||||
"System.Xml.XmlDocument": "4.3.0"
|
"System.Xml.XmlDocument": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"DnsClient": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.0",
|
||||||
|
"contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Win32.Registry": "5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Fare": {
|
"Fare": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.1.1",
|
"resolved": "2.1.1",
|
||||||
@ -2818,10 +2826,11 @@
|
|||||||
"AspNetCoreRateLimit": "[4.0.2, )",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
||||||
"Azure.Storage.Blobs": "[12.11.0, )",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "[12.9.0, )",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "[1.0.1907, )",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "[5.12.0, )",
|
"Braintree": "[5.12.0, )",
|
||||||
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "[3.0.1, )",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
|
@ -143,8 +143,8 @@
|
|||||||
},
|
},
|
||||||
"Azure.Core": {
|
"Azure.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.24.0",
|
"resolved": "1.25.0",
|
||||||
"contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
|
"contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
@ -181,28 +181,28 @@
|
|||||||
},
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.11.0",
|
"resolved": "12.14.1",
|
||||||
"contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==",
|
"contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Common": {
|
"Azure.Storage.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.10.0",
|
"resolved": "12.13.0",
|
||||||
"contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==",
|
"contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Core": "1.22.0",
|
"Azure.Core": "1.25.0",
|
||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.9.0",
|
"resolved": "12.12.0",
|
||||||
"contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==",
|
"contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Memory.Data": "1.0.2",
|
"System.Memory.Data": "1.0.2",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
@ -246,6 +246,14 @@
|
|||||||
"resolved": "2.0.123",
|
"resolved": "2.0.123",
|
||||||
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
||||||
},
|
},
|
||||||
|
"DnsClient": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.0",
|
||||||
|
"contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Win32.Registry": "5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Fare": {
|
"Fare": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.1.1",
|
"resolved": "2.1.1",
|
||||||
@ -3402,10 +3410,11 @@
|
|||||||
"AspNetCoreRateLimit": "[4.0.2, )",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
||||||
"Azure.Storage.Blobs": "[12.11.0, )",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "[12.9.0, )",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "[1.0.1907, )",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "[5.12.0, )",
|
"Braintree": "[5.12.0, )",
|
||||||
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "[3.0.1, )",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
|
@ -131,8 +131,8 @@
|
|||||||
},
|
},
|
||||||
"Azure.Core": {
|
"Azure.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.24.0",
|
"resolved": "1.25.0",
|
||||||
"contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
|
"contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
@ -169,28 +169,28 @@
|
|||||||
},
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.11.0",
|
"resolved": "12.14.1",
|
||||||
"contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==",
|
"contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Common": {
|
"Azure.Storage.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.10.0",
|
"resolved": "12.13.0",
|
||||||
"contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==",
|
"contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Core": "1.22.0",
|
"Azure.Core": "1.25.0",
|
||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.9.0",
|
"resolved": "12.12.0",
|
||||||
"contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==",
|
"contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Memory.Data": "1.0.2",
|
"System.Memory.Data": "1.0.2",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
@ -234,6 +234,14 @@
|
|||||||
"resolved": "2.0.123",
|
"resolved": "2.0.123",
|
||||||
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
||||||
},
|
},
|
||||||
|
"DnsClient": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.0",
|
||||||
|
"contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Win32.Registry": "5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Fare": {
|
"Fare": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.1.1",
|
"resolved": "2.1.1",
|
||||||
@ -3247,10 +3255,11 @@
|
|||||||
"AspNetCoreRateLimit": "[4.0.2, )",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
||||||
"Azure.Storage.Blobs": "[12.11.0, )",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "[12.9.0, )",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "[1.0.1907, )",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "[5.12.0, )",
|
"Braintree": "[5.12.0, )",
|
||||||
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "[3.0.1, )",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
|
@ -4,6 +4,13 @@ param (
|
|||||||
$Name
|
$Name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# DB service provider name
|
||||||
|
$service = "mysql"
|
||||||
|
|
||||||
|
Write-Output "--- Attempting to start $service service ---"
|
||||||
|
|
||||||
|
docker-compose --profile $service up -d --no-recreate
|
||||||
|
|
||||||
dotnet tool restore
|
dotnet tool restore
|
||||||
|
|
||||||
$providers = @{
|
$providers = @{
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# syntax = docker/dockerfile:1.2
|
||||||
###############################################
|
###############################################
|
||||||
# Build stage #
|
# Build stage #
|
||||||
###############################################
|
###############################################
|
||||||
@ -13,7 +14,12 @@ RUN apt-get update && apt-get install -y \
|
|||||||
WORKDIR /tmp
|
WORKDIR /tmp
|
||||||
|
|
||||||
# Download tags from 'clients' repository
|
# Download tags from 'clients' repository
|
||||||
RUN curl https://api.github.com/repos/bitwarden/clients/git/refs/tags --output tags.json
|
RUN --mount=type=secret,id=GH_PAT,target=/etc/secrets/GH_PAT if [ -e "/etc/secrets/GH_PAT" ]; then \
|
||||||
|
curl --header "Authorization: token $(cat /etc/secrets/GH_PAT)" \
|
||||||
|
https://api.github.com/repos/bitwarden/clients/git/refs/tags --output tags.json ; else \
|
||||||
|
curl https://api.github.com/repos/bitwarden/clients/git/refs/tags --output tags.json ; fi
|
||||||
|
|
||||||
|
RUN cat tags.json
|
||||||
|
|
||||||
# Grab last tag/release of the 'web' client
|
# Grab last tag/release of the 'web' client
|
||||||
RUN cat tags.json | jq -r 'last(.[] | select(.ref|test("refs/tags/web-v[0-9]{4}.[0-9]{1,2}.[0-9]+"))) | .ref | split("/")[2]' > tag.txt
|
RUN cat tags.json | jq -r 'last(.[] | select(.ref|test("refs/tags/web-v[0-9]{4}.[0-9]{1,2}.[0-9]+"))) | .ref | split("/")[2]' > tag.txt
|
||||||
|
@ -65,8 +65,8 @@
|
|||||||
},
|
},
|
||||||
"Azure.Core": {
|
"Azure.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.24.0",
|
"resolved": "1.25.0",
|
||||||
"contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
|
"contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
@ -103,28 +103,28 @@
|
|||||||
},
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.11.0",
|
"resolved": "12.14.1",
|
||||||
"contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==",
|
"contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Common": {
|
"Azure.Storage.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.10.0",
|
"resolved": "12.13.0",
|
||||||
"contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==",
|
"contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Core": "1.22.0",
|
"Azure.Core": "1.25.0",
|
||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.9.0",
|
"resolved": "12.12.0",
|
||||||
"contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==",
|
"contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Memory.Data": "1.0.2",
|
"System.Memory.Data": "1.0.2",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
@ -156,6 +156,14 @@
|
|||||||
"resolved": "2.4.3",
|
"resolved": "2.4.3",
|
||||||
"contentHash": "U2FC9Y8NyIxxU6MpFFdWWu1xwiqz/61v/Doou7kmVjpeIEMLWyiNNkzNlSE84kyJ0O1LKApuEj5z48Ow0Hi4OQ=="
|
"contentHash": "U2FC9Y8NyIxxU6MpFFdWWu1xwiqz/61v/Doou7kmVjpeIEMLWyiNNkzNlSE84kyJ0O1LKApuEj5z48Ow0Hi4OQ=="
|
||||||
},
|
},
|
||||||
|
"DnsClient": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.0",
|
||||||
|
"contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Win32.Registry": "5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Fido2": {
|
"Fido2": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "3.0.1",
|
"resolved": "3.0.1",
|
||||||
@ -2656,10 +2664,11 @@
|
|||||||
"AspNetCoreRateLimit": "[4.0.2, )",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
||||||
"Azure.Storage.Blobs": "[12.11.0, )",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "[12.9.0, )",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "[1.0.1907, )",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "[5.12.0, )",
|
"Braintree": "[5.12.0, )",
|
||||||
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "[3.0.1, )",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
|
31
src/Admin/Jobs/DeleteUnverifiedOrganizationDomainsJob.cs
Normal file
31
src/Admin/Jobs/DeleteUnverifiedOrganizationDomainsJob.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using Bit.Core;
|
||||||
|
using Bit.Core.Jobs;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Quartz;
|
||||||
|
|
||||||
|
namespace Bit.Admin.Jobs;
|
||||||
|
|
||||||
|
public class DeleteUnverifiedOrganizationDomainsJob : BaseJob
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public DeleteUnverifiedOrganizationDomainsJob(
|
||||||
|
IServiceProvider serviceProvider,
|
||||||
|
ILogger<DeleteUnverifiedOrganizationDomainsJob> logger)
|
||||||
|
: base(logger)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteUnverifiedOrganizationDomainsJob: Start");
|
||||||
|
using (var serviceScope = _serviceProvider.CreateScope())
|
||||||
|
{
|
||||||
|
var organizationDomainService =
|
||||||
|
serviceScope.ServiceProvider.GetRequiredService<IOrganizationDomainService>();
|
||||||
|
await organizationDomainService.OrganizationDomainMaintenanceAsync();
|
||||||
|
}
|
||||||
|
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteUnverifiedOrganizationDomainsJob: End");
|
||||||
|
}
|
||||||
|
}
|
@ -64,6 +64,11 @@ public class JobsHostedService : BaseJobsHostedService
|
|||||||
.StartNow()
|
.StartNow()
|
||||||
.WithCronSchedule("0 */15 * ? * *")
|
.WithCronSchedule("0 */15 * ? * *")
|
||||||
.Build();
|
.Build();
|
||||||
|
var everyDayAtTwoAmUtcTrigger = TriggerBuilder.Create()
|
||||||
|
.WithIdentity("EveryDayAtTwoAmUtcTrigger")
|
||||||
|
.StartNow()
|
||||||
|
.WithCronSchedule("0 0 2 ? * * *")
|
||||||
|
.Build();
|
||||||
|
|
||||||
var jobs = new List<Tuple<Type, ITrigger>>
|
var jobs = new List<Tuple<Type, ITrigger>>
|
||||||
{
|
{
|
||||||
@ -74,6 +79,7 @@ public class JobsHostedService : BaseJobsHostedService
|
|||||||
new Tuple<Type, ITrigger>(typeof(DeleteCiphersJob), everyDayAtMidnightUtc),
|
new Tuple<Type, ITrigger>(typeof(DeleteCiphersJob), everyDayAtMidnightUtc),
|
||||||
new Tuple<Type, ITrigger>(typeof(DatabaseExpiredSponsorshipsJob), everyMondayAtMidnightTrigger),
|
new Tuple<Type, ITrigger>(typeof(DatabaseExpiredSponsorshipsJob), everyMondayAtMidnightTrigger),
|
||||||
new Tuple<Type, ITrigger>(typeof(DeleteAuthRequestsJob), everyFifteenMinutesTrigger),
|
new Tuple<Type, ITrigger>(typeof(DeleteAuthRequestsJob), everyFifteenMinutesTrigger),
|
||||||
|
new Tuple<Type, ITrigger>(typeof(DeleteUnverifiedOrganizationDomainsJob), everyDayAtTwoAmUtcTrigger),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!_globalSettings.SelfHosted)
|
if (!_globalSettings.SelfHosted)
|
||||||
@ -98,5 +104,6 @@ public class JobsHostedService : BaseJobsHostedService
|
|||||||
services.AddTransient<DeleteSendsJob>();
|
services.AddTransient<DeleteSendsJob>();
|
||||||
services.AddTransient<DeleteCiphersJob>();
|
services.AddTransient<DeleteCiphersJob>();
|
||||||
services.AddTransient<DeleteAuthRequestsJob>();
|
services.AddTransient<DeleteAuthRequestsJob>();
|
||||||
|
services.AddTransient<DeleteUnverifiedOrganizationDomainsJob>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ public class OrganizationViewModel
|
|||||||
UserInvitedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Invited);
|
UserInvitedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Invited);
|
||||||
UserAcceptedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Accepted);
|
UserAcceptedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Accepted);
|
||||||
UserConfirmedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Confirmed);
|
UserConfirmedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Confirmed);
|
||||||
OccupiedSeatCount = orgUsers.Count(u => u.OccupiesOrganizationSeat);
|
OccupiedSeatCount = UserInvitedCount + UserAcceptedCount + UserConfirmedCount;
|
||||||
CipherCount = ciphers.Count();
|
CipherCount = ciphers.Count();
|
||||||
CollectionCount = collections.Count();
|
CollectionCount = collections.Count();
|
||||||
GroupCount = groups?.Count() ?? 0;
|
GroupCount = groups?.Count() ?? 0;
|
||||||
|
@ -94,8 +94,8 @@
|
|||||||
},
|
},
|
||||||
"Azure.Core": {
|
"Azure.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.24.0",
|
"resolved": "1.25.0",
|
||||||
"contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
|
"contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
@ -132,28 +132,28 @@
|
|||||||
},
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.11.0",
|
"resolved": "12.14.1",
|
||||||
"contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==",
|
"contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Common": {
|
"Azure.Storage.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.10.0",
|
"resolved": "12.13.0",
|
||||||
"contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==",
|
"contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Core": "1.22.0",
|
"Azure.Core": "1.25.0",
|
||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.9.0",
|
"resolved": "12.12.0",
|
||||||
"contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==",
|
"contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Memory.Data": "1.0.2",
|
"System.Memory.Data": "1.0.2",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
@ -182,21 +182,29 @@
|
|||||||
},
|
},
|
||||||
"dbup-core": {
|
"dbup-core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.5.0",
|
"resolved": "5.0.8",
|
||||||
"contentHash": "CR00QMAtHjfeMhwxFC5haoA0q4KZ5s6Y/AdZaT6oFjySik2eFEqVasuLgWSPKSiR7ti3z01BtiR7aD3nVckAsg==",
|
"contentHash": "d+3RxJDftcarp1Y7jI78HRdRWRC7VFjM+rB2CFHWDmao6OixuLrqiyEo1DeuMNrWLTR5mmE8p1YTpFOvozI9ZQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.CSharp": "4.4.0",
|
"Microsoft.CSharp": "4.7.0",
|
||||||
"System.Diagnostics.TraceSource": "4.3.0"
|
"System.Diagnostics.TraceSource": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dbup-sqlserver": {
|
"dbup-sqlserver": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.5.0",
|
"resolved": "5.0.8",
|
||||||
"contentHash": "/4hy4qmbWmtbLJGq8XCH3mtlgMld2G8rbXcjNDhqkq5y6dGZDW03OI4UsnQRxBiTQD5aYOcLuycK1dCJYhkdSw==",
|
"contentHash": "b954l5Zgj9qgHtm16SLq2qGLJ0gIZtrWdh6JHoUsCLMHYW+0K2Oevabquw447At4U6X2t4CNuy7ZLHYf/Z/8yg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Azure.Services.AppAuthentication": "1.3.1",
|
"Microsoft.Azure.Services.AppAuthentication": "1.6.2",
|
||||||
"System.Data.SqlClient": "4.6.0",
|
"Microsoft.Data.SqlClient": "5.0.1",
|
||||||
"dbup-core": "4.5.0"
|
"dbup-core": "5.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DnsClient": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.0",
|
||||||
|
"contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Win32.Registry": "5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Fido2": {
|
"Fido2": {
|
||||||
@ -494,10 +502,10 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.Azure.Services.AppAuthentication": {
|
"Microsoft.Azure.Services.AppAuthentication": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.3.1",
|
"resolved": "1.6.2",
|
||||||
"contentHash": "59CEcmUSlg5nYOzcyhdoUu+EQH4wrjCKj7dNuuPMeIjDCikAON9/KQXTQLfzfWTjDwqHIRptAAj0DTBp25lFcg==",
|
"contentHash": "rSQhTv43ionr9rWvE4vxIe/i73XR5hoBYfh7UUgdaVOGW1MZeikR9RmgaJhonTylimCcCuJvrU0zXsSIFOsTGw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.IdentityModel.Clients.ActiveDirectory": "4.3.0",
|
"Microsoft.IdentityModel.Clients.ActiveDirectory": "5.2.9",
|
||||||
"System.Diagnostics.Process": "4.3.0"
|
"System.Diagnostics.Process": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1081,15 +1089,22 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.IdentityModel.Clients.ActiveDirectory": {
|
"Microsoft.IdentityModel.Clients.ActiveDirectory": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.3.0",
|
"resolved": "5.2.9",
|
||||||
"contentHash": "IRXnTCHxwnpnGBHVnTWd8RBJk7nsBsNZVl8j20kh234bP+oBILkt+6Iw5vQg5Q+sZmALt3Oq6X3Kx7qY71XyVw==",
|
"contentHash": "WhBAG/9hWiMHIXve4ZgwXP3spRwf7kFFfejf76QA5BvumgnPp8iDkDCiJugzAcpW1YaHB526z1UVxHhVT1E5qw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"Microsoft.CSharp": "4.3.0",
|
||||||
"NETStandard.Library": "1.6.1",
|
"NETStandard.Library": "1.6.1",
|
||||||
|
"System.ComponentModel.TypeConverter": "4.3.0",
|
||||||
|
"System.Dynamic.Runtime": "4.3.0",
|
||||||
|
"System.Net.Http": "4.3.4",
|
||||||
|
"System.Private.Uri": "4.3.2",
|
||||||
|
"System.Runtime.Serialization.Formatters": "4.3.0",
|
||||||
"System.Runtime.Serialization.Json": "4.3.0",
|
"System.Runtime.Serialization.Json": "4.3.0",
|
||||||
"System.Runtime.Serialization.Primitives": "4.3.0",
|
"System.Runtime.Serialization.Primitives": "4.3.0",
|
||||||
"System.Security.Cryptography.X509Certificates": "4.3.0",
|
"System.Security.Cryptography.X509Certificates": "4.3.0",
|
||||||
"System.Security.SecureString": "4.3.0",
|
"System.Security.SecureString": "4.3.0",
|
||||||
"System.Xml.XDocument": "4.3.0"
|
"System.Xml.XDocument": "4.3.0",
|
||||||
|
"System.Xml.XmlDocument": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Microsoft.IdentityModel.JsonWebTokens": {
|
"Microsoft.IdentityModel.JsonWebTokens": {
|
||||||
@ -1513,16 +1528,6 @@
|
|||||||
"Microsoft.NETCore.Targets": "1.1.0"
|
"Microsoft.NETCore.Targets": "1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"runtime.native.System.Data.SqlClient.sni": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.5.0",
|
|
||||||
"contentHash": "AJfX7owAAkMjWQYhoml5IBfXh8UyYPjktn8pK0BFGAdKgBS7HqMz1fw5vdzfZUWfhtTPDGCjgNttt46ZyEmSjg==",
|
|
||||||
"dependencies": {
|
|
||||||
"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
|
|
||||||
"runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
|
|
||||||
"runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"runtime.native.System.IO.Compression": {
|
"runtime.native.System.IO.Compression": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.3.0",
|
"resolved": "4.3.0",
|
||||||
@ -1615,21 +1620,6 @@
|
|||||||
"resolved": "4.3.2",
|
"resolved": "4.3.2",
|
||||||
"contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg=="
|
"contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg=="
|
||||||
},
|
},
|
||||||
"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.4.0",
|
|
||||||
"contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg=="
|
|
||||||
},
|
|
||||||
"runtime.win-x64.runtime.native.System.Data.SqlClient.sni": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.4.0",
|
|
||||||
"contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ=="
|
|
||||||
},
|
|
||||||
"runtime.win-x86.runtime.native.System.Data.SqlClient.sni": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.4.0",
|
|
||||||
"contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA=="
|
|
||||||
},
|
|
||||||
"SendGrid": {
|
"SendGrid": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "9.27.0",
|
"resolved": "9.27.0",
|
||||||
@ -1904,29 +1894,69 @@
|
|||||||
},
|
},
|
||||||
"System.Collections.NonGeneric": {
|
"System.Collections.NonGeneric": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.0.1",
|
"resolved": "4.3.0",
|
||||||
"contentHash": "hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==",
|
"contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"System.Diagnostics.Debug": "4.0.11",
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
"System.Globalization": "4.0.11",
|
"System.Globalization": "4.3.0",
|
||||||
"System.Resources.ResourceManager": "4.0.1",
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
"System.Runtime": "4.1.0",
|
"System.Runtime": "4.3.0",
|
||||||
"System.Runtime.Extensions": "4.1.0",
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
"System.Threading": "4.0.11"
|
"System.Threading": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"System.Collections.Specialized": {
|
"System.Collections.Specialized": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.0.1",
|
"resolved": "4.3.0",
|
||||||
"contentHash": "/HKQyVP0yH1I0YtK7KJL/28snxHNH/bi+0lgk/+MbURF6ULhAE31MDI+NZDerNWu264YbxklXCCygISgm+HMug==",
|
"contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"System.Collections.NonGeneric": "4.0.1",
|
"System.Collections.NonGeneric": "4.3.0",
|
||||||
"System.Globalization": "4.0.11",
|
"System.Globalization": "4.3.0",
|
||||||
"System.Globalization.Extensions": "4.0.1",
|
"System.Globalization.Extensions": "4.3.0",
|
||||||
"System.Resources.ResourceManager": "4.0.1",
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
"System.Runtime": "4.1.0",
|
"System.Runtime": "4.3.0",
|
||||||
"System.Runtime.Extensions": "4.1.0",
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
"System.Threading": "4.0.11"
|
"System.Threading": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.ComponentModel": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.ComponentModel.Primitives": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.ComponentModel": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.ComponentModel.TypeConverter": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Collections.NonGeneric": "4.3.0",
|
||||||
|
"System.Collections.Specialized": "4.3.0",
|
||||||
|
"System.ComponentModel": "4.3.0",
|
||||||
|
"System.ComponentModel.Primitives": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Linq": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Extensions": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Reflection.TypeExtensions": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"System.Composition": {
|
"System.Composition": {
|
||||||
@ -2046,17 +2076,6 @@
|
|||||||
"System.Text.Encoding": "4.3.0"
|
"System.Text.Encoding": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"System.Data.SqlClient": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.6.0",
|
|
||||||
"contentHash": "gwItUWW1BMCckicFO85c8frFaMK8SGqYn5IeA3GSX4Lmid+CjXETfoHz7Uv+Vx6L0No7iRc/7cBL8gd6o9k9/g==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Win32.Registry": "4.5.0",
|
|
||||||
"System.Security.Principal.Windows": "4.5.0",
|
|
||||||
"System.Text.Encoding.CodePages": "4.5.0",
|
|
||||||
"runtime.native.System.Data.SqlClient.sni": "4.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Diagnostics.Debug": {
|
"System.Diagnostics.Debug": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.3.0",
|
"resolved": "4.3.0",
|
||||||
@ -2160,24 +2179,23 @@
|
|||||||
},
|
},
|
||||||
"System.Dynamic.Runtime": {
|
"System.Dynamic.Runtime": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.0.11",
|
"resolved": "4.3.0",
|
||||||
"contentHash": "db34f6LHYM0U0JpE+sOmjar27BnqTVkbLJhgfwMpTdgTigG/Hna3m2MYVwnFzGGKnEJk2UXFuoVTr8WUbU91/A==",
|
"contentHash": "SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"System.Collections": "4.0.11",
|
"System.Collections": "4.3.0",
|
||||||
"System.Diagnostics.Debug": "4.0.11",
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
"System.Globalization": "4.0.11",
|
"System.Linq": "4.3.0",
|
||||||
"System.Linq": "4.1.0",
|
"System.Linq.Expressions": "4.3.0",
|
||||||
"System.Linq.Expressions": "4.1.0",
|
"System.ObjectModel": "4.3.0",
|
||||||
"System.ObjectModel": "4.0.12",
|
"System.Reflection": "4.3.0",
|
||||||
"System.Reflection": "4.1.0",
|
"System.Reflection.Emit": "4.3.0",
|
||||||
"System.Reflection.Emit": "4.0.1",
|
"System.Reflection.Emit.ILGeneration": "4.3.0",
|
||||||
"System.Reflection.Emit.ILGeneration": "4.0.1",
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
"System.Reflection.Primitives": "4.0.1",
|
"System.Reflection.TypeExtensions": "4.3.0",
|
||||||
"System.Reflection.TypeExtensions": "4.1.0",
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
"System.Resources.ResourceManager": "4.0.1",
|
"System.Runtime": "4.3.0",
|
||||||
"System.Runtime": "4.1.0",
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
"System.Runtime.Extensions": "4.1.0",
|
"System.Threading": "4.3.0"
|
||||||
"System.Threading": "4.0.11"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"System.Formats.Asn1": {
|
"System.Formats.Asn1": {
|
||||||
@ -2808,6 +2826,18 @@
|
|||||||
"System.Runtime.Extensions": "4.3.0"
|
"System.Runtime.Extensions": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"System.Runtime.Serialization.Formatters": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "KT591AkTNFOTbhZlaeMVvfax3RqhH1EJlcwF50Wm7sfnBLuHiOeZRRKrr1ns3NESkM20KPZ5Ol/ueMq5vg4QoQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Serialization.Primitives": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"System.Runtime.Serialization.Json": {
|
"System.Runtime.Serialization.Json": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.3.0",
|
"resolved": "4.3.0",
|
||||||
@ -3299,7 +3329,7 @@
|
|||||||
"commercial.core": {
|
"commercial.core": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "[2023.1.0, )"
|
"Core": "[2023.2.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core": {
|
"core": {
|
||||||
@ -3310,10 +3340,11 @@
|
|||||||
"AspNetCoreRateLimit": "[4.0.2, )",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
||||||
"Azure.Storage.Blobs": "[12.11.0, )",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "[12.9.0, )",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "[1.0.1907, )",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "[5.12.0, )",
|
"Braintree": "[5.12.0, )",
|
||||||
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "[3.0.1, )",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
@ -3345,7 +3376,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "[2023.1.0, )",
|
"Core": "[2023.2.0, )",
|
||||||
"Dapper": "[2.0.123, )"
|
"Dapper": "[2.0.123, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3353,7 +3384,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[11.0.0, )",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[11.0.0, )",
|
||||||
"Core": "[2023.1.0, )",
|
"Core": "[2023.2.0, )",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "[6.0.12, )",
|
"Microsoft.EntityFrameworkCore.Relational": "[6.0.12, )",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "[6.0.12, )",
|
"Microsoft.EntityFrameworkCore.SqlServer": "[6.0.12, )",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "[6.0.12, )",
|
"Microsoft.EntityFrameworkCore.Sqlite": "[6.0.12, )",
|
||||||
@ -3365,38 +3396,38 @@
|
|||||||
"migrator": {
|
"migrator": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "[2023.1.0, )",
|
"Core": "[2023.2.0, )",
|
||||||
"Microsoft.Extensions.Logging": "[6.0.0, )",
|
"Microsoft.Extensions.Logging": "[6.0.0, )",
|
||||||
"dbup-sqlserver": "[4.5.0, )"
|
"dbup-sqlserver": "[5.0.8, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mysqlmigrations": {
|
"mysqlmigrations": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "[2023.1.0, )",
|
"Core": "[2023.2.0, )",
|
||||||
"Infrastructure.EntityFramework": "[2023.1.0, )"
|
"Infrastructure.EntityFramework": "[2023.2.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"postgresmigrations": {
|
"postgresmigrations": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "[2023.1.0, )",
|
"Core": "[2023.2.0, )",
|
||||||
"Infrastructure.EntityFramework": "[2023.1.0, )"
|
"Infrastructure.EntityFramework": "[2023.2.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "[2023.1.0, )",
|
"Core": "[2023.2.0, )",
|
||||||
"Infrastructure.Dapper": "[2023.1.0, )",
|
"Infrastructure.Dapper": "[2023.2.0, )",
|
||||||
"Infrastructure.EntityFramework": "[2023.1.0, )"
|
"Infrastructure.EntityFramework": "[2023.2.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sqlitemigrations": {
|
"sqlitemigrations": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "[2023.1.0, )",
|
"Core": "[2023.2.0, )",
|
||||||
"Infrastructure.EntityFramework": "[2023.1.0, )"
|
"Infrastructure.EntityFramework": "[2023.2.0, )"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
143
src/Api/Controllers/OrganizationDomainController.cs
Normal file
143
src/Api/Controllers/OrganizationDomainController.cs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
using Bit.Api.Models.Request;
|
||||||
|
using Bit.Api.Models.Request.Organizations;
|
||||||
|
using Bit.Api.Models.Response;
|
||||||
|
using Bit.Api.Models.Response.Organizations;
|
||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Bit.Api.Controllers;
|
||||||
|
|
||||||
|
[Route("organizations")]
|
||||||
|
[Authorize("Application")]
|
||||||
|
public class OrganizationDomainController : Controller
|
||||||
|
{
|
||||||
|
private readonly ICreateOrganizationDomainCommand _createOrganizationDomainCommand;
|
||||||
|
private readonly IVerifyOrganizationDomainCommand _verifyOrganizationDomainCommand;
|
||||||
|
private readonly IDeleteOrganizationDomainCommand _deleteOrganizationDomainCommand;
|
||||||
|
private readonly IGetOrganizationDomainByIdQuery _getOrganizationDomainByIdQuery;
|
||||||
|
private readonly IGetOrganizationDomainByOrganizationIdQuery _getOrganizationDomainByOrganizationIdQuery;
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
|
private readonly IOrganizationDomainRepository _organizationDomainRepository;
|
||||||
|
|
||||||
|
public OrganizationDomainController(
|
||||||
|
ICreateOrganizationDomainCommand createOrganizationDomainCommand,
|
||||||
|
IVerifyOrganizationDomainCommand verifyOrganizationDomainCommand,
|
||||||
|
IDeleteOrganizationDomainCommand deleteOrganizationDomainCommand,
|
||||||
|
IGetOrganizationDomainByIdQuery getOrganizationDomainByIdQuery,
|
||||||
|
IGetOrganizationDomainByOrganizationIdQuery getOrganizationDomainByOrganizationIdQuery,
|
||||||
|
ICurrentContext currentContext,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IOrganizationDomainRepository organizationDomainRepository)
|
||||||
|
{
|
||||||
|
_createOrganizationDomainCommand = createOrganizationDomainCommand;
|
||||||
|
_verifyOrganizationDomainCommand = verifyOrganizationDomainCommand;
|
||||||
|
_deleteOrganizationDomainCommand = deleteOrganizationDomainCommand;
|
||||||
|
_getOrganizationDomainByIdQuery = getOrganizationDomainByIdQuery;
|
||||||
|
_getOrganizationDomainByOrganizationIdQuery = getOrganizationDomainByOrganizationIdQuery;
|
||||||
|
_currentContext = currentContext;
|
||||||
|
_organizationRepository = organizationRepository;
|
||||||
|
_organizationDomainRepository = organizationDomainRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{orgId}/domain")]
|
||||||
|
public async Task<ListResponseModel<OrganizationDomainResponseModel>> Get(string orgId)
|
||||||
|
{
|
||||||
|
var orgIdGuid = new Guid(orgId);
|
||||||
|
await ValidateOrganizationAccessAsync(orgIdGuid);
|
||||||
|
|
||||||
|
var domains = await _getOrganizationDomainByOrganizationIdQuery
|
||||||
|
.GetDomainsByOrganizationId(orgIdGuid);
|
||||||
|
var response = domains.Select(x => new OrganizationDomainResponseModel(x)).ToList();
|
||||||
|
return new ListResponseModel<OrganizationDomainResponseModel>(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{orgId}/domain/{id}")]
|
||||||
|
public async Task<OrganizationDomainResponseModel> Get(string orgId, string id)
|
||||||
|
{
|
||||||
|
var orgIdGuid = new Guid(orgId);
|
||||||
|
var IdGuid = new Guid(id);
|
||||||
|
await ValidateOrganizationAccessAsync(orgIdGuid);
|
||||||
|
|
||||||
|
var domain = await _getOrganizationDomainByIdQuery.GetOrganizationDomainById(IdGuid);
|
||||||
|
if (domain is null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OrganizationDomainResponseModel(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("{orgId}/domain")]
|
||||||
|
public async Task<OrganizationDomainResponseModel> Post(string orgId,
|
||||||
|
[FromBody] OrganizationDomainRequestModel model)
|
||||||
|
{
|
||||||
|
var orgIdGuid = new Guid(orgId);
|
||||||
|
await ValidateOrganizationAccessAsync(orgIdGuid);
|
||||||
|
|
||||||
|
var organizationDomain = new OrganizationDomain
|
||||||
|
{
|
||||||
|
OrganizationId = orgIdGuid,
|
||||||
|
Txt = model.Txt,
|
||||||
|
DomainName = model.DomainName.ToLower()
|
||||||
|
};
|
||||||
|
|
||||||
|
var domain = await _createOrganizationDomainCommand.CreateAsync(organizationDomain);
|
||||||
|
return new OrganizationDomainResponseModel(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("{orgId}/domain/{id}/verify")]
|
||||||
|
public async Task<OrganizationDomainResponseModel> Verify(string orgId, string id)
|
||||||
|
{
|
||||||
|
var orgIdGuid = new Guid(orgId);
|
||||||
|
var idGuid = new Guid(id);
|
||||||
|
await ValidateOrganizationAccessAsync(orgIdGuid);
|
||||||
|
|
||||||
|
var domain = await _verifyOrganizationDomainCommand.VerifyOrganizationDomain(idGuid);
|
||||||
|
return new OrganizationDomainResponseModel(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{orgId}/domain/{id}")]
|
||||||
|
[HttpPost("{orgId}/domain/{id}/remove")]
|
||||||
|
public async Task RemoveDomain(string orgId, string id)
|
||||||
|
{
|
||||||
|
var orgIdGuid = new Guid(orgId);
|
||||||
|
var idGuid = new Guid(id);
|
||||||
|
await ValidateOrganizationAccessAsync(orgIdGuid);
|
||||||
|
|
||||||
|
await _deleteOrganizationDomainCommand.DeleteAsync(idGuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpPost("domain/sso/details")] // must be post to accept email cleanly
|
||||||
|
public async Task<OrganizationDomainSsoDetailsResponseModel> GetOrgDomainSsoDetails(
|
||||||
|
[FromBody] OrganizationDomainSsoDetailsRequestModel model)
|
||||||
|
{
|
||||||
|
var ssoResult = await _organizationDomainRepository.GetOrganizationDomainSsoDetailsAsync(model.Email);
|
||||||
|
if (ssoResult is null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException("Claimed org domain not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OrganizationDomainSsoDetailsResponseModel(ssoResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ValidateOrganizationAccessAsync(Guid orgIdGuid)
|
||||||
|
{
|
||||||
|
if (!await _currentContext.ManageSso(orgIdGuid))
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
|
||||||
|
if (organization == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ using Bit.Api.Models.Request.Accounts;
|
|||||||
using Bit.Api.Models.Request.Organizations;
|
using Bit.Api.Models.Request.Organizations;
|
||||||
using Bit.Api.Models.Response;
|
using Bit.Api.Models.Response;
|
||||||
using Bit.Api.Models.Response.Organizations;
|
using Bit.Api.Models.Response.Organizations;
|
||||||
|
using Bit.Api.SecretsManager;
|
||||||
using Bit.Api.Utilities;
|
using Bit.Api.Utilities;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -38,6 +39,7 @@ public class OrganizationsController : Controller
|
|||||||
private readonly IRotateOrganizationApiKeyCommand _rotateOrganizationApiKeyCommand;
|
private readonly IRotateOrganizationApiKeyCommand _rotateOrganizationApiKeyCommand;
|
||||||
private readonly ICreateOrganizationApiKeyCommand _createOrganizationApiKeyCommand;
|
private readonly ICreateOrganizationApiKeyCommand _createOrganizationApiKeyCommand;
|
||||||
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
||||||
|
private readonly IUpdateOrganizationLicenseCommand _updateOrganizationLicenseCommand;
|
||||||
private readonly ICloudGetOrganizationLicenseQuery _cloudGetOrganizationLicenseQuery;
|
private readonly ICloudGetOrganizationLicenseQuery _cloudGetOrganizationLicenseQuery;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
@ -55,6 +57,7 @@ public class OrganizationsController : Controller
|
|||||||
IRotateOrganizationApiKeyCommand rotateOrganizationApiKeyCommand,
|
IRotateOrganizationApiKeyCommand rotateOrganizationApiKeyCommand,
|
||||||
ICreateOrganizationApiKeyCommand createOrganizationApiKeyCommand,
|
ICreateOrganizationApiKeyCommand createOrganizationApiKeyCommand,
|
||||||
IOrganizationApiKeyRepository organizationApiKeyRepository,
|
IOrganizationApiKeyRepository organizationApiKeyRepository,
|
||||||
|
IUpdateOrganizationLicenseCommand updateOrganizationLicenseCommand,
|
||||||
ICloudGetOrganizationLicenseQuery cloudGetOrganizationLicenseQuery,
|
ICloudGetOrganizationLicenseQuery cloudGetOrganizationLicenseQuery,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
@ -71,6 +74,7 @@ public class OrganizationsController : Controller
|
|||||||
_rotateOrganizationApiKeyCommand = rotateOrganizationApiKeyCommand;
|
_rotateOrganizationApiKeyCommand = rotateOrganizationApiKeyCommand;
|
||||||
_createOrganizationApiKeyCommand = createOrganizationApiKeyCommand;
|
_createOrganizationApiKeyCommand = createOrganizationApiKeyCommand;
|
||||||
_organizationApiKeyRepository = organizationApiKeyRepository;
|
_organizationApiKeyRepository = organizationApiKeyRepository;
|
||||||
|
_updateOrganizationLicenseCommand = updateOrganizationLicenseCommand;
|
||||||
_cloudGetOrganizationLicenseQuery = cloudGetOrganizationLicenseQuery;
|
_cloudGetOrganizationLicenseQuery = cloudGetOrganizationLicenseQuery;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
@ -135,6 +139,7 @@ public class OrganizationsController : Controller
|
|||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OrganizationSubscriptionResponseModel(organization, subscriptionInfo);
|
return new OrganizationSubscriptionResponseModel(organization, subscriptionInfo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -255,7 +260,7 @@ public class OrganizationsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var updateBilling = !_globalSettings.SelfHosted && (model.BusinessName != organization.BusinessName ||
|
var updateBilling = !_globalSettings.SelfHosted && (model.BusinessName != organization.BusinessName ||
|
||||||
model.BillingEmail != organization.BillingEmail);
|
model.BillingEmail != organization.BillingEmail);
|
||||||
|
|
||||||
var hasRequiredPermissions = updateBilling
|
var hasRequiredPermissions = updateBilling
|
||||||
? await _currentContext.ManageBilling(orgIdGuid)
|
? await _currentContext.ManageBilling(orgIdGuid)
|
||||||
@ -304,11 +309,7 @@ public class OrganizationsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var result = await _organizationService.UpgradePlanAsync(orgIdGuid, model.ToOrganizationUpgrade());
|
var result = await _organizationService.UpgradePlanAsync(orgIdGuid, model.ToOrganizationUpgrade());
|
||||||
return new PaymentResponseModel
|
return new PaymentResponseModel { Success = result.Item1, PaymentIntentClientSecret = result.Item2 };
|
||||||
{
|
|
||||||
Success = result.Item1,
|
|
||||||
PaymentIntentClientSecret = result.Item2
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/subscription")]
|
[HttpPost("{id}/subscription")]
|
||||||
@ -335,11 +336,7 @@ public class OrganizationsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var result = await _organizationService.AdjustSeatsAsync(orgIdGuid, model.SeatAdjustment.Value);
|
var result = await _organizationService.AdjustSeatsAsync(orgIdGuid, model.SeatAdjustment.Value);
|
||||||
return new PaymentResponseModel
|
return new PaymentResponseModel { Success = true, PaymentIntentClientSecret = result };
|
||||||
{
|
|
||||||
Success = true,
|
|
||||||
PaymentIntentClientSecret = result
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/storage")]
|
[HttpPost("{id}/storage")]
|
||||||
@ -353,11 +350,7 @@ public class OrganizationsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var result = await _organizationService.AdjustStorageAsync(orgIdGuid, model.StorageGbAdjustment.Value);
|
var result = await _organizationService.AdjustStorageAsync(orgIdGuid, model.StorageGbAdjustment.Value);
|
||||||
return new PaymentResponseModel
|
return new PaymentResponseModel { Success = true, PaymentIntentClientSecret = result };
|
||||||
{
|
|
||||||
Success = true,
|
|
||||||
PaymentIntentClientSecret = result
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/verify-bank")]
|
[HttpPost("{id}/verify-bank")]
|
||||||
@ -471,7 +464,15 @@ public class OrganizationsController : Controller
|
|||||||
throw new BadRequestException("Invalid license");
|
throw new BadRequestException("Invalid license");
|
||||||
}
|
}
|
||||||
|
|
||||||
await _organizationService.UpdateLicenseAsync(new Guid(id), license);
|
var selfHostedOrganizationDetails = await _organizationRepository.GetSelfHostedOrganizationDetailsById(orgIdGuid);
|
||||||
|
if (selfHostedOrganizationDetails == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingOrganization = await _organizationRepository.GetByLicenseKeyAsync(license.LicenseKey);
|
||||||
|
|
||||||
|
await _updateOrganizationLicenseCommand.UpdateLicenseAsync(selfHostedOrganizationDetails, license, existingOrganization);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/import")]
|
[HttpPost("{id}/import")]
|
||||||
@ -548,7 +549,8 @@ public class OrganizationsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}/api-key-information/{type?}")]
|
[HttpGet("{id}/api-key-information/{type?}")]
|
||||||
public async Task<ListResponseModel<OrganizationApiKeyInformation>> ApiKeyInformation(Guid id, [FromRoute] OrganizationApiKeyType? type)
|
public async Task<ListResponseModel<OrganizationApiKeyInformation>> ApiKeyInformation(Guid id,
|
||||||
|
[FromRoute] OrganizationApiKeyType? type)
|
||||||
{
|
{
|
||||||
if (!await HasApiKeyAccessAsync(id, type))
|
if (!await HasApiKeyAccessAsync(id, type))
|
||||||
{
|
{
|
||||||
@ -577,8 +579,8 @@ public class OrganizationsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var organizationApiKey = await _getOrganizationApiKeyQuery
|
var organizationApiKey = await _getOrganizationApiKeyQuery
|
||||||
.GetOrganizationApiKeyAsync(organization.Id, model.Type) ??
|
.GetOrganizationApiKeyAsync(organization.Id, model.Type) ??
|
||||||
await _createOrganizationApiKeyCommand.CreateAsync(organization.Id, model.Type);
|
await _createOrganizationApiKeyCommand.CreateAsync(organization.Id, model.Type);
|
||||||
|
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
@ -679,7 +681,8 @@ public class OrganizationsController : Controller
|
|||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var org = await _organizationService.UpdateOrganizationKeysAsync(new Guid(id), model.PublicKey, model.EncryptedPrivateKey);
|
var org = await _organizationService.UpdateOrganizationKeysAsync(new Guid(id), model.PublicKey,
|
||||||
|
model.EncryptedPrivateKey);
|
||||||
return new OrganizationKeysResponseModel(org);
|
return new OrganizationKeysResponseModel(org);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,4 +728,34 @@ public class OrganizationsController : Controller
|
|||||||
|
|
||||||
return new OrganizationSsoResponseModel(organization, _globalSettings, ssoConfig);
|
return new OrganizationSsoResponseModel(organization, _globalSettings, ssoConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a temporary endpoint to self-enroll in secrets manager
|
||||||
|
[SecretsManager]
|
||||||
|
[SelfHosted(NotSelfHostedOnly = true)]
|
||||||
|
[HttpPost("{id}/enroll-secrets-manager")]
|
||||||
|
public async Task EnrollSecretsManager(Guid id, [FromBody] OrganizationEnrollSecretsManagerRequestModel model)
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
if (!await _currentContext.OrganizationAdmin(id))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||||
|
if (organization == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
organization.UseSecretsManager = model.Enabled;
|
||||||
|
await _organizationService.UpdateAsync(organization);
|
||||||
|
|
||||||
|
// Turn on Secrets Manager for the user
|
||||||
|
if (model.Enabled)
|
||||||
|
{
|
||||||
|
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(id, userId);
|
||||||
|
orgUser.AccessSecretsManager = true;
|
||||||
|
await _organizationUserRepository.ReplaceAsync(orgUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ public class SelfHostedOrganizationLicensesController : Controller
|
|||||||
private readonly IOrganizationService _organizationService;
|
private readonly IOrganizationService _organizationService;
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
|
private readonly IUpdateOrganizationLicenseCommand _updateOrganizationLicenseCommand;
|
||||||
|
|
||||||
public SelfHostedOrganizationLicensesController(
|
public SelfHostedOrganizationLicensesController(
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
@ -34,7 +35,8 @@ public class SelfHostedOrganizationLicensesController : Controller
|
|||||||
IOrganizationConnectionRepository organizationConnectionRepository,
|
IOrganizationConnectionRepository organizationConnectionRepository,
|
||||||
IOrganizationService organizationService,
|
IOrganizationService organizationService,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IUserService userService)
|
IUserService userService,
|
||||||
|
IUpdateOrganizationLicenseCommand updateOrganizationLicenseCommand)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_selfHostedGetOrganizationLicenseQuery = selfHostedGetOrganizationLicenseQuery;
|
_selfHostedGetOrganizationLicenseQuery = selfHostedGetOrganizationLicenseQuery;
|
||||||
@ -42,6 +44,7 @@ public class SelfHostedOrganizationLicensesController : Controller
|
|||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
|
_updateOrganizationLicenseCommand = updateOrganizationLicenseCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("")]
|
[HttpPost("")]
|
||||||
@ -79,25 +82,33 @@ public class SelfHostedOrganizationLicensesController : Controller
|
|||||||
throw new BadRequestException("Invalid license");
|
throw new BadRequestException("Invalid license");
|
||||||
}
|
}
|
||||||
|
|
||||||
await _organizationService.UpdateLicenseAsync(new Guid(id), license);
|
var selfHostedOrganizationDetails = await _organizationRepository.GetSelfHostedOrganizationDetailsById(orgIdGuid);
|
||||||
|
if (selfHostedOrganizationDetails == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentOrganization = await _organizationRepository.GetByLicenseKeyAsync(license.LicenseKey);
|
||||||
|
|
||||||
|
await _updateOrganizationLicenseCommand.UpdateLicenseAsync(selfHostedOrganizationDetails, license, currentOrganization);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/sync")]
|
[HttpPost("{id}/sync")]
|
||||||
public async Task SyncLicenseAsync(string id)
|
public async Task SyncLicenseAsync(string id)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(new Guid(id));
|
var selfHostedOrganizationDetails = await _organizationRepository.GetSelfHostedOrganizationDetailsById(new Guid(id));
|
||||||
if (organization == null)
|
if (selfHostedOrganizationDetails == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await _currentContext.OrganizationOwner(organization.Id))
|
if (!await _currentContext.OrganizationOwner(selfHostedOrganizationDetails.Id))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var billingSyncConnection =
|
var billingSyncConnection =
|
||||||
(await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organization.Id,
|
(await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(selfHostedOrganizationDetails.Id,
|
||||||
OrganizationConnectionType.CloudBillingSync)).FirstOrDefault();
|
OrganizationConnectionType.CloudBillingSync)).FirstOrDefault();
|
||||||
if (billingSyncConnection == null)
|
if (billingSyncConnection == null)
|
||||||
{
|
{
|
||||||
@ -105,9 +116,10 @@ public class SelfHostedOrganizationLicensesController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var license =
|
var license =
|
||||||
await _selfHostedGetOrganizationLicenseQuery.GetLicenseAsync(organization, billingSyncConnection);
|
await _selfHostedGetOrganizationLicenseQuery.GetLicenseAsync(selfHostedOrganizationDetails, billingSyncConnection);
|
||||||
|
var currentOrganization = await _organizationRepository.GetByLicenseKeyAsync(license.LicenseKey);
|
||||||
|
|
||||||
await _organizationService.UpdateLicenseAsync(organization.Id, license);
|
await _updateOrganizationLicenseCommand.UpdateLicenseAsync(selfHostedOrganizationDetails, license, currentOrganization);
|
||||||
|
|
||||||
var config = billingSyncConnection.GetConfig<BillingSyncConfig>();
|
var config = billingSyncConnection.GetConfig<BillingSyncConfig>();
|
||||||
config.LastLicenseSync = DateTime.Now;
|
config.LastLicenseSync = DateTime.Now;
|
||||||
|
@ -11,7 +11,6 @@ using Bit.Core.Repositories;
|
|||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Core.Utilities.Duo;
|
|
||||||
using Fido2NetLib;
|
using Fido2NetLib;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
@ -153,7 +152,7 @@ public class TwoFactorController : Controller
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
||||||
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
await duoApi.JSONApiCall("GET", "/auth/v2/check");
|
||||||
}
|
}
|
||||||
catch (DuoException)
|
catch (DuoException)
|
||||||
{
|
{
|
||||||
@ -210,7 +209,7 @@ public class TwoFactorController : Controller
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
|
||||||
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
|
await duoApi.JSONApiCall("GET", "/auth/v2/check");
|
||||||
}
|
}
|
||||||
catch (DuoException)
|
catch (DuoException)
|
||||||
{
|
{
|
||||||
@ -295,21 +294,14 @@ public class TwoFactorController : Controller
|
|||||||
if (await _verifyAuthRequestCommand
|
if (await _verifyAuthRequestCommand
|
||||||
.VerifyAuthRequestAsync(new Guid(model.AuthRequestId), model.AuthRequestAccessCode))
|
.VerifyAuthRequestAsync(new Guid(model.AuthRequestId), model.AuthRequestAccessCode))
|
||||||
{
|
{
|
||||||
var isBecauseNewDeviceLogin = await IsNewDeviceLoginAsync(user, model);
|
await _userService.SendTwoFactorEmailAsync(user);
|
||||||
|
|
||||||
await _userService.SendTwoFactorEmailAsync(user, isBecauseNewDeviceLogin);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (await _userService.VerifySecretAsync(user, model.Secret))
|
||||||
{
|
{
|
||||||
if (await _userService.VerifySecretAsync(user, model.Secret))
|
await _userService.SendTwoFactorEmailAsync(user);
|
||||||
{
|
return;
|
||||||
var isBecauseNewDeviceLogin = await IsNewDeviceLoginAsync(user, model);
|
|
||||||
|
|
||||||
await _userService.SendTwoFactorEmailAsync(user, isBecauseNewDeviceLogin);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,41 +382,18 @@ public class TwoFactorController : Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Leaving this for backwards compatibilty on clients")]
|
||||||
[HttpGet("get-device-verification-settings")]
|
[HttpGet("get-device-verification-settings")]
|
||||||
public async Task<DeviceVerificationResponseModel> GetDeviceVerificationSettings()
|
public Task<DeviceVerificationResponseModel> GetDeviceVerificationSettings()
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
return Task.FromResult(new DeviceVerificationResponseModel(false, false));
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
throw new UnauthorizedAccessException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (User.Claims.HasSsoIdP())
|
|
||||||
{
|
|
||||||
return new DeviceVerificationResponseModel(false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
var canUserEditDeviceVerificationSettings = _userService.CanEditDeviceVerificationSettings(user);
|
|
||||||
return new DeviceVerificationResponseModel(canUserEditDeviceVerificationSettings, canUserEditDeviceVerificationSettings && user.UnknownDeviceVerificationEnabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Leaving this for backwards compatibilty on clients")]
|
||||||
[HttpPut("device-verification-settings")]
|
[HttpPut("device-verification-settings")]
|
||||||
public async Task<DeviceVerificationResponseModel> PutDeviceVerificationSettings([FromBody] DeviceVerificationRequestModel model)
|
public Task<DeviceVerificationResponseModel> PutDeviceVerificationSettings([FromBody] DeviceVerificationRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
return Task.FromResult(new DeviceVerificationResponseModel(false, false));
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
throw new UnauthorizedAccessException();
|
|
||||||
}
|
|
||||||
if (!_userService.CanEditDeviceVerificationSettings(user)
|
|
||||||
|| User.Claims.HasSsoIdP())
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Can't update device verification settings");
|
|
||||||
}
|
|
||||||
|
|
||||||
model.ToUser(user);
|
|
||||||
await _userService.SaveUserAsync(user);
|
|
||||||
return new DeviceVerificationResponseModel(true, user.UnknownDeviceVerificationEnabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<User> CheckAsync(SecretVerificationRequestModel model, bool premium)
|
private async Task<User> CheckAsync(SecretVerificationRequestModel model, bool premium)
|
||||||
@ -467,17 +436,4 @@ public class TwoFactorController : Controller
|
|||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> IsNewDeviceLoginAsync(User user, TwoFactorEmailRequestModel model)
|
|
||||||
{
|
|
||||||
if (user.GetTwoFactorProvider(TwoFactorProviderType.Email) is null
|
|
||||||
&&
|
|
||||||
await _userService.Needs2FABecauseNewDeviceAsync(user, model.DeviceIdentifier, null))
|
|
||||||
{
|
|
||||||
model.ToUser(user);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,12 @@ public class JobsHostedService : BaseJobsHostedService
|
|||||||
.WithIntervalInHours(24)
|
.WithIntervalInHours(24)
|
||||||
.RepeatForever())
|
.RepeatForever())
|
||||||
.Build();
|
.Build();
|
||||||
|
var validateOrganizationDomainTrigger = TriggerBuilder.Create()
|
||||||
|
.WithIdentity("ValidateOrganizationDomainTrigger")
|
||||||
|
.StartNow()
|
||||||
|
.WithCronSchedule("0 0 * * * ?")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
var jobs = new List<Tuple<Type, ITrigger>>
|
var jobs = new List<Tuple<Type, ITrigger>>
|
||||||
{
|
{
|
||||||
@ -54,7 +60,8 @@ public class JobsHostedService : BaseJobsHostedService
|
|||||||
new Tuple<Type, ITrigger>(typeof(EmergencyAccessNotificationJob), emergencyAccessNotificationTrigger),
|
new Tuple<Type, ITrigger>(typeof(EmergencyAccessNotificationJob), emergencyAccessNotificationTrigger),
|
||||||
new Tuple<Type, ITrigger>(typeof(EmergencyAccessTimeoutJob), emergencyAccessTimeoutTrigger),
|
new Tuple<Type, ITrigger>(typeof(EmergencyAccessTimeoutJob), emergencyAccessTimeoutTrigger),
|
||||||
new Tuple<Type, ITrigger>(typeof(ValidateUsersJob), everyTopOfTheSixthHourTrigger),
|
new Tuple<Type, ITrigger>(typeof(ValidateUsersJob), everyTopOfTheSixthHourTrigger),
|
||||||
new Tuple<Type, ITrigger>(typeof(ValidateOrganizationsJob), everyTwelfthHourAndThirtyMinutesTrigger)
|
new Tuple<Type, ITrigger>(typeof(ValidateOrganizationsJob), everyTwelfthHourAndThirtyMinutesTrigger),
|
||||||
|
new Tuple<Type, ITrigger>(typeof(ValidateOrganizationDomainJob), validateOrganizationDomainTrigger),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication)
|
if (_globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication)
|
||||||
@ -78,5 +85,6 @@ public class JobsHostedService : BaseJobsHostedService
|
|||||||
services.AddTransient<EmergencyAccessTimeoutJob>();
|
services.AddTransient<EmergencyAccessTimeoutJob>();
|
||||||
services.AddTransient<ValidateUsersJob>();
|
services.AddTransient<ValidateUsersJob>();
|
||||||
services.AddTransient<ValidateOrganizationsJob>();
|
services.AddTransient<ValidateOrganizationsJob>();
|
||||||
|
services.AddTransient<ValidateOrganizationDomainJob>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
src/Api/Jobs/ValidateOrganizationDomainJob.cs
Normal file
30
src/Api/Jobs/ValidateOrganizationDomainJob.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using Bit.Core;
|
||||||
|
using Bit.Core.Jobs;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Quartz;
|
||||||
|
|
||||||
|
namespace Bit.Api.Jobs;
|
||||||
|
|
||||||
|
public class ValidateOrganizationDomainJob : BaseJob
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
public ValidateOrganizationDomainJob(
|
||||||
|
IServiceProvider serviceProvider,
|
||||||
|
ILogger<ValidateOrganizationDomainJob> logger)
|
||||||
|
: base(logger)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: ValidateOrganizationDomainJob: Start");
|
||||||
|
using (var serviceScope = _serviceProvider.CreateScope())
|
||||||
|
{
|
||||||
|
var organizationDomainService =
|
||||||
|
serviceScope.ServiceProvider.GetRequiredService<IOrganizationDomainService>();
|
||||||
|
await organizationDomainService.ValidateOrganizationsDomainAsync();
|
||||||
|
}
|
||||||
|
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: ValidateOrganizationDomainJob: End");
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,10 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Bit.Core.Entities;
|
|
||||||
|
|
||||||
namespace Bit.Api.Models.Request;
|
namespace Bit.Api.Models.Request;
|
||||||
|
|
||||||
public class DeviceVerificationRequestModel
|
public class DeviceVerificationRequestModel
|
||||||
{
|
{
|
||||||
|
[Obsolete("Leaving this for backwards compatibilty on clients")]
|
||||||
[Required]
|
[Required]
|
||||||
public bool UnknownDeviceVerificationEnabled { get; set; }
|
public bool UnknownDeviceVerificationEnabled { get; set; }
|
||||||
|
|
||||||
public User ToUser(User user)
|
|
||||||
{
|
|
||||||
user.UnknownDeviceVerificationEnabled = UnknownDeviceVerificationEnabled;
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,6 @@ public class GroupRequestModel
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
public bool? AccessAll { get; set; }
|
public bool? AccessAll { get; set; }
|
||||||
[StringLength(300)]
|
|
||||||
public string ExternalId { get; set; }
|
|
||||||
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
|
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
|
||||||
public IEnumerable<Guid> Users { get; set; }
|
public IEnumerable<Guid> Users { get; set; }
|
||||||
|
|
||||||
@ -27,7 +25,6 @@ public class GroupRequestModel
|
|||||||
{
|
{
|
||||||
existingGroup.Name = Name;
|
existingGroup.Name = Name;
|
||||||
existingGroup.AccessAll = AccessAll.Value;
|
existingGroup.AccessAll = AccessAll.Value;
|
||||||
existingGroup.ExternalId = ExternalId;
|
|
||||||
return existingGroup;
|
return existingGroup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/Api/Models/Request/OrganizationDomainRequestModel.cs
Normal file
12
src/Api/Models/Request/OrganizationDomainRequestModel.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bit.Api.Models.Request;
|
||||||
|
|
||||||
|
public class OrganizationDomainRequestModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Txt { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string DomainName { get; set; }
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bit.Api.Models.Request.Organizations;
|
||||||
|
|
||||||
|
public class OrganizationDomainSsoDetailsRequestModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
public string Email { get; set; }
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace Bit.Api.Models.Request.Organizations;
|
||||||
|
|
||||||
|
public class OrganizationEnrollSecretsManagerRequestModel
|
||||||
|
{
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
}
|
@ -12,9 +12,6 @@ public class OrganizationUpdateRequestModel
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
[StringLength(50)]
|
[StringLength(50)]
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
[Obsolete("2022-08-03 Moved to Org SSO request model, left for backwards compatability. Remove with EC-489.")]
|
|
||||||
[StringLength(50)]
|
|
||||||
public string Identifier { get; set; }
|
|
||||||
[EmailAddress]
|
[EmailAddress]
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(256)]
|
[StringLength(256)]
|
||||||
@ -31,7 +28,6 @@ public class OrganizationUpdateRequestModel
|
|||||||
existingOrganization.BusinessName = BusinessName;
|
existingOrganization.BusinessName = BusinessName;
|
||||||
existingOrganization.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
|
existingOrganization.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
|
||||||
}
|
}
|
||||||
existingOrganization.Identifier = Identifier;
|
|
||||||
Keys?.ToOrganization(existingOrganization);
|
Keys?.ToOrganization(existingOrganization);
|
||||||
return existingOrganization;
|
return existingOrganization;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ using Bit.Api.Models.Request.Accounts;
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models;
|
using Bit.Core.Models;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Fido2NetLib;
|
using Fido2NetLib;
|
||||||
|
|
||||||
namespace Bit.Api.Models.Request;
|
namespace Bit.Api.Models.Request;
|
||||||
@ -104,7 +105,7 @@ public class UpdateTwoFactorDuoRequestModel : SecretVerificationRequestModel, IV
|
|||||||
|
|
||||||
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if (!Core.Utilities.Duo.DuoApi.ValidHost(Host))
|
if (!DuoApi.ValidHost(Host))
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("Host is invalid.", new string[] { nameof(Host) });
|
yield return new ValidationResult("Host is invalid.", new string[] { nameof(Host) });
|
||||||
}
|
}
|
||||||
@ -202,8 +203,6 @@ public class TwoFactorEmailRequestModel : SecretVerificationRequestModel
|
|||||||
[StringLength(256)]
|
[StringLength(256)]
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
|
|
||||||
public string DeviceIdentifier { get; set; }
|
|
||||||
|
|
||||||
public string AuthRequestId { get; set; }
|
public string AuthRequestId { get; set; }
|
||||||
|
|
||||||
public User ToUser(User extistingUser)
|
public User ToUser(User extistingUser)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Bit.Api.Models.Response;
|
namespace Bit.Api.Models.Response;
|
||||||
|
|
||||||
|
[Obsolete("Leaving this for backwards compatibilty on clients")]
|
||||||
public class DeviceVerificationResponseModel : ResponseModel
|
public class DeviceVerificationResponseModel : ResponseModel
|
||||||
{
|
{
|
||||||
public DeviceVerificationResponseModel(bool isDeviceVerificationSectionEnabled, bool unknownDeviceVerificationEnabled)
|
public DeviceVerificationResponseModel(bool isDeviceVerificationSectionEnabled, bool unknownDeviceVerificationEnabled)
|
||||||
|
@ -31,6 +31,9 @@ public class EventResponseModel : ResponseModel
|
|||||||
IpAddress = ev.IpAddress;
|
IpAddress = ev.IpAddress;
|
||||||
InstallationId = ev.InstallationId;
|
InstallationId = ev.InstallationId;
|
||||||
SystemUser = ev.SystemUser;
|
SystemUser = ev.SystemUser;
|
||||||
|
DomainName = ev.DomainName;
|
||||||
|
SecretId = ev.SecretId;
|
||||||
|
ServiceAccountId = ev.ServiceAccountId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventType Type { get; set; }
|
public EventType Type { get; set; }
|
||||||
@ -50,4 +53,7 @@ public class EventResponseModel : ResponseModel
|
|||||||
public DeviceType? DeviceType { get; set; }
|
public DeviceType? DeviceType { get; set; }
|
||||||
public string IpAddress { get; set; }
|
public string IpAddress { get; set; }
|
||||||
public EventSystemUser? SystemUser { get; set; }
|
public EventSystemUser? SystemUser { get; set; }
|
||||||
|
public string DomainName { get; set; }
|
||||||
|
public Guid? SecretId { get; set; }
|
||||||
|
public Guid? ServiceAccountId { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Models.Api;
|
||||||
|
|
||||||
|
namespace Bit.Api.Models.Response.Organizations;
|
||||||
|
|
||||||
|
public class OrganizationDomainResponseModel : ResponseModel
|
||||||
|
{
|
||||||
|
public OrganizationDomainResponseModel(OrganizationDomain organizationDomain, string obj = "organizationDomain")
|
||||||
|
: base(obj)
|
||||||
|
{
|
||||||
|
if (organizationDomain == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(organizationDomain));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id = organizationDomain.Id.ToString();
|
||||||
|
OrganizationId = organizationDomain.OrganizationId.ToString();
|
||||||
|
Txt = organizationDomain.Txt;
|
||||||
|
DomainName = organizationDomain.DomainName;
|
||||||
|
CreationDate = organizationDomain.CreationDate;
|
||||||
|
NextRunDate = organizationDomain.NextRunDate;
|
||||||
|
JobRunCount = organizationDomain.JobRunCount;
|
||||||
|
VerifiedDate = organizationDomain.VerifiedDate;
|
||||||
|
LastCheckedDate = organizationDomain.LastCheckedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string OrganizationId { get; set; }
|
||||||
|
public string Txt { get; set; }
|
||||||
|
public string DomainName { get; set; }
|
||||||
|
public DateTime CreationDate { get; set; }
|
||||||
|
public DateTime NextRunDate { get; set; }
|
||||||
|
public int JobRunCount { get; set; }
|
||||||
|
public DateTime? VerifiedDate { get; set; }
|
||||||
|
public DateTime? LastCheckedDate { get; set; }
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
using Bit.Core.Models.Api;
|
||||||
|
using Bit.Core.Models.Data.Organizations;
|
||||||
|
|
||||||
|
namespace Bit.Api.Models.Response.Organizations;
|
||||||
|
|
||||||
|
public class OrganizationDomainSsoDetailsResponseModel : ResponseModel
|
||||||
|
{
|
||||||
|
public OrganizationDomainSsoDetailsResponseModel(OrganizationDomainSsoDetailsData data, string obj = "organizationDomainSsoDetails")
|
||||||
|
: base(obj)
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
SsoAvailable = data.SsoAvailable;
|
||||||
|
DomainName = data.DomainName;
|
||||||
|
OrganizationIdentifier = data.OrganizationIdentifier;
|
||||||
|
SsoRequired = data.SsoRequired;
|
||||||
|
VerifiedDate = data.VerifiedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SsoAvailable { get; private set; }
|
||||||
|
public string DomainName { get; private set; }
|
||||||
|
public string OrganizationIdentifier { get; private set; }
|
||||||
|
public bool SsoRequired { get; private set; }
|
||||||
|
public DateTime? VerifiedDate { get; private set; }
|
||||||
|
}
|
@ -17,7 +17,6 @@ public class OrganizationResponseModel : ResponseModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
Id = organization.Id.ToString();
|
Id = organization.Id.ToString();
|
||||||
Identifier = organization.Identifier;
|
|
||||||
Name = organization.Name;
|
Name = organization.Name;
|
||||||
BusinessName = organization.BusinessName;
|
BusinessName = organization.BusinessName;
|
||||||
BusinessAddress1 = organization.BusinessAddress1;
|
BusinessAddress1 = organization.BusinessAddress1;
|
||||||
@ -51,7 +50,6 @@ public class OrganizationResponseModel : ResponseModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
public string Identifier { get; set; }
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
public string BusinessAddress1 { get; set; }
|
public string BusinessAddress1 { get; set; }
|
||||||
|
@ -23,6 +23,7 @@ public class OrganizationUserResponseModel : ResponseModel
|
|||||||
Type = organizationUser.Type;
|
Type = organizationUser.Type;
|
||||||
Status = organizationUser.Status;
|
Status = organizationUser.Status;
|
||||||
AccessAll = organizationUser.AccessAll;
|
AccessAll = organizationUser.AccessAll;
|
||||||
|
ExternalId = organizationUser.ExternalId;
|
||||||
AccessSecretsManager = organizationUser.AccessSecretsManager;
|
AccessSecretsManager = organizationUser.AccessSecretsManager;
|
||||||
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organizationUser.Permissions);
|
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organizationUser.Permissions);
|
||||||
ResetPasswordEnrolled = !string.IsNullOrEmpty(organizationUser.ResetPasswordKey);
|
ResetPasswordEnrolled = !string.IsNullOrEmpty(organizationUser.ResetPasswordKey);
|
||||||
@ -41,6 +42,7 @@ public class OrganizationUserResponseModel : ResponseModel
|
|||||||
Type = organizationUser.Type;
|
Type = organizationUser.Type;
|
||||||
Status = organizationUser.Status;
|
Status = organizationUser.Status;
|
||||||
AccessAll = organizationUser.AccessAll;
|
AccessAll = organizationUser.AccessAll;
|
||||||
|
ExternalId = organizationUser.ExternalId;
|
||||||
AccessSecretsManager = organizationUser.AccessSecretsManager;
|
AccessSecretsManager = organizationUser.AccessSecretsManager;
|
||||||
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organizationUser.Permissions);
|
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organizationUser.Permissions);
|
||||||
ResetPasswordEnrolled = !string.IsNullOrEmpty(organizationUser.ResetPasswordKey);
|
ResetPasswordEnrolled = !string.IsNullOrEmpty(organizationUser.ResetPasswordKey);
|
||||||
@ -52,6 +54,7 @@ public class OrganizationUserResponseModel : ResponseModel
|
|||||||
public OrganizationUserType Type { get; set; }
|
public OrganizationUserType Type { get; set; }
|
||||||
public OrganizationUserStatusType Status { get; set; }
|
public OrganizationUserStatusType Status { get; set; }
|
||||||
public bool AccessAll { get; set; }
|
public bool AccessAll { get; set; }
|
||||||
|
public string ExternalId { get; set; }
|
||||||
public bool AccessSecretsManager { get; set; }
|
public bool AccessSecretsManager { get; set; }
|
||||||
public Permissions Permissions { get; set; }
|
public Permissions Permissions { get; set; }
|
||||||
public bool ResetPasswordEnrolled { get; set; }
|
public bool ResetPasswordEnrolled { get; set; }
|
||||||
@ -86,6 +89,7 @@ public class OrganizationUserUserDetailsResponseModel : OrganizationUserResponse
|
|||||||
|
|
||||||
Name = organizationUser.Name;
|
Name = organizationUser.Name;
|
||||||
Email = organizationUser.Email;
|
Email = organizationUser.Email;
|
||||||
|
AvatarColor = organizationUser.AvatarColor;
|
||||||
TwoFactorEnabled = twoFactorEnabled;
|
TwoFactorEnabled = twoFactorEnabled;
|
||||||
SsoBound = !string.IsNullOrWhiteSpace(organizationUser.SsoExternalId);
|
SsoBound = !string.IsNullOrWhiteSpace(organizationUser.SsoExternalId);
|
||||||
Collections = organizationUser.Collections.Select(c => new SelectionReadOnlyResponseModel(c));
|
Collections = organizationUser.Collections.Select(c => new SelectionReadOnlyResponseModel(c));
|
||||||
@ -94,8 +98,10 @@ public class OrganizationUserUserDetailsResponseModel : OrganizationUserResponse
|
|||||||
ResetPasswordEnrolled = ResetPasswordEnrolled && !organizationUser.UsesKeyConnector;
|
ResetPasswordEnrolled = ResetPasswordEnrolled && !organizationUser.UsesKeyConnector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
|
public string AvatarColor { get; set; }
|
||||||
public bool TwoFactorEnabled { get; set; }
|
public bool TwoFactorEnabled { get; set; }
|
||||||
public bool SsoBound { get; set; }
|
public bool SsoBound { get; set; }
|
||||||
public IEnumerable<SelectionReadOnlyResponseModel> Collections { get; set; }
|
public IEnumerable<SelectionReadOnlyResponseModel> Collections { get; set; }
|
||||||
|
@ -1,27 +1,54 @@
|
|||||||
using Bit.Api.SecretsManager.Models.Request;
|
using Bit.Api.Models.Response;
|
||||||
|
using Bit.Api.SecretsManager.Models.Request;
|
||||||
using Bit.Api.SecretsManager.Models.Response;
|
using Bit.Api.SecretsManager.Models.Response;
|
||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Bit.Api.SecretsManager.Controllers;
|
namespace Bit.Api.SecretsManager.Controllers;
|
||||||
|
|
||||||
[SecretsManager]
|
[SecretsManager]
|
||||||
|
[Authorize("secrets")]
|
||||||
[Route("access-policies")]
|
[Route("access-policies")]
|
||||||
public class AccessPoliciesController : Controller
|
public class AccessPoliciesController : Controller
|
||||||
{
|
{
|
||||||
|
private const int _maxBulkCreation = 15;
|
||||||
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
||||||
private readonly ICreateAccessPoliciesCommand _createAccessPoliciesCommand;
|
private readonly ICreateAccessPoliciesCommand _createAccessPoliciesCommand;
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly IDeleteAccessPolicyCommand _deleteAccessPolicyCommand;
|
private readonly IDeleteAccessPolicyCommand _deleteAccessPolicyCommand;
|
||||||
|
private readonly IGroupRepository _groupRepository;
|
||||||
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
private readonly IUpdateAccessPolicyCommand _updateAccessPolicyCommand;
|
private readonly IUpdateAccessPolicyCommand _updateAccessPolicyCommand;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
public AccessPoliciesController(
|
public AccessPoliciesController(
|
||||||
|
IUserService userService,
|
||||||
|
ICurrentContext currentContext,
|
||||||
IAccessPolicyRepository accessPolicyRepository,
|
IAccessPolicyRepository accessPolicyRepository,
|
||||||
|
IServiceAccountRepository serviceAccountRepository,
|
||||||
|
IGroupRepository groupRepository,
|
||||||
|
IProjectRepository projectRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
ICreateAccessPoliciesCommand createAccessPoliciesCommand,
|
ICreateAccessPoliciesCommand createAccessPoliciesCommand,
|
||||||
IDeleteAccessPolicyCommand deleteAccessPolicyCommand,
|
IDeleteAccessPolicyCommand deleteAccessPolicyCommand,
|
||||||
IUpdateAccessPolicyCommand updateAccessPolicyCommand)
|
IUpdateAccessPolicyCommand updateAccessPolicyCommand)
|
||||||
{
|
{
|
||||||
|
_userService = userService;
|
||||||
|
_currentContext = currentContext;
|
||||||
|
_serviceAccountRepository = serviceAccountRepository;
|
||||||
|
_projectRepository = projectRepository;
|
||||||
|
_groupRepository = groupRepository;
|
||||||
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_accessPolicyRepository = accessPolicyRepository;
|
_accessPolicyRepository = accessPolicyRepository;
|
||||||
_createAccessPoliciesCommand = createAccessPoliciesCommand;
|
_createAccessPoliciesCommand = createAccessPoliciesCommand;
|
||||||
_deleteAccessPolicyCommand = deleteAccessPolicyCommand;
|
_deleteAccessPolicyCommand = deleteAccessPolicyCommand;
|
||||||
@ -32,37 +59,244 @@ public class AccessPoliciesController : Controller
|
|||||||
public async Task<ProjectAccessPoliciesResponseModel> CreateProjectAccessPoliciesAsync([FromRoute] Guid id,
|
public async Task<ProjectAccessPoliciesResponseModel> CreateProjectAccessPoliciesAsync([FromRoute] Guid id,
|
||||||
[FromBody] AccessPoliciesCreateRequest request)
|
[FromBody] AccessPoliciesCreateRequest request)
|
||||||
{
|
{
|
||||||
|
if (request.Count() > _maxBulkCreation)
|
||||||
|
{
|
||||||
|
throw new BadRequestException($"Can process no more than {_maxBulkCreation} creation requests at once.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var project = await _projectRepository.GetByIdAsync(id);
|
||||||
|
if (project == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var (accessClient, userId) = await GetAccessClientTypeAsync(project.OrganizationId);
|
||||||
var policies = request.ToBaseAccessPoliciesForProject(id);
|
var policies = request.ToBaseAccessPoliciesForProject(id);
|
||||||
var results = await _createAccessPoliciesCommand.CreateAsync(policies);
|
await _createAccessPoliciesCommand.CreateManyAsync(policies, userId, accessClient);
|
||||||
|
var results = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(id);
|
||||||
return new ProjectAccessPoliciesResponseModel(results);
|
return new ProjectAccessPoliciesResponseModel(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("/projects/{id}/access-policies")]
|
[HttpGet("/projects/{id}/access-policies")]
|
||||||
public async Task<ProjectAccessPoliciesResponseModel> GetProjectAccessPoliciesAsync([FromRoute] Guid id)
|
public async Task<ProjectAccessPoliciesResponseModel> GetProjectAccessPoliciesAsync([FromRoute] Guid id)
|
||||||
{
|
{
|
||||||
|
var project = await _projectRepository.GetByIdAsync(id);
|
||||||
|
await CheckUserHasWriteAccessToProjectAsync(project);
|
||||||
|
|
||||||
var results = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(id);
|
var results = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(id);
|
||||||
return new ProjectAccessPoliciesResponseModel(results);
|
return new ProjectAccessPoliciesResponseModel(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("/service-accounts/{id}/access-policies")]
|
||||||
|
public async Task<ServiceAccountAccessPoliciesResponseModel> CreateServiceAccountAccessPoliciesAsync(
|
||||||
|
[FromRoute] Guid id,
|
||||||
|
[FromBody] AccessPoliciesCreateRequest request)
|
||||||
|
{
|
||||||
|
if (request.Count() > _maxBulkCreation)
|
||||||
|
{
|
||||||
|
throw new BadRequestException($"Can process no more than {_maxBulkCreation} creation requests at once.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
||||||
|
if (serviceAccount == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var (accessClient, userId) = await GetAccessClientTypeAsync(serviceAccount.OrganizationId);
|
||||||
|
var policies = request.ToBaseAccessPoliciesForServiceAccount(id);
|
||||||
|
await _createAccessPoliciesCommand.CreateManyAsync(policies, userId, accessClient);
|
||||||
|
var results = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(id);
|
||||||
|
return new ServiceAccountAccessPoliciesResponseModel(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("/service-accounts/{id}/access-policies")]
|
||||||
|
public async Task<ServiceAccountAccessPoliciesResponseModel> GetServiceAccountAccessPoliciesAsync(
|
||||||
|
[FromRoute] Guid id)
|
||||||
|
{
|
||||||
|
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
||||||
|
await CheckUserHasWriteAccessToServiceAccountAsync(serviceAccount);
|
||||||
|
|
||||||
|
var results = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(id);
|
||||||
|
return new ServiceAccountAccessPoliciesResponseModel(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("/service-accounts/{id}/granted-policies")]
|
||||||
|
public async Task<ListResponseModel<ServiceAccountProjectAccessPolicyResponseModel>>
|
||||||
|
GetServiceAccountGrantedPoliciesAsync([FromRoute] Guid id)
|
||||||
|
{
|
||||||
|
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
||||||
|
if (serviceAccount == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var (accessClient, userId) = await GetAccessClientTypeAsync(serviceAccount.OrganizationId);
|
||||||
|
var results = await _accessPolicyRepository.GetManyByServiceAccountIdAsync(id, userId, accessClient);
|
||||||
|
var responses = results.Select(ap =>
|
||||||
|
new ServiceAccountProjectAccessPolicyResponseModel((ServiceAccountProjectAccessPolicy)ap));
|
||||||
|
return new ListResponseModel<ServiceAccountProjectAccessPolicyResponseModel>(responses);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("/service-accounts/{id}/granted-policies")]
|
||||||
|
public async Task<ListResponseModel<ServiceAccountProjectAccessPolicyResponseModel>>
|
||||||
|
CreateServiceAccountGrantedPoliciesAsync([FromRoute] Guid id,
|
||||||
|
[FromBody] List<GrantedAccessPolicyRequest> requests)
|
||||||
|
{
|
||||||
|
if (requests.Count > _maxBulkCreation)
|
||||||
|
{
|
||||||
|
throw new BadRequestException($"Can process no more than {_maxBulkCreation} creation requests at once.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
||||||
|
if (serviceAccount == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var (accessClient, userId) = await GetAccessClientTypeAsync(serviceAccount.OrganizationId);
|
||||||
|
var policies = requests.Select(request => request.ToServiceAccountProjectAccessPolicy(id));
|
||||||
|
var results =
|
||||||
|
await _createAccessPoliciesCommand.CreateManyAsync(new List<BaseAccessPolicy>(policies), userId, accessClient);
|
||||||
|
var responses = results.Select(ap =>
|
||||||
|
new ServiceAccountProjectAccessPolicyResponseModel((ServiceAccountProjectAccessPolicy)ap));
|
||||||
|
return new ListResponseModel<ServiceAccountProjectAccessPolicyResponseModel>(responses);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPut("{id}")]
|
[HttpPut("{id}")]
|
||||||
public async Task<BaseAccessPolicyResponseModel> UpdateAccessPolicyAsync([FromRoute] Guid id,
|
public async Task<BaseAccessPolicyResponseModel> UpdateAccessPolicyAsync([FromRoute] Guid id,
|
||||||
[FromBody] AccessPolicyUpdateRequest request)
|
[FromBody] AccessPolicyUpdateRequest request)
|
||||||
{
|
{
|
||||||
var result = await _updateAccessPolicyCommand.UpdateAsync(id, request.Read, request.Write);
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var result = await _updateAccessPolicyCommand.UpdateAsync(id, request.Read, request.Write, userId);
|
||||||
|
|
||||||
return result switch
|
return result switch
|
||||||
{
|
{
|
||||||
UserProjectAccessPolicy accessPolicy => new UserProjectAccessPolicyResponseModel(accessPolicy),
|
UserProjectAccessPolicy accessPolicy => new UserProjectAccessPolicyResponseModel(accessPolicy),
|
||||||
|
UserServiceAccountAccessPolicy accessPolicy =>
|
||||||
|
new UserServiceAccountAccessPolicyResponseModel(accessPolicy),
|
||||||
GroupProjectAccessPolicy accessPolicy => new GroupProjectAccessPolicyResponseModel(accessPolicy),
|
GroupProjectAccessPolicy accessPolicy => new GroupProjectAccessPolicyResponseModel(accessPolicy),
|
||||||
|
GroupServiceAccountAccessPolicy accessPolicy => new GroupServiceAccountAccessPolicyResponseModel(
|
||||||
|
accessPolicy),
|
||||||
ServiceAccountProjectAccessPolicy accessPolicy => new ServiceAccountProjectAccessPolicyResponseModel(
|
ServiceAccountProjectAccessPolicy accessPolicy => new ServiceAccountProjectAccessPolicyResponseModel(
|
||||||
accessPolicy),
|
accessPolicy),
|
||||||
_ => throw new ArgumentException("Unsupported access policy type provided.")
|
_ => throw new ArgumentException("Unsupported access policy type provided."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
public async Task DeleteAccessPolicyAsync([FromRoute] Guid id)
|
public async Task DeleteAccessPolicyAsync([FromRoute] Guid id)
|
||||||
{
|
{
|
||||||
await _deleteAccessPolicyCommand.DeleteAsync(id);
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
await _deleteAccessPolicyCommand.DeleteAsync(id, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("/organizations/{id}/access-policies/people/potential-grantees")]
|
||||||
|
public async Task<ListResponseModel<PotentialGranteeResponseModel>> GetPeoplePotentialGranteesAsync(
|
||||||
|
[FromRoute] Guid id)
|
||||||
|
{
|
||||||
|
if (!_currentContext.AccessSecretsManager(id))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var groups = await _groupRepository.GetManyByOrganizationIdAsync(id);
|
||||||
|
var groupResponses = groups.Select(g => new PotentialGranteeResponseModel(g));
|
||||||
|
|
||||||
|
var organizationUsers =
|
||||||
|
await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id);
|
||||||
|
var userResponses = organizationUsers
|
||||||
|
.Where(user => user.AccessSecretsManager)
|
||||||
|
.Select(userDetails => new PotentialGranteeResponseModel(userDetails));
|
||||||
|
|
||||||
|
return new ListResponseModel<PotentialGranteeResponseModel>(userResponses.Concat(groupResponses));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("/organizations/{id}/access-policies/service-accounts/potential-grantees")]
|
||||||
|
public async Task<ListResponseModel<PotentialGranteeResponseModel>> GetServiceAccountsPotentialGranteesAsync(
|
||||||
|
[FromRoute] Guid id)
|
||||||
|
{
|
||||||
|
var (accessClient, userId) = await GetAccessClientTypeAsync(id);
|
||||||
|
|
||||||
|
var serviceAccounts =
|
||||||
|
await _serviceAccountRepository.GetManyByOrganizationIdWriteAccessAsync(id,
|
||||||
|
userId,
|
||||||
|
accessClient);
|
||||||
|
var serviceAccountResponses =
|
||||||
|
serviceAccounts.Select(serviceAccount => new PotentialGranteeResponseModel(serviceAccount));
|
||||||
|
|
||||||
|
return new ListResponseModel<PotentialGranteeResponseModel>(serviceAccountResponses);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("/organizations/{id}/access-policies/projects/potential-grantees")]
|
||||||
|
public async Task<ListResponseModel<PotentialGranteeResponseModel>> GetProjectPotentialGranteesAsync(
|
||||||
|
[FromRoute] Guid id)
|
||||||
|
{
|
||||||
|
var (accessClient, userId) = await GetAccessClientTypeAsync(id);
|
||||||
|
|
||||||
|
var projects =
|
||||||
|
await _projectRepository.GetManyByOrganizationIdWriteAccessAsync(id,
|
||||||
|
userId,
|
||||||
|
accessClient);
|
||||||
|
var projectResponses =
|
||||||
|
projects.Select(project => new PotentialGranteeResponseModel(project));
|
||||||
|
|
||||||
|
return new ListResponseModel<PotentialGranteeResponseModel>(projectResponses);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckUserHasWriteAccessToProjectAsync(Project project)
|
||||||
|
{
|
||||||
|
if (project == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var (accessClient, userId) = await GetAccessClientTypeAsync(project.OrganizationId);
|
||||||
|
var hasAccess = accessClient switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => true,
|
||||||
|
AccessClientType.User => await _projectRepository.UserHasWriteAccessToProject(project.Id, userId),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!hasAccess)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckUserHasWriteAccessToServiceAccountAsync(ServiceAccount serviceAccount)
|
||||||
|
{
|
||||||
|
if (serviceAccount == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var (accessClient, userId) = await GetAccessClientTypeAsync(serviceAccount.OrganizationId);
|
||||||
|
var hasAccess = accessClient switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => true,
|
||||||
|
AccessClientType.User => await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(
|
||||||
|
serviceAccount.Id, userId),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!hasAccess)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<(AccessClientType AccessClientType, Guid UserId)> GetAccessClientTypeAsync(Guid organizationId)
|
||||||
|
{
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||||
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
return (accessClient, userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,8 @@ public class ProjectsController : Controller
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _createProjectCommand.CreateAsync(createRequest.ToProject(organizationId));
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var result = await _createProjectCommand.CreateAsync(createRequest.ToProject(organizationId), userId);
|
||||||
return new ProjectResponseModel(result);
|
return new ProjectResponseModel(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,13 @@
|
|||||||
using Bit.Api.SecretsManager.Models.Request;
|
using Bit.Api.SecretsManager.Models.Request;
|
||||||
using Bit.Api.SecretsManager.Models.Response;
|
using Bit.Api.SecretsManager.Models.Response;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Identity;
|
||||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@ -15,23 +19,32 @@ namespace Bit.Api.SecretsManager.Controllers;
|
|||||||
public class SecretsController : Controller
|
public class SecretsController : Controller
|
||||||
{
|
{
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
|
private readonly IProjectRepository _projectRepository;
|
||||||
private readonly ISecretRepository _secretRepository;
|
private readonly ISecretRepository _secretRepository;
|
||||||
private readonly ICreateSecretCommand _createSecretCommand;
|
private readonly ICreateSecretCommand _createSecretCommand;
|
||||||
private readonly IUpdateSecretCommand _updateSecretCommand;
|
private readonly IUpdateSecretCommand _updateSecretCommand;
|
||||||
private readonly IDeleteSecretCommand _deleteSecretCommand;
|
private readonly IDeleteSecretCommand _deleteSecretCommand;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
private readonly IEventService _eventService;
|
||||||
|
|
||||||
public SecretsController(
|
public SecretsController(
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
|
IProjectRepository projectRepository,
|
||||||
ISecretRepository secretRepository,
|
ISecretRepository secretRepository,
|
||||||
ICreateSecretCommand createSecretCommand,
|
ICreateSecretCommand createSecretCommand,
|
||||||
IUpdateSecretCommand updateSecretCommand,
|
IUpdateSecretCommand updateSecretCommand,
|
||||||
IDeleteSecretCommand deleteSecretCommand)
|
IDeleteSecretCommand deleteSecretCommand,
|
||||||
|
IUserService userService,
|
||||||
|
IEventService eventService)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
|
_projectRepository = projectRepository;
|
||||||
_secretRepository = secretRepository;
|
_secretRepository = secretRepository;
|
||||||
_createSecretCommand = createSecretCommand;
|
_createSecretCommand = createSecretCommand;
|
||||||
_updateSecretCommand = updateSecretCommand;
|
_updateSecretCommand = updateSecretCommand;
|
||||||
_deleteSecretCommand = deleteSecretCommand;
|
_deleteSecretCommand = deleteSecretCommand;
|
||||||
|
_userService = userService;
|
||||||
|
_eventService = eventService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("organizations/{organizationId}/secrets")]
|
[HttpGet("organizations/{organizationId}/secrets")]
|
||||||
@ -42,7 +55,12 @@ public class SecretsController : Controller
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var secrets = await _secretRepository.GetManyByOrganizationIdAsync(organizationId);
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||||
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
|
var secrets = await _secretRepository.GetManyByOrganizationIdAsync(organizationId, userId, accessClient);
|
||||||
|
|
||||||
return new SecretWithProjectsListResponseModel(secrets);
|
return new SecretWithProjectsListResponseModel(secrets);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +72,8 @@ public class SecretsController : Controller
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _createSecretCommand.CreateAsync(createRequest.ToSecret(organizationId));
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var result = await _createSecretCommand.CreateAsync(createRequest.ToSecret(organizationId), userId);
|
||||||
return new SecretResponseModel(result);
|
return new SecretResponseModel(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,34 +81,81 @@ public class SecretsController : Controller
|
|||||||
public async Task<SecretResponseModel> GetAsync([FromRoute] Guid id)
|
public async Task<SecretResponseModel> GetAsync([FromRoute] Guid id)
|
||||||
{
|
{
|
||||||
var secret = await _secretRepository.GetByIdAsync(id);
|
var secret = await _secretRepository.GetByIdAsync(id);
|
||||||
if (secret == null)
|
|
||||||
|
if (secret == null || !_currentContext.AccessSecretsManager(secret.OrganizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!await UserHasReadAccessToSecret(secret))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentContext.ClientType == ClientType.ServiceAccount)
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
await _eventService.LogServiceAccountSecretEventAsync(userId, secret, EventType.Secret_Retrieved);
|
||||||
|
}
|
||||||
|
|
||||||
return new SecretResponseModel(secret);
|
return new SecretResponseModel(secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("projects/{projectId}/secrets")]
|
[HttpGet("projects/{projectId}/secrets")]
|
||||||
public async Task<SecretWithProjectsListResponseModel> GetSecretsByProjectAsync([FromRoute] Guid projectId)
|
public async Task<SecretWithProjectsListResponseModel> GetSecretsByProjectAsync([FromRoute] Guid projectId)
|
||||||
{
|
{
|
||||||
var secrets = await _secretRepository.GetManyByProjectIdAsync(projectId);
|
var project = await _projectRepository.GetByIdAsync(projectId);
|
||||||
var responses = secrets.Select(s => new SecretResponseModel(s));
|
if (project == null || !_currentContext.AccessSecretsManager(project.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var orgAdmin = await _currentContext.OrganizationAdmin(project.OrganizationId);
|
||||||
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
|
var secrets = await _secretRepository.GetManyByProjectIdAsync(projectId, userId, accessClient);
|
||||||
|
|
||||||
return new SecretWithProjectsListResponseModel(secrets);
|
return new SecretWithProjectsListResponseModel(secrets);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("secrets/{id}")]
|
[HttpPut("secrets/{id}")]
|
||||||
public async Task<SecretResponseModel> UpdateAsync([FromRoute] Guid id, [FromBody] SecretUpdateRequestModel updateRequest)
|
public async Task<SecretResponseModel> UpdateSecretAsync([FromRoute] Guid id, [FromBody] SecretUpdateRequestModel updateRequest)
|
||||||
{
|
{
|
||||||
var result = await _updateSecretCommand.UpdateAsync(updateRequest.ToSecret(id));
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var secret = updateRequest.ToSecret(id);
|
||||||
|
var result = await _updateSecretCommand.UpdateAsync(secret, userId);
|
||||||
return new SecretResponseModel(result);
|
return new SecretResponseModel(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Once permissions are setup for Secrets Manager need to enforce them on delete.
|
|
||||||
[HttpPost("secrets/delete")]
|
[HttpPost("secrets/delete")]
|
||||||
public async Task<ListResponseModel<BulkDeleteResponseModel>> BulkDeleteAsync([FromBody] List<Guid> ids)
|
public async Task<ListResponseModel<BulkDeleteResponseModel>> BulkDeleteAsync([FromBody] List<Guid> ids)
|
||||||
{
|
{
|
||||||
var results = await _deleteSecretCommand.DeleteSecrets(ids);
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var results = await _deleteSecretCommand.DeleteSecrets(ids, userId);
|
||||||
var responses = results.Select(r => new BulkDeleteResponseModel(r.Item1.Id, r.Item2));
|
var responses = results.Select(r => new BulkDeleteResponseModel(r.Item1.Id, r.Item2));
|
||||||
return new ListResponseModel<BulkDeleteResponseModel>(responses);
|
return new ListResponseModel<BulkDeleteResponseModel>(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> UserHasReadAccessToSecret(Secret secret)
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var orgAdmin = await _currentContext.OrganizationAdmin(secret.OrganizationId);
|
||||||
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
var hasAccess = orgAdmin;
|
||||||
|
|
||||||
|
if (secret.Projects?.Count > 0)
|
||||||
|
{
|
||||||
|
Guid projectId = secret.Projects.FirstOrDefault().Id;
|
||||||
|
hasAccess = accessClient switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => true,
|
||||||
|
AccessClientType.User => await _projectRepository.UserHasReadAccessToProject(projectId, userId),
|
||||||
|
AccessClientType.ServiceAccount => await _projectRepository.ServiceAccountHasReadAccessToProject(projectId, userId),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasAccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
using Bit.Api.SecretsManager.Models.Request;
|
||||||
|
using Bit.Api.SecretsManager.Models.Response;
|
||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.SecretsManager.Commands.Porting.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Bit.Api.SecretsManager.Controllers;
|
||||||
|
|
||||||
|
[SecretsManager]
|
||||||
|
public class SecretsManagerPortingController : Controller
|
||||||
|
{
|
||||||
|
private readonly ISecretRepository _secretRepository;
|
||||||
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
private readonly IImportCommand _importCommand;
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
|
|
||||||
|
public SecretsManagerPortingController(ISecretRepository secretRepository, IProjectRepository projectRepository, IUserService userService, IImportCommand importCommand, ICurrentContext currentContext)
|
||||||
|
{
|
||||||
|
_secretRepository = secretRepository;
|
||||||
|
_projectRepository = projectRepository;
|
||||||
|
_userService = userService;
|
||||||
|
_importCommand = importCommand;
|
||||||
|
_currentContext = currentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("sm/{organizationId}/export")]
|
||||||
|
public async Task<SMExportResponseModel> Export([FromRoute] Guid organizationId, [FromRoute] string format = "json")
|
||||||
|
{
|
||||||
|
if (!await _currentContext.OrganizationAdmin(organizationId))
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var projects = await _projectRepository.GetManyByOrganizationIdAsync(organizationId, userId, AccessClientType.NoAccessCheck);
|
||||||
|
var secrets = await _secretRepository.GetManyByOrganizationIdAsync(organizationId, userId, AccessClientType.NoAccessCheck);
|
||||||
|
|
||||||
|
if (projects == null && secrets == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SMExportResponseModel(projects, secrets);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("sm/{organizationId}/import")]
|
||||||
|
public async Task Import([FromRoute] Guid organizationId, [FromBody] SMImportRequestModel importRequest)
|
||||||
|
{
|
||||||
|
if (!await _currentContext.OrganizationAdmin(organizationId))
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (importRequest.Projects?.Count() > 1000 || importRequest.Secrets?.Count() > 6000)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("You cannot import this much data at once, the limit is 1000 projects and 6000 secrets.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _importCommand.ImportAsync(organizationId, importRequest.ToSMImport());
|
||||||
|
}
|
||||||
|
}
|
80
src/Api/SecretsManager/Controllers/SecretsTrashController.cs
Normal file
80
src/Api/SecretsManager/Controllers/SecretsTrashController.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
using Bit.Api.SecretsManager.Models.Response;
|
||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.SecretsManager.Commands.Trash.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Bit.Api.SecretsManager.Controllers;
|
||||||
|
|
||||||
|
[SecretsManager]
|
||||||
|
[Authorize("secrets")]
|
||||||
|
public class TrashController : Controller
|
||||||
|
{
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
|
private readonly ISecretRepository _secretRepository;
|
||||||
|
private readonly IEmptyTrashCommand _emptyTrashCommand;
|
||||||
|
private readonly IRestoreTrashCommand _restoreTrashCommand;
|
||||||
|
|
||||||
|
public TrashController(
|
||||||
|
ICurrentContext currentContext,
|
||||||
|
ISecretRepository secretRepository,
|
||||||
|
IEmptyTrashCommand emptyTrashCommand,
|
||||||
|
IRestoreTrashCommand restoreTrashCommand)
|
||||||
|
{
|
||||||
|
_currentContext = currentContext;
|
||||||
|
_secretRepository = secretRepository;
|
||||||
|
_emptyTrashCommand = emptyTrashCommand;
|
||||||
|
_restoreTrashCommand = restoreTrashCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("secrets/{organizationId}/trash")]
|
||||||
|
public async Task<SecretWithProjectsListResponseModel> ListByOrganizationAsync(Guid organizationId)
|
||||||
|
{
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await _currentContext.OrganizationAdmin(organizationId))
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var secrets = await _secretRepository.GetManyByOrganizationIdInTrashAsync(organizationId);
|
||||||
|
return new SecretWithProjectsListResponseModel(secrets);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("secrets/{organizationId}/trash/empty")]
|
||||||
|
public async Task EmptyTrashAsync(Guid organizationId, [FromBody] List<Guid> ids)
|
||||||
|
{
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await _currentContext.OrganizationAdmin(organizationId))
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException();
|
||||||
|
}
|
||||||
|
|
||||||
|
await _emptyTrashCommand.EmptyTrash(organizationId, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("secrets/{organizationId}/trash/restore")]
|
||||||
|
public async Task RestoreTrashAsync(Guid organizationId, [FromBody] List<Guid> ids)
|
||||||
|
{
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await _currentContext.OrganizationAdmin(organizationId))
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException();
|
||||||
|
}
|
||||||
|
|
||||||
|
await _restoreTrashCommand.RestoreTrash(organizationId, ids);
|
||||||
|
}
|
||||||
|
}
|
@ -19,20 +19,23 @@ namespace Bit.Api.SecretsManager.Controllers;
|
|||||||
public class ServiceAccountsController : Controller
|
public class ServiceAccountsController : Controller
|
||||||
{
|
{
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
private readonly IApiKeyRepository _apiKeyRepository;
|
private readonly IApiKeyRepository _apiKeyRepository;
|
||||||
private readonly ICreateAccessTokenCommand _createAccessTokenCommand;
|
private readonly ICreateAccessTokenCommand _createAccessTokenCommand;
|
||||||
private readonly ICreateServiceAccountCommand _createServiceAccountCommand;
|
private readonly ICreateServiceAccountCommand _createServiceAccountCommand;
|
||||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
|
||||||
private readonly IUpdateServiceAccountCommand _updateServiceAccountCommand;
|
private readonly IUpdateServiceAccountCommand _updateServiceAccountCommand;
|
||||||
private readonly IUserService _userService;
|
private readonly IRevokeAccessTokensCommand _revokeAccessTokensCommand;
|
||||||
|
|
||||||
public ServiceAccountsController(
|
public ServiceAccountsController(
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
IServiceAccountRepository serviceAccountRepository,
|
IServiceAccountRepository serviceAccountRepository,
|
||||||
|
IApiKeyRepository apiKeyRepository,
|
||||||
ICreateAccessTokenCommand createAccessTokenCommand,
|
ICreateAccessTokenCommand createAccessTokenCommand,
|
||||||
IApiKeyRepository apiKeyRepository, ICreateServiceAccountCommand createServiceAccountCommand,
|
ICreateServiceAccountCommand createServiceAccountCommand,
|
||||||
IUpdateServiceAccountCommand updateServiceAccountCommand)
|
IUpdateServiceAccountCommand updateServiceAccountCommand,
|
||||||
|
IRevokeAccessTokensCommand revokeAccessTokensCommand)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
@ -40,6 +43,7 @@ public class ServiceAccountsController : Controller
|
|||||||
_apiKeyRepository = apiKeyRepository;
|
_apiKeyRepository = apiKeyRepository;
|
||||||
_createServiceAccountCommand = createServiceAccountCommand;
|
_createServiceAccountCommand = createServiceAccountCommand;
|
||||||
_updateServiceAccountCommand = updateServiceAccountCommand;
|
_updateServiceAccountCommand = updateServiceAccountCommand;
|
||||||
|
_revokeAccessTokensCommand = revokeAccessTokensCommand;
|
||||||
_createAccessTokenCommand = createAccessTokenCommand;
|
_createAccessTokenCommand = createAccessTokenCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,4 +133,37 @@ public class ServiceAccountsController : Controller
|
|||||||
var result = await _createAccessTokenCommand.CreateAsync(request.ToApiKey(id), userId);
|
var result = await _createAccessTokenCommand.CreateAsync(request.ToApiKey(id), userId);
|
||||||
return new AccessTokenCreationResponseModel(result);
|
return new AccessTokenCreationResponseModel(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("{id}/access-tokens/revoke")]
|
||||||
|
public async Task RevokeAccessTokensAsync(Guid id, [FromBody] RevokeAccessTokensRequest request)
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
||||||
|
if (serviceAccount == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_currentContext.AccessSecretsManager(serviceAccount.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var orgAdmin = await _currentContext.OrganizationAdmin(serviceAccount.OrganizationId);
|
||||||
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
|
var hasAccess = accessClient switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => true,
|
||||||
|
AccessClientType.User => await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(id, userId),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!hasAccess)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
await _revokeAccessTokensCommand.RevokeAsync(serviceAccount, request.Ids);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ public class AccessPoliciesCreateRequest
|
|||||||
|
|
||||||
public IEnumerable<AccessPolicyRequest>? ServiceAccountAccessPolicyRequests { get; set; }
|
public IEnumerable<AccessPolicyRequest>? ServiceAccountAccessPolicyRequests { get; set; }
|
||||||
|
|
||||||
public List<BaseAccessPolicy> ToBaseAccessPoliciesForProject(Guid projectId)
|
public List<BaseAccessPolicy> ToBaseAccessPoliciesForProject(Guid grantedProjectId)
|
||||||
{
|
{
|
||||||
if (UserAccessPolicyRequests == null && GroupAccessPolicyRequests == null && ServiceAccountAccessPolicyRequests == null)
|
if (UserAccessPolicyRequests == null && GroupAccessPolicyRequests == null && ServiceAccountAccessPolicyRequests == null)
|
||||||
{
|
{
|
||||||
@ -21,20 +21,77 @@ public class AccessPoliciesCreateRequest
|
|||||||
}
|
}
|
||||||
|
|
||||||
var userAccessPolicies = UserAccessPolicyRequests?
|
var userAccessPolicies = UserAccessPolicyRequests?
|
||||||
.Select(x => x.ToUserProjectAccessPolicy(projectId)).ToList();
|
.Select(x => x.ToUserProjectAccessPolicy(grantedProjectId)).ToList();
|
||||||
|
|
||||||
var groupAccessPolicies = GroupAccessPolicyRequests?
|
var groupAccessPolicies = GroupAccessPolicyRequests?
|
||||||
.Select(x => x.ToGroupProjectAccessPolicy(projectId)).ToList();
|
.Select(x => x.ToGroupProjectAccessPolicy(grantedProjectId)).ToList();
|
||||||
|
|
||||||
var serviceAccountAccessPolicies = ServiceAccountAccessPolicyRequests?
|
var serviceAccountAccessPolicies = ServiceAccountAccessPolicyRequests?
|
||||||
.Select(x => x.ToServiceAccountProjectAccessPolicy(projectId)).ToList();
|
.Select(x => x.ToServiceAccountProjectAccessPolicy(grantedProjectId)).ToList();
|
||||||
|
|
||||||
var policies = new List<BaseAccessPolicy>();
|
var policies = new List<BaseAccessPolicy>();
|
||||||
if (userAccessPolicies != null) { policies.AddRange(userAccessPolicies); }
|
if (userAccessPolicies != null)
|
||||||
if (groupAccessPolicies != null) { policies.AddRange(groupAccessPolicies); }
|
{
|
||||||
if (serviceAccountAccessPolicies != null) { policies.AddRange(serviceAccountAccessPolicies); }
|
policies.AddRange(userAccessPolicies);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupAccessPolicies != null)
|
||||||
|
{
|
||||||
|
policies.AddRange(groupAccessPolicies);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceAccountAccessPolicies != null)
|
||||||
|
{
|
||||||
|
policies.AddRange(serviceAccountAccessPolicies);
|
||||||
|
}
|
||||||
return policies;
|
return policies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<BaseAccessPolicy> ToBaseAccessPoliciesForServiceAccount(Guid grantedServiceAccountId)
|
||||||
|
{
|
||||||
|
if (UserAccessPolicyRequests == null && GroupAccessPolicyRequests == null)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("No creation requests provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var userAccessPolicies = UserAccessPolicyRequests?
|
||||||
|
.Select(x => x.ToUserServiceAccountAccessPolicy(grantedServiceAccountId)).ToList();
|
||||||
|
|
||||||
|
var groupAccessPolicies = GroupAccessPolicyRequests?
|
||||||
|
.Select(x => x.ToGroupServiceAccountAccessPolicy(grantedServiceAccountId)).ToList();
|
||||||
|
|
||||||
|
var policies = new List<BaseAccessPolicy>();
|
||||||
|
if (userAccessPolicies != null)
|
||||||
|
{
|
||||||
|
policies.AddRange(userAccessPolicies);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupAccessPolicies != null)
|
||||||
|
{
|
||||||
|
policies.AddRange(groupAccessPolicies);
|
||||||
|
}
|
||||||
|
return policies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count()
|
||||||
|
{
|
||||||
|
var total = 0;
|
||||||
|
|
||||||
|
if (UserAccessPolicyRequests != null)
|
||||||
|
{
|
||||||
|
total += UserAccessPolicyRequests.Count();
|
||||||
|
}
|
||||||
|
if (GroupAccessPolicyRequests != null)
|
||||||
|
{
|
||||||
|
total += GroupAccessPolicyRequests.Count();
|
||||||
|
}
|
||||||
|
if (ServiceAccountAccessPolicyRequests != null)
|
||||||
|
{
|
||||||
|
total += ServiceAccountAccessPolicyRequests.Count();
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AccessPolicyRequest
|
public class AccessPolicyRequest
|
||||||
@ -74,4 +131,22 @@ public class AccessPolicyRequest
|
|||||||
Read = Read,
|
Read = Read,
|
||||||
Write = Write
|
Write = Write
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public UserServiceAccountAccessPolicy ToUserServiceAccountAccessPolicy(Guid id) =>
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
OrganizationUserId = GranteeId,
|
||||||
|
GrantedServiceAccountId = id,
|
||||||
|
Read = Read,
|
||||||
|
Write = Write
|
||||||
|
};
|
||||||
|
|
||||||
|
public GroupServiceAccountAccessPolicy ToGroupServiceAccountAccessPolicy(Guid id) =>
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
GroupId = GranteeId,
|
||||||
|
GrantedServiceAccountId = id,
|
||||||
|
Read = Read,
|
||||||
|
Write = Write
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
|
||||||
|
namespace Bit.Api.SecretsManager.Models.Request;
|
||||||
|
|
||||||
|
public class GrantedAccessPolicyRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public Guid GrantedId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool Read { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool Write { get; set; }
|
||||||
|
|
||||||
|
public ServiceAccountProjectAccessPolicy ToServiceAccountProjectAccessPolicy(Guid serviceAccountId) =>
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
ServiceAccountId = serviceAccountId,
|
||||||
|
GrantedProjectId = GrantedId,
|
||||||
|
Read = Read,
|
||||||
|
Write = Write,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
public class RevokeAccessTokensRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public Guid[] Ids { get; set; }
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Bit.Core.SecretsManager.Commands.Porting;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
|
namespace Bit.Api.SecretsManager.Models.Request;
|
||||||
|
|
||||||
|
public class SMImportRequestModel
|
||||||
|
{
|
||||||
|
public IEnumerable<InnerProjectImportRequestModel> Projects { get; set; }
|
||||||
|
public IEnumerable<InnerSecretImportRequestModel> Secrets { get; set; }
|
||||||
|
|
||||||
|
public class InnerProjectImportRequestModel
|
||||||
|
{
|
||||||
|
public InnerProjectImportRequestModel() { }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[EncryptedString]
|
||||||
|
[EncryptedStringLength(1000)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InnerSecretImportRequestModel
|
||||||
|
{
|
||||||
|
public InnerSecretImportRequestModel() { }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[EncryptedString]
|
||||||
|
[EncryptedStringLength(1000)]
|
||||||
|
public string Key { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[EncryptedString]
|
||||||
|
[EncryptedStringLength(1000)]
|
||||||
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[EncryptedString]
|
||||||
|
[EncryptedStringLength(1000)]
|
||||||
|
public string Note { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public IEnumerable<Guid> ProjectIds { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public SMImport ToSMImport()
|
||||||
|
{
|
||||||
|
return new SMImport
|
||||||
|
{
|
||||||
|
Projects = Projects?.Select(p => new SMImport.InnerProject
|
||||||
|
{
|
||||||
|
Id = p.Id,
|
||||||
|
Name = p.Name,
|
||||||
|
}),
|
||||||
|
Secrets = Secrets?.Select(s => new SMImport.InnerSecret
|
||||||
|
{
|
||||||
|
Id = s.Id,
|
||||||
|
Key = s.Key,
|
||||||
|
Value = s.Value,
|
||||||
|
Note = s.Note,
|
||||||
|
ProjectIds = s.ProjectIds,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Models.Api;
|
using Bit.Core.Models.Api;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
|
||||||
@ -20,6 +21,11 @@ public abstract class BaseAccessPolicyResponseModel : ResponseModel
|
|||||||
public bool Write { get; set; }
|
public bool Write { get; set; }
|
||||||
public DateTime CreationDate { get; set; }
|
public DateTime CreationDate { get; set; }
|
||||||
public DateTime RevisionDate { get; set; }
|
public DateTime RevisionDate { get; set; }
|
||||||
|
|
||||||
|
public string? GetUserDisplayName(User? user)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(user?.Name) ? user?.Email : user?.Name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserProjectAccessPolicyResponseModel : BaseAccessPolicyResponseModel
|
public class UserProjectAccessPolicyResponseModel : BaseAccessPolicyResponseModel
|
||||||
@ -30,7 +36,7 @@ public class UserProjectAccessPolicyResponseModel : BaseAccessPolicyResponseMode
|
|||||||
{
|
{
|
||||||
OrganizationUserId = accessPolicy.OrganizationUserId;
|
OrganizationUserId = accessPolicy.OrganizationUserId;
|
||||||
GrantedProjectId = accessPolicy.GrantedProjectId;
|
GrantedProjectId = accessPolicy.GrantedProjectId;
|
||||||
OrganizationUserName = accessPolicy.User?.Name;
|
OrganizationUserName = GetUserDisplayName(accessPolicy.User);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserProjectAccessPolicyResponseModel() : base(new UserProjectAccessPolicy(), _objectName)
|
public UserProjectAccessPolicyResponseModel() : base(new UserProjectAccessPolicy(), _objectName)
|
||||||
@ -51,7 +57,7 @@ public class UserServiceAccountAccessPolicyResponseModel : BaseAccessPolicyRespo
|
|||||||
{
|
{
|
||||||
OrganizationUserId = accessPolicy.OrganizationUserId;
|
OrganizationUserId = accessPolicy.OrganizationUserId;
|
||||||
GrantedServiceAccountId = accessPolicy.GrantedServiceAccountId;
|
GrantedServiceAccountId = accessPolicy.GrantedServiceAccountId;
|
||||||
OrganizationUserName = accessPolicy.User?.Name;
|
OrganizationUserName = GetUserDisplayName(accessPolicy.User);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserServiceAccountAccessPolicyResponseModel() : base(new UserServiceAccountAccessPolicy(), _objectName)
|
public UserServiceAccountAccessPolicyResponseModel() : base(new UserServiceAccountAccessPolicy(), _objectName)
|
||||||
@ -115,6 +121,7 @@ public class ServiceAccountProjectAccessPolicyResponseModel : BaseAccessPolicyRe
|
|||||||
ServiceAccountId = accessPolicy.ServiceAccountId;
|
ServiceAccountId = accessPolicy.ServiceAccountId;
|
||||||
GrantedProjectId = accessPolicy.GrantedProjectId;
|
GrantedProjectId = accessPolicy.GrantedProjectId;
|
||||||
ServiceAccountName = accessPolicy.ServiceAccount?.Name;
|
ServiceAccountName = accessPolicy.ServiceAccount?.Name;
|
||||||
|
GrantedProjectName = accessPolicy.GrantedProject?.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServiceAccountProjectAccessPolicyResponseModel()
|
public ServiceAccountProjectAccessPolicyResponseModel()
|
||||||
@ -125,4 +132,5 @@ public class ServiceAccountProjectAccessPolicyResponseModel : BaseAccessPolicyRe
|
|||||||
public Guid? ServiceAccountId { get; set; }
|
public Guid? ServiceAccountId { get; set; }
|
||||||
public string? ServiceAccountName { get; set; }
|
public string? ServiceAccountName { get; set; }
|
||||||
public Guid? GrantedProjectId { get; set; }
|
public Guid? GrantedProjectId { get; set; }
|
||||||
|
public string? GrantedProjectName { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Models.Api;
|
||||||
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
|
||||||
|
namespace Bit.Api.SecretsManager.Models.Response;
|
||||||
|
|
||||||
|
public class PotentialGranteeResponseModel : ResponseModel
|
||||||
|
{
|
||||||
|
private const string _objectName = "potentialGrantee";
|
||||||
|
|
||||||
|
public PotentialGranteeResponseModel(Group group)
|
||||||
|
: base(_objectName)
|
||||||
|
{
|
||||||
|
if (group == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id = group.Id.ToString();
|
||||||
|
Name = group.Name;
|
||||||
|
Type = "group";
|
||||||
|
}
|
||||||
|
|
||||||
|
public PotentialGranteeResponseModel(OrganizationUserUserDetails user)
|
||||||
|
: base(_objectName)
|
||||||
|
{
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id = user.Id.ToString();
|
||||||
|
Name = user.Name;
|
||||||
|
Email = user.Email;
|
||||||
|
Type = "user";
|
||||||
|
}
|
||||||
|
|
||||||
|
public PotentialGranteeResponseModel(ServiceAccount serviceAccount)
|
||||||
|
: base(_objectName)
|
||||||
|
{
|
||||||
|
if (serviceAccount == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(serviceAccount));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id = serviceAccount.Id.ToString();
|
||||||
|
Name = serviceAccount.Name;
|
||||||
|
Type = "serviceAccount";
|
||||||
|
}
|
||||||
|
|
||||||
|
public PotentialGranteeResponseModel(Project project)
|
||||||
|
: base(_objectName)
|
||||||
|
{
|
||||||
|
if (project == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(project));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id = project.Id.ToString();
|
||||||
|
Name = project.Name;
|
||||||
|
Type = "project";
|
||||||
|
}
|
||||||
|
|
||||||
|
public PotentialGranteeResponseModel() : base(_objectName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string Type { get; set; }
|
||||||
|
public string? Email { get; set; }
|
||||||
|
}
|
@ -11,6 +11,7 @@ public class ProjectAccessPoliciesResponseModel : ResponseModel
|
|||||||
: base(_objectName)
|
: base(_objectName)
|
||||||
{
|
{
|
||||||
foreach (var baseAccessPolicy in baseAccessPolicies)
|
foreach (var baseAccessPolicy in baseAccessPolicies)
|
||||||
|
{
|
||||||
switch (baseAccessPolicy)
|
switch (baseAccessPolicy)
|
||||||
{
|
{
|
||||||
case UserProjectAccessPolicy accessPolicy:
|
case UserProjectAccessPolicy accessPolicy:
|
||||||
@ -24,6 +25,7 @@ public class ProjectAccessPoliciesResponseModel : ResponseModel
|
|||||||
new ServiceAccountProjectAccessPolicyResponseModel(accessPolicy));
|
new ServiceAccountProjectAccessPolicyResponseModel(accessPolicy));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProjectAccessPoliciesResponseModel() : base(_objectName)
|
public ProjectAccessPoliciesResponseModel() : base(_objectName)
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
using Bit.Core.Models.Api;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
|
||||||
|
namespace Bit.Api.SecretsManager.Models.Response;
|
||||||
|
|
||||||
|
public class SMExportResponseModel : ResponseModel
|
||||||
|
{
|
||||||
|
public SMExportResponseModel(IEnumerable<Project> projects, IEnumerable<Secret> secrets, string obj = "SecretsManagerExportResponseModel") : base(obj)
|
||||||
|
{
|
||||||
|
Secrets = secrets?.Select(s => new InnerSecretExportResponseModel(s));
|
||||||
|
Projects = projects?.Select(p => new InnerProjectExportResponseModel(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<InnerProjectExportResponseModel> Projects { get; set; }
|
||||||
|
public IEnumerable<InnerSecretExportResponseModel> Secrets { get; set; }
|
||||||
|
|
||||||
|
public class InnerProjectExportResponseModel
|
||||||
|
{
|
||||||
|
public InnerProjectExportResponseModel(Project project)
|
||||||
|
{
|
||||||
|
Id = project.Id;
|
||||||
|
Name = project.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InnerSecretExportResponseModel
|
||||||
|
{
|
||||||
|
public InnerSecretExportResponseModel(Secret secret)
|
||||||
|
{
|
||||||
|
Id = secret.Id;
|
||||||
|
Key = secret.Key;
|
||||||
|
Value = secret.Value;
|
||||||
|
Note = secret.Note;
|
||||||
|
ProjectIds = secret.Projects?.Select(p => p.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Key { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
public string Note { get; set; }
|
||||||
|
public IEnumerable<Guid> ProjectIds { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
using Bit.Core.Models.Api;
|
||||||
|
using Bit.Core.SecretsManager.Commands.Porting;
|
||||||
|
|
||||||
|
namespace Bit.Api.SecretsManager.Models.Response;
|
||||||
|
|
||||||
|
public class SMImportResponseModel : ResponseModel
|
||||||
|
{
|
||||||
|
public SMImportResponseModel(SMImport import, string obj = "SecretsManagerImportResponseModel") : base(obj)
|
||||||
|
{
|
||||||
|
Projects = import.Projects?.Select(p => new InnerProjectImportResponseModel(p));
|
||||||
|
Secrets = import.Secrets?.Select(s => new InnerSecretImportResponseModel(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<InnerProjectImportResponseModel> Projects { get; set; }
|
||||||
|
public IEnumerable<InnerSecretImportResponseModel> Secrets { get; set; }
|
||||||
|
|
||||||
|
public class InnerProjectImportResponseModel
|
||||||
|
{
|
||||||
|
public InnerProjectImportResponseModel() { }
|
||||||
|
|
||||||
|
public InnerProjectImportResponseModel(SMImport.InnerProject project)
|
||||||
|
{
|
||||||
|
Id = project.Id;
|
||||||
|
Name = project.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InnerSecretImportResponseModel
|
||||||
|
{
|
||||||
|
public InnerSecretImportResponseModel() { }
|
||||||
|
|
||||||
|
public InnerSecretImportResponseModel(SMImport.InnerSecret secret)
|
||||||
|
{
|
||||||
|
Id = secret.Id;
|
||||||
|
Key = secret.Key;
|
||||||
|
Value = secret.Value;
|
||||||
|
Note = secret.Note;
|
||||||
|
ProjectIds = secret.ProjectIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Key { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
public string Note { get; set; }
|
||||||
|
public IEnumerable<Guid> ProjectIds { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
using Bit.Core.Models.Api;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
|
||||||
|
namespace Bit.Api.SecretsManager.Models.Response;
|
||||||
|
|
||||||
|
public class ServiceAccountAccessPoliciesResponseModel : ResponseModel
|
||||||
|
{
|
||||||
|
private const string _objectName = "serviceAccountAccessPolicies";
|
||||||
|
|
||||||
|
public ServiceAccountAccessPoliciesResponseModel(IEnumerable<BaseAccessPolicy> baseAccessPolicies)
|
||||||
|
: base(_objectName)
|
||||||
|
{
|
||||||
|
if (baseAccessPolicies == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var baseAccessPolicy in baseAccessPolicies)
|
||||||
|
{
|
||||||
|
switch (baseAccessPolicy)
|
||||||
|
{
|
||||||
|
case UserServiceAccountAccessPolicy accessPolicy:
|
||||||
|
UserAccessPolicies.Add(new UserServiceAccountAccessPolicyResponseModel(accessPolicy));
|
||||||
|
break;
|
||||||
|
case GroupServiceAccountAccessPolicy accessPolicy:
|
||||||
|
GroupAccessPolicies.Add(new GroupServiceAccountAccessPolicyResponseModel(accessPolicy));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceAccountAccessPoliciesResponseModel() : base(_objectName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UserServiceAccountAccessPolicyResponseModel> UserAccessPolicies { get; set; } = new();
|
||||||
|
|
||||||
|
public List<GroupServiceAccountAccessPolicyResponseModel> GroupAccessPolicies { get; set; } = new();
|
||||||
|
}
|
@ -92,6 +92,11 @@ public class ExceptionHandlerFilterAttribute : ExceptionFilterAttribute
|
|||||||
errorMessage = "Unauthorized.";
|
errorMessage = "Unauthorized.";
|
||||||
context.HttpContext.Response.StatusCode = 401;
|
context.HttpContext.Response.StatusCode = 401;
|
||||||
}
|
}
|
||||||
|
else if (exception is ConflictException)
|
||||||
|
{
|
||||||
|
errorMessage = exception.Message;
|
||||||
|
context.HttpContext.Response.StatusCode = 409;
|
||||||
|
}
|
||||||
else if (exception is AggregateException aggregateException)
|
else if (exception is AggregateException aggregateException)
|
||||||
{
|
{
|
||||||
context.HttpContext.Response.StatusCode = 400;
|
context.HttpContext.Response.StatusCode = 400;
|
||||||
|
@ -85,8 +85,8 @@
|
|||||||
},
|
},
|
||||||
"Azure.Core": {
|
"Azure.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.24.0",
|
"resolved": "1.25.0",
|
||||||
"contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
|
"contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
@ -123,28 +123,28 @@
|
|||||||
},
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.11.0",
|
"resolved": "12.14.1",
|
||||||
"contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==",
|
"contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Common": {
|
"Azure.Storage.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.10.0",
|
"resolved": "12.13.0",
|
||||||
"contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==",
|
"contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Core": "1.22.0",
|
"Azure.Core": "1.25.0",
|
||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.9.0",
|
"resolved": "12.12.0",
|
||||||
"contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==",
|
"contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Memory.Data": "1.0.2",
|
"System.Memory.Data": "1.0.2",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
@ -171,6 +171,14 @@
|
|||||||
"resolved": "2.0.123",
|
"resolved": "2.0.123",
|
||||||
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
||||||
},
|
},
|
||||||
|
"DnsClient": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.0",
|
||||||
|
"contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Win32.Registry": "5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Fido2": {
|
"Fido2": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "3.0.1",
|
"resolved": "3.0.1",
|
||||||
@ -2794,10 +2802,11 @@
|
|||||||
"AspNetCoreRateLimit": "[4.0.2, )",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
||||||
"Azure.Storage.Blobs": "[12.11.0, )",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "[12.9.0, )",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "[1.0.1907, )",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "[5.12.0, )",
|
"Braintree": "[5.12.0, )",
|
||||||
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "[3.0.1, )",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
|
@ -74,8 +74,8 @@
|
|||||||
},
|
},
|
||||||
"Azure.Core": {
|
"Azure.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.24.0",
|
"resolved": "1.25.0",
|
||||||
"contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==",
|
"contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
@ -112,28 +112,28 @@
|
|||||||
},
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.11.0",
|
"resolved": "12.14.1",
|
||||||
"contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==",
|
"contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Common": {
|
"Azure.Storage.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.10.0",
|
"resolved": "12.13.0",
|
||||||
"contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==",
|
"contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Core": "1.22.0",
|
"Azure.Core": "1.25.0",
|
||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.9.0",
|
"resolved": "12.12.0",
|
||||||
"contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==",
|
"contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Storage.Common": "12.10.0",
|
"Azure.Storage.Common": "12.13.0",
|
||||||
"System.Memory.Data": "1.0.2",
|
"System.Memory.Data": "1.0.2",
|
||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
@ -160,6 +160,14 @@
|
|||||||
"resolved": "2.0.123",
|
"resolved": "2.0.123",
|
||||||
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
|
||||||
},
|
},
|
||||||
|
"DnsClient": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.0",
|
||||||
|
"contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Win32.Registry": "5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Fido2": {
|
"Fido2": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "3.0.1",
|
"resolved": "3.0.1",
|
||||||
@ -3245,10 +3253,11 @@
|
|||||||
"AspNetCoreRateLimit": "[4.0.2, )",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )",
|
||||||
"Azure.Storage.Blobs": "[12.11.0, )",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "[12.9.0, )",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "[1.0.1907, )",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "[5.12.0, )",
|
"Braintree": "[5.12.0, )",
|
||||||
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "[3.0.1, )",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
|
@ -35,6 +35,7 @@ public class CurrentContext : ICurrentContext
|
|||||||
public virtual string ClientId { get; set; }
|
public virtual string ClientId { get; set; }
|
||||||
public virtual Version ClientVersion { get; set; }
|
public virtual Version ClientVersion { get; set; }
|
||||||
public virtual ClientType ClientType { get; set; }
|
public virtual ClientType ClientType { get; set; }
|
||||||
|
public virtual Guid? ServiceAccountOrganizationId { get; set; }
|
||||||
|
|
||||||
public CurrentContext(IProviderUserRepository providerUserRepository)
|
public CurrentContext(IProviderUserRepository providerUserRepository)
|
||||||
{
|
{
|
||||||
@ -146,6 +147,11 @@ public class CurrentContext : ICurrentContext
|
|||||||
ClientType = c;
|
ClientType = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ClientType == ClientType.ServiceAccount)
|
||||||
|
{
|
||||||
|
ServiceAccountOrganizationId = new Guid(GetClaimValue(claimsDict, Claims.Organization));
|
||||||
|
}
|
||||||
|
|
||||||
DeviceIdentifier = GetClaimValue(claimsDict, Claims.Device);
|
DeviceIdentifier = GetClaimValue(claimsDict, Claims.Device);
|
||||||
|
|
||||||
Organizations = GetOrganizations(claimsDict, orgApi);
|
Organizations = GetOrganizations(claimsDict, orgApi);
|
||||||
@ -445,6 +451,11 @@ public class CurrentContext : ICurrentContext
|
|||||||
|
|
||||||
public bool AccessSecretsManager(Guid orgId)
|
public bool AccessSecretsManager(Guid orgId)
|
||||||
{
|
{
|
||||||
|
if (ServiceAccountOrganizationId.HasValue && ServiceAccountOrganizationId.Value == orgId)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return Organizations?.Any(o => o.Id == orgId && o.AccessSecretsManager) ?? false;
|
return Organizations?.Any(o => o.Id == orgId && o.AccessSecretsManager) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,9 +24,10 @@
|
|||||||
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.7.0.150" />
|
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.7.0.150" />
|
||||||
<PackageReference Include="AWSSDK.SQS" Version="3.7.2.47" />
|
<PackageReference Include="AWSSDK.SQS" Version="3.7.2.47" />
|
||||||
<PackageReference Include="Azure.Extensions.AspNetCore.DataProtection.Blobs" Version="1.2.1" />
|
<PackageReference Include="Azure.Extensions.AspNetCore.DataProtection.Blobs" Version="1.2.1" />
|
||||||
<PackageReference Include="Azure.Storage.Blobs" Version="12.11.0" />
|
<PackageReference Include="Azure.Storage.Blobs" Version="12.14.1" />
|
||||||
<PackageReference Include="Azure.Storage.Queues" Version="12.9.0" />
|
<PackageReference Include="Azure.Storage.Queues" Version="12.12.0" />
|
||||||
<PackageReference Include="BitPay.Light" Version="1.0.1907" />
|
<PackageReference Include="BitPay.Light" Version="1.0.1907" />
|
||||||
|
<PackageReference Include="DnsClient" Version="1.7.0" />
|
||||||
<PackageReference Include="Fido2.AspNet" Version="3.0.1" />
|
<PackageReference Include="Fido2.AspNet" Version="3.0.1" />
|
||||||
<PackageReference Include="Handlebars.Net" Version="2.1.2" />
|
<PackageReference Include="Handlebars.Net" Version="2.1.2" />
|
||||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
||||||
|
@ -28,6 +28,9 @@ public class Event : ITableObject<Guid>, IEvent
|
|||||||
IpAddress = e.IpAddress;
|
IpAddress = e.IpAddress;
|
||||||
ActingUserId = e.ActingUserId;
|
ActingUserId = e.ActingUserId;
|
||||||
SystemUser = e.SystemUser;
|
SystemUser = e.SystemUser;
|
||||||
|
DomainName = e.DomainName;
|
||||||
|
SecretId = e.SecretId;
|
||||||
|
ServiceAccountId = e.ServiceAccountId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
@ -49,6 +52,9 @@ public class Event : ITableObject<Guid>, IEvent
|
|||||||
public string IpAddress { get; set; }
|
public string IpAddress { get; set; }
|
||||||
public Guid? ActingUserId { get; set; }
|
public Guid? ActingUserId { get; set; }
|
||||||
public EventSystemUser? SystemUser { get; set; }
|
public EventSystemUser? SystemUser { get; set; }
|
||||||
|
public string DomainName { get; set; }
|
||||||
|
public Guid? SecretId { get; set; }
|
||||||
|
public Guid? ServiceAccountId { get; set; }
|
||||||
|
|
||||||
public void SetNewId()
|
public void SetNewId()
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models;
|
using Bit.Core.Models;
|
||||||
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.Entities;
|
namespace Bit.Core.Entities;
|
||||||
@ -198,4 +199,33 @@ public class Organization : ITableObject<Guid>, ISubscriber, IStorable, IStorabl
|
|||||||
|
|
||||||
return providers[provider];
|
return providers[provider];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateFromLicense(OrganizationLicense license)
|
||||||
|
{
|
||||||
|
Name = license.Name;
|
||||||
|
BusinessName = license.BusinessName;
|
||||||
|
BillingEmail = license.BillingEmail;
|
||||||
|
PlanType = license.PlanType;
|
||||||
|
Seats = license.Seats;
|
||||||
|
MaxCollections = license.MaxCollections;
|
||||||
|
UseGroups = license.UseGroups;
|
||||||
|
UseDirectory = license.UseDirectory;
|
||||||
|
UseEvents = license.UseEvents;
|
||||||
|
UseTotp = license.UseTotp;
|
||||||
|
Use2fa = license.Use2fa;
|
||||||
|
UseApi = license.UseApi;
|
||||||
|
UsePolicies = license.UsePolicies;
|
||||||
|
UseSso = license.UseSso;
|
||||||
|
UseKeyConnector = license.UseKeyConnector;
|
||||||
|
UseScim = license.UseScim;
|
||||||
|
UseResetPassword = license.UseResetPassword;
|
||||||
|
SelfHost = license.SelfHost;
|
||||||
|
UsersGetPremium = license.UsersGetPremium;
|
||||||
|
UseCustomPermissions = license.UseCustomPermissions;
|
||||||
|
Plan = license.Plan;
|
||||||
|
Enabled = license.Enabled;
|
||||||
|
ExpirationDate = license.Expires;
|
||||||
|
LicenseKey = license.LicenseKey;
|
||||||
|
RevisionDate = DateTime.UtcNow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
48
src/Core/Entities/OrganizationDomain.cs
Normal file
48
src/Core/Entities/OrganizationDomain.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
|
namespace Bit.Core.Entities;
|
||||||
|
|
||||||
|
public class OrganizationDomain : ITableObject<Guid>
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid OrganizationId { get; set; }
|
||||||
|
public string Txt { get; set; }
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string DomainName { get; set; }
|
||||||
|
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
|
||||||
|
public DateTime? VerifiedDate { get; private set; }
|
||||||
|
public DateTime NextRunDate { get; private set; }
|
||||||
|
public DateTime? LastCheckedDate { get; private set; }
|
||||||
|
public int JobRunCount { get; private set; }
|
||||||
|
public void SetNewId() => Id = CoreHelpers.GenerateComb();
|
||||||
|
|
||||||
|
public void SetNextRunDate(int interval)
|
||||||
|
{
|
||||||
|
//verification can take up to 72 hours
|
||||||
|
//1st job runs after 12hrs, 2nd after 24hrs and 3rd after 36hrs
|
||||||
|
NextRunDate = JobRunCount == 0
|
||||||
|
? CreationDate.AddHours(interval)
|
||||||
|
: NextRunDate.AddHours((JobRunCount + 1) * interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetJobRunCount()
|
||||||
|
{
|
||||||
|
if (JobRunCount == 3)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JobRunCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVerifiedDate()
|
||||||
|
{
|
||||||
|
VerifiedDate = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLastCheckedDate()
|
||||||
|
{
|
||||||
|
LastCheckedDate = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
}
|
@ -62,7 +62,6 @@ public class User : ITableObject<Guid>, ISubscriber, IStorable, IStorableSubscri
|
|||||||
public bool UsesKeyConnector { get; set; }
|
public bool UsesKeyConnector { get; set; }
|
||||||
public int FailedLoginCount { get; set; }
|
public int FailedLoginCount { get; set; }
|
||||||
public DateTime? LastFailedLoginDate { get; set; }
|
public DateTime? LastFailedLoginDate { get; set; }
|
||||||
public bool UnknownDeviceVerificationEnabled { get; set; }
|
|
||||||
[MaxLength(7)]
|
[MaxLength(7)]
|
||||||
public string AvatarColor { get; set; }
|
public string AvatarColor { get; set; }
|
||||||
public DateTime? LastPasswordChangeDate { get; set; }
|
public DateTime? LastPasswordChangeDate { get; set; }
|
||||||
|
@ -48,4 +48,6 @@ public enum DeviceType : byte
|
|||||||
SafariExtension = 20,
|
SafariExtension = 20,
|
||||||
[Display(Name = "SDK")]
|
[Display(Name = "SDK")]
|
||||||
SDK = 21,
|
SDK = 21,
|
||||||
|
[Display(Name = "Server")]
|
||||||
|
Server = 22
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,6 @@
|
|||||||
|
|
||||||
public enum EventSystemUser : byte
|
public enum EventSystemUser : byte
|
||||||
{
|
{
|
||||||
SCIM = 1
|
SCIM = 1,
|
||||||
|
DomainVerification = 2
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
namespace Bit.Core.Enums;
|
namespace Bit.Core.Enums;
|
||||||
|
|
||||||
|
// Increment by 100 for each new set of events
|
||||||
public enum EventType : int
|
public enum EventType : int
|
||||||
{
|
{
|
||||||
User_LoggedIn = 1000,
|
User_LoggedIn = 1000,
|
||||||
@ -75,4 +76,11 @@ public enum EventType : int
|
|||||||
ProviderOrganization_Added = 1901,
|
ProviderOrganization_Added = 1901,
|
||||||
ProviderOrganization_Removed = 1902,
|
ProviderOrganization_Removed = 1902,
|
||||||
ProviderOrganization_VaultAccessed = 1903,
|
ProviderOrganization_VaultAccessed = 1903,
|
||||||
|
|
||||||
|
OrganizationDomain_Added = 2000,
|
||||||
|
OrganizationDomain_Removed = 2001,
|
||||||
|
OrganizationDomain_Verified = 2002,
|
||||||
|
OrganizationDomain_NotVerified = 2003,
|
||||||
|
|
||||||
|
Secret_Retrieved = 2100,
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user