diff --git a/.github/workflows/build-migrator-cli.yml b/.github/workflows/build-migrator-cli.yml new file mode 100644 index 0000000000..29c846c0b7 --- /dev/null +++ b/.github/workflows/build-migrator-cli.yml @@ -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" diff --git a/.github/workflows/build-self-host.yml b/.github/workflows/build-self-host.yml index e4ccb2eb2d..9050ce333a 100644 --- a/.github/workflows/build-self-host.yml +++ b/.github/workflows/build-self-host.yml @@ -9,6 +9,12 @@ on: paths-ignore: - ".github/workflows/**" workflow_dispatch: + pull_request: + branches-ignore: + - "l10n_master" + - "gh-pages" + paths: + - ".github/workflows/build-self-host.yml" jobs: build-docker: @@ -48,11 +54,21 @@ jobs: run: az acr login -n bitwardenqa - name: Login to Azure - Prod Subscription - if: ${{ env.is_publish_branch == 'true' }} uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf with: creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} + - name: 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 if: ${{ env.is_publish_branch == 'true' }} id: retrieve-secrets @@ -102,9 +118,9 @@ jobs: IMAGE_TAG: ${{ steps.tag.outputs.image_tag }} run: | if [ "$IMAGE_TAG" = "dev" ] || [ "$IMAGE_TAG" = "beta" ]; then - echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG},bitwarden/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT + echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG},bitwardenprod.azurecr.io/self-host:${IMAGE_TAG},bitwarden/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT else - echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT + echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG},bitwardenprod.azurecr.io/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT fi - name: Build Docker image @@ -118,6 +134,8 @@ jobs: linux/arm64/v8 push: true 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 if: ${{ env.is_publish_branch == 'true' }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9845c2ed2a..27de690f80 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -191,74 +191,145 @@ jobs: include: - project_name: Admin base_path: ./src - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true - project_name: Api base_path: ./src - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true - project_name: Attachments base_path: ./util - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] - project_name: Events base_path: ./src - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true - project_name: EventsProcessor base_path: ./src - docker_repos: [bitwardenqa.azurecr.io] + docker_repos: [bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true - project_name: Icons base_path: ./src - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true - project_name: Identity base_path: ./src - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true - project_name: MsSql base_path: ./util - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] - project_name: Nginx base_path: ./util - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] - project_name: Notifications base_path: ./src - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true - project_name: Server base_path: ./util - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true - project_name: Setup base_path: ./util - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true - project_name: Sso base_path: ./bitwarden_license/src - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true - project_name: Scim base_path: ./bitwarden_license/src - docker_repos: [bitwarden, bitwardenqa.azurecr.io] + docker_repos: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true - project_name: Billing base_path: ./src - docker_repos: [bitwardenqa.azurecr.io] + docker_repos: [bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] dotnet: true steps: - name: Checkout repo 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: | IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name if [[ "$IMAGE_TAG" == "master" ]]; then IMAGE_TAG=dev + elif [[ "$IMAGE_TAG" == "rc" ]] || [[ "$IMAGE_TAG" == "hotfix-rc" ]]; then + IMAGE_TAG=beta fi - echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV - ########## Build Docker Image ########## + echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT + - name: Setup project name id: setup run: | @@ -267,6 +338,13 @@ jobs: echo "PROJECT_NAME: $PROJECT_NAME" 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 if: ${{ matrix.dotnet }} uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 @@ -281,125 +359,26 @@ jobs: -d ${{ matrix.base_path }}/${{ matrix.project_name }}/obj/build-output/publish - name: Build Docker image - env: - 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 + uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5 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 - run: az acr login -n bitwardenqa - - - name: Tag and push image to QA ACR + - name: Push to DockerHub + if: contains(matrix.docker_repos, 'bitwarden') && env.is_publish_branch == 'true' env: + IMAGE_TAG: ${{ steps.tag.outputs.image_tag }} PROJECT_NAME: ${{ steps.setup.outputs.project_name }} - REGISTRY: bitwardenqa.azurecr.io run: | - docker tag $PROJECT_NAME \ - $REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }} - docker push $REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }} + docker tag bitwardenprod.azurecr.io/$PROJECT_NAME:$IMAGE_TAG bitwarden/$PROJECT_NAME:$IMAGE_TAG + docker push bitwarden/$PROJECT_NAME:$IMAGE_TAG - 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: | docker logout echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV diff --git a/.github/workflows/cleanup-after-pr.yml b/.github/workflows/cleanup-after-pr.yml index 696d84c8f4..ab6920aca2 100644 --- a/.github/workflows/cleanup-after-pr.yml +++ b/.github/workflows/cleanup-after-pr.yml @@ -22,10 +22,21 @@ jobs: - name: Login to Azure ACR 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 ########## - name: Remove the docker image from ACR env: - REGISTRY_NAME: bitwardenqa + REGISTRIES: | + registries: + - bitwardenprod + - bitwardenqa SERVICES: | services: - Admin @@ -45,21 +56,24 @@ jobs: run: | for SERVICE in $(echo "${{ env.SERVICES }}" | yq e ".services[]" - ) do - SERVICE_NAME=$(echo $SERVICE | awk '{print tolower($0)}') - IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name + for REGISTRY in $( echo "${{ env.REGISTRIES }}" | yq e ".registries[]" - ) + do + SERVICE_NAME=$(echo $SERVICE | awk '{print tolower($0)}') + IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name - echo "[*] Checking if remote exists: $REGISTRY_NAME.azurecr.io/$SERVICE_NAME:$IMAGE_TAG" - TAG_EXISTS=$( - az acr repository show-tags --name $REGISTRY_NAME --repository $SERVICE_NAME \ - | jq --arg $TAG "$IMAGE_TAG" -e '. | any(. == "$TAG")' - ) + echo "[*] Checking if remote exists: $REGISTRY.azurecr.io/$SERVICE_NAME:$IMAGE_TAG" + TAG_EXISTS=$( + az acr repository show-tags --name $REGISTRY --repository $SERVICE_NAME \ + | jq --arg $TAG "$IMAGE_TAG" -e '. | any(. == "$TAG")' + ) - if [[ "$TAG_EXISTS" == "true" ]]; then - echo "[*] Tag exists. Removing tag" - az acr repository delete --name $REGISTRY_NAME --image $SERVICE_NAME:$IMAGE_TAG --yes - else - echo "[*] Tag does not exist. No action needed" - fi + if [[ "$TAG_EXISTS" == "true" ]]; then + echo "[*] Tag exists. Removing tag" + az acr repository delete --name $REGISTRY --image $SERVICE_NAME:$IMAGE_TAG --yes + else + echo "[*] Tag does not exist. No action needed" + fi + done done - name: Log out of Docker diff --git a/.github/workflows/container-registry-purge.yml b/.github/workflows/container-registry-purge.yml index 8c9db2cbbd..b00b627809 100644 --- a/.github/workflows/container-registry-purge.yml +++ b/.github/workflows/container-registry-purge.yml @@ -11,28 +11,15 @@ jobs: purge: name: Purge old images runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - include: - - name: bitwardenqa - - name: bitwardenprod steps: - name: Login to Azure - if: matrix.name == 'bitwardenprod' uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf with: creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} - - name: Login to Azure - if: matrix.name == 'bitwardenqa' - uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf - with: - creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }} - - name: Purge images env: - REGISTRY: ${{ matrix.name }} + REGISTRY: bitwardenprod AGO_DUR_VER: "180d" AGO_DUR: "30d" run: | diff --git a/.github/workflows/database.yml b/.github/workflows/database.yml index 0e4a0171b3..4ef3aeaaeb 100644 --- a/.github/workflows/database.yml +++ b/.github/workflows/database.yml @@ -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 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 uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 with: name: report.xml - path: report.xml + path: | + report.xml + diff.sql - name: Validate XML run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 70f46ab4c7..fb1e5adc6d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -188,7 +188,7 @@ jobs: origin_docker_repo: bitwarden - project_name: EventsProcessor prod_acr: true - origin_docker_repo: bitwardenqa.azurecr.io + origin_docker_repo: bitwardenprod.azurecr.io - project_name: Icons origin_docker_repo: bitwarden prod_acr: true @@ -209,7 +209,7 @@ jobs: - project_name: Scim origin_docker_repo: bitwarden - project_name: Billing - origin_docker_repo: bitwardenqa.azurecr.io + origin_docker_repo: bitwardenprod.azurecr.io steps: - name: Print environment env: @@ -277,31 +277,19 @@ jobs: docker logout echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV - ########## ACR QA ########## - - name: Login to Azure - QA Subscription + ########## ACR PROD ########## + - name: Login to Azure - PROD Subscription uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a with: - creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }} + creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} - name: Login to Azure ACR - run: az acr login -n bitwardenqa - - - name: Pull latest project image - if: matrix.origin_docker_repo == 'bitwardenqa.azurecr.io' - env: - PROJECT_NAME: ${{ steps.setup.outputs.project_name }} - REGISTRY: bitwardenqa.azurecr.io - run: | - if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then - docker pull $REGISTRY/$PROJECT_NAME:latest - else - docker pull $REGISTRY/$PROJECT_NAME:$_BRANCH_NAME - fi + run: az acr login -n bitwardenprod - name: Tag version and latest env: PROJECT_NAME: ${{ steps.setup.outputs.project_name }} - REGISTRY: bitwardenqa.azurecr.io + REGISTRY: bitwardenprod.azurecr.io ORIGIN_REGISTRY: ${{ matrix.origin_docker_repo }} run: | if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then @@ -313,43 +301,6 @@ jobs: - name: Push version and latest image if: ${{ github.event.inputs.release_type != 'Dry Run' }} - env: - PROJECT_NAME: ${{ steps.setup.outputs.project_name }} - REGISTRY: bitwardenqa.azurecr.io - run: | - docker push $REGISTRY/$PROJECT_NAME:latest - docker push $REGISTRY/$PROJECT_NAME:$_RELEASE_VERSION - - - name: Log out of Docker - run: docker logout - - ########## ACR PROD ########## - - name: Login to Azure - PROD Subscription - if: matrix.prod_acr == true - uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a - with: - creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} - - - name: Login to Azure ACR - if: matrix.prod_acr == true - run: az acr login -n bitwardenprod - - - name: Tag version and latest - if: matrix.prod_acr == true - env: - PROJECT_NAME: ${{ steps.setup.outputs.project_name }} - REGISTRY: bitwardenprod.azurecr.io - ORIGIN_REGISTRY: ${{ matrix.origin_docker_repo }} - run: | - if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then - docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:latest $REGISTRY/$PROJECT_NAME:dryrun - else - docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:$_BRANCH_NAME $REGISTRY/$PROJECT_NAME:$_RELEASE_VERSION - docker tag $ORIGIN_REGISTRY/$PROJECT_NAME:$_BRANCH_NAME $REGISTRY/$PROJECT_NAME:latest - fi - - - name: Push version and latest image - if: ${{ github.event.inputs.release_type != 'Dry Run' && matrix.prod_acr == true }} env: PROJECT_NAME: ${{ steps.setup.outputs.project_name }} REGISTRY: bitwardenprod.azurecr.io @@ -358,7 +309,6 @@ jobs: docker push $REGISTRY/$PROJECT_NAME:latest - name: Log out of Docker - if: matrix.prod_acr == true run: docker logout release: diff --git a/Directory.Build.props b/Directory.Build.props index ce11b46f50..f4decd764e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ net6.0 - 2023.1.0 + 2023.2.1 Bit.$(MSBuildProjectName) true enable diff --git a/README.md b/README.md index e6c0dcd85e..a3996383c3 100644 --- a/README.md +++ b/README.md @@ -80,16 +80,3 @@ Consider installing our git pre-commit hook for automatic formatting. ```bash git config --local core.hooksPath .git-hooks ``` - -### File Scoped Namespaces - -We recently migrated to using file scoped namespaces to save some horizontal space. All previous branches will need to update to avoid large merge conflicts using the following steps: - -1. Check out your local Branch -2. Run `git merge 9b7aef0763ad14e229b337c3b5b27cb411009792` -3. Resolve any merge conflicts, commit. -4. Run `dotnet format` -5. Commit -6. Run `git merge -Xours 7f5f010e1eea400300c47f776604ecf46c4b4f2d` -7. Fix Merge conflicts -8. Push diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/CreateAccessPoliciesCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/CreateAccessPoliciesCommand.cs index 05af497e65..ec9ce3f357 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/CreateAccessPoliciesCommand.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/CreateAccessPoliciesCommand.cs @@ -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.Entities; using Bit.Core.SecretsManager.Repositories; @@ -8,13 +9,36 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies; public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand { 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; + _projectRepository = projectRepository; + _serviceAccountRepository = serviceAccountRepository; } - public async Task> CreateAsync(List accessPolicies) + private static IEnumerable GetDistinctGrantedProjectIds(List accessPolicies) + { + var userGrantedIds = accessPolicies.OfType().Select(ap => ap.GrantedProjectId); + var groupGrantedIds = accessPolicies.OfType().Select(ap => ap.GrantedProjectId); + var saGrantedIds = accessPolicies.OfType().Select(ap => ap.GrantedProjectId); + return userGrantedIds.Concat(groupGrantedIds).Concat(saGrantedIds).Distinct(); + } + + private static IEnumerable GetDistinctGrantedServiceAccountIds(List accessPolicies) + { + var userGrantedIds = accessPolicies.OfType().Select(ap => ap.GrantedServiceAccountId); + var groupGrantedIds = accessPolicies.OfType() + .Select(ap => ap.GrantedServiceAccountId); + return userGrantedIds.Concat(groupGrantedIds).Distinct(); + } + + private static void CheckForDistinctAccessPolicies(IReadOnlyCollection accessPolicies) { var distinctAccessPolicies = accessPolicies.DistinctBy(baseAccessPolicy => { @@ -22,8 +46,12 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand { UserProjectAccessPolicy ap => new Tuple(ap.OrganizationUserId, ap.GrantedProjectId), GroupProjectAccessPolicy ap => new Tuple(ap.GroupId, ap.GrantedProjectId), - ServiceAccountProjectAccessPolicy ap => new Tuple(ap.ServiceAccountId, ap.GrantedProjectId), - _ => throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy)) + ServiceAccountProjectAccessPolicy ap => new Tuple(ap.ServiceAccountId, + ap.GrantedProjectId), + UserServiceAccountAccessPolicy ap => new Tuple(ap.OrganizationUserId, + ap.GrantedServiceAccountId), + GroupServiceAccountAccessPolicy ap => new Tuple(ap.GroupId, ap.GrantedServiceAccountId), + _ => throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy)), }; }).ToList(); @@ -31,7 +59,44 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand { throw new BadRequestException("Resources must be unique"); } + } + public async Task> CreateManyAsync(List 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 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 accessPolicies) + { foreach (var accessPolicy in accessPolicies) { if (await _accessPolicyRepository.AccessPolicyExists(accessPolicy)) @@ -39,7 +104,42 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand 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(); + } } } diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/DeleteAccessPolicyCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/DeleteAccessPolicyCommand.cs index 0795d62905..ad5b3da14c 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/DeleteAccessPolicyCommand.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/DeleteAccessPolicyCommand.cs @@ -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.Entities; using Bit.Core.SecretsManager.Repositories; namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies; @@ -7,14 +10,23 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies; public class DeleteAccessPolicyCommand : IDeleteAccessPolicyCommand { 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; + _currentContext = currentContext; } - - public async Task DeleteAsync(Guid id) + public async Task DeleteAsync(Guid id, Guid userId) { var accessPolicy = await _accessPolicyRepository.GetByIdAsync(id); if (accessPolicy == null) @@ -22,6 +34,74 @@ public class DeleteAccessPolicyCommand : IDeleteAccessPolicyCommand throw new NotFoundException(); } + if (!await IsAllowedToDeleteAsync(accessPolicy, userId)) + { + throw new NotFoundException(); + } + await _accessPolicyRepository.DeleteAsync(id); } + + private async Task 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 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; + } } diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/UpdateAccessPolicyCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/UpdateAccessPolicyCommand.cs index b7ebd1706b..bf83b735cf 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/UpdateAccessPolicyCommand.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/AccessPolicies/UpdateAccessPolicyCommand.cs @@ -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.Entities; using Bit.Core.SecretsManager.Repositories; @@ -8,13 +10,23 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies; public class UpdateAccessPolicyCommand : IUpdateAccessPolicyCommand { 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; + _currentContext = currentContext; + _projectRepository = projectRepository; + _serviceAccountRepository = serviceAccountRepository; } - public async Task UpdateAsync(Guid id, bool read, bool write) + public async Task UpdateAsync(Guid id, bool read, bool write, Guid userId) { var accessPolicy = await _accessPolicyRepository.GetByIdAsync(id); if (accessPolicy == null) @@ -22,11 +34,78 @@ public class UpdateAccessPolicyCommand : IUpdateAccessPolicyCommand throw new NotFoundException(); } + if (!await IsAllowedToUpdateAsync(accessPolicy, userId)) + { + throw new NotFoundException(); + } + accessPolicy.Read = read; accessPolicy.Write = write; accessPolicy.RevisionDate = DateTime.UtcNow; - await _accessPolicyRepository.ReplaceAsync(accessPolicy); return accessPolicy; } + + private async Task 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 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; + } } diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Porting/ImportCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Porting/ImportCommand.cs new file mode 100644 index 0000000000..9520f6f00f --- /dev/null +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Porting/ImportCommand.cs @@ -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(); + var importedSecrets = new List(); + + 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(); + var secrets = new List(); + + 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, + }; + } +} diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/CreateProjectCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/CreateProjectCommand.cs index 6e86088dcb..055fda0c8b 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/CreateProjectCommand.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Projects/CreateProjectCommand.cs @@ -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.Repositories; @@ -6,15 +7,35 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.Projects; public class CreateProjectCommand : ICreateProjectCommand { + private readonly IAccessPolicyRepository _accessPolicyRepository; + private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IProjectRepository _projectRepository; - public CreateProjectCommand(IProjectRepository projectRepository) + public CreateProjectCommand( + IAccessPolicyRepository accessPolicyRepository, + IOrganizationUserRepository organizationUserRepository, + IProjectRepository projectRepository + ) { + _accessPolicyRepository = accessPolicyRepository; + _organizationUserRepository = organizationUserRepository; _projectRepository = projectRepository; } - public async Task CreateAsync(Project project) + public async Task 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 { accessPolicy }); + return createdProject; } } diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/CreateSecretCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/CreateSecretCommand.cs index d224247c1d..61558ad228 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/CreateSecretCommand.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/CreateSecretCommand.cs @@ -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.Repositories; @@ -7,14 +10,34 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.Secrets; public class CreateSecretCommand : ICreateSecretCommand { 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; + _projectRepository = projectRepository; + _currentContext = currentContext; } - public async Task CreateAsync(Secret secret) + public async Task 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); } } diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/DeleteSecretCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/DeleteSecretCommand.cs index c1872717b7..2d59cffe3b 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/DeleteSecretCommand.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/DeleteSecretCommand.cs @@ -1,4 +1,5 @@ using Bit.Core.Context; +using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.SecretsManager.Commands.Secrets.Interfaces; using Bit.Core.SecretsManager.Entities; @@ -10,25 +11,27 @@ public class DeleteSecretCommand : IDeleteSecretCommand { private readonly ICurrentContext _currentContext; private readonly ISecretRepository _secretRepository; + private readonly IProjectRepository _projectRepository; - public DeleteSecretCommand(ICurrentContext currentContext, ISecretRepository secretRepository) + public DeleteSecretCommand(ISecretRepository secretRepository, IProjectRepository projectRepository, ICurrentContext currentContext) { _currentContext = currentContext; _secretRepository = secretRepository; + _projectRepository = projectRepository; } - public async Task>> DeleteSecrets(List ids) + public async Task>> DeleteSecrets(List 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(); } // Ensure all secrets belongs to the same organization var organizationId = secrets.First().OrganizationId; - if (secrets.Any(p => p.OrganizationId != organizationId)) + if (secrets.Any(secret => secret.OrganizationId != organizationId)) { throw new BadRequestException(); } @@ -38,21 +41,46 @@ public class DeleteSecretCommand : IDeleteSecretCommand 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>(); + var deleteIds = new List(); + + foreach (var secret in secrets) { - var secret = secrets.FirstOrDefault(secret => secret.Id == id); - if (secret == null) + var hasAccess = orgAdmin; + + 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, "access denied")); } - // TODO Once permissions are implemented add check for each secret here. else { - return new Tuple(secret, ""); + deleteIds.Add(secret.Id); + results.Add(new Tuple(secret, "")); } - }).ToList(); + } + + + + if (deleteIds.Count > 0) + { + await _secretRepository.SoftDeleteManyByIdAsync(deleteIds); + } - await _secretRepository.SoftDeleteManyByIdAsync(ids); return results; } } diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/UpdateSecretCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/UpdateSecretCommand.cs index 4bd0cf4f31..583208adce 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/UpdateSecretCommand.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Secrets/UpdateSecretCommand.cs @@ -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.Entities; using Bit.Core.SecretsManager.Repositories; @@ -8,23 +10,45 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.Secrets; public class UpdateSecretCommand : IUpdateSecretCommand { 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; + _projectRepository = projectRepository; + _currentContext = currentContext; } - public async Task UpdateAsync(Secret secret) + public async Task UpdateAsync(Secret updatedSecret, Guid userId) { - var existingSecret = await _secretRepository.GetByIdAsync(secret.Id); - if (existingSecret == null) + var secret = await _secretRepository.GetByIdAsync(updatedSecret.Id); + if (secret == null || !_currentContext.AccessSecretsManager(secret.OrganizationId)) { throw new NotFoundException(); } - secret.OrganizationId = existingSecret.OrganizationId; - secret.CreationDate = existingSecret.CreationDate; - secret.DeletedDate = existingSecret.DeletedDate; + var orgAdmin = await _currentContext.OrganizationAdmin(secret.OrganizationId); + var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin); + + 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; await _secretRepository.UpdateAsync(secret); diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/ServiceAccounts/RevokeAccessTokensCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/ServiceAccounts/RevokeAccessTokensCommand.cs new file mode 100644 index 0000000000..7e3474c0c8 --- /dev/null +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/ServiceAccounts/RevokeAccessTokensCommand.cs @@ -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 Ids) + { + var accessTokens = await _apiKeyRepository.GetManyByServiceAccountIdAsync(serviceAccount.Id); + + var tokensToDelete = accessTokens.Where(at => Ids.Contains(at.Id)); + + await _apiKeyRepository.DeleteManyAsync(tokensToDelete); + } +} diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Trash/EmptyTrashCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Trash/EmptyTrashCommand.cs new file mode 100644 index 0000000000..9951d05752 --- /dev/null +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Trash/EmptyTrashCommand.cs @@ -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 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); + } +} diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Trash/RestoreTrashCommand.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Trash/RestoreTrashCommand.cs new file mode 100644 index 0000000000..ad0b5bb7a2 --- /dev/null +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/Commands/Trash/RestoreTrashCommand.cs @@ -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 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); + } +} diff --git a/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerCollectionExtensions.cs b/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerCollectionExtensions.cs index dc645d1a1e..3f871d8532 100644 --- a/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerCollectionExtensions.cs +++ b/bitwarden_license/src/Commercial.Core/SecretsManager/SecretsManagerCollectionExtensions.cs @@ -1,13 +1,17 @@ using Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies; 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.Secrets; 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.AccessTokens.Interfaces; +using Bit.Core.SecretsManager.Commands.Porting.Interfaces; using Bit.Core.SecretsManager.Commands.Projects.Interfaces; using Bit.Core.SecretsManager.Commands.Secrets.Interfaces; using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces; +using Bit.Core.SecretsManager.Commands.Trash.Interfaces; using Microsoft.Extensions.DependencyInjection; namespace Bit.Commercial.Core.SecretsManager; @@ -24,9 +28,13 @@ public static class SecretsManagerCollectionExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } } diff --git a/bitwarden_license/src/Commercial.Core/packages.lock.json b/bitwarden_license/src/Commercial.Core/packages.lock.json index 730790bc84..3bd4c03afd 100644 --- a/bitwarden_license/src/Commercial.Core/packages.lock.json +++ b/bitwarden_license/src/Commercial.Core/packages.lock.json @@ -45,8 +45,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -83,28 +83,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -126,6 +126,14 @@ "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": { "type": "Transitive", "resolved": "3.0.1", @@ -2549,10 +2557,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/AccessPolicyRepository.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/AccessPolicyRepository.cs index 1cf066190e..b456309051 100644 --- a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/AccessPolicyRepository.cs +++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/AccessPolicyRepository.cs @@ -1,10 +1,13 @@ -using AutoMapper; +using System.Linq.Expressions; +using AutoMapper; +using Bit.Core.Enums; using Bit.Core.SecretsManager.Repositories; using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Infrastructure.EntityFramework.SecretsManager.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; + namespace Bit.Commercial.Infrastructure.EntityFramework.SecretsManager.Repositories; public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPolicyRepository @@ -14,147 +17,160 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli { } + private static Expression> 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> CreateManyAsync(List baseAccessPolicies) { - using (var scope = ServiceScopeFactory.CreateScope()) + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + foreach (var baseAccessPolicy in baseAccessPolicies) { - var dbContext = GetDatabaseContext(scope); - foreach (var baseAccessPolicy in baseAccessPolicies) - { - baseAccessPolicy.SetNewId(); - switch (baseAccessPolicy) - { - case Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy: - { - var entity = - Mapper.Map(accessPolicy); - await dbContext.AddAsync(entity); - break; - } - case Core.SecretsManager.Entities.UserServiceAccountAccessPolicy accessPolicy: - { - var entity = - Mapper.Map(accessPolicy); - await dbContext.AddAsync(entity); - break; - } - case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy: - { - var entity = Mapper.Map(accessPolicy); - await dbContext.AddAsync(entity); - break; - } - case Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy accessPolicy: - { - var entity = Mapper.Map(accessPolicy); - await dbContext.AddAsync(entity); - break; - } - case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy: - { - var entity = Mapper.Map(accessPolicy); - await dbContext.AddAsync(entity); - break; - } - } - } - - await dbContext.SaveChangesAsync(); - return baseAccessPolicies; - } - } - - public async Task AccessPolicyExists(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy) - { - using (var scope = ServiceScopeFactory.CreateScope()) - { - var dbContext = GetDatabaseContext(scope); + baseAccessPolicy.SetNewId(); 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; + var entity = + Mapper.Map(accessPolicy); + await dbContext.AddAsync(entity); + break; + } + case Core.SecretsManager.Entities.UserServiceAccountAccessPolicy accessPolicy: + { + var entity = + Mapper.Map(accessPolicy); + await dbContext.AddAsync(entity); + break; } case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy: { - var policy = await dbContext.GroupProjectAccessPolicy - .Where(c => c.GroupId == accessPolicy.GroupId && - c.GrantedProjectId == accessPolicy.GrantedProjectId) - .FirstOrDefaultAsync(); - return policy != null; + var entity = Mapper.Map(accessPolicy); + await dbContext.AddAsync(entity); + break; + } + case Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy accessPolicy: + { + var entity = Mapper.Map(accessPolicy); + await dbContext.AddAsync(entity); + break; } case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy: { - var policy = await dbContext.ServiceAccountProjectAccessPolicy - .Where(c => c.ServiceAccountId == accessPolicy.ServiceAccountId && - c.GrantedProjectId == accessPolicy.GrantedProjectId) - .FirstOrDefaultAsync(); - return policy != null; + var entity = Mapper.Map(accessPolicy); + await dbContext.AddAsync(entity); + break; } - default: - throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy)); } } + + await dbContext.SaveChangesAsync(); + return baseAccessPolicies; + } + + public async Task AccessPolicyExists(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy) + { + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + switch (baseAccessPolicy) + { + case Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy: + { + var policy = await dbContext.UserProjectAccessPolicy + .Where(c => c.OrganizationUserId == accessPolicy.OrganizationUserId && + c.GrantedProjectId == accessPolicy.GrantedProjectId) + .FirstOrDefaultAsync(); + return policy != null; + } + case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy: + { + var policy = await dbContext.GroupProjectAccessPolicy + .Where(c => c.GroupId == accessPolicy.GroupId && + c.GrantedProjectId == accessPolicy.GrantedProjectId) + .FirstOrDefaultAsync(); + return policy != null; + } + case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy: + { + var policy = await dbContext.ServiceAccountProjectAccessPolicy + .Where(c => c.ServiceAccountId == accessPolicy.ServiceAccountId && + c.GrantedProjectId == accessPolicy.GrantedProjectId) + .FirstOrDefaultAsync(); + return policy != null; + } + 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 GetByIdAsync(Guid id) { - using (var scope = ServiceScopeFactory.CreateScope()) - { - var dbContext = GetDatabaseContext(scope); - var entity = await dbContext.AccessPolicies.Where(ap => ap.Id == id) - .Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User) - .Include(ap => ((GroupProjectAccessPolicy)ap).Group) - .Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount) - .FirstOrDefaultAsync(); + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + var entity = await dbContext.AccessPolicies.Where(ap => ap.Id == id) + .Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User) + .Include(ap => ((UserProjectAccessPolicy)ap).GrantedProject) + .Include(ap => ((GroupProjectAccessPolicy)ap).Group) + .Include(ap => ((GroupProjectAccessPolicy)ap).GrantedProject) + .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 null; - } - - return MapToCore(entity); - } + return entity == null ? null : MapToCore(entity); } 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); - var entity = await dbContext.AccessPolicies.FindAsync(baseAccessPolicy.Id); - if (entity != null) - { - dbContext.AccessPolicies.Attach(entity); - entity.Write = baseAccessPolicy.Write; - entity.Read = baseAccessPolicy.Read; - entity.RevisionDate = baseAccessPolicy.RevisionDate; - await dbContext.SaveChangesAsync(); - } + dbContext.AccessPolicies.Attach(entity); + entity.Write = baseAccessPolicy.Write; + entity.Read = baseAccessPolicy.Read; + entity.RevisionDate = baseAccessPolicy.RevisionDate; + await dbContext.SaveChangesAsync(); } } public async Task> GetManyByGrantedProjectIdAsync(Guid id) { - using (var scope = ServiceScopeFactory.CreateScope()) - { - var dbContext = GetDatabaseContext(scope); + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); - var entities = await dbContext.AccessPolicies.Where(ap => - ((UserProjectAccessPolicy)ap).GrantedProjectId == id || - ((GroupProjectAccessPolicy)ap).GrantedProjectId == id || - ((ServiceAccountProjectAccessPolicy)ap).GrantedProjectId == id) - .Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User) - .Include(ap => ((GroupProjectAccessPolicy)ap).Group) - .Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount) - .ToListAsync(); - - return entities.Select(MapToCore); - } + var entities = await dbContext.AccessPolicies.Where(ap => + ((UserProjectAccessPolicy)ap).GrantedProjectId == id || + ((GroupProjectAccessPolicy)ap).GrantedProjectId == id || + ((ServiceAccountProjectAccessPolicy)ap).GrantedProjectId == id) + .Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User) + .Include(ap => ((GroupProjectAccessPolicy)ap).Group) + .Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount) + .ToListAsync(); + return entities.Select(MapToCore); } public async Task> GetManyByGrantedServiceAccountIdAsync(Guid id) @@ -174,28 +190,51 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli 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); - var entity = await dbContext.AccessPolicies.FindAsync(id); - if (entity != null) - { - dbContext.Remove(entity); - await dbContext.SaveChangesAsync(); - } + dbContext.Remove(entity); + await dbContext.SaveChangesAsync(); } } - private Core.SecretsManager.Entities.BaseAccessPolicy MapToCore(BaseAccessPolicy baseAccessPolicyEntity) + public async Task> 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(ap), GroupProjectAccessPolicy ap => Mapper.Map(ap), - ServiceAccountProjectAccessPolicy ap => Mapper.Map(ap), - UserServiceAccountAccessPolicy ap => Mapper.Map(ap), - GroupServiceAccountAccessPolicy ap => Mapper.Map(ap), - _ => throw new ArgumentException("Unsupported access policy type") + ServiceAccountProjectAccessPolicy ap => Mapper + .Map(ap), + UserServiceAccountAccessPolicy ap => + Mapper.Map(ap), + GroupServiceAccountAccessPolicy ap => Mapper + .Map(ap), + _ => throw new ArgumentException("Unsupported access policy type"), }; - } } diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs index bf35a47dc2..782486601c 100644 --- a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs +++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs @@ -45,6 +45,24 @@ public class ProjectRepository : Repository>(projects); } + public async Task> 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>(projects); + } + private static Expression> UserHasReadAccessToProject(Guid userId) => p => p.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) || p.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read)); @@ -56,6 +74,9 @@ public class ProjectRepository : Repository> ServiceAccountHasReadAccessToProject(Guid serviceAccountId) => p => p.ServiceAccountAccessPolicies.Any(ap => ap.ServiceAccount.Id == serviceAccountId && ap.Read); + private static Expression> ServiceAccountHasWriteAccessToProject(Guid serviceAccountId) => p => + p.ServiceAccountAccessPolicies.Any(ap => ap.ServiceAccount.Id == serviceAccountId && ap.Write); + public async Task DeleteManyByIdAsync(IEnumerable ids) { using (var scope = ServiceScopeFactory.CreateScope()) @@ -82,6 +103,28 @@ public class ProjectRepository : Repository 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 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 UserHasReadAccessToProject(Guid id, Guid userId) { using var scope = ServiceScopeFactory.CreateScope(); @@ -103,4 +146,14 @@ public class ProjectRepository : Repository> ImportAsync(IEnumerable projects) + { + using var scope = ServiceScopeFactory.CreateScope(); + var entities = projects.Select(p => Mapper.Map(p)); + var dbContext = GetDatabaseContext(scope); + await GetDbSet(dbContext).AddRangeAsync(entities); + await dbContext.SaveChangesAsync(); + return projects; + } } diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/SecretRepository.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/SecretRepository.cs index a17af71e60..e08f89efbe 100644 --- a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/SecretRepository.cs +++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/SecretRepository.cs @@ -1,4 +1,6 @@ -using AutoMapper; +using System.Linq.Expressions; +using AutoMapper; +using Bit.Core.Enums; using Bit.Core.SecretsManager.Repositories; using Bit.Infrastructure.EntityFramework; using Bit.Infrastructure.EntityFramework.Repositories; @@ -34,18 +36,48 @@ public class SecretRepository : Repository ids.Contains(c.Id) && c.DeletedDate == null) + .Include(c => c.Projects) .ToListAsync(); return Mapper.Map>(secrets); } } - public async Task> GetManyByOrganizationIdAsync(Guid organizationId) + private static Expression> ServiceAccountHasReadAccessToSecret(Guid serviceAccountId) => s => + s.Projects.Any(p => + p.ServiceAccountAccessPolicies.Any(ap => ap.ServiceAccount.Id == serviceAccountId && ap.Read)); + + private static Expression> 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> 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>(secrets); + } + + public async Task> GetManyByOrganizationIdInTrashByIdsAsync(Guid organizationId, IEnumerable ids) { using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); 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") .OrderBy(c => c.RevisionDate) .ToListAsync(); @@ -54,19 +86,42 @@ public class SecretRepository : Repository> GetManyByProjectIdAsync(Guid projectId) + public async Task> GetManyByOrganizationIdInTrashAsync(Guid organizationId) { using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); var secrets = await dbContext.Secret - .Where(s => s.Projects.Any(p => p.Id == projectId) && s.DeletedDate == null).Include("Projects") - .OrderBy(s => s.RevisionDate).ToListAsync(); + .Where(c => c.OrganizationId == organizationId && c.DeletedDate != null) + .Include("Projects") + .OrderBy(c => c.RevisionDate) + .ToListAsync(); return Mapper.Map>(secrets); } } + public async Task> GetManyByProjectIdAsync(Guid projectId, 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>(secrets); + } + } + public override async Task CreateAsync(Core.SecretsManager.Entities.Secret secret) { using (var scope = ServiceScopeFactory.CreateScope()) @@ -92,11 +147,11 @@ public class SecretRepository : Repository UpdateAsync(Core.SecretsManager.Entities.Secret secret) { - using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); var mappedEntity = Mapper.Map(secret); + var entity = await dbContext.Secret .Include("Projects") .FirstAsync(s => s.Id == secret.Id); @@ -136,4 +191,65 @@ public class SecretRepository : Repository 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 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> ImportAsync(IEnumerable secrets) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var entities = new List(); + var projects = secrets + .SelectMany(s => s.Projects ?? Enumerable.Empty()) + .DistinctBy(p => p.Id) + .Select(p => Mapper.Map(p)) + .ToDictionary(p => p.Id, p => p); + + dbContext.AttachRange(projects.Values); + + foreach (var s in secrets) + { + var entity = Mapper.Map(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; + } } diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ServiceAccountRepository.cs b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ServiceAccountRepository.cs index 4632abc8f7..5dd560c456 100644 --- a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ServiceAccountRepository.cs +++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ServiceAccountRepository.cs @@ -54,6 +54,23 @@ public class ServiceAccountRepository : Repository> 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>(serviceAccounts); + } + private static Expression> UserHasReadAccessToServiceAccount(Guid userId) => sa => sa.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) || sa.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read)); diff --git a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/packages.lock.json b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/packages.lock.json index f2b6cb0b24..9a1d2310a9 100644 --- a/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/packages.lock.json +++ b/bitwarden_license/src/Commercial.Infrastructure.EntityFramework/packages.lock.json @@ -63,8 +63,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -101,28 +101,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -144,6 +144,14 @@ "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": { "type": "Transitive", "resolved": "3.0.1", @@ -2722,10 +2730,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/bitwarden_license/src/Scim/Controllers/v2/UsersController.cs b/bitwarden_license/src/Scim/Controllers/v2/UsersController.cs index ea58a90af3..b198ff79e6 100644 --- a/bitwarden_license/src/Scim/Controllers/v2/UsersController.cs +++ b/bitwarden_license/src/Scim/Controllers/v2/UsersController.cs @@ -97,11 +97,11 @@ public class UsersController : Controller 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) { - await _organizationService.RevokeUserAsync(orgUser, null); + await _organizationService.RevokeUserAsync(orgUser, EventSystemUser.SCIM); } // Have to get full details object for response model diff --git a/bitwarden_license/src/Scim/packages.lock.json b/bitwarden_license/src/Scim/packages.lock.json index e0ee3394cd..a68dd3d1eb 100644 --- a/bitwarden_license/src/Scim/packages.lock.json +++ b/bitwarden_license/src/Scim/packages.lock.json @@ -71,8 +71,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -109,28 +109,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -157,6 +157,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2998,10 +3006,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/bitwarden_license/src/Sso/Controllers/AccountController.cs b/bitwarden_license/src/Sso/Controllers/AccountController.cs index fdbcf0f0d1..2f8e8e90f7 100644 --- a/bitwarden_license/src/Sso/Controllers/AccountController.cs +++ b/bitwarden_license/src/Sso/Controllers/AccountController.cs @@ -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 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 availableSeats = initialSeatCount - occupiedSeats; var prorationDate = DateTime.UtcNow; diff --git a/bitwarden_license/src/Sso/packages.lock.json b/bitwarden_license/src/Sso/packages.lock.json index a55e1aa2be..679027d573 100644 --- a/bitwarden_license/src/Sso/packages.lock.json +++ b/bitwarden_license/src/Sso/packages.lock.json @@ -74,8 +74,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -112,28 +112,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -160,6 +160,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2868,10 +2876,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/CreateAccessPoliciesCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/CreateAccessPoliciesCommandTests.cs index 6005f1a9a9..2c61e5ec80 100644 --- a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/CreateAccessPoliciesCommandTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/CreateAccessPoliciesCommandTests.cs @@ -1,7 +1,10 @@ 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.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 Bit.Test.Common.Helpers; @@ -11,21 +14,233 @@ using Xunit; namespace Bit.Commercial.Core.Test.SecretsManager.AccessPolicies; [SutProviderCustomize] +[ProjectCustomize] public class CreateAccessPoliciesCommandTests { - [Theory] - [BitAutoData] - public async Task CreateAsync_CallsCreate(List userProjectAccessPolicies, + private static List MakeGrantedProjectAccessPolicies(Guid grantedProjectId, List userProjectAccessPolicies, List groupProjectAccessPolicies, - List serviceAccountProjectAccessPolicies, - SutProvider sutProvider) + List serviceAccountProjectAccessPolicies) { var data = new List(); + 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(groupProjectAccessPolicies); data.AddRange(serviceAccountProjectAccessPolicies); + return data; + } - await sutProvider.Sut.CreateAsync(data); + private static List MakeGrantedServiceAccountAccessPolicies(Guid grantedServiceAccountId, List userServiceAccountAccessPolicies, + List groupServiceAccountAccessPolicies) + { + var data = new List(); + 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 MakeDuplicate(List 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 sutProvider, + PermissionType permissionType, Project project, Guid userId) + { + if (permissionType == PermissionType.RunAsUserWithPermission) + { + sutProvider.GetDependency().UserHasWriteAccessToProject(project.Id, userId) + .Returns(true); + } + } + + private static void SetupPermission(SutProvider sutProvider, + PermissionType permissionType, ServiceAccount serviceAccount, Guid userId) + { + if (permissionType == PermissionType.RunAsUserWithPermission) + { + sutProvider.GetDependency() + .UserHasWriteAccessToServiceAccount(serviceAccount.Id, userId).Returns(true); + } + } + + [Theory] + [BitAutoData] + public async Task CreateMany_AlreadyExists_Throws_BadRequestException( + Guid userId, + Project project, + ServiceAccount serviceAccount, + List userProjectAccessPolicies, + List groupProjectAccessPolicies, + List serviceAccountProjectAccessPolicies, + List userServiceAccountAccessPolicies, + List groupServiceAccountAccessPolicies, + SutProvider sutProvider) + { + var data = MakeGrantedProjectAccessPolicies(project.Id, userProjectAccessPolicies, groupProjectAccessPolicies, + serviceAccountProjectAccessPolicies); + var saData = MakeGrantedServiceAccountAccessPolicies(serviceAccount.Id, userServiceAccountAccessPolicies, groupServiceAccountAccessPolicies); + data = data.Concat(saData).ToList(); + + sutProvider.GetDependency().AccessPolicyExists(Arg.Any()) + .Returns(true); + + await Assert.ThrowsAsync(() => + sutProvider.Sut.CreateManyAsync(data, userId, AccessClientType.NoAccessCheck)); + + await sutProvider.GetDependency().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 userProjectAccessPolicies, + List groupProjectAccessPolicies, + List serviceAccountProjectAccessPolicies, + List userServiceAccountAccessPolicies, + List groupServiceAccountAccessPolicies, + SutProvider 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(() => + sutProvider.Sut.CreateManyAsync(data, userId, AccessClientType.NoAccessCheck)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .CreateManyAsync(Arg.Any>()); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async Task CreateMany_Success( + PermissionType permissionType, + Guid userId, + Project project, + ServiceAccount serviceAccount, + List userProjectAccessPolicies, + List groupProjectAccessPolicies, + List serviceAccountProjectAccessPolicies, + List userServiceAccountAccessPolicies, + List groupServiceAccountAccessPolicies, + SutProvider 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().Received(1) .CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data))); @@ -33,94 +248,26 @@ public class CreateAccessPoliciesCommandTests [Theory] [BitAutoData] - public async Task CreateAsync_AlreadyExists_Throws_BadRequestException( + public async Task CreateMany_UserWithoutPermission_Throws( + Guid userId, + Project project, + ServiceAccount serviceAccount, List userProjectAccessPolicies, List groupProjectAccessPolicies, List serviceAccountProjectAccessPolicies, + List userServiceAccountAccessPolicies, + List groupServiceAccountAccessPolicies, SutProvider sutProvider) { - var data = new List(); - data.AddRange(userProjectAccessPolicies); - data.AddRange(groupProjectAccessPolicies); - data.AddRange(serviceAccountProjectAccessPolicies); + var data = MakeGrantedProjectAccessPolicies(project.Id, userProjectAccessPolicies, groupProjectAccessPolicies, + serviceAccountProjectAccessPolicies); + var saData = MakeGrantedServiceAccountAccessPolicies(serviceAccount.Id, userServiceAccountAccessPolicies, groupServiceAccountAccessPolicies); + data = data.Concat(saData).ToList(); - sutProvider.GetDependency().AccessPolicyExists(Arg.Any()) - .Returns(true); + await Assert.ThrowsAsync(() => + sutProvider.Sut.CreateManyAsync(data, userId, AccessClientType.User)); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.CreateAsync(data)); - - await sutProvider.GetDependency().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 userProjectAccessPolicies, - List groupProjectAccessPolicies, - List serviceAccountProjectAccessPolicies, - SutProvider sutProvider - ) - { - var data = new List(); - 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().AccessPolicyExists(Arg.Any()) - .Returns(true); - - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.CreateAsync(data)); - - await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().CreateManyAsync(default); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .CreateManyAsync(Arg.Any>()); } } diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/DeleteAccessPolicyCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/DeleteAccessPolicyCommandTests.cs index dfb09eb440..c1c5743d7c 100644 --- a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/DeleteAccessPolicyCommandTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/DeleteAccessPolicyCommandTests.cs @@ -1,7 +1,11 @@ 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.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; @@ -11,28 +15,216 @@ using Xunit; namespace Bit.Commercial.Core.Test.SecretsManager.AccessPolicies; [SutProviderCustomize] +[ProjectCustomize] public class DeleteAccessPolicyCommandTests { + private static void SetupPermission(SutProvider sutProvider, + PermissionType permissionType, Project grantedProject, Guid userId) + { + switch (permissionType) + { + case PermissionType.RunAsAdmin: + sutProvider.GetDependency().OrganizationAdmin(grantedProject.OrganizationId) + .Returns(true); + break; + case PermissionType.RunAsUserWithPermission: + sutProvider.GetDependency().UserHasWriteAccessToProject(grantedProject.Id, userId) + .Returns(true); + break; + default: + throw new ArgumentOutOfRangeException(nameof(permissionType), permissionType, null); + } + } + + private static void SetupPermission(SutProvider sutProvider, + PermissionType permissionType, ServiceAccount grantedServiceAccount, Guid userId) + { + switch (permissionType) + { + case PermissionType.RunAsAdmin: + sutProvider.GetDependency().OrganizationAdmin(grantedServiceAccount.OrganizationId) + .Returns(true); + break; + case PermissionType.RunAsUserWithPermission: + sutProvider.GetDependency() + .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] [BitAutoData] - public async Task DeleteAccessPolicy_Throws_NotFoundException(Guid data, + public async Task DeleteAccessPolicy_Throws_NotFoundException(Guid data, Guid userId, SutProvider sutProvider) { + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(true); sutProvider.GetDependency().GetByIdAsync(data).ReturnsNull(); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteAsync(data)); + await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteAsync(data, userId)); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().DeleteAsync(default); } [Theory] [BitAutoData] - public async Task DeleteAccessPolicy_Success(Guid data, + public async Task DeleteAccessPolicy_SmNotEnabled_Throws_NotFoundException(Guid data, Guid userId, SutProvider sutProvider) { - sutProvider.GetDependency().GetByIdAsync(data) - .Returns(new UserProjectAccessPolicy { Id = data }); + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(false); + sutProvider.GetDependency().GetByIdAsync(data).ReturnsNull(); + await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteAsync(data, userId)); + await sutProvider.GetDependency().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 sutProvider) + { + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(true); + var policyToReturn = + CreatePolicyToReturn(accessPolicyType, data, grantedProject, mockGroup, mockServiceAccount); + SetupPermission(sutProvider, permissionType, grantedProject, userId); + + sutProvider.GetDependency().GetByIdAsync(data) + .Returns(policyToReturn); + + await sutProvider.Sut.DeleteAsync(data, userId); await sutProvider.GetDependency().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 sutProvider) + { + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(true); + var policyToReturn = + CreatePolicyToReturn(accessPolicyType, data, grantedProject, mockGroup, mockServiceAccount); + + sutProvider.GetDependency().GetByIdAsync(data) + .Returns(policyToReturn); + + await Assert.ThrowsAsync(() => + sutProvider.Sut.DeleteAsync(data, userId)); + + await sutProvider.GetDependency().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 sutProvider) + { + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(true); + var policyToReturn = CreatePolicyToReturn(accessPolicyType, data, grantedServiceAccount, mockGroup); + SetupPermission(sutProvider, permissionType, grantedServiceAccount, userId); + sutProvider.GetDependency().GetByIdAsync(data) + .Returns(policyToReturn); + + await sutProvider.Sut.DeleteAsync(data, userId); + + await sutProvider.GetDependency().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 sutProvider) + { + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(true); + var policyToReturn = CreatePolicyToReturn(accessPolicyType, data, grantedServiceAccount, mockGroup); + + sutProvider.GetDependency().GetByIdAsync(data) + .Returns(policyToReturn); + + await Assert.ThrowsAsync(() => + sutProvider.Sut.DeleteAsync(data, userId)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().DeleteAsync(default); + } } diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/UpdateAccessPolicyCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/UpdateAccessPolicyCommandTests.cs index 482be48391..a0f69c9f25 100644 --- a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/UpdateAccessPolicyCommandTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/AccessPolicies/UpdateAccessPolicyCommandTests.cs @@ -1,7 +1,11 @@ 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.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 Bit.Test.Common.Helpers; @@ -11,30 +15,246 @@ using Xunit; namespace Bit.Commercial.Core.Test.SecretsManager.AccessPolicies; [SutProviderCustomize] +[ProjectCustomize] public class UpdateAccessPolicyCommandTests { + private static void SetupPermission(SutProvider sutProvider, + PermissionType permissionType, Project grantedProject, Guid userId) + { + switch (permissionType) + { + case PermissionType.RunAsAdmin: + sutProvider.GetDependency().OrganizationAdmin(grantedProject.OrganizationId) + .Returns(true); + break; + case PermissionType.RunAsUserWithPermission: + sutProvider.GetDependency().UserHasWriteAccessToProject(grantedProject.Id, userId) + .Returns(true); + break; + default: + throw new ArgumentOutOfRangeException(nameof(permissionType), permissionType, null); + } + } + + private static void SetupPermission(SutProvider sutProvider, + PermissionType permissionType, ServiceAccount grantedServiceAccount, Guid userId) + { + switch (permissionType) + { + case PermissionType.RunAsAdmin: + sutProvider.GetDependency().OrganizationAdmin(grantedServiceAccount.OrganizationId) + .Returns(true); + break; + case PermissionType.RunAsUserWithPermission: + sutProvider.GetDependency() + .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] [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 sutProvider) { - var exception = - await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateAsync(data, read, write)); + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(true); + await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateAsync(data, read, write, userId)); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().ReplaceAsync(default); } [Theory] [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 sutProvider) { - var existingPolicy = new UserProjectAccessPolicy { Id = data, Read = true, Write = true }; - sutProvider.GetDependency().GetByIdAsync(data).Returns(existingPolicy); - var result = await sutProvider.Sut.UpdateAsync(data, read, write); - await sutProvider.GetDependency().Received(1).ReplaceAsync(existingPolicy); + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(false); + await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateAsync(data, read, write, userId)); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().ReplaceAsync(default); + } + + [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 sutProvider) + { + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(true); + var policyToReturn = + CreatePolicyToReturn(accessPolicyType, data, grantedProject, mockGroup, mockServiceAccount); + SetupPermission(sutProvider, permissionType, grantedProject, userId); + + sutProvider.GetDependency().GetByIdAsync(data).Returns(policyToReturn); + var result = await sutProvider.Sut.UpdateAsync(data, read, write, userId); + await sutProvider.GetDependency().Received(1).ReplaceAsync(policyToReturn); AssertHelper.AssertRecent(result.RevisionDate); Assert.Equal(read, result.Read); 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 sutProvider) + { + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(true); + var policyToReturn = + CreatePolicyToReturn(accessPolicyType, data, grantedProject, mockGroup, mockServiceAccount); + sutProvider.GetDependency().GetByIdAsync(data).Returns(policyToReturn); + + await Assert.ThrowsAsync(() => + sutProvider.Sut.UpdateAsync(data, read, write, userId)); + await sutProvider.GetDependency().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 sutProvider) + { + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(true); + var policyToReturn = CreatePolicyToReturn(accessPolicyType, grantedServiceAccount, data, mockGroup); + SetupPermission(sutProvider, permissionType, grantedServiceAccount, userId); + + sutProvider.GetDependency().GetByIdAsync(data).Returns(policyToReturn); + var result = await sutProvider.Sut.UpdateAsync(data, read, write, userId); + await sutProvider.GetDependency().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 sutProvider) + { + sutProvider.GetDependency().AccessSecretsManager(Arg.Any()).Returns(true); + var policyToReturn = CreatePolicyToReturn(accessPolicyType, grantedServiceAccount, data, mockGroup); + sutProvider.GetDependency().GetByIdAsync(data).Returns(policyToReturn); + + await Assert.ThrowsAsync(() => + sutProvider.Sut.UpdateAsync(data, read, write, userId)); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().ReplaceAsync(default); + } } diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Enums/AccessPolicyType.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Enums/AccessPolicyType.cs new file mode 100644 index 0000000000..c46505b162 --- /dev/null +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Enums/AccessPolicyType.cs @@ -0,0 +1,11 @@ +namespace Bit.Commercial.Core.Test.SecretsManager.Enums; + +public enum AccessPolicyType +{ + UserProjectAccessPolicy, + GroupProjectAccessPolicy, + ServiceAccountProjectAccessPolicy, + UserServiceAccountAccessPolicy, + GroupServiceAccountAccessPolicy, + +} diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Enums/PermissionType.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Enums/PermissionType.cs new file mode 100644 index 0000000000..1c4e88e91c --- /dev/null +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Enums/PermissionType.cs @@ -0,0 +1,7 @@ +namespace Bit.Commercial.Core.Test.SecretsManager.Enums; + +public enum PermissionType +{ + RunAsAdmin, + RunAsUserWithPermission, +} diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Projects/CreateProjectCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Projects/CreateProjectCommandTests.cs new file mode 100644 index 0000000000..3739d4699f --- /dev/null +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Projects/CreateProjectCommandTests.cs @@ -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 sutProvider) + { + sutProvider.GetDependency() + .GetByOrganizationAsync(Arg.Any(), Arg.Any()) + .Returns(new OrganizationUser() { Id = userId }); + + sutProvider.GetDependency() + .CreateAsync(Arg.Any()) + .Returns(data); + + await sutProvider.Sut.CreateAsync(data, userId); + + await sutProvider.GetDependency().Received(1) + .CreateAsync(Arg.Is(data)); + + await sutProvider.GetDependency().Received(1) + .CreateManyAsync(Arg.Any>()); + } +} diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/CreateSecretCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/CreateSecretCommandTests.cs index 9accbc53fa..78b8c2a02e 100644 --- a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/CreateSecretCommandTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/CreateSecretCommandTests.cs @@ -1,4 +1,7 @@ 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.Repositories; using Bit.Core.Test.SecretsManager.AutoFixture.SecretsFixture; @@ -14,14 +17,54 @@ namespace Bit.Commercial.Core.Test.SecretsManager.Secrets; public class CreateSecretCommandTests { [Theory] - [BitAutoData] - public async Task CreateAsync_CallsCreate(Secret data, - SutProvider sutProvider) + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async Task CreateAsync_Success(PermissionType permissionType, Secret data, + SutProvider sutProvider, Guid userId, Project mockProject) { - await sutProvider.Sut.CreateAsync(data); + data.Projects = new List() { mockProject }; + + if (permissionType == PermissionType.RunAsAdmin) + { + sutProvider.GetDependency().OrganizationAdmin(data.OrganizationId).Returns(true); + } + else + { + sutProvider.GetDependency().OrganizationAdmin(data.OrganizationId).Returns(false); + sutProvider.GetDependency().UserHasWriteAccessToProject((Guid)(data.Projects?.First().Id), userId).Returns(true); + } + + await sutProvider.Sut.CreateAsync(data, userId); await sutProvider.GetDependency().Received(1) .CreateAsync(data); } + + + [Theory] + [BitAutoData] + public async Task CreateAsync_UserWithoutPermission_ThrowsNotFound(Secret data, + SutProvider sutProvider, Guid userId, Project mockProject) + { + data.Projects = new List() { mockProject }; + + sutProvider.GetDependency().OrganizationAdmin(data.OrganizationId).Returns(false); + sutProvider.GetDependency().UserHasWriteAccessToProject((Guid)(data.Projects?.First().Id), userId).Returns(false); + + await Assert.ThrowsAsync(() => + sutProvider.Sut.CreateAsync(data, userId)); + } + + [Theory] + [BitAutoData] + public async Task CreateAsync_NoProjects_User_ThrowsNotFound(Secret data, + SutProvider sutProvider, Guid userId) + { + data.Projects = null; + sutProvider.GetDependency().OrganizationAdmin(data.OrganizationId).Returns(false); + + await Assert.ThrowsAsync(() => + sutProvider.Sut.CreateAsync(data, userId)); + } } diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/DeleteSecretCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/DeleteSecretCommandTests.cs index cee70548e7..8026fcd8ee 100644 --- a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/DeleteSecretCommandTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/DeleteSecretCommandTests.cs @@ -1,16 +1,20 @@ 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.Repositories; +using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; +using Bit.Test.Common.Helpers; using NSubstitute; using Xunit; namespace Bit.Commercial.Core.Test.SecretsManager.Secrets; [SutProviderCustomize] +[ProjectCustomize] public class DeleteSecretCommandTests { [Theory] @@ -20,7 +24,7 @@ public class DeleteSecretCommandTests { sutProvider.GetDependency().GetManyByIds(data).Returns(new List()); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteSecrets(data)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteSecrets(data, default)); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().SoftDeleteManyByIdAsync(default); } @@ -36,22 +40,39 @@ public class DeleteSecretCommandTests }; sutProvider.GetDependency().GetManyByIds(data).Returns(new List() { secret }); - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteSecrets(data)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteSecrets(data, default)); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().SoftDeleteManyByIdAsync(default); } [Theory] - [BitAutoData] - public async Task DeleteSecrets_Success(List data, - SutProvider sutProvider) + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async Task DeleteSecrets_Success(PermissionType permissionType, List data, + SutProvider sutProvider, Guid userId, Guid organizationId, Project mockProject) { + List projects = null; + + if (permissionType == PermissionType.RunAsAdmin) + { + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(true); + } + else + { + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(false); + sutProvider.GetDependency().UserHasWriteAccessToProject(mockProject.Id, userId).Returns(true); + projects = new List() { mockProject }; + } + + var secrets = new List(); foreach (Guid id in data) { var secret = new Secret() { - Id = id + Id = id, + OrganizationId = organizationId, + Projects = projects }; secrets.Add(secret); } @@ -59,9 +80,9 @@ public class DeleteSecretCommandTests sutProvider.GetDependency().GetManyByIds(data).Returns(secrets); sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); - var results = await sutProvider.Sut.DeleteSecrets(data); + var results = await sutProvider.Sut.DeleteSecrets(data, userId); + await sutProvider.GetDependency().Received(1).SoftDeleteManyByIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data))); - await sutProvider.GetDependency().Received(1).SoftDeleteManyByIdAsync(Arg.Is(data)); foreach (var result in results) { Assert.Equal("", result.Item2); diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/UpdateSecretCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/UpdateSecretCommandTests.cs index 683868d7ed..faa6e7ec5e 100644 --- a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/UpdateSecretCommandTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Secrets/UpdateSecretCommandTests.cs @@ -1,7 +1,10 @@ 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.Repositories; +using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture; using Bit.Core.Test.SecretsManager.AutoFixture.SecretsFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -13,23 +16,38 @@ namespace Bit.Commercial.Core.Test.SecretsManager.Secrets; [SutProviderCustomize] [SecretCustomize] +[ProjectCustomize] public class UpdateSecretCommandTests { [Theory] [BitAutoData] public async Task UpdateAsync_SecretDoesNotExist_ThrowsNotFound(Secret data, SutProvider sutProvider) { - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateAsync(data)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.UpdateAsync(data, default)); await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().UpdateAsync(default); } [Theory] - [BitAutoData] - public async Task UpdateAsync_CallsReplaceAsync(Secret data, SutProvider sutProvider) + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async Task UpdateAsync_Success(PermissionType permissionType, Secret data, SutProvider sutProvider, Guid userId, Project mockProject) { + sutProvider.GetDependency().AccessSecretsManager(data.OrganizationId).Returns(true); + + if (permissionType == PermissionType.RunAsAdmin) + { + sutProvider.GetDependency().OrganizationAdmin(data.OrganizationId).Returns(true); + } + else + { + data.Projects = new List() { mockProject }; + sutProvider.GetDependency().OrganizationAdmin(data.OrganizationId).Returns(false); + sutProvider.GetDependency().UserHasWriteAccessToProject((Guid)(data.Projects?.First().Id), userId).Returns(true); + } + sutProvider.GetDependency().GetByIdAsync(data.Id).Returns(data); - await sutProvider.Sut.UpdateAsync(data); + await sutProvider.Sut.UpdateAsync(data, userId); await sutProvider.GetDependency().Received(1) .UpdateAsync(data); @@ -37,11 +55,14 @@ public class UpdateSecretCommandTests [Theory] [BitAutoData] - public async Task UpdateAsync_DoesNotModifyOrganizationId(Secret existingSecret, SutProvider sutProvider) + public async Task UpdateAsync_DoesNotModifyOrganizationId(Secret existingSecret, SutProvider sutProvider, Guid userId) { - sutProvider.GetDependency().GetByIdAsync(existingSecret.Id).Returns(existingSecret); - var updatedOrgId = Guid.NewGuid(); + sutProvider.GetDependency().OrganizationAdmin(existingSecret.OrganizationId).Returns(true); + sutProvider.GetDependency().AccessSecretsManager(existingSecret.OrganizationId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(existingSecret.Id).Returns(existingSecret); + sutProvider.GetDependency().OrganizationAdmin(updatedOrgId).Returns(true); + var secretUpdate = new Secret() { OrganizationId = updatedOrgId, @@ -49,7 +70,7 @@ public class UpdateSecretCommandTests 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.NotEqual(existingSecret.OrganizationId, updatedOrgId); @@ -57,9 +78,11 @@ public class UpdateSecretCommandTests [Theory] [BitAutoData] - public async Task UpdateAsync_DoesNotModifyCreationDate(Secret existingSecret, SutProvider sutProvider) + public async Task UpdateAsync_DoesNotModifyCreationDate(Secret existingSecret, SutProvider sutProvider, Guid userId) { + sutProvider.GetDependency().AccessSecretsManager(existingSecret.OrganizationId).Returns(true); sutProvider.GetDependency().GetByIdAsync(existingSecret.Id).Returns(existingSecret); + sutProvider.GetDependency().OrganizationAdmin(existingSecret.OrganizationId).Returns(true); var updatedCreationDate = DateTime.UtcNow; var secretUpdate = new Secret() @@ -67,9 +90,10 @@ public class UpdateSecretCommandTests CreationDate = updatedCreationDate, Id = existingSecret.Id, 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.NotEqual(existingSecret.CreationDate, updatedCreationDate); @@ -77,9 +101,11 @@ public class UpdateSecretCommandTests [Theory] [BitAutoData] - public async Task UpdateAsync_DoesNotModifyDeletionDate(Secret existingSecret, SutProvider sutProvider) + public async Task UpdateAsync_DoesNotModifyDeletionDate(Secret existingSecret, SutProvider sutProvider, Guid userId) { + sutProvider.GetDependency().AccessSecretsManager(existingSecret.OrganizationId).Returns(true); sutProvider.GetDependency().GetByIdAsync(existingSecret.Id).Returns(existingSecret); + sutProvider.GetDependency().OrganizationAdmin(existingSecret.OrganizationId).Returns(true); var updatedDeletionDate = DateTime.UtcNow; var secretUpdate = new Secret() @@ -87,9 +113,10 @@ public class UpdateSecretCommandTests DeletedDate = updatedDeletionDate, Id = existingSecret.Id, 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.NotEqual(existingSecret.DeletedDate, updatedDeletionDate); @@ -98,9 +125,12 @@ public class UpdateSecretCommandTests [Theory] [BitAutoData] - public async Task UpdateAsync_RevisionDateIsUpdatedToUtcNow(Secret existingSecret, SutProvider sutProvider) + public async Task UpdateAsync_RevisionDateIsUpdatedToUtcNow(Secret existingSecret, SutProvider sutProvider, Guid userId) { + sutProvider.GetDependency().OrganizationAdmin(existingSecret.OrganizationId).Returns(true); + sutProvider.GetDependency().AccessSecretsManager(existingSecret.OrganizationId).Returns(true); sutProvider.GetDependency().GetByIdAsync(existingSecret.Id).Returns(existingSecret); + sutProvider.GetDependency().OrganizationAdmin(existingSecret.OrganizationId).Returns(true); var updatedRevisionDate = DateTime.UtcNow.AddDays(10); var secretUpdate = new Secret() @@ -108,11 +138,12 @@ public class UpdateSecretCommandTests RevisionDate = updatedRevisionDate, Id = existingSecret.Id, 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); } } diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/ServiceAccounts/RevokeAccessTokenCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/ServiceAccounts/RevokeAccessTokenCommandTests.cs new file mode 100644 index 0000000000..5f077c4ace --- /dev/null +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/ServiceAccounts/RevokeAccessTokenCommandTests.cs @@ -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 sutProvider) + { + var apiKey1 = new ApiKey + { + Id = Guid.NewGuid(), + ServiceAccountId = serviceAccount.Id + }; + + var apiKey2 = new ApiKey + { + Id = Guid.NewGuid(), + ServiceAccountId = serviceAccount.Id + }; + + sutProvider.GetDependency() + .GetManyByServiceAccountIdAsync(serviceAccount.Id) + .Returns(new List { apiKey1, apiKey2 }); + + await sutProvider.Sut.RevokeAsync(serviceAccount, new List { apiKey1.Id }); + + await sutProvider.GetDependency().Received(1) + .DeleteManyAsync(Arg.Is>(arg => arg.SequenceEqual(new List { apiKey1 }))); + } +} diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Trash/EmptyTrashCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Trash/EmptyTrashCommandTests.cs new file mode 100644 index 0000000000..603e5fcf4a --- /dev/null +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Trash/EmptyTrashCommandTests.cs @@ -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 sutProvider) + { + s1.DeletedDate = DateTime.Now; + + var ids = new List { s1.Id, s2.Id }; + sutProvider.GetDependency() + .GetManyByOrganizationIdInTrashByIdsAsync(orgId, ids) + .Returns(new List { s1 }); + + await Assert.ThrowsAsync(() => sutProvider.Sut.EmptyTrash(orgId, ids)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().RestoreManyByIdAsync(default); + } + + [Theory] + [BitAutoData] + public async Task EmptyTrash_Success(Guid orgId, Secret s1, Secret s2, SutProvider sutProvider) + { + s1.DeletedDate = DateTime.Now; + + var ids = new List { s1.Id, s2.Id }; + sutProvider.GetDependency() + .GetManyByOrganizationIdInTrashByIdsAsync(orgId, ids) + .Returns(new List { s1, s2 }); + + await sutProvider.Sut.EmptyTrash(orgId, ids); + + await sutProvider.GetDependency().Received(1).HardDeleteManyByIdAsync(ids); + } +} diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Trash/RestoreTrashCommandTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Trash/RestoreTrashCommandTests.cs new file mode 100644 index 0000000000..1054ad154f --- /dev/null +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Trash/RestoreTrashCommandTests.cs @@ -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 sutProvider) + { + s1.DeletedDate = DateTime.Now; + + var ids = new List { s1.Id, s2.Id }; + sutProvider.GetDependency() + .GetManyByOrganizationIdInTrashByIdsAsync(orgId, ids) + .Returns(new List { s1 }); + + await Assert.ThrowsAsync(() => sutProvider.Sut.RestoreTrash(orgId, ids)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().RestoreManyByIdAsync(default); + } + + [Theory] + [BitAutoData] + public async Task RestoreTrash_Success(Guid orgId, Secret s1, Secret s2, SutProvider sutProvider) + { + s1.DeletedDate = DateTime.Now; + + var ids = new List { s1.Id, s2.Id }; + sutProvider.GetDependency() + .GetManyByOrganizationIdInTrashByIdsAsync(orgId, ids) + .Returns(new List { s1, s2 }); + + await sutProvider.Sut.RestoreTrash(orgId, ids); + + await sutProvider.GetDependency().Received(1).RestoreManyByIdAsync(ids); + } +} diff --git a/bitwarden_license/test/Commercial.Core.Test/packages.lock.json b/bitwarden_license/test/Commercial.Core.Test/packages.lock.json index b2acfe5143..5699c2e8a8 100644 --- a/bitwarden_license/test/Commercial.Core.Test/packages.lock.json +++ b/bitwarden_license/test/Commercial.Core.Test/packages.lock.json @@ -105,8 +105,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -143,28 +143,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -203,6 +203,14 @@ "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": { "type": "Transitive", "resolved": "2.1.1", @@ -2818,10 +2826,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json b/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json index 787f71bdc9..00e5f26b92 100644 --- a/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json +++ b/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json @@ -143,8 +143,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -181,28 +181,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -246,6 +246,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -3402,10 +3410,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/bitwarden_license/test/Scim.Test/packages.lock.json b/bitwarden_license/test/Scim.Test/packages.lock.json index 8f391f166c..8c8143f8f6 100644 --- a/bitwarden_license/test/Scim.Test/packages.lock.json +++ b/bitwarden_license/test/Scim.Test/packages.lock.json @@ -131,8 +131,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -169,28 +169,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -234,6 +234,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -3247,10 +3255,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/dev/ef_migrate.ps1 b/dev/ef_migrate.ps1 index 97e9939344..14cf13088b 100644 --- a/dev/ef_migrate.ps1 +++ b/dev/ef_migrate.ps1 @@ -4,6 +4,13 @@ param ( $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 $providers = @{ diff --git a/docker-unified/Dockerfile b/docker-unified/Dockerfile index 110ca7991b..04952c3d1c 100644 --- a/docker-unified/Dockerfile +++ b/docker-unified/Dockerfile @@ -1,3 +1,4 @@ +# syntax = docker/dockerfile:1.2 ############################################### # Build stage # ############################################### @@ -13,7 +14,12 @@ RUN apt-get update && apt-get install -y \ WORKDIR /tmp # 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 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 diff --git a/perf/MicroBenchmarks/packages.lock.json b/perf/MicroBenchmarks/packages.lock.json index 94f9b5bb6e..d19999b140 100644 --- a/perf/MicroBenchmarks/packages.lock.json +++ b/perf/MicroBenchmarks/packages.lock.json @@ -65,8 +65,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -103,28 +103,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -156,6 +156,14 @@ "resolved": "2.4.3", "contentHash": "U2FC9Y8NyIxxU6MpFFdWWu1xwiqz/61v/Doou7kmVjpeIEMLWyiNNkzNlSE84kyJ0O1LKApuEj5z48Ow0Hi4OQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2656,10 +2664,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/src/Admin/Jobs/DeleteUnverifiedOrganizationDomainsJob.cs b/src/Admin/Jobs/DeleteUnverifiedOrganizationDomainsJob.cs new file mode 100644 index 0000000000..0d2f2d8606 --- /dev/null +++ b/src/Admin/Jobs/DeleteUnverifiedOrganizationDomainsJob.cs @@ -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 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(); + await organizationDomainService.OrganizationDomainMaintenanceAsync(); + } + _logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteUnverifiedOrganizationDomainsJob: End"); + } +} diff --git a/src/Admin/Jobs/JobsHostedService.cs b/src/Admin/Jobs/JobsHostedService.cs index fd83e07af8..5aa770befc 100644 --- a/src/Admin/Jobs/JobsHostedService.cs +++ b/src/Admin/Jobs/JobsHostedService.cs @@ -64,6 +64,11 @@ public class JobsHostedService : BaseJobsHostedService .StartNow() .WithCronSchedule("0 */15 * ? * *") .Build(); + var everyDayAtTwoAmUtcTrigger = TriggerBuilder.Create() + .WithIdentity("EveryDayAtTwoAmUtcTrigger") + .StartNow() + .WithCronSchedule("0 0 2 ? * * *") + .Build(); var jobs = new List> { @@ -74,6 +79,7 @@ public class JobsHostedService : BaseJobsHostedService new Tuple(typeof(DeleteCiphersJob), everyDayAtMidnightUtc), new Tuple(typeof(DatabaseExpiredSponsorshipsJob), everyMondayAtMidnightTrigger), new Tuple(typeof(DeleteAuthRequestsJob), everyFifteenMinutesTrigger), + new Tuple(typeof(DeleteUnverifiedOrganizationDomainsJob), everyDayAtTwoAmUtcTrigger), }; if (!_globalSettings.SelfHosted) @@ -98,5 +104,6 @@ public class JobsHostedService : BaseJobsHostedService services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } } diff --git a/src/Admin/Models/OrganizationViewModel.cs b/src/Admin/Models/OrganizationViewModel.cs index 363851bb49..e0fb75d80e 100644 --- a/src/Admin/Models/OrganizationViewModel.cs +++ b/src/Admin/Models/OrganizationViewModel.cs @@ -20,7 +20,7 @@ public class OrganizationViewModel UserInvitedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Invited); UserAcceptedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Accepted); UserConfirmedCount = orgUsers.Count(u => u.Status == OrganizationUserStatusType.Confirmed); - OccupiedSeatCount = orgUsers.Count(u => u.OccupiesOrganizationSeat); + OccupiedSeatCount = UserInvitedCount + UserAcceptedCount + UserConfirmedCount; CipherCount = ciphers.Count(); CollectionCount = collections.Count(); GroupCount = groups?.Count() ?? 0; diff --git a/src/Admin/packages.lock.json b/src/Admin/packages.lock.json index 87e5807e1b..ca31e68892 100644 --- a/src/Admin/packages.lock.json +++ b/src/Admin/packages.lock.json @@ -94,8 +94,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -132,28 +132,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -182,21 +182,29 @@ }, "dbup-core": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "CR00QMAtHjfeMhwxFC5haoA0q4KZ5s6Y/AdZaT6oFjySik2eFEqVasuLgWSPKSiR7ti3z01BtiR7aD3nVckAsg==", + "resolved": "5.0.8", + "contentHash": "d+3RxJDftcarp1Y7jI78HRdRWRC7VFjM+rB2CFHWDmao6OixuLrqiyEo1DeuMNrWLTR5mmE8p1YTpFOvozI9ZQ==", "dependencies": { - "Microsoft.CSharp": "4.4.0", + "Microsoft.CSharp": "4.7.0", "System.Diagnostics.TraceSource": "4.3.0" } }, "dbup-sqlserver": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "/4hy4qmbWmtbLJGq8XCH3mtlgMld2G8rbXcjNDhqkq5y6dGZDW03OI4UsnQRxBiTQD5aYOcLuycK1dCJYhkdSw==", + "resolved": "5.0.8", + "contentHash": "b954l5Zgj9qgHtm16SLq2qGLJ0gIZtrWdh6JHoUsCLMHYW+0K2Oevabquw447At4U6X2t4CNuy7ZLHYf/Z/8yg==", "dependencies": { - "Microsoft.Azure.Services.AppAuthentication": "1.3.1", - "System.Data.SqlClient": "4.6.0", - "dbup-core": "4.5.0" + "Microsoft.Azure.Services.AppAuthentication": "1.6.2", + "Microsoft.Data.SqlClient": "5.0.1", + "dbup-core": "5.0.8" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" } }, "Fido2": { @@ -494,10 +502,10 @@ }, "Microsoft.Azure.Services.AppAuthentication": { "type": "Transitive", - "resolved": "1.3.1", - "contentHash": "59CEcmUSlg5nYOzcyhdoUu+EQH4wrjCKj7dNuuPMeIjDCikAON9/KQXTQLfzfWTjDwqHIRptAAj0DTBp25lFcg==", + "resolved": "1.6.2", + "contentHash": "rSQhTv43ionr9rWvE4vxIe/i73XR5hoBYfh7UUgdaVOGW1MZeikR9RmgaJhonTylimCcCuJvrU0zXsSIFOsTGw==", "dependencies": { - "Microsoft.IdentityModel.Clients.ActiveDirectory": "4.3.0", + "Microsoft.IdentityModel.Clients.ActiveDirectory": "5.2.9", "System.Diagnostics.Process": "4.3.0" } }, @@ -1081,15 +1089,22 @@ }, "Microsoft.IdentityModel.Clients.ActiveDirectory": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "IRXnTCHxwnpnGBHVnTWd8RBJk7nsBsNZVl8j20kh234bP+oBILkt+6Iw5vQg5Q+sZmALt3Oq6X3Kx7qY71XyVw==", + "resolved": "5.2.9", + "contentHash": "WhBAG/9hWiMHIXve4ZgwXP3spRwf7kFFfejf76QA5BvumgnPp8iDkDCiJugzAcpW1YaHB526z1UVxHhVT1E5qw==", "dependencies": { + "Microsoft.CSharp": "4.3.0", "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.Primitives": "4.3.0", "System.Security.Cryptography.X509Certificates": "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": { @@ -1513,16 +1528,6 @@ "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": { "type": "Transitive", "resolved": "4.3.0", @@ -1615,21 +1620,6 @@ "resolved": "4.3.2", "contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==" }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==" - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==" - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==" - }, "SendGrid": { "type": "Transitive", "resolved": "9.27.0", @@ -1904,29 +1894,69 @@ }, "System.Collections.NonGeneric": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==", + "resolved": "4.3.0", + "contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", "dependencies": { - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Collections.Specialized": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "/HKQyVP0yH1I0YtK7KJL/28snxHNH/bi+0lgk/+MbURF6ULhAE31MDI+NZDerNWu264YbxklXCCygISgm+HMug==", + "resolved": "4.3.0", + "contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==", "dependencies": { - "System.Collections.NonGeneric": "4.0.1", - "System.Globalization": "4.0.11", - "System.Globalization.Extensions": "4.0.1", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" + "System.Collections.NonGeneric": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.ComponentModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.ComponentModel.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": { @@ -2046,17 +2076,6 @@ "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": { "type": "Transitive", "resolved": "4.3.0", @@ -2160,24 +2179,23 @@ }, "System.Dynamic.Runtime": { "type": "Transitive", - "resolved": "4.0.11", - "contentHash": "db34f6LHYM0U0JpE+sOmjar27BnqTVkbLJhgfwMpTdgTigG/Hna3m2MYVwnFzGGKnEJk2UXFuoVTr8WUbU91/A==", + "resolved": "4.3.0", + "contentHash": "SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==", "dependencies": { - "System.Collections": "4.0.11", - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.Linq": "4.1.0", - "System.Linq.Expressions": "4.1.0", - "System.ObjectModel": "4.0.12", - "System.Reflection": "4.1.0", - "System.Reflection.Emit": "4.0.1", - "System.Reflection.Emit.ILGeneration": "4.0.1", - "System.Reflection.Primitives": "4.0.1", - "System.Reflection.TypeExtensions": "4.1.0", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Formats.Asn1": { @@ -2808,6 +2826,18 @@ "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": { "type": "Transitive", "resolved": "4.3.0", @@ -3299,7 +3329,7 @@ "commercial.core": { "type": "Project", "dependencies": { - "Core": "[2023.1.0, )" + "Core": "[2023.2.0, )" } }, "core": { @@ -3310,10 +3340,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", @@ -3345,7 +3376,7 @@ "infrastructure.dapper": { "type": "Project", "dependencies": { - "Core": "[2023.1.0, )", + "Core": "[2023.2.0, )", "Dapper": "[2.0.123, )" } }, @@ -3353,7 +3384,7 @@ "type": "Project", "dependencies": { "AutoMapper.Extensions.Microsoft.DependencyInjection": "[11.0.0, )", - "Core": "[2023.1.0, )", + "Core": "[2023.2.0, )", "Microsoft.EntityFrameworkCore.Relational": "[6.0.12, )", "Microsoft.EntityFrameworkCore.SqlServer": "[6.0.12, )", "Microsoft.EntityFrameworkCore.Sqlite": "[6.0.12, )", @@ -3365,38 +3396,38 @@ "migrator": { "type": "Project", "dependencies": { - "Core": "[2023.1.0, )", + "Core": "[2023.2.0, )", "Microsoft.Extensions.Logging": "[6.0.0, )", - "dbup-sqlserver": "[4.5.0, )" + "dbup-sqlserver": "[5.0.8, )" } }, "mysqlmigrations": { "type": "Project", "dependencies": { - "Core": "[2023.1.0, )", - "Infrastructure.EntityFramework": "[2023.1.0, )" + "Core": "[2023.2.0, )", + "Infrastructure.EntityFramework": "[2023.2.0, )" } }, "postgresmigrations": { "type": "Project", "dependencies": { - "Core": "[2023.1.0, )", - "Infrastructure.EntityFramework": "[2023.1.0, )" + "Core": "[2023.2.0, )", + "Infrastructure.EntityFramework": "[2023.2.0, )" } }, "sharedweb": { "type": "Project", "dependencies": { - "Core": "[2023.1.0, )", - "Infrastructure.Dapper": "[2023.1.0, )", - "Infrastructure.EntityFramework": "[2023.1.0, )" + "Core": "[2023.2.0, )", + "Infrastructure.Dapper": "[2023.2.0, )", + "Infrastructure.EntityFramework": "[2023.2.0, )" } }, "sqlitemigrations": { "type": "Project", "dependencies": { - "Core": "[2023.1.0, )", - "Infrastructure.EntityFramework": "[2023.1.0, )" + "Core": "[2023.2.0, )", + "Infrastructure.EntityFramework": "[2023.2.0, )" } } } diff --git a/src/Api/Controllers/OrganizationDomainController.cs b/src/Api/Controllers/OrganizationDomainController.cs new file mode 100644 index 0000000000..23e4f51bc7 --- /dev/null +++ b/src/Api/Controllers/OrganizationDomainController.cs @@ -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> 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(response); + } + + [HttpGet("{orgId}/domain/{id}")] + public async Task 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 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 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 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(); + } + } +} diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index baf0dbfaaf..e4c4003199 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -4,6 +4,7 @@ using Bit.Api.Models.Request.Accounts; using Bit.Api.Models.Request.Organizations; using Bit.Api.Models.Response; using Bit.Api.Models.Response.Organizations; +using Bit.Api.SecretsManager; using Bit.Api.Utilities; using Bit.Core.Context; using Bit.Core.Enums; @@ -38,6 +39,7 @@ public class OrganizationsController : Controller private readonly IRotateOrganizationApiKeyCommand _rotateOrganizationApiKeyCommand; private readonly ICreateOrganizationApiKeyCommand _createOrganizationApiKeyCommand; private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository; + private readonly IUpdateOrganizationLicenseCommand _updateOrganizationLicenseCommand; private readonly ICloudGetOrganizationLicenseQuery _cloudGetOrganizationLicenseQuery; private readonly GlobalSettings _globalSettings; @@ -55,6 +57,7 @@ public class OrganizationsController : Controller IRotateOrganizationApiKeyCommand rotateOrganizationApiKeyCommand, ICreateOrganizationApiKeyCommand createOrganizationApiKeyCommand, IOrganizationApiKeyRepository organizationApiKeyRepository, + IUpdateOrganizationLicenseCommand updateOrganizationLicenseCommand, ICloudGetOrganizationLicenseQuery cloudGetOrganizationLicenseQuery, GlobalSettings globalSettings) { @@ -71,6 +74,7 @@ public class OrganizationsController : Controller _rotateOrganizationApiKeyCommand = rotateOrganizationApiKeyCommand; _createOrganizationApiKeyCommand = createOrganizationApiKeyCommand; _organizationApiKeyRepository = organizationApiKeyRepository; + _updateOrganizationLicenseCommand = updateOrganizationLicenseCommand; _cloudGetOrganizationLicenseQuery = cloudGetOrganizationLicenseQuery; _globalSettings = globalSettings; } @@ -135,6 +139,7 @@ public class OrganizationsController : Controller { throw new NotFoundException(); } + return new OrganizationSubscriptionResponseModel(organization, subscriptionInfo); } else @@ -255,7 +260,7 @@ public class OrganizationsController : Controller } var updateBilling = !_globalSettings.SelfHosted && (model.BusinessName != organization.BusinessName || - model.BillingEmail != organization.BillingEmail); + model.BillingEmail != organization.BillingEmail); var hasRequiredPermissions = updateBilling ? await _currentContext.ManageBilling(orgIdGuid) @@ -304,11 +309,7 @@ public class OrganizationsController : Controller } var result = await _organizationService.UpgradePlanAsync(orgIdGuid, model.ToOrganizationUpgrade()); - return new PaymentResponseModel - { - Success = result.Item1, - PaymentIntentClientSecret = result.Item2 - }; + return new PaymentResponseModel { Success = result.Item1, PaymentIntentClientSecret = result.Item2 }; } [HttpPost("{id}/subscription")] @@ -335,11 +336,7 @@ public class OrganizationsController : Controller } var result = await _organizationService.AdjustSeatsAsync(orgIdGuid, model.SeatAdjustment.Value); - return new PaymentResponseModel - { - Success = true, - PaymentIntentClientSecret = result - }; + return new PaymentResponseModel { Success = true, PaymentIntentClientSecret = result }; } [HttpPost("{id}/storage")] @@ -353,11 +350,7 @@ public class OrganizationsController : Controller } var result = await _organizationService.AdjustStorageAsync(orgIdGuid, model.StorageGbAdjustment.Value); - return new PaymentResponseModel - { - Success = true, - PaymentIntentClientSecret = result - }; + return new PaymentResponseModel { Success = true, PaymentIntentClientSecret = result }; } [HttpPost("{id}/verify-bank")] @@ -471,7 +464,15 @@ public class OrganizationsController : Controller 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")] @@ -548,7 +549,8 @@ public class OrganizationsController : Controller } [HttpGet("{id}/api-key-information/{type?}")] - public async Task> ApiKeyInformation(Guid id, [FromRoute] OrganizationApiKeyType? type) + public async Task> ApiKeyInformation(Guid id, + [FromRoute] OrganizationApiKeyType? type) { if (!await HasApiKeyAccessAsync(id, type)) { @@ -577,8 +579,8 @@ public class OrganizationsController : Controller } var organizationApiKey = await _getOrganizationApiKeyQuery - .GetOrganizationApiKeyAsync(organization.Id, model.Type) ?? - await _createOrganizationApiKeyCommand.CreateAsync(organization.Id, model.Type); + .GetOrganizationApiKeyAsync(organization.Id, model.Type) ?? + await _createOrganizationApiKeyCommand.CreateAsync(organization.Id, model.Type); var user = await _userService.GetUserByPrincipalAsync(User); if (user == null) @@ -679,7 +681,8 @@ public class OrganizationsController : Controller 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); } @@ -725,4 +728,34 @@ public class OrganizationsController : Controller 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); + } + } } diff --git a/src/Api/Controllers/SelfHosted/SelfHostedOrganizationLicensesController.cs b/src/Api/Controllers/SelfHosted/SelfHostedOrganizationLicensesController.cs index 92cf50ae41..c821204c75 100644 --- a/src/Api/Controllers/SelfHosted/SelfHostedOrganizationLicensesController.cs +++ b/src/Api/Controllers/SelfHosted/SelfHostedOrganizationLicensesController.cs @@ -27,6 +27,7 @@ public class SelfHostedOrganizationLicensesController : Controller private readonly IOrganizationService _organizationService; private readonly IOrganizationRepository _organizationRepository; private readonly IUserService _userService; + private readonly IUpdateOrganizationLicenseCommand _updateOrganizationLicenseCommand; public SelfHostedOrganizationLicensesController( ICurrentContext currentContext, @@ -34,7 +35,8 @@ public class SelfHostedOrganizationLicensesController : Controller IOrganizationConnectionRepository organizationConnectionRepository, IOrganizationService organizationService, IOrganizationRepository organizationRepository, - IUserService userService) + IUserService userService, + IUpdateOrganizationLicenseCommand updateOrganizationLicenseCommand) { _currentContext = currentContext; _selfHostedGetOrganizationLicenseQuery = selfHostedGetOrganizationLicenseQuery; @@ -42,6 +44,7 @@ public class SelfHostedOrganizationLicensesController : Controller _organizationService = organizationService; _organizationRepository = organizationRepository; _userService = userService; + _updateOrganizationLicenseCommand = updateOrganizationLicenseCommand; } [HttpPost("")] @@ -79,25 +82,33 @@ public class SelfHostedOrganizationLicensesController : Controller 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")] public async Task SyncLicenseAsync(string id) { - var organization = await _organizationRepository.GetByIdAsync(new Guid(id)); - if (organization == null) + var selfHostedOrganizationDetails = await _organizationRepository.GetSelfHostedOrganizationDetailsById(new Guid(id)); + if (selfHostedOrganizationDetails == null) { throw new NotFoundException(); } - if (!await _currentContext.OrganizationOwner(organization.Id)) + if (!await _currentContext.OrganizationOwner(selfHostedOrganizationDetails.Id)) { throw new NotFoundException(); } var billingSyncConnection = - (await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organization.Id, + (await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(selfHostedOrganizationDetails.Id, OrganizationConnectionType.CloudBillingSync)).FirstOrDefault(); if (billingSyncConnection == null) { @@ -105,9 +116,10 @@ public class SelfHostedOrganizationLicensesController : Controller } 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(); config.LastLicenseSync = DateTime.Now; diff --git a/src/Api/Controllers/TwoFactorController.cs b/src/Api/Controllers/TwoFactorController.cs index 4ba48c9d14..244d3c7387 100644 --- a/src/Api/Controllers/TwoFactorController.cs +++ b/src/Api/Controllers/TwoFactorController.cs @@ -11,7 +11,6 @@ using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; using Bit.Core.Utilities; -using Bit.Core.Utilities.Duo; using Fido2NetLib; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; @@ -153,7 +152,7 @@ public class TwoFactorController : Controller try { var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host); - duoApi.JSONApiCall("GET", "/auth/v2/check"); + await duoApi.JSONApiCall("GET", "/auth/v2/check"); } catch (DuoException) { @@ -210,7 +209,7 @@ public class TwoFactorController : Controller try { var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host); - duoApi.JSONApiCall("GET", "/auth/v2/check"); + await duoApi.JSONApiCall("GET", "/auth/v2/check"); } catch (DuoException) { @@ -295,21 +294,14 @@ public class TwoFactorController : Controller if (await _verifyAuthRequestCommand .VerifyAuthRequestAsync(new Guid(model.AuthRequestId), model.AuthRequestAccessCode)) { - var isBecauseNewDeviceLogin = await IsNewDeviceLoginAsync(user, model); - - await _userService.SendTwoFactorEmailAsync(user, isBecauseNewDeviceLogin); + await _userService.SendTwoFactorEmailAsync(user); return; } } - else + else if (await _userService.VerifySecretAsync(user, model.Secret)) { - if (await _userService.VerifySecretAsync(user, model.Secret)) - { - var isBecauseNewDeviceLogin = await IsNewDeviceLoginAsync(user, model); - - await _userService.SendTwoFactorEmailAsync(user, isBecauseNewDeviceLogin); - return; - } + await _userService.SendTwoFactorEmailAsync(user); + return; } } @@ -390,41 +382,18 @@ public class TwoFactorController : Controller } } + [Obsolete("Leaving this for backwards compatibilty on clients")] [HttpGet("get-device-verification-settings")] - public async Task GetDeviceVerificationSettings() + public Task GetDeviceVerificationSettings() { - var user = await _userService.GetUserByPrincipalAsync(User); - 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); + return Task.FromResult(new DeviceVerificationResponseModel(false, false)); } + [Obsolete("Leaving this for backwards compatibilty on clients")] [HttpPut("device-verification-settings")] - public async Task PutDeviceVerificationSettings([FromBody] DeviceVerificationRequestModel model) + public Task PutDeviceVerificationSettings([FromBody] DeviceVerificationRequestModel model) { - var user = await _userService.GetUserByPrincipalAsync(User); - 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); + return Task.FromResult(new DeviceVerificationResponseModel(false, false)); } private async Task CheckAsync(SecretVerificationRequestModel model, bool premium) @@ -467,17 +436,4 @@ public class TwoFactorController : Controller await Task.Delay(500); } } - - private async Task 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; - } } diff --git a/src/Api/Jobs/JobsHostedService.cs b/src/Api/Jobs/JobsHostedService.cs index 241a012428..eb35556ab2 100644 --- a/src/Api/Jobs/JobsHostedService.cs +++ b/src/Api/Jobs/JobsHostedService.cs @@ -47,6 +47,12 @@ public class JobsHostedService : BaseJobsHostedService .WithIntervalInHours(24) .RepeatForever()) .Build(); + var validateOrganizationDomainTrigger = TriggerBuilder.Create() + .WithIdentity("ValidateOrganizationDomainTrigger") + .StartNow() + .WithCronSchedule("0 0 * * * ?") + .Build(); + var jobs = new List> { @@ -54,7 +60,8 @@ public class JobsHostedService : BaseJobsHostedService new Tuple(typeof(EmergencyAccessNotificationJob), emergencyAccessNotificationTrigger), new Tuple(typeof(EmergencyAccessTimeoutJob), emergencyAccessTimeoutTrigger), new Tuple(typeof(ValidateUsersJob), everyTopOfTheSixthHourTrigger), - new Tuple(typeof(ValidateOrganizationsJob), everyTwelfthHourAndThirtyMinutesTrigger) + new Tuple(typeof(ValidateOrganizationsJob), everyTwelfthHourAndThirtyMinutesTrigger), + new Tuple(typeof(ValidateOrganizationDomainJob), validateOrganizationDomainTrigger), }; if (_globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication) @@ -78,5 +85,6 @@ public class JobsHostedService : BaseJobsHostedService services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } } diff --git a/src/Api/Jobs/ValidateOrganizationDomainJob.cs b/src/Api/Jobs/ValidateOrganizationDomainJob.cs new file mode 100644 index 0000000000..3d0f2d76c1 --- /dev/null +++ b/src/Api/Jobs/ValidateOrganizationDomainJob.cs @@ -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 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(); + await organizationDomainService.ValidateOrganizationsDomainAsync(); + } + _logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: ValidateOrganizationDomainJob: End"); + } +} diff --git a/src/Api/Models/Request/DeviceVerificationRequestModel.cs b/src/Api/Models/Request/DeviceVerificationRequestModel.cs index d81471916b..466010af1f 100644 --- a/src/Api/Models/Request/DeviceVerificationRequestModel.cs +++ b/src/Api/Models/Request/DeviceVerificationRequestModel.cs @@ -1,16 +1,10 @@ using System.ComponentModel.DataAnnotations; -using Bit.Core.Entities; namespace Bit.Api.Models.Request; public class DeviceVerificationRequestModel { + [Obsolete("Leaving this for backwards compatibilty on clients")] [Required] public bool UnknownDeviceVerificationEnabled { get; set; } - - public User ToUser(User user) - { - user.UnknownDeviceVerificationEnabled = UnknownDeviceVerificationEnabled; - return user; - } } diff --git a/src/Api/Models/Request/GroupRequestModel.cs b/src/Api/Models/Request/GroupRequestModel.cs index 6fcb34825a..aa52a08af2 100644 --- a/src/Api/Models/Request/GroupRequestModel.cs +++ b/src/Api/Models/Request/GroupRequestModel.cs @@ -10,8 +10,6 @@ public class GroupRequestModel public string Name { get; set; } [Required] public bool? AccessAll { get; set; } - [StringLength(300)] - public string ExternalId { get; set; } public IEnumerable Collections { get; set; } public IEnumerable Users { get; set; } @@ -27,7 +25,6 @@ public class GroupRequestModel { existingGroup.Name = Name; existingGroup.AccessAll = AccessAll.Value; - existingGroup.ExternalId = ExternalId; return existingGroup; } } diff --git a/src/Api/Models/Request/OrganizationDomainRequestModel.cs b/src/Api/Models/Request/OrganizationDomainRequestModel.cs new file mode 100644 index 0000000000..28786bf586 --- /dev/null +++ b/src/Api/Models/Request/OrganizationDomainRequestModel.cs @@ -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; } +} diff --git a/src/Api/Models/Request/Organizations/OrganizationDomainSsoDetailsRequestModel.cs b/src/Api/Models/Request/Organizations/OrganizationDomainSsoDetailsRequestModel.cs new file mode 100644 index 0000000000..9f9db5b34a --- /dev/null +++ b/src/Api/Models/Request/Organizations/OrganizationDomainSsoDetailsRequestModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace Bit.Api.Models.Request.Organizations; + +public class OrganizationDomainSsoDetailsRequestModel +{ + [Required] + [EmailAddress] + public string Email { get; set; } +} diff --git a/src/Api/Models/Request/Organizations/OrganizationEnrollSecretsManagerRequestModel.cs b/src/Api/Models/Request/Organizations/OrganizationEnrollSecretsManagerRequestModel.cs new file mode 100644 index 0000000000..7befaa25c6 --- /dev/null +++ b/src/Api/Models/Request/Organizations/OrganizationEnrollSecretsManagerRequestModel.cs @@ -0,0 +1,6 @@ +namespace Bit.Api.Models.Request.Organizations; + +public class OrganizationEnrollSecretsManagerRequestModel +{ + public bool Enabled { get; set; } +} diff --git a/src/Api/Models/Request/Organizations/OrganizationUpdateRequestModel.cs b/src/Api/Models/Request/Organizations/OrganizationUpdateRequestModel.cs index 0bd16767d2..79983a5549 100644 --- a/src/Api/Models/Request/Organizations/OrganizationUpdateRequestModel.cs +++ b/src/Api/Models/Request/Organizations/OrganizationUpdateRequestModel.cs @@ -12,9 +12,6 @@ public class OrganizationUpdateRequestModel public string Name { get; set; } [StringLength(50)] 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] [Required] [StringLength(256)] @@ -31,7 +28,6 @@ public class OrganizationUpdateRequestModel existingOrganization.BusinessName = BusinessName; existingOrganization.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim(); } - existingOrganization.Identifier = Identifier; Keys?.ToOrganization(existingOrganization); return existingOrganization; } diff --git a/src/Api/Models/Request/TwoFactorRequestModels.cs b/src/Api/Models/Request/TwoFactorRequestModels.cs index 6f34432d78..a2b236edfa 100644 --- a/src/Api/Models/Request/TwoFactorRequestModels.cs +++ b/src/Api/Models/Request/TwoFactorRequestModels.cs @@ -3,6 +3,7 @@ using Bit.Api.Models.Request.Accounts; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models; +using Bit.Core.Utilities; using Fido2NetLib; namespace Bit.Api.Models.Request; @@ -104,7 +105,7 @@ public class UpdateTwoFactorDuoRequestModel : SecretVerificationRequestModel, IV public override IEnumerable 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) }); } @@ -202,8 +203,6 @@ public class TwoFactorEmailRequestModel : SecretVerificationRequestModel [StringLength(256)] public string Email { get; set; } - public string DeviceIdentifier { get; set; } - public string AuthRequestId { get; set; } public User ToUser(User extistingUser) diff --git a/src/Api/Models/Response/DeviceVerificationResponseModel.cs b/src/Api/Models/Response/DeviceVerificationResponseModel.cs index 0358ff7771..d38e65ee80 100644 --- a/src/Api/Models/Response/DeviceVerificationResponseModel.cs +++ b/src/Api/Models/Response/DeviceVerificationResponseModel.cs @@ -2,6 +2,7 @@ namespace Bit.Api.Models.Response; +[Obsolete("Leaving this for backwards compatibilty on clients")] public class DeviceVerificationResponseModel : ResponseModel { public DeviceVerificationResponseModel(bool isDeviceVerificationSectionEnabled, bool unknownDeviceVerificationEnabled) diff --git a/src/Api/Models/Response/EventResponseModel.cs b/src/Api/Models/Response/EventResponseModel.cs index e9ef73a57f..68695b3ab8 100644 --- a/src/Api/Models/Response/EventResponseModel.cs +++ b/src/Api/Models/Response/EventResponseModel.cs @@ -31,6 +31,9 @@ public class EventResponseModel : ResponseModel IpAddress = ev.IpAddress; InstallationId = ev.InstallationId; SystemUser = ev.SystemUser; + DomainName = ev.DomainName; + SecretId = ev.SecretId; + ServiceAccountId = ev.ServiceAccountId; } public EventType Type { get; set; } @@ -50,4 +53,7 @@ public class EventResponseModel : ResponseModel public DeviceType? DeviceType { get; set; } public string IpAddress { get; set; } public EventSystemUser? SystemUser { get; set; } + public string DomainName { get; set; } + public Guid? SecretId { get; set; } + public Guid? ServiceAccountId { get; set; } } diff --git a/src/Api/Models/Response/Organizations/OrganizationDomainResponseModel.cs b/src/Api/Models/Response/Organizations/OrganizationDomainResponseModel.cs new file mode 100644 index 0000000000..b05a732ee7 --- /dev/null +++ b/src/Api/Models/Response/Organizations/OrganizationDomainResponseModel.cs @@ -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; } +} diff --git a/src/Api/Models/Response/Organizations/OrganizationDomainSsoDetailsResponseModel.cs b/src/Api/Models/Response/Organizations/OrganizationDomainSsoDetailsResponseModel.cs new file mode 100644 index 0000000000..44c4acea90 --- /dev/null +++ b/src/Api/Models/Response/Organizations/OrganizationDomainSsoDetailsResponseModel.cs @@ -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; } +} diff --git a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs index 4df4a4dce2..0e284ab9cb 100644 --- a/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs +++ b/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs @@ -17,7 +17,6 @@ public class OrganizationResponseModel : ResponseModel } Id = organization.Id.ToString(); - Identifier = organization.Identifier; Name = organization.Name; BusinessName = organization.BusinessName; BusinessAddress1 = organization.BusinessAddress1; @@ -51,7 +50,6 @@ public class OrganizationResponseModel : ResponseModel } public string Id { get; set; } - public string Identifier { get; set; } public string Name { get; set; } public string BusinessName { get; set; } public string BusinessAddress1 { get; set; } diff --git a/src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs b/src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs index 4c7424744c..1ce055a869 100644 --- a/src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs +++ b/src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs @@ -23,6 +23,7 @@ public class OrganizationUserResponseModel : ResponseModel Type = organizationUser.Type; Status = organizationUser.Status; AccessAll = organizationUser.AccessAll; + ExternalId = organizationUser.ExternalId; AccessSecretsManager = organizationUser.AccessSecretsManager; Permissions = CoreHelpers.LoadClassFromJsonData(organizationUser.Permissions); ResetPasswordEnrolled = !string.IsNullOrEmpty(organizationUser.ResetPasswordKey); @@ -41,6 +42,7 @@ public class OrganizationUserResponseModel : ResponseModel Type = organizationUser.Type; Status = organizationUser.Status; AccessAll = organizationUser.AccessAll; + ExternalId = organizationUser.ExternalId; AccessSecretsManager = organizationUser.AccessSecretsManager; Permissions = CoreHelpers.LoadClassFromJsonData(organizationUser.Permissions); ResetPasswordEnrolled = !string.IsNullOrEmpty(organizationUser.ResetPasswordKey); @@ -52,6 +54,7 @@ public class OrganizationUserResponseModel : ResponseModel public OrganizationUserType Type { get; set; } public OrganizationUserStatusType Status { get; set; } public bool AccessAll { get; set; } + public string ExternalId { get; set; } public bool AccessSecretsManager { get; set; } public Permissions Permissions { get; set; } public bool ResetPasswordEnrolled { get; set; } @@ -86,6 +89,7 @@ public class OrganizationUserUserDetailsResponseModel : OrganizationUserResponse Name = organizationUser.Name; Email = organizationUser.Email; + AvatarColor = organizationUser.AvatarColor; TwoFactorEnabled = twoFactorEnabled; SsoBound = !string.IsNullOrWhiteSpace(organizationUser.SsoExternalId); Collections = organizationUser.Collections.Select(c => new SelectionReadOnlyResponseModel(c)); @@ -94,8 +98,10 @@ public class OrganizationUserUserDetailsResponseModel : OrganizationUserResponse ResetPasswordEnrolled = ResetPasswordEnrolled && !organizationUser.UsesKeyConnector; } + public string Name { get; set; } public string Email { get; set; } + public string AvatarColor { get; set; } public bool TwoFactorEnabled { get; set; } public bool SsoBound { get; set; } public IEnumerable Collections { get; set; } diff --git a/src/Api/SecretsManager/Controllers/AccessPoliciesController.cs b/src/Api/SecretsManager/Controllers/AccessPoliciesController.cs index d2d3a4f28b..876a71fa3d 100644 --- a/src/Api/SecretsManager/Controllers/AccessPoliciesController.cs +++ b/src/Api/SecretsManager/Controllers/AccessPoliciesController.cs @@ -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.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.Entities; using Bit.Core.SecretsManager.Repositories; +using Bit.Core.Services; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Bit.Api.SecretsManager.Controllers; [SecretsManager] +[Authorize("secrets")] [Route("access-policies")] public class AccessPoliciesController : Controller { + private const int _maxBulkCreation = 15; private readonly IAccessPolicyRepository _accessPolicyRepository; private readonly ICreateAccessPoliciesCommand _createAccessPoliciesCommand; + private readonly ICurrentContext _currentContext; 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 IUserService _userService; public AccessPoliciesController( + IUserService userService, + ICurrentContext currentContext, IAccessPolicyRepository accessPolicyRepository, + IServiceAccountRepository serviceAccountRepository, + IGroupRepository groupRepository, + IProjectRepository projectRepository, + IOrganizationUserRepository organizationUserRepository, ICreateAccessPoliciesCommand createAccessPoliciesCommand, IDeleteAccessPolicyCommand deleteAccessPolicyCommand, IUpdateAccessPolicyCommand updateAccessPolicyCommand) { + _userService = userService; + _currentContext = currentContext; + _serviceAccountRepository = serviceAccountRepository; + _projectRepository = projectRepository; + _groupRepository = groupRepository; + _organizationUserRepository = organizationUserRepository; _accessPolicyRepository = accessPolicyRepository; _createAccessPoliciesCommand = createAccessPoliciesCommand; _deleteAccessPolicyCommand = deleteAccessPolicyCommand; @@ -32,37 +59,244 @@ public class AccessPoliciesController : Controller public async Task CreateProjectAccessPoliciesAsync([FromRoute] Guid id, [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 results = await _createAccessPoliciesCommand.CreateAsync(policies); + await _createAccessPoliciesCommand.CreateManyAsync(policies, userId, accessClient); + var results = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(id); return new ProjectAccessPoliciesResponseModel(results); } [HttpGet("/projects/{id}/access-policies")] public async Task GetProjectAccessPoliciesAsync([FromRoute] Guid id) { + var project = await _projectRepository.GetByIdAsync(id); + await CheckUserHasWriteAccessToProjectAsync(project); + var results = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(id); return new ProjectAccessPoliciesResponseModel(results); } + [HttpPost("/service-accounts/{id}/access-policies")] + public async Task 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 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> + 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(responses); + } + + [HttpPost("/service-accounts/{id}/granted-policies")] + public async Task> + CreateServiceAccountGrantedPoliciesAsync([FromRoute] Guid id, + [FromBody] List 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(policies), userId, accessClient); + var responses = results.Select(ap => + new ServiceAccountProjectAccessPolicyResponseModel((ServiceAccountProjectAccessPolicy)ap)); + return new ListResponseModel(responses); + } + [HttpPut("{id}")] public async Task UpdateAccessPolicyAsync([FromRoute] Guid id, [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 { UserProjectAccessPolicy accessPolicy => new UserProjectAccessPolicyResponseModel(accessPolicy), + UserServiceAccountAccessPolicy accessPolicy => + new UserServiceAccountAccessPolicyResponseModel(accessPolicy), GroupProjectAccessPolicy accessPolicy => new GroupProjectAccessPolicyResponseModel(accessPolicy), + GroupServiceAccountAccessPolicy accessPolicy => new GroupServiceAccountAccessPolicyResponseModel( + accessPolicy), ServiceAccountProjectAccessPolicy accessPolicy => new ServiceAccountProjectAccessPolicyResponseModel( accessPolicy), - _ => throw new ArgumentException("Unsupported access policy type provided.") + _ => throw new ArgumentException("Unsupported access policy type provided."), }; } [HttpDelete("{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> 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(userResponses.Concat(groupResponses)); + } + + [HttpGet("/organizations/{id}/access-policies/service-accounts/potential-grantees")] + public async Task> 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(serviceAccountResponses); + } + + [HttpGet("/organizations/{id}/access-policies/projects/potential-grantees")] + public async Task> 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(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); } } diff --git a/src/Api/SecretsManager/Controllers/ProjectsController.cs b/src/Api/SecretsManager/Controllers/ProjectsController.cs index 437079d87d..9dfb8f11cc 100644 --- a/src/Api/SecretsManager/Controllers/ProjectsController.cs +++ b/src/Api/SecretsManager/Controllers/ProjectsController.cs @@ -65,7 +65,8 @@ public class ProjectsController : Controller 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); } diff --git a/src/Api/SecretsManager/Controllers/SecretsController.cs b/src/Api/SecretsManager/Controllers/SecretsController.cs index 3ddf4699d7..9cf0a91edd 100644 --- a/src/Api/SecretsManager/Controllers/SecretsController.cs +++ b/src/Api/SecretsManager/Controllers/SecretsController.cs @@ -2,9 +2,13 @@ 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.Identity; using Bit.Core.SecretsManager.Commands.Secrets.Interfaces; +using Bit.Core.SecretsManager.Entities; using Bit.Core.SecretsManager.Repositories; +using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -15,23 +19,32 @@ namespace Bit.Api.SecretsManager.Controllers; public class SecretsController : Controller { private readonly ICurrentContext _currentContext; + private readonly IProjectRepository _projectRepository; private readonly ISecretRepository _secretRepository; private readonly ICreateSecretCommand _createSecretCommand; private readonly IUpdateSecretCommand _updateSecretCommand; private readonly IDeleteSecretCommand _deleteSecretCommand; + private readonly IUserService _userService; + private readonly IEventService _eventService; public SecretsController( ICurrentContext currentContext, + IProjectRepository projectRepository, ISecretRepository secretRepository, ICreateSecretCommand createSecretCommand, IUpdateSecretCommand updateSecretCommand, - IDeleteSecretCommand deleteSecretCommand) + IDeleteSecretCommand deleteSecretCommand, + IUserService userService, + IEventService eventService) { _currentContext = currentContext; + _projectRepository = projectRepository; _secretRepository = secretRepository; _createSecretCommand = createSecretCommand; _updateSecretCommand = updateSecretCommand; _deleteSecretCommand = deleteSecretCommand; + _userService = userService; + _eventService = eventService; } [HttpGet("organizations/{organizationId}/secrets")] @@ -42,7 +55,12 @@ public class SecretsController : Controller 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); } @@ -54,7 +72,8 @@ public class SecretsController : Controller 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); } @@ -62,34 +81,81 @@ public class SecretsController : Controller public async Task GetAsync([FromRoute] Guid id) { var secret = await _secretRepository.GetByIdAsync(id); - if (secret == null) + + if (secret == null || !_currentContext.AccessSecretsManager(secret.OrganizationId)) { 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); } [HttpGet("projects/{projectId}/secrets")] public async Task GetSecretsByProjectAsync([FromRoute] Guid projectId) { - var secrets = await _secretRepository.GetManyByProjectIdAsync(projectId); - var responses = secrets.Select(s => new SecretResponseModel(s)); + var project = await _projectRepository.GetByIdAsync(projectId); + 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); } [HttpPut("secrets/{id}")] - public async Task UpdateAsync([FromRoute] Guid id, [FromBody] SecretUpdateRequestModel updateRequest) + public async Task 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); } - // TODO Once permissions are setup for Secrets Manager need to enforce them on delete. [HttpPost("secrets/delete")] public async Task> BulkDeleteAsync([FromBody] List 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)); return new ListResponseModel(responses); } + + public async Task 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; + } } diff --git a/src/Api/SecretsManager/Controllers/SecretsManagerPortingController.cs b/src/Api/SecretsManager/Controllers/SecretsManagerPortingController.cs new file mode 100644 index 0000000000..d1e75a328b --- /dev/null +++ b/src/Api/SecretsManager/Controllers/SecretsManagerPortingController.cs @@ -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 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()); + } +} diff --git a/src/Api/SecretsManager/Controllers/SecretsTrashController.cs b/src/Api/SecretsManager/Controllers/SecretsTrashController.cs new file mode 100644 index 0000000000..6f0b65d458 --- /dev/null +++ b/src/Api/SecretsManager/Controllers/SecretsTrashController.cs @@ -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 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 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 ids) + { + if (!_currentContext.AccessSecretsManager(organizationId)) + { + throw new NotFoundException(); + } + + if (!await _currentContext.OrganizationAdmin(organizationId)) + { + throw new UnauthorizedAccessException(); + } + + await _restoreTrashCommand.RestoreTrash(organizationId, ids); + } +} diff --git a/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs b/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs index 175db2a1f2..ccc851c8a2 100644 --- a/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs +++ b/src/Api/SecretsManager/Controllers/ServiceAccountsController.cs @@ -19,20 +19,23 @@ namespace Bit.Api.SecretsManager.Controllers; public class ServiceAccountsController : Controller { private readonly ICurrentContext _currentContext; + private readonly IUserService _userService; + private readonly IServiceAccountRepository _serviceAccountRepository; private readonly IApiKeyRepository _apiKeyRepository; private readonly ICreateAccessTokenCommand _createAccessTokenCommand; private readonly ICreateServiceAccountCommand _createServiceAccountCommand; - private readonly IServiceAccountRepository _serviceAccountRepository; private readonly IUpdateServiceAccountCommand _updateServiceAccountCommand; - private readonly IUserService _userService; + private readonly IRevokeAccessTokensCommand _revokeAccessTokensCommand; public ServiceAccountsController( ICurrentContext currentContext, IUserService userService, IServiceAccountRepository serviceAccountRepository, + IApiKeyRepository apiKeyRepository, ICreateAccessTokenCommand createAccessTokenCommand, - IApiKeyRepository apiKeyRepository, ICreateServiceAccountCommand createServiceAccountCommand, - IUpdateServiceAccountCommand updateServiceAccountCommand) + ICreateServiceAccountCommand createServiceAccountCommand, + IUpdateServiceAccountCommand updateServiceAccountCommand, + IRevokeAccessTokensCommand revokeAccessTokensCommand) { _currentContext = currentContext; _userService = userService; @@ -40,6 +43,7 @@ public class ServiceAccountsController : Controller _apiKeyRepository = apiKeyRepository; _createServiceAccountCommand = createServiceAccountCommand; _updateServiceAccountCommand = updateServiceAccountCommand; + _revokeAccessTokensCommand = revokeAccessTokensCommand; _createAccessTokenCommand = createAccessTokenCommand; } @@ -129,4 +133,37 @@ public class ServiceAccountsController : Controller var result = await _createAccessTokenCommand.CreateAsync(request.ToApiKey(id), userId); 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); + } } diff --git a/src/Api/SecretsManager/Models/Request/AccessPoliciesCreateRequest.cs b/src/Api/SecretsManager/Models/Request/AccessPoliciesCreateRequest.cs index eab1c1dbaf..e4de0a87a6 100644 --- a/src/Api/SecretsManager/Models/Request/AccessPoliciesCreateRequest.cs +++ b/src/Api/SecretsManager/Models/Request/AccessPoliciesCreateRequest.cs @@ -13,7 +13,7 @@ public class AccessPoliciesCreateRequest public IEnumerable? ServiceAccountAccessPolicyRequests { get; set; } - public List ToBaseAccessPoliciesForProject(Guid projectId) + public List ToBaseAccessPoliciesForProject(Guid grantedProjectId) { if (UserAccessPolicyRequests == null && GroupAccessPolicyRequests == null && ServiceAccountAccessPolicyRequests == null) { @@ -21,20 +21,77 @@ public class AccessPoliciesCreateRequest } var userAccessPolicies = UserAccessPolicyRequests? - .Select(x => x.ToUserProjectAccessPolicy(projectId)).ToList(); + .Select(x => x.ToUserProjectAccessPolicy(grantedProjectId)).ToList(); var groupAccessPolicies = GroupAccessPolicyRequests? - .Select(x => x.ToGroupProjectAccessPolicy(projectId)).ToList(); + .Select(x => x.ToGroupProjectAccessPolicy(grantedProjectId)).ToList(); var serviceAccountAccessPolicies = ServiceAccountAccessPolicyRequests? - .Select(x => x.ToServiceAccountProjectAccessPolicy(projectId)).ToList(); + .Select(x => x.ToServiceAccountProjectAccessPolicy(grantedProjectId)).ToList(); var policies = new List(); - if (userAccessPolicies != null) { policies.AddRange(userAccessPolicies); } - if (groupAccessPolicies != null) { policies.AddRange(groupAccessPolicies); } - if (serviceAccountAccessPolicies != null) { policies.AddRange(serviceAccountAccessPolicies); } + if (userAccessPolicies != null) + { + policies.AddRange(userAccessPolicies); + } + + if (groupAccessPolicies != null) + { + policies.AddRange(groupAccessPolicies); + } + + if (serviceAccountAccessPolicies != null) + { + policies.AddRange(serviceAccountAccessPolicies); + } return policies; } + + public List 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(); + 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 @@ -74,4 +131,22 @@ public class AccessPolicyRequest Read = Read, 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 + }; } diff --git a/src/Api/SecretsManager/Models/Request/GrantedAccessPolicyRequest.cs b/src/Api/SecretsManager/Models/Request/GrantedAccessPolicyRequest.cs new file mode 100644 index 0000000000..0b8f1633c1 --- /dev/null +++ b/src/Api/SecretsManager/Models/Request/GrantedAccessPolicyRequest.cs @@ -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, + }; +} diff --git a/src/Api/SecretsManager/Models/Request/RevokeAccessTokensRequest.cs b/src/Api/SecretsManager/Models/Request/RevokeAccessTokensRequest.cs new file mode 100644 index 0000000000..ecced7a5cd --- /dev/null +++ b/src/Api/SecretsManager/Models/Request/RevokeAccessTokensRequest.cs @@ -0,0 +1,7 @@ +using System.ComponentModel.DataAnnotations; + +public class RevokeAccessTokensRequest +{ + [Required] + public Guid[] Ids { get; set; } +} diff --git a/src/Api/SecretsManager/Models/Request/SMImportRequestModel.cs b/src/Api/SecretsManager/Models/Request/SMImportRequestModel.cs new file mode 100644 index 0000000000..13cc3b6f73 --- /dev/null +++ b/src/Api/SecretsManager/Models/Request/SMImportRequestModel.cs @@ -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 Projects { get; set; } + public IEnumerable 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 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, + }), + }; + } +} diff --git a/src/Api/SecretsManager/Models/Response/AccessPolicyResponseModel.cs b/src/Api/SecretsManager/Models/Response/AccessPolicyResponseModel.cs index 17337ab699..a298e15b3a 100644 --- a/src/Api/SecretsManager/Models/Response/AccessPolicyResponseModel.cs +++ b/src/Api/SecretsManager/Models/Response/AccessPolicyResponseModel.cs @@ -1,4 +1,5 @@ #nullable enable +using Bit.Core.Entities; using Bit.Core.Models.Api; using Bit.Core.SecretsManager.Entities; @@ -20,6 +21,11 @@ public abstract class BaseAccessPolicyResponseModel : ResponseModel public bool Write { get; set; } public DateTime CreationDate { 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 @@ -30,7 +36,7 @@ public class UserProjectAccessPolicyResponseModel : BaseAccessPolicyResponseMode { OrganizationUserId = accessPolicy.OrganizationUserId; GrantedProjectId = accessPolicy.GrantedProjectId; - OrganizationUserName = accessPolicy.User?.Name; + OrganizationUserName = GetUserDisplayName(accessPolicy.User); } public UserProjectAccessPolicyResponseModel() : base(new UserProjectAccessPolicy(), _objectName) @@ -51,7 +57,7 @@ public class UserServiceAccountAccessPolicyResponseModel : BaseAccessPolicyRespo { OrganizationUserId = accessPolicy.OrganizationUserId; GrantedServiceAccountId = accessPolicy.GrantedServiceAccountId; - OrganizationUserName = accessPolicy.User?.Name; + OrganizationUserName = GetUserDisplayName(accessPolicy.User); } public UserServiceAccountAccessPolicyResponseModel() : base(new UserServiceAccountAccessPolicy(), _objectName) @@ -115,6 +121,7 @@ public class ServiceAccountProjectAccessPolicyResponseModel : BaseAccessPolicyRe ServiceAccountId = accessPolicy.ServiceAccountId; GrantedProjectId = accessPolicy.GrantedProjectId; ServiceAccountName = accessPolicy.ServiceAccount?.Name; + GrantedProjectName = accessPolicy.GrantedProject?.Name; } public ServiceAccountProjectAccessPolicyResponseModel() @@ -125,4 +132,5 @@ public class ServiceAccountProjectAccessPolicyResponseModel : BaseAccessPolicyRe public Guid? ServiceAccountId { get; set; } public string? ServiceAccountName { get; set; } public Guid? GrantedProjectId { get; set; } + public string? GrantedProjectName { get; set; } } diff --git a/src/Api/SecretsManager/Models/Response/PotentialGranteeResponseModel.cs b/src/Api/SecretsManager/Models/Response/PotentialGranteeResponseModel.cs new file mode 100644 index 0000000000..b76af966fe --- /dev/null +++ b/src/Api/SecretsManager/Models/Response/PotentialGranteeResponseModel.cs @@ -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; } +} diff --git a/src/Api/SecretsManager/Models/Response/ProjectAccessPoliciesResponseModel.cs b/src/Api/SecretsManager/Models/Response/ProjectAccessPoliciesResponseModel.cs index f03411cd39..7308d1833c 100644 --- a/src/Api/SecretsManager/Models/Response/ProjectAccessPoliciesResponseModel.cs +++ b/src/Api/SecretsManager/Models/Response/ProjectAccessPoliciesResponseModel.cs @@ -11,6 +11,7 @@ public class ProjectAccessPoliciesResponseModel : ResponseModel : base(_objectName) { foreach (var baseAccessPolicy in baseAccessPolicies) + { switch (baseAccessPolicy) { case UserProjectAccessPolicy accessPolicy: @@ -24,6 +25,7 @@ public class ProjectAccessPoliciesResponseModel : ResponseModel new ServiceAccountProjectAccessPolicyResponseModel(accessPolicy)); break; } + } } public ProjectAccessPoliciesResponseModel() : base(_objectName) diff --git a/src/Api/SecretsManager/Models/Response/SMExportResponseModel.cs b/src/Api/SecretsManager/Models/Response/SMExportResponseModel.cs new file mode 100644 index 0000000000..6d83117c32 --- /dev/null +++ b/src/Api/SecretsManager/Models/Response/SMExportResponseModel.cs @@ -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 projects, IEnumerable secrets, string obj = "SecretsManagerExportResponseModel") : base(obj) + { + Secrets = secrets?.Select(s => new InnerSecretExportResponseModel(s)); + Projects = projects?.Select(p => new InnerProjectExportResponseModel(p)); + } + + public IEnumerable Projects { get; set; } + public IEnumerable 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 ProjectIds { get; set; } + } +} diff --git a/src/Api/SecretsManager/Models/Response/SMImportResponseModel.cs b/src/Api/SecretsManager/Models/Response/SMImportResponseModel.cs new file mode 100644 index 0000000000..25d9956c43 --- /dev/null +++ b/src/Api/SecretsManager/Models/Response/SMImportResponseModel.cs @@ -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 Projects { get; set; } + public IEnumerable 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 ProjectIds { get; set; } + } +} diff --git a/src/Api/SecretsManager/Models/Response/ServiceAccountAccessPoliciesResponseModel.cs b/src/Api/SecretsManager/Models/Response/ServiceAccountAccessPoliciesResponseModel.cs new file mode 100644 index 0000000000..6f047cf881 --- /dev/null +++ b/src/Api/SecretsManager/Models/Response/ServiceAccountAccessPoliciesResponseModel.cs @@ -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 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 UserAccessPolicies { get; set; } = new(); + + public List GroupAccessPolicies { get; set; } = new(); +} diff --git a/src/Api/Utilities/ExceptionHandlerFilterAttribute.cs b/src/Api/Utilities/ExceptionHandlerFilterAttribute.cs index 60506e46d0..4dadedeb3e 100644 --- a/src/Api/Utilities/ExceptionHandlerFilterAttribute.cs +++ b/src/Api/Utilities/ExceptionHandlerFilterAttribute.cs @@ -92,6 +92,11 @@ public class ExceptionHandlerFilterAttribute : ExceptionFilterAttribute errorMessage = "Unauthorized."; context.HttpContext.Response.StatusCode = 401; } + else if (exception is ConflictException) + { + errorMessage = exception.Message; + context.HttpContext.Response.StatusCode = 409; + } else if (exception is AggregateException aggregateException) { context.HttpContext.Response.StatusCode = 400; diff --git a/src/Api/packages.lock.json b/src/Api/packages.lock.json index 95bc2f7548..e540064cd3 100644 --- a/src/Api/packages.lock.json +++ b/src/Api/packages.lock.json @@ -85,8 +85,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -123,28 +123,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -171,6 +171,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2794,10 +2802,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/src/Billing/packages.lock.json b/src/Billing/packages.lock.json index ae50591cf6..dc46ffb62a 100644 --- a/src/Billing/packages.lock.json +++ b/src/Billing/packages.lock.json @@ -74,8 +74,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -112,28 +112,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -160,6 +160,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -3245,10 +3253,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/src/Core/Context/CurrentContext.cs b/src/Core/Context/CurrentContext.cs index 4411509cd5..598f30bbb2 100644 --- a/src/Core/Context/CurrentContext.cs +++ b/src/Core/Context/CurrentContext.cs @@ -35,6 +35,7 @@ public class CurrentContext : ICurrentContext public virtual string ClientId { get; set; } public virtual Version ClientVersion { get; set; } public virtual ClientType ClientType { get; set; } + public virtual Guid? ServiceAccountOrganizationId { get; set; } public CurrentContext(IProviderUserRepository providerUserRepository) { @@ -146,6 +147,11 @@ public class CurrentContext : ICurrentContext ClientType = c; } + if (ClientType == ClientType.ServiceAccount) + { + ServiceAccountOrganizationId = new Guid(GetClaimValue(claimsDict, Claims.Organization)); + } + DeviceIdentifier = GetClaimValue(claimsDict, Claims.Device); Organizations = GetOrganizations(claimsDict, orgApi); @@ -445,6 +451,11 @@ public class CurrentContext : ICurrentContext public bool AccessSecretsManager(Guid orgId) { + if (ServiceAccountOrganizationId.HasValue && ServiceAccountOrganizationId.Value == orgId) + { + return true; + } + return Organizations?.Any(o => o.Id == orgId && o.AccessSecretsManager) ?? false; } diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index aabb3d71c4..1ddc93a95e 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -24,9 +24,10 @@ - - + + + diff --git a/src/Core/Entities/Event.cs b/src/Core/Entities/Event.cs index 6800b1120e..e666c76f76 100644 --- a/src/Core/Entities/Event.cs +++ b/src/Core/Entities/Event.cs @@ -28,6 +28,9 @@ public class Event : ITableObject, IEvent IpAddress = e.IpAddress; ActingUserId = e.ActingUserId; SystemUser = e.SystemUser; + DomainName = e.DomainName; + SecretId = e.SecretId; + ServiceAccountId = e.ServiceAccountId; } public Guid Id { get; set; } @@ -49,6 +52,9 @@ public class Event : ITableObject, IEvent public string IpAddress { get; set; } public Guid? ActingUserId { 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() { diff --git a/src/Core/Entities/Organization.cs b/src/Core/Entities/Organization.cs index 391df3512d..2ea5114ac0 100644 --- a/src/Core/Entities/Organization.cs +++ b/src/Core/Entities/Organization.cs @@ -2,6 +2,7 @@ using System.Text.Json; using Bit.Core.Enums; using Bit.Core.Models; +using Bit.Core.Models.Business; using Bit.Core.Utilities; namespace Bit.Core.Entities; @@ -198,4 +199,33 @@ public class Organization : ITableObject, ISubscriber, IStorable, IStorabl 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; + } } diff --git a/src/Core/Entities/OrganizationDomain.cs b/src/Core/Entities/OrganizationDomain.cs new file mode 100644 index 0000000000..d275d075b2 --- /dev/null +++ b/src/Core/Entities/OrganizationDomain.cs @@ -0,0 +1,48 @@ +using System.ComponentModel.DataAnnotations; +using Bit.Core.Utilities; + +namespace Bit.Core.Entities; + +public class OrganizationDomain : ITableObject +{ + 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; + } +} diff --git a/src/Core/Entities/User.cs b/src/Core/Entities/User.cs index 3b59729c14..d54bf0a308 100644 --- a/src/Core/Entities/User.cs +++ b/src/Core/Entities/User.cs @@ -62,7 +62,6 @@ public class User : ITableObject, ISubscriber, IStorable, IStorableSubscri public bool UsesKeyConnector { get; set; } public int FailedLoginCount { get; set; } public DateTime? LastFailedLoginDate { get; set; } - public bool UnknownDeviceVerificationEnabled { get; set; } [MaxLength(7)] public string AvatarColor { get; set; } public DateTime? LastPasswordChangeDate { get; set; } diff --git a/src/Core/Enums/DeviceType.cs b/src/Core/Enums/DeviceType.cs index 596a43b038..9534cfd71a 100644 --- a/src/Core/Enums/DeviceType.cs +++ b/src/Core/Enums/DeviceType.cs @@ -48,4 +48,6 @@ public enum DeviceType : byte SafariExtension = 20, [Display(Name = "SDK")] SDK = 21, + [Display(Name = "Server")] + Server = 22 } diff --git a/src/Core/Enums/EventSystemUser.cs b/src/Core/Enums/EventSystemUser.cs index 0f19d126c8..09c4e68f41 100644 --- a/src/Core/Enums/EventSystemUser.cs +++ b/src/Core/Enums/EventSystemUser.cs @@ -2,5 +2,6 @@ public enum EventSystemUser : byte { - SCIM = 1 + SCIM = 1, + DomainVerification = 2 } diff --git a/src/Core/Enums/EventType.cs b/src/Core/Enums/EventType.cs index 09a1afffd9..82339ac8a7 100644 --- a/src/Core/Enums/EventType.cs +++ b/src/Core/Enums/EventType.cs @@ -1,5 +1,6 @@ namespace Bit.Core.Enums; +// Increment by 100 for each new set of events public enum EventType : int { User_LoggedIn = 1000, @@ -75,4 +76,11 @@ public enum EventType : int ProviderOrganization_Added = 1901, ProviderOrganization_Removed = 1902, ProviderOrganization_VaultAccessed = 1903, + + OrganizationDomain_Added = 2000, + OrganizationDomain_Removed = 2001, + OrganizationDomain_Verified = 2002, + OrganizationDomain_NotVerified = 2003, + + Secret_Retrieved = 2100, } diff --git a/src/Core/Exceptions/ConflictException.cs b/src/Core/Exceptions/ConflictException.cs index 8a3186ac81..27b90a657f 100644 --- a/src/Core/Exceptions/ConflictException.cs +++ b/src/Core/Exceptions/ConflictException.cs @@ -1,3 +1,7 @@ namespace Bit.Core.Exceptions; -public class ConflictException : Exception { } +public class ConflictException : Exception +{ + public ConflictException() : base("Conflict.") { } + public ConflictException(string message) : base(message) { } +} diff --git a/src/Core/Exceptions/DnsQueryException.cs b/src/Core/Exceptions/DnsQueryException.cs new file mode 100644 index 0000000000..57b2c56daa --- /dev/null +++ b/src/Core/Exceptions/DnsQueryException.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.Exceptions; + +public class DnsQueryException : Exception +{ + public DnsQueryException(string message) + : base(message) { } +} diff --git a/src/Core/Exceptions/DomainClaimedException.cs b/src/Core/Exceptions/DomainClaimedException.cs new file mode 100644 index 0000000000..09ccb3d0d8 --- /dev/null +++ b/src/Core/Exceptions/DomainClaimedException.cs @@ -0,0 +1,10 @@ +namespace Bit.Core.Exceptions; + +public class DomainClaimedException : Exception +{ + public DomainClaimedException() + : base("The domain is not available to be claimed.") + { + + } +} diff --git a/src/Core/Exceptions/DomainVerifiedException.cs b/src/Core/Exceptions/DomainVerifiedException.cs new file mode 100644 index 0000000000..d3a3fd4de4 --- /dev/null +++ b/src/Core/Exceptions/DomainVerifiedException.cs @@ -0,0 +1,10 @@ +namespace Bit.Core.Exceptions; + +public class DomainVerifiedException : Exception +{ + public DomainVerifiedException() + : base("Domain has already been verified.") + { + + } +} diff --git a/src/Core/Exceptions/DuplicateDomainException.cs b/src/Core/Exceptions/DuplicateDomainException.cs new file mode 100644 index 0000000000..8d347dda55 --- /dev/null +++ b/src/Core/Exceptions/DuplicateDomainException.cs @@ -0,0 +1,10 @@ +namespace Bit.Core.Exceptions; + +public class DuplicateDomainException : Exception +{ + public DuplicateDomainException() + : base("A domain already exists for this organization.") + { + + } +} diff --git a/src/Core/MailTemplates/Handlebars/NewDeviceLoginTwoFactorEmail.html.hbs b/src/Core/MailTemplates/Handlebars/NewDeviceLoginTwoFactorEmail.html.hbs deleted file mode 100644 index d44380b577..0000000000 --- a/src/Core/MailTemplates/Handlebars/NewDeviceLoginTwoFactorEmail.html.hbs +++ /dev/null @@ -1,19 +0,0 @@ -{{#>FullHtmlLayout}} - - - - - - - - - - -
- Your two-step verification code is: {{Token}} -
- Use this code to complete logging in with Bitwarden. -
- This email was sent because you are logging in from a device we don’t recognize. If you did not request this code, you may want to change your master password. You can view our tips for selecting a secure master password here. -
-{{/FullHtmlLayout}} diff --git a/src/Core/MailTemplates/Handlebars/NewDeviceLoginTwoFactorEmail.text.hbs b/src/Core/MailTemplates/Handlebars/NewDeviceLoginTwoFactorEmail.text.hbs deleted file mode 100644 index 94a0f4a09a..0000000000 --- a/src/Core/MailTemplates/Handlebars/NewDeviceLoginTwoFactorEmail.text.hbs +++ /dev/null @@ -1,7 +0,0 @@ -{{#>BasicTextLayout}} -Your two-step verification code is: {{Token}} - -Use this code to complete logging in with Bitwarden. - -This email was sent because you are logging in from a device we don’t recognize. If you did not request this code, you may want to change your master password (https://bitwarden.com/help/master-password/#change-master-password). You can view our tips for selecting a secure master password here (https://bitwarden.com/blog/picking-the-right-password-for-your-password-manager/). -{{/BasicTextLayout}} \ No newline at end of file diff --git a/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.html.hbs b/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.html.hbs new file mode 100644 index 0000000000..11b482acda --- /dev/null +++ b/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.html.hbs @@ -0,0 +1,27 @@ +{{#>FullHtmlLayout}} + + + + + + + + + + + + + +
+ The domain {{DomainName}} in your Bitwarden organization could not be verified. +
+ Check the corresponding record in your domain host. Then reverify this domain in Bitwarden to use it for your organization. +
+ The domain will be removed from your organization in 7 days if it is not verified. +
+ + Manage Domains + +
+
+{{/FullHtmlLayout}} \ No newline at end of file diff --git a/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.text.hbs b/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.text.hbs new file mode 100644 index 0000000000..f056bf26c3 --- /dev/null +++ b/src/Core/MailTemplates/Handlebars/OrganizationDomainUnverified.text.hbs @@ -0,0 +1,10 @@ +{{#>BasicTextLayout}} +The domain {{DomainName}} in your Bitwarden organization could not be verified. + +Check the corresponding record in your domain host. Then reverify this domain in Bitwarden to use it for your organization. + +The domain will be removed from your organization in 7 days if it is not verified. + +{{Url}} + +{{/BasicTextLayout}} \ No newline at end of file diff --git a/src/Core/Models/Api/Response/Duo/DuoResponseModel.cs b/src/Core/Models/Api/Response/Duo/DuoResponseModel.cs new file mode 100644 index 0000000000..573d77ab0c --- /dev/null +++ b/src/Core/Models/Api/Response/Duo/DuoResponseModel.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; + +namespace Bit.Core.Models.Api.Response.Duo; + +public class DuoResponseModel +{ + [JsonPropertyName("stat")] + public string Stat { get; set; } + + [JsonPropertyName("code")] + public int? Code { get; set; } + + [JsonPropertyName("message")] + public string Message { get; set; } + + [JsonPropertyName("message_detail")] + public string MessageDetail { get; set; } + + [JsonPropertyName("response")] + public Response Response { get; set; } +} + +public class Response +{ + [JsonPropertyName("time")] + public int Time { get; set; } +} diff --git a/src/Core/Models/Business/OrganizationLicense.cs b/src/Core/Models/Business/OrganizationLicense.cs index 03cba08c49..58cce4cffb 100644 --- a/src/Core/Models/Business/OrganizationLicense.cs +++ b/src/Core/Models/Business/OrganizationLicense.cs @@ -199,21 +199,42 @@ public class OrganizationLicense : ILicense } } - public bool CanUse(IGlobalSettings globalSettings) + public bool CanUse(IGlobalSettings globalSettings, ILicensingService licensingService, out string exception) { if (!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow) { + exception = "Invalid license. Your organization is disabled or the license has expired."; return false; } - if (ValidLicenseVersion) + if (!ValidLicenseVersion) { - return InstallationId == globalSettings.Installation.Id && SelfHost; + exception = $"Version {Version} is not supported."; + return false; } - else + + if (InstallationId != globalSettings.Installation.Id || !SelfHost) { - throw new NotSupportedException($"Version {Version} is not supported."); + exception = "Invalid license. Make sure your license allows for on-premise " + + "hosting of organizations and that the installation id matches your current installation."; + return false; } + + if (LicenseType != null && LicenseType != Enums.LicenseType.Organization) + { + exception = "Premium licenses cannot be applied to an organization. " + + "Upload this license from your personal account settings page."; + return false; + } + + if (!licensingService.VerifyLicense(this)) + { + exception = "Invalid license."; + return false; + } + + exception = ""; + return true; } public bool VerifyData(Organization organization, IGlobalSettings globalSettings) diff --git a/src/Core/Models/Data/EventMessage.cs b/src/Core/Models/Data/EventMessage.cs index d140a86cdb..6d2a1f2b4e 100644 --- a/src/Core/Models/Data/EventMessage.cs +++ b/src/Core/Models/Data/EventMessage.cs @@ -32,4 +32,7 @@ public class EventMessage : IEvent public string IpAddress { get; set; } public Guid? IdempotencyId { get; private set; } = Guid.NewGuid(); public EventSystemUser? SystemUser { get; set; } + public string DomainName { get; set; } + public Guid? SecretId { get; set; } + public Guid? ServiceAccountId { get; set; } } diff --git a/src/Core/Models/Data/EventTableEntity.cs b/src/Core/Models/Data/EventTableEntity.cs index 0668fdc1d8..df4a85acaf 100644 --- a/src/Core/Models/Data/EventTableEntity.cs +++ b/src/Core/Models/Data/EventTableEntity.cs @@ -27,6 +27,9 @@ public class EventTableEntity : TableEntity, IEvent IpAddress = e.IpAddress; ActingUserId = e.ActingUserId; SystemUser = e.SystemUser; + DomainName = e.DomainName; + SecretId = e.SecretId; + ServiceAccountId = e.ServiceAccountId; } public DateTime Date { get; set; } @@ -46,6 +49,9 @@ public class EventTableEntity : TableEntity, IEvent public string IpAddress { get; set; } public Guid? ActingUserId { get; set; } public EventSystemUser? SystemUser { get; set; } + public string DomainName { get; set; } + public Guid? SecretId { get; set; } + public Guid? ServiceAccountId { get; set; } public override IDictionary WriteEntity(OperationContext operationContext) { @@ -152,6 +158,24 @@ public class EventTableEntity : TableEntity, IEvent }); } + if (e.OrganizationId.HasValue && e.ServiceAccountId.HasValue) + { + entities.Add(new EventTableEntity(e) + { + PartitionKey = pKey, + RowKey = $"ServiceAccountId={e.ServiceAccountId}__Date={dateKey}__Uniquifier={uniquifier}" + }); + } + + if (e.SecretId.HasValue) + { + entities.Add(new EventTableEntity(e) + { + PartitionKey = pKey, + RowKey = $"SecretId={e.CipherId}__Date={dateKey}__Uniquifier={uniquifier}" + }); + } + return entities; } diff --git a/src/Core/Models/Data/IEvent.cs b/src/Core/Models/Data/IEvent.cs index 290b9e66e4..6a177e39ca 100644 --- a/src/Core/Models/Data/IEvent.cs +++ b/src/Core/Models/Data/IEvent.cs @@ -21,4 +21,7 @@ public interface IEvent string IpAddress { get; set; } DateTime Date { get; set; } EventSystemUser? SystemUser { get; set; } + string DomainName { get; set; } + Guid? SecretId { get; set; } + Guid? ServiceAccountId { get; set; } } diff --git a/src/Core/Models/Data/Organizations/OrganizationDomainSsoDetailsData.cs b/src/Core/Models/Data/Organizations/OrganizationDomainSsoDetailsData.cs new file mode 100644 index 0000000000..1a87db792e --- /dev/null +++ b/src/Core/Models/Data/Organizations/OrganizationDomainSsoDetailsData.cs @@ -0,0 +1,16 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Models.Data.Organizations; + +public class OrganizationDomainSsoDetailsData +{ + public Guid OrganizationId { get; set; } + public string OrganizationName { get; set; } + public string DomainName { get; set; } + public bool SsoAvailable { get; set; } + public string OrganizationIdentifier { get; set; } + public bool SsoRequired { get; set; } + public PolicyType? PolicyType { get; set; } + public DateTime? VerifiedDate { get; set; } + public bool OrganizationEnabled { get; set; } +} diff --git a/src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserUserDetails.cs b/src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserUserDetails.cs index 74e06182bf..98bad1ccb6 100644 --- a/src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserUserDetails.cs +++ b/src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserUserDetails.cs @@ -12,6 +12,7 @@ public class OrganizationUserUserDetails : IExternal, ITwoFactorProvidersUser public Guid? UserId { get; set; } public string Name { get; set; } public string Email { get; set; } + public string AvatarColor { get; set; } public string TwoFactorProviders { get; set; } public bool? Premium { get; set; } public OrganizationUserStatusType Status { get; set; } @@ -61,11 +62,9 @@ public class OrganizationUserUserDetails : IExternal, ITwoFactorProvidersUser return Premium.GetValueOrDefault(false); } - public bool OccupiesOrganizationSeat + public Permissions GetPermissions() { - get - { - return Status != OrganizationUserStatusType.Revoked; - } + return string.IsNullOrWhiteSpace(Permissions) ? null + : CoreHelpers.LoadClassFromJsonData(Permissions); } } diff --git a/src/Core/Models/Data/Organizations/SelfHostedOrganizationDetails.cs b/src/Core/Models/Data/Organizations/SelfHostedOrganizationDetails.cs new file mode 100644 index 0000000000..a2c0811762 --- /dev/null +++ b/src/Core/Models/Data/Organizations/SelfHostedOrganizationDetails.cs @@ -0,0 +1,145 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Models.Business; +using Bit.Core.Models.OrganizationConnectionConfigs; + +namespace Bit.Core.Models.Data.Organizations; + +public class SelfHostedOrganizationDetails : Organization +{ + public int OccupiedSeatCount { get; set; } + public int CollectionCount { get; set; } + public int GroupCount { get; set; } + public IEnumerable OrganizationUsers { get; set; } + public IEnumerable Policies { get; set; } + public SsoConfig SsoConfig { get; set; } + public IEnumerable ScimConnections { get; set; } + + public bool CanUseLicense(OrganizationLicense license, out string exception) + { + if (license.Seats.HasValue && OccupiedSeatCount > license.Seats.Value) + { + exception = $"Your organization currently has {OccupiedSeatCount} seats filled. " + + $"Your new license only has ({license.Seats.Value}) seats. Remove some users."; + return false; + } + + if (license.MaxCollections.HasValue && CollectionCount > license.MaxCollections.Value) + { + exception = $"Your organization currently has {CollectionCount} collections. " + + $"Your new license allows for a maximum of ({license.MaxCollections.Value}) collections. " + + "Remove some collections."; + return false; + } + + if (!license.UseGroups && UseGroups && GroupCount > 1) + { + exception = $"Your organization currently has {GroupCount} groups. " + + $"Your new license does not allow for the use of groups. Remove all groups."; + return false; + } + + var enabledPolicyCount = Policies.Count(p => p.Enabled); + if (!license.UsePolicies && UsePolicies && enabledPolicyCount > 0) + { + exception = $"Your organization currently has {enabledPolicyCount} enabled " + + $"policies. Your new license does not allow for the use of policies. Disable all policies."; + return false; + } + + if (!license.UseSso && UseSso && SsoConfig is { Enabled: true }) + { + exception = $"Your organization currently has a SSO configuration. " + + $"Your new license does not allow for the use of SSO. Disable your SSO configuration."; + return false; + } + + if (!license.UseKeyConnector && UseKeyConnector && SsoConfig?.Data != null && + SsoConfig.GetData().KeyConnectorEnabled) + { + exception = $"Your organization currently has Key Connector enabled. " + + $"Your new license does not allow for the use of Key Connector. Disable your Key Connector."; + return false; + } + + if (!license.UseScim && UseScim && ScimConnections != null && + ScimConnections.Any(c => c.GetConfig() is { Enabled: true })) + { + exception = "Your new plan does not allow the SCIM feature. " + + "Disable your SCIM configuration."; + return false; + } + + if (!license.UseCustomPermissions && UseCustomPermissions && + OrganizationUsers.Any(ou => ou.Type == OrganizationUserType.Custom)) + { + exception = "Your new plan does not allow the Custom Permissions feature. " + + "Disable your Custom Permissions configuration."; + return false; + } + + if (!license.UseResetPassword && UseResetPassword && + Policies.Any(p => p.Type == PolicyType.ResetPassword && p.Enabled)) + { + exception = "Your new license does not allow the Password Reset feature. " + + "Disable your Password Reset policy."; + return false; + } + + exception = ""; + return true; + } + + public Organization ToOrganization() + { + // Any new Organization properties must be added here for them to flow through to self-hosted organizations + return new Organization + { + Id = Id, + Identifier = Identifier, + Name = Name, + BusinessName = BusinessName, + BusinessAddress1 = BusinessAddress1, + BusinessAddress2 = BusinessAddress2, + BusinessAddress3 = BusinessAddress3, + BusinessCountry = BusinessCountry, + BusinessTaxNumber = BusinessTaxNumber, + BillingEmail = BillingEmail, + Plan = Plan, + PlanType = PlanType, + Seats = Seats, + MaxCollections = MaxCollections, + UsePolicies = UsePolicies, + UseSso = UseSso, + UseKeyConnector = UseKeyConnector, + UseScim = UseScim, + UseGroups = UseGroups, + UseDirectory = UseDirectory, + UseEvents = UseEvents, + UseTotp = UseTotp, + Use2fa = Use2fa, + UseApi = UseApi, + UseResetPassword = UseResetPassword, + UseSecretsManager = UseSecretsManager, + SelfHost = SelfHost, + UsersGetPremium = UsersGetPremium, + UseCustomPermissions = UseCustomPermissions, + Storage = Storage, + MaxStorageGb = MaxStorageGb, + Gateway = Gateway, + GatewayCustomerId = GatewayCustomerId, + GatewaySubscriptionId = GatewaySubscriptionId, + ReferenceData = ReferenceData, + Enabled = Enabled, + LicenseKey = LicenseKey, + PublicKey = PublicKey, + PrivateKey = PrivateKey, + TwoFactorProviders = TwoFactorProviders, + ExpirationDate = ExpirationDate, + CreationDate = CreationDate, + RevisionDate = RevisionDate, + MaxAutoscaleSeats = MaxAutoscaleSeats, + OwnersNotifiedOfAutoscaling = OwnersNotifiedOfAutoscaling, + }; + } +} diff --git a/src/Core/Models/Mail/OrganizationDomainUnverifiedViewModel.cs b/src/Core/Models/Mail/OrganizationDomainUnverifiedViewModel.cs new file mode 100644 index 0000000000..a0547ed3a1 --- /dev/null +++ b/src/Core/Models/Mail/OrganizationDomainUnverifiedViewModel.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.Models.Mail; + +public class OrganizationDomainUnverifiedViewModel +{ + public string Url { get; set; } + public string DomainName { get; set; } +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/CreateOrganizationDomainCommand.cs b/src/Core/OrganizationFeatures/OrganizationDomains/CreateOrganizationDomainCommand.cs new file mode 100644 index 0000000000..e5aa4a09ad --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationDomains/CreateOrganizationDomainCommand.cs @@ -0,0 +1,76 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Bit.Core.Settings; +using Microsoft.Extensions.Logging; + +namespace Bit.Core.OrganizationFeatures.OrganizationDomains; + +public class CreateOrganizationDomainCommand : ICreateOrganizationDomainCommand +{ + private readonly IOrganizationDomainRepository _organizationDomainRepository; + private readonly IEventService _eventService; + private readonly IDnsResolverService _dnsResolverService; + private readonly ILogger _logger; + private readonly IGlobalSettings _globalSettings; + + public CreateOrganizationDomainCommand( + IOrganizationDomainRepository organizationDomainRepository, + IEventService eventService, + IDnsResolverService dnsResolverService, + ILogger logger, + IGlobalSettings globalSettings) + { + _organizationDomainRepository = organizationDomainRepository; + _eventService = eventService; + _dnsResolverService = dnsResolverService; + _logger = logger; + _globalSettings = globalSettings; + } + + public async Task CreateAsync(OrganizationDomain organizationDomain) + { + //Domains claimed and verified by an organization cannot be claimed + var claimedDomain = + await _organizationDomainRepository.GetClaimedDomainsByDomainNameAsync(organizationDomain.DomainName); + if (claimedDomain.Any()) + { + throw new ConflictException("The domain is not available to be claimed."); + } + + //check for duplicate domain entry for an organization + var duplicateOrgDomain = + await _organizationDomainRepository.GetDomainByOrgIdAndDomainNameAsync(organizationDomain.OrganizationId, + organizationDomain.DomainName); + if (duplicateOrgDomain is not null) + { + throw new ConflictException("A domain already exists for this organization."); + } + + try + { + if (await _dnsResolverService.ResolveAsync(organizationDomain.DomainName, organizationDomain.Txt)) + { + organizationDomain.SetVerifiedDate(); + } + } + catch (Exception e) + { + _logger.LogError("Error verifying Organization domain.", e); + } + + organizationDomain.SetNextRunDate(_globalSettings.DomainVerification.VerificationInterval); + organizationDomain.SetLastCheckedDate(); + + var orgDomain = await _organizationDomainRepository.CreateAsync(organizationDomain); + + await _eventService.LogOrganizationDomainEventAsync(orgDomain, EventType.OrganizationDomain_Added); + await _eventService.LogOrganizationDomainEventAsync(orgDomain, + orgDomain.VerifiedDate != null ? EventType.OrganizationDomain_Verified : EventType.OrganizationDomain_NotVerified); + + return orgDomain; + } +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommand.cs b/src/Core/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommand.cs new file mode 100644 index 0000000000..c420609130 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommand.cs @@ -0,0 +1,32 @@ +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; +using Bit.Core.Repositories; +using Bit.Core.Services; + +namespace Bit.Core.OrganizationFeatures.OrganizationDomains; + +public class DeleteOrganizationDomainCommand : IDeleteOrganizationDomainCommand +{ + private readonly IOrganizationDomainRepository _organizationDomainRepository; + private readonly IEventService _eventService; + + public DeleteOrganizationDomainCommand(IOrganizationDomainRepository organizationDomainRepository, + IEventService eventService) + { + _organizationDomainRepository = organizationDomainRepository; + _eventService = eventService; + } + + public async Task DeleteAsync(Guid id) + { + var domain = await _organizationDomainRepository.GetByIdAsync(id); + if (domain is null) + { + throw new NotFoundException(); + } + + await _organizationDomainRepository.DeleteAsync(domain); + await _eventService.LogOrganizationDomainEventAsync(domain, EventType.OrganizationDomain_Removed); + } +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQuery.cs b/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQuery.cs new file mode 100644 index 0000000000..8037fa8ec2 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQuery.cs @@ -0,0 +1,18 @@ +using Bit.Core.Entities; +using Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; +using Bit.Core.Repositories; + +namespace Bit.Core.OrganizationFeatures.OrganizationDomains; + +public class GetOrganizationDomainByIdQuery : IGetOrganizationDomainByIdQuery +{ + private readonly IOrganizationDomainRepository _organizationDomainRepository; + + public GetOrganizationDomainByIdQuery(IOrganizationDomainRepository organizationDomainRepository) + { + _organizationDomainRepository = organizationDomainRepository; + } + + public async Task GetOrganizationDomainById(Guid id) + => await _organizationDomainRepository.GetByIdAsync(id); +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQuery.cs b/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQuery.cs new file mode 100644 index 0000000000..6b94dbf173 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQuery.cs @@ -0,0 +1,18 @@ +using Bit.Core.Entities; +using Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; +using Bit.Core.Repositories; + +namespace Bit.Core.OrganizationFeatures.OrganizationDomains; + +public class GetOrganizationDomainByOrganizationIdQuery : IGetOrganizationDomainByOrganizationIdQuery +{ + private readonly IOrganizationDomainRepository _organizationDomainRepository; + + public GetOrganizationDomainByOrganizationIdQuery(IOrganizationDomainRepository organizationDomainRepository) + { + _organizationDomainRepository = organizationDomainRepository; + } + + public async Task> GetDomainsByOrganizationId(Guid orgId) + => await _organizationDomainRepository.GetDomainsByOrganizationIdAsync(orgId); +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/ICreateOrganizationDomainCommand.cs b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/ICreateOrganizationDomainCommand.cs new file mode 100644 index 0000000000..79df45396c --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/ICreateOrganizationDomainCommand.cs @@ -0,0 +1,8 @@ +using Bit.Core.Entities; + +namespace Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; + +public interface ICreateOrganizationDomainCommand +{ + Task CreateAsync(OrganizationDomain organizationDomain); +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IDeleteOrganizationDomainCommand.cs b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IDeleteOrganizationDomainCommand.cs new file mode 100644 index 0000000000..4a5cc1c556 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IDeleteOrganizationDomainCommand.cs @@ -0,0 +1,6 @@ +namespace Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; + +public interface IDeleteOrganizationDomainCommand +{ + Task DeleteAsync(Guid id); +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByIdQuery.cs b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByIdQuery.cs new file mode 100644 index 0000000000..765007f42f --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByIdQuery.cs @@ -0,0 +1,8 @@ +using Bit.Core.Entities; + +namespace Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; + +public interface IGetOrganizationDomainByIdQuery +{ + Task GetOrganizationDomainById(Guid id); +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByOrganizationIdQuery.cs b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByOrganizationIdQuery.cs new file mode 100644 index 0000000000..1377cb48fb --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IGetOrganizationDomainByOrganizationIdQuery.cs @@ -0,0 +1,8 @@ +using Bit.Core.Entities; + +namespace Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; + +public interface IGetOrganizationDomainByOrganizationIdQuery +{ + Task> GetDomainsByOrganizationId(Guid orgId); +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IVerifyOrganizationDomainCommand.cs b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IVerifyOrganizationDomainCommand.cs new file mode 100644 index 0000000000..1d070cf3c1 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationDomains/Interfaces/IVerifyOrganizationDomainCommand.cs @@ -0,0 +1,8 @@ +using Bit.Core.Entities; + +namespace Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; + +public interface IVerifyOrganizationDomainCommand +{ + Task VerifyOrganizationDomain(Guid id); +} diff --git a/src/Core/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs b/src/Core/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs new file mode 100644 index 0000000000..a2b38a3573 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommand.cs @@ -0,0 +1,73 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Microsoft.Extensions.Logging; + +namespace Bit.Core.OrganizationFeatures.OrganizationDomains; + +public class VerifyOrganizationDomainCommand : IVerifyOrganizationDomainCommand +{ + private readonly IOrganizationDomainRepository _organizationDomainRepository; + private readonly IDnsResolverService _dnsResolverService; + private readonly IEventService _eventService; + private readonly ILogger _logger; + + public VerifyOrganizationDomainCommand( + IOrganizationDomainRepository organizationDomainRepository, + IDnsResolverService dnsResolverService, + IEventService eventService, + ILogger logger) + { + _organizationDomainRepository = organizationDomainRepository; + _dnsResolverService = dnsResolverService; + _eventService = eventService; + _logger = logger; + } + + public async Task VerifyOrganizationDomain(Guid id) + { + var domain = await _organizationDomainRepository.GetByIdAsync(id); + if (domain is null) + { + throw new NotFoundException(); + } + + if (domain.VerifiedDate is not null) + { + domain.SetLastCheckedDate(); + await _organizationDomainRepository.ReplaceAsync(domain); + throw new ConflictException("Domain has already been verified."); + } + + var claimedDomain = + await _organizationDomainRepository.GetClaimedDomainsByDomainNameAsync(domain.DomainName); + if (claimedDomain.Any()) + { + domain.SetLastCheckedDate(); + await _organizationDomainRepository.ReplaceAsync(domain); + throw new ConflictException("The domain is not available to be claimed."); + } + + try + { + if (await _dnsResolverService.ResolveAsync(domain.DomainName, domain.Txt)) + { + domain.SetVerifiedDate(); + } + } + catch (Exception e) + { + _logger.LogError("Error verifying Organization domain. {errorMessage}", e.Message); + } + + domain.SetLastCheckedDate(); + await _organizationDomainRepository.ReplaceAsync(domain); + + await _eventService.LogOrganizationDomainEventAsync(domain, + domain.VerifiedDate != null ? EventType.OrganizationDomain_Verified : EventType.OrganizationDomain_NotVerified); + return domain; + } +} diff --git a/src/Core/OrganizationFeatures/OrganizationLicenses/Cloud/CloudGetOrganizationLicenseQuery.cs b/src/Core/OrganizationFeatures/OrganizationLicenses/Cloud/CloudGetOrganizationLicenseQuery.cs index ff8a6d34fb..a8d3a1638f 100644 --- a/src/Core/OrganizationFeatures/OrganizationLicenses/Cloud/CloudGetOrganizationLicenseQuery.cs +++ b/src/Core/OrganizationFeatures/OrganizationLicenses/Cloud/CloudGetOrganizationLicenseQuery.cs @@ -32,7 +32,7 @@ public class CloudGetOrganizationLicenseQuery : ICloudGetOrganizationLicenseQuer throw new BadRequestException("Invalid installation id"); } - var subInfo = await _paymentService.GetSubscriptionAsync(organization); - return new OrganizationLicense(organization, subInfo, installationId, _licensingService, version); + var subscriptionInfo = await _paymentService.GetSubscriptionAsync(organization); + return new OrganizationLicense(organization, subscriptionInfo, installationId, _licensingService, version); } } diff --git a/src/Core/OrganizationFeatures/OrganizationLicenses/Interfaces/IUpdateOrganizationLicenseCommand.cs b/src/Core/OrganizationFeatures/OrganizationLicenses/Interfaces/IUpdateOrganizationLicenseCommand.cs new file mode 100644 index 0000000000..2ba82c1c62 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationLicenses/Interfaces/IUpdateOrganizationLicenseCommand.cs @@ -0,0 +1,13 @@ +#nullable enable + +using Bit.Core.Entities; +using Bit.Core.Models.Business; +using Bit.Core.Models.Data.Organizations; + +namespace Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces; + +public interface IUpdateOrganizationLicenseCommand +{ + Task UpdateLicenseAsync(SelfHostedOrganizationDetails selfHostedOrganization, + OrganizationLicense license, Organization? currentOrganizationUsingLicenseKey); +} diff --git a/src/Core/OrganizationFeatures/OrganizationLicenses/UpdateOrganizationLicenseCommand.cs b/src/Core/OrganizationFeatures/OrganizationLicenses/UpdateOrganizationLicenseCommand.cs new file mode 100644 index 0000000000..583f521434 --- /dev/null +++ b/src/Core/OrganizationFeatures/OrganizationLicenses/UpdateOrganizationLicenseCommand.cs @@ -0,0 +1,66 @@ +#nullable enable + +using System.Text.Json; +using Bit.Core.Entities; +using Bit.Core.Exceptions; +using Bit.Core.Models.Business; +using Bit.Core.Models.Data.Organizations; +using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces; +using Bit.Core.Services; +using Bit.Core.Settings; +using Bit.Core.Utilities; + +namespace Bit.Core.OrganizationFeatures.OrganizationLicenses; + +public class UpdateOrganizationLicenseCommand : IUpdateOrganizationLicenseCommand +{ + private readonly ILicensingService _licensingService; + private readonly IGlobalSettings _globalSettings; + private readonly IOrganizationService _organizationService; + + public UpdateOrganizationLicenseCommand( + ILicensingService licensingService, + IGlobalSettings globalSettings, + IOrganizationService organizationService) + { + _licensingService = licensingService; + _globalSettings = globalSettings; + _organizationService = organizationService; + } + + public async Task UpdateLicenseAsync(SelfHostedOrganizationDetails selfHostedOrganization, + OrganizationLicense license, Organization? currentOrganizationUsingLicenseKey) + { + if (currentOrganizationUsingLicenseKey != null && currentOrganizationUsingLicenseKey.Id != selfHostedOrganization.Id) + { + throw new BadRequestException("License is already in use by another organization."); + } + + var canUse = license.CanUse(_globalSettings, _licensingService, out var exception) && + selfHostedOrganization.CanUseLicense(license, out exception); + + if (!canUse) + { + throw new BadRequestException(exception); + } + + await WriteLicenseFileAsync(selfHostedOrganization, license); + await UpdateOrganizationAsync(selfHostedOrganization, license); + } + + private async Task WriteLicenseFileAsync(Organization organization, OrganizationLicense license) + { + var dir = $"{_globalSettings.LicenseDirectory}/organization"; + Directory.CreateDirectory(dir); + await using var fs = new FileStream(Path.Combine(dir, $"{organization.Id}.json"), FileMode.Create); + await JsonSerializer.SerializeAsync(fs, license, JsonHelpers.Indented); + } + + private async Task UpdateOrganizationAsync(SelfHostedOrganizationDetails selfHostedOrganizationDetails, OrganizationLicense license) + { + var organization = selfHostedOrganizationDetails.ToOrganization(); + organization.UpdateFromLicense(license); + + await _organizationService.ReplaceAndUpdateCacheAsync(organization); + } +} diff --git a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs index b8e2a775e2..983fa3b352 100644 --- a/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs +++ b/src/Core/OrganizationFeatures/OrganizationServiceCollectionExtensions.cs @@ -7,6 +7,8 @@ using Bit.Core.OrganizationFeatures.OrganizationCollections; using Bit.Core.OrganizationFeatures.OrganizationCollections.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationConnections; using Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces; +using Bit.Core.OrganizationFeatures.OrganizationDomains; +using Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationLicenses; using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise; @@ -34,7 +36,8 @@ public static class OrganizationServiceCollectionExtensions services.AddOrganizationApiKeyCommandsQueries(); services.AddOrganizationCollectionCommands(); services.AddOrganizationGroupCommands(); - services.AddOrganizationLicenseCommandQueries(); + services.AddOrganizationLicenseCommandsQueries(); + services.AddOrganizationDomainCommandsQueries(); } private static void AddOrganizationConnectionCommands(this IServiceCollection services) @@ -88,10 +91,20 @@ public static class OrganizationServiceCollectionExtensions services.AddScoped(); } - private static void AddOrganizationLicenseCommandQueries(this IServiceCollection services) + private static void AddOrganizationLicenseCommandsQueries(this IServiceCollection services) { services.AddScoped(); services.AddScoped(); + services.AddScoped(); + } + + private static void AddOrganizationDomainCommandsQueries(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } private static void AddTokenizers(this IServiceCollection services) diff --git a/src/Core/Repositories/IOrganizationDomainRepository.cs b/src/Core/Repositories/IOrganizationDomainRepository.cs new file mode 100644 index 0000000000..dfe5358541 --- /dev/null +++ b/src/Core/Repositories/IOrganizationDomainRepository.cs @@ -0,0 +1,15 @@ +using Bit.Core.Entities; +using Bit.Core.Models.Data.Organizations; + +namespace Bit.Core.Repositories; + +public interface IOrganizationDomainRepository : IRepository +{ + Task> GetClaimedDomainsByDomainNameAsync(string domainName); + Task> GetDomainsByOrganizationIdAsync(Guid orgId); + Task> GetManyByNextRunDateAsync(DateTime date); + Task GetOrganizationDomainSsoDetailsAsync(string email); + Task GetDomainByOrgIdAndDomainNameAsync(Guid orgId, string domainName); + Task> GetExpiredOrganizationDomainsAsync(); + Task DeleteExpiredAsync(int expirationPeriod); +} diff --git a/src/Core/Repositories/IOrganizationRepository.cs b/src/Core/Repositories/IOrganizationRepository.cs index 690bff9139..e8bdc869ac 100644 --- a/src/Core/Repositories/IOrganizationRepository.cs +++ b/src/Core/Repositories/IOrganizationRepository.cs @@ -11,4 +11,6 @@ public interface IOrganizationRepository : IRepository Task> SearchAsync(string name, string userEmail, bool? paid, int skip, int take); Task UpdateStorageAsync(Guid id); Task> GetManyAbilitiesAsync(); + Task GetByLicenseKeyAsync(string licenseKey); + Task GetSelfHostedOrganizationDetailsById(Guid id); } diff --git a/src/Core/Repositories/IOrganizationUserRepository.cs b/src/Core/Repositories/IOrganizationUserRepository.cs index 3844b1a87d..207295b8b1 100644 --- a/src/Core/Repositories/IOrganizationUserRepository.cs +++ b/src/Core/Repositories/IOrganizationUserRepository.cs @@ -13,6 +13,7 @@ public interface IOrganizationUserRepository : IRepository> GetManyByUserAsync(Guid userId); Task> GetManyByOrganizationAsync(Guid organizationId, OrganizationUserType? type); Task GetCountByOrganizationAsync(Guid organizationId, string email, bool onlyRegisteredUsers); + Task GetOccupiedSeatCountByOrganizationIdAsync(Guid organizationId); Task> SelectKnownEmailsAsync(Guid organizationId, IEnumerable emails, bool onlyRegisteredUsers); Task GetByOrganizationAsync(Guid organizationId, Guid userId); Task>> GetByIdWithCollectionsAsync(Guid id); diff --git a/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/ICreateAccessPoliciesCommand.cs b/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/ICreateAccessPoliciesCommand.cs index 9ec9ff0dbd..5baa486e07 100644 --- a/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/ICreateAccessPoliciesCommand.cs +++ b/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/ICreateAccessPoliciesCommand.cs @@ -1,8 +1,9 @@ -using Bit.Core.SecretsManager.Entities; +using Bit.Core.Enums; +using Bit.Core.SecretsManager.Entities; namespace Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces; public interface ICreateAccessPoliciesCommand { - Task> CreateAsync(List accessPolicies); + Task> CreateManyAsync(List accessPolicies, Guid userId, AccessClientType accessType); } diff --git a/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/IDeleteAccessPolicyCommand.cs b/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/IDeleteAccessPolicyCommand.cs index de3215a02c..e4d313b409 100644 --- a/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/IDeleteAccessPolicyCommand.cs +++ b/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/IDeleteAccessPolicyCommand.cs @@ -2,5 +2,5 @@ public interface IDeleteAccessPolicyCommand { - Task DeleteAsync(Guid id); + Task DeleteAsync(Guid id, Guid userId); } diff --git a/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/IUpdateAccessPolicyCommand.cs b/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/IUpdateAccessPolicyCommand.cs index 225f6a752e..afeb47e9fd 100644 --- a/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/IUpdateAccessPolicyCommand.cs +++ b/src/Core/SecretsManager/Commands/AccessPolicies/Interfaces/IUpdateAccessPolicyCommand.cs @@ -4,5 +4,5 @@ namespace Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces; public interface IUpdateAccessPolicyCommand { - public Task UpdateAsync(Guid id, bool read, bool write); + public Task UpdateAsync(Guid id, bool read, bool write, Guid userId); } diff --git a/src/Core/SecretsManager/Commands/Porting/Interfaces/IImportCommand.cs b/src/Core/SecretsManager/Commands/Porting/Interfaces/IImportCommand.cs new file mode 100644 index 0000000000..7950e1a17c --- /dev/null +++ b/src/Core/SecretsManager/Commands/Porting/Interfaces/IImportCommand.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.SecretsManager.Commands.Porting.Interfaces; + +public interface IImportCommand +{ + Task ImportAsync(Guid organizationId, SMImport import); + SMImport AssignNewIds(SMImport import); +} diff --git a/src/Core/SecretsManager/Commands/Porting/SMImport.cs b/src/Core/SecretsManager/Commands/Porting/SMImport.cs new file mode 100644 index 0000000000..0e61b3acaf --- /dev/null +++ b/src/Core/SecretsManager/Commands/Porting/SMImport.cs @@ -0,0 +1,41 @@ +namespace Bit.Core.SecretsManager.Commands.Porting; + +public class SMImport +{ + public IEnumerable Projects { get; set; } + public IEnumerable Secrets { get; set; } + + public class InnerProject + { + public InnerProject() { } + + public InnerProject(Core.SecretsManager.Entities.Project project) + { + Id = project.Id; + Name = project.Name; + } + + public Guid Id { get; set; } + public string Name { get; set; } + } + + public class InnerSecret + { + public InnerSecret() { } + + public InnerSecret(Core.SecretsManager.Entities.Secret secret) + { + Id = secret.Id; + Key = secret.Key; + Value = secret.Value; + Note = secret.Note; + ProjectIds = secret.Projects != null && secret.Projects.Any() ? secret.Projects.Select(p => p.Id) : null; + } + + public Guid Id { get; set; } + public string Key { get; set; } + public string Value { get; set; } + public string Note { get; set; } + public IEnumerable ProjectIds { get; set; } + } +} diff --git a/src/Core/SecretsManager/Commands/Projects/Interfaces/ICreateProjectCommand.cs b/src/Core/SecretsManager/Commands/Projects/Interfaces/ICreateProjectCommand.cs index c5f43d360e..50cd714cb8 100644 --- a/src/Core/SecretsManager/Commands/Projects/Interfaces/ICreateProjectCommand.cs +++ b/src/Core/SecretsManager/Commands/Projects/Interfaces/ICreateProjectCommand.cs @@ -4,5 +4,5 @@ namespace Bit.Core.SecretsManager.Commands.Projects.Interfaces; public interface ICreateProjectCommand { - Task CreateAsync(Project project); + Task CreateAsync(Project project, Guid userId); } diff --git a/src/Core/SecretsManager/Commands/Secrets/Interfaces/ICreateSecretCommand.cs b/src/Core/SecretsManager/Commands/Secrets/Interfaces/ICreateSecretCommand.cs index 9757346175..2fad757847 100644 --- a/src/Core/SecretsManager/Commands/Secrets/Interfaces/ICreateSecretCommand.cs +++ b/src/Core/SecretsManager/Commands/Secrets/Interfaces/ICreateSecretCommand.cs @@ -4,5 +4,5 @@ namespace Bit.Core.SecretsManager.Commands.Secrets.Interfaces; public interface ICreateSecretCommand { - Task CreateAsync(Secret secret); + Task CreateAsync(Secret secret, Guid userId); } diff --git a/src/Core/SecretsManager/Commands/Secrets/Interfaces/IDeleteSecretCommand.cs b/src/Core/SecretsManager/Commands/Secrets/Interfaces/IDeleteSecretCommand.cs index 493060e52d..2517c70df7 100644 --- a/src/Core/SecretsManager/Commands/Secrets/Interfaces/IDeleteSecretCommand.cs +++ b/src/Core/SecretsManager/Commands/Secrets/Interfaces/IDeleteSecretCommand.cs @@ -4,6 +4,6 @@ namespace Bit.Core.SecretsManager.Commands.Secrets.Interfaces; public interface IDeleteSecretCommand { - Task>> DeleteSecrets(List ids); + Task>> DeleteSecrets(List ids, Guid userId); } diff --git a/src/Core/SecretsManager/Commands/Secrets/Interfaces/IUpdateSecretCommand.cs b/src/Core/SecretsManager/Commands/Secrets/Interfaces/IUpdateSecretCommand.cs index 8c2f61abc0..23e6910c8b 100644 --- a/src/Core/SecretsManager/Commands/Secrets/Interfaces/IUpdateSecretCommand.cs +++ b/src/Core/SecretsManager/Commands/Secrets/Interfaces/IUpdateSecretCommand.cs @@ -4,5 +4,5 @@ namespace Bit.Core.SecretsManager.Commands.Secrets.Interfaces; public interface IUpdateSecretCommand { - Task UpdateAsync(Secret secret); + Task UpdateAsync(Secret secret, Guid userId); } diff --git a/src/Core/SecretsManager/Commands/ServiceAccounts/Interfaces/IRevokeAccessTokensCommand.cs b/src/Core/SecretsManager/Commands/ServiceAccounts/Interfaces/IRevokeAccessTokensCommand.cs new file mode 100644 index 0000000000..a6aab55d9d --- /dev/null +++ b/src/Core/SecretsManager/Commands/ServiceAccounts/Interfaces/IRevokeAccessTokensCommand.cs @@ -0,0 +1,8 @@ +using Bit.Core.SecretsManager.Entities; + +namespace Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces; + +public interface IRevokeAccessTokensCommand +{ + Task RevokeAsync(ServiceAccount serviceAccount, IEnumerable ids); +} diff --git a/src/Core/SecretsManager/Commands/Trash/IEmptyTrashCommand.cs b/src/Core/SecretsManager/Commands/Trash/IEmptyTrashCommand.cs new file mode 100644 index 0000000000..74c5c21a0a --- /dev/null +++ b/src/Core/SecretsManager/Commands/Trash/IEmptyTrashCommand.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.SecretsManager.Commands.Trash.Interfaces; + +public interface IEmptyTrashCommand +{ + Task EmptyTrash(Guid organizationId, List ids); +} + diff --git a/src/Core/SecretsManager/Commands/Trash/IRestoreTrashCommand.cs b/src/Core/SecretsManager/Commands/Trash/IRestoreTrashCommand.cs new file mode 100644 index 0000000000..561b1ffd82 --- /dev/null +++ b/src/Core/SecretsManager/Commands/Trash/IRestoreTrashCommand.cs @@ -0,0 +1,6 @@ +namespace Bit.Core.SecretsManager.Commands.Trash.Interfaces; + +public interface IRestoreTrashCommand +{ + Task RestoreTrash(Guid organizationId, List ids); +} diff --git a/src/Core/SecretsManager/Entities/AccessPolicy.cs b/src/Core/SecretsManager/Entities/AccessPolicy.cs index 62e79d0f22..bce9de0629 100644 --- a/src/Core/SecretsManager/Entities/AccessPolicy.cs +++ b/src/Core/SecretsManager/Entities/AccessPolicy.cs @@ -24,34 +24,39 @@ public abstract class BaseAccessPolicy public class UserProjectAccessPolicy : BaseAccessPolicy { public Guid? OrganizationUserId { get; set; } - public Guid? GrantedProjectId { get; set; } public User? User { get; set; } + public Guid? GrantedProjectId { get; set; } + public Project? GrantedProject { get; set; } } public class UserServiceAccountAccessPolicy : BaseAccessPolicy { public Guid? OrganizationUserId { get; set; } - public Guid? GrantedServiceAccountId { get; set; } public User? User { get; set; } + public Guid? GrantedServiceAccountId { get; set; } + public ServiceAccount? GrantedServiceAccount { get; set; } } public class GroupProjectAccessPolicy : BaseAccessPolicy { public Guid? GroupId { get; set; } - public Guid? GrantedProjectId { get; set; } public Group? Group { get; set; } + public Guid? GrantedProjectId { get; set; } + public Project? GrantedProject { get; set; } } public class GroupServiceAccountAccessPolicy : BaseAccessPolicy { public Guid? GroupId { get; set; } - public Guid? GrantedServiceAccountId { get; set; } public Group? Group { get; set; } + public Guid? GrantedServiceAccountId { get; set; } + public ServiceAccount? GrantedServiceAccount { get; set; } } public class ServiceAccountProjectAccessPolicy : BaseAccessPolicy { public Guid? ServiceAccountId { get; set; } - public Guid? GrantedProjectId { get; set; } public ServiceAccount? ServiceAccount { get; set; } + public Guid? GrantedProjectId { get; set; } + public Project? GrantedProject { get; set; } } diff --git a/src/Core/SecretsManager/Repositories/IAccessPolicyRepository.cs b/src/Core/SecretsManager/Repositories/IAccessPolicyRepository.cs index dbe05074fa..7241c81eb3 100644 --- a/src/Core/SecretsManager/Repositories/IAccessPolicyRepository.cs +++ b/src/Core/SecretsManager/Repositories/IAccessPolicyRepository.cs @@ -1,4 +1,5 @@ #nullable enable +using Bit.Core.Enums; using Bit.Core.SecretsManager.Entities; namespace Bit.Core.SecretsManager.Repositories; @@ -10,6 +11,8 @@ public interface IAccessPolicyRepository Task GetByIdAsync(Guid id); Task> GetManyByGrantedProjectIdAsync(Guid id); Task> GetManyByGrantedServiceAccountIdAsync(Guid id); + Task> GetManyByServiceAccountIdAsync(Guid id, Guid userId, + AccessClientType accessType); Task ReplaceAsync(BaseAccessPolicy baseAccessPolicy); Task DeleteAsync(Guid id); } diff --git a/src/Core/SecretsManager/Repositories/IApiKeyRepository.cs b/src/Core/SecretsManager/Repositories/IApiKeyRepository.cs index dc48b5295d..eead6d9d75 100644 --- a/src/Core/SecretsManager/Repositories/IApiKeyRepository.cs +++ b/src/Core/SecretsManager/Repositories/IApiKeyRepository.cs @@ -8,4 +8,5 @@ public interface IApiKeyRepository : IRepository { Task GetDetailsByIdAsync(Guid id); Task> GetManyByServiceAccountIdAsync(Guid id); + Task DeleteManyAsync(IEnumerable objs); } diff --git a/src/Core/SecretsManager/Repositories/IProjectRepository.cs b/src/Core/SecretsManager/Repositories/IProjectRepository.cs index ee752f5455..dddd2e21d9 100644 --- a/src/Core/SecretsManager/Repositories/IProjectRepository.cs +++ b/src/Core/SecretsManager/Repositories/IProjectRepository.cs @@ -6,11 +6,15 @@ namespace Bit.Core.SecretsManager.Repositories; public interface IProjectRepository { Task> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId, AccessClientType accessType); + Task> GetManyByOrganizationIdWriteAccessAsync(Guid organizationId, Guid userId, AccessClientType accessType); Task> GetManyByIds(IEnumerable ids); Task GetByIdAsync(Guid id); Task CreateAsync(Project project); Task ReplaceAsync(Project project); Task DeleteManyByIdAsync(IEnumerable ids); + Task> ImportAsync(IEnumerable projects); Task UserHasReadAccessToProject(Guid id, Guid userId); Task UserHasWriteAccessToProject(Guid id, Guid userId); + Task ServiceAccountHasWriteAccessToProject(Guid id, Guid userId); + Task ServiceAccountHasReadAccessToProject(Guid id, Guid userId); } diff --git a/src/Core/SecretsManager/Repositories/ISecretRepository.cs b/src/Core/SecretsManager/Repositories/ISecretRepository.cs index 0b96def9f7..bfc59fed6a 100644 --- a/src/Core/SecretsManager/Repositories/ISecretRepository.cs +++ b/src/Core/SecretsManager/Repositories/ISecretRepository.cs @@ -1,14 +1,20 @@ -using Bit.Core.SecretsManager.Entities; +using Bit.Core.Enums; +using Bit.Core.SecretsManager.Entities; namespace Bit.Core.SecretsManager.Repositories; public interface ISecretRepository { - Task> GetManyByOrganizationIdAsync(Guid organizationId); + Task> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId, AccessClientType accessType); + Task> GetManyByOrganizationIdInTrashAsync(Guid organizationId); + Task> GetManyByOrganizationIdInTrashByIdsAsync(Guid organizationId, IEnumerable ids); Task> GetManyByIds(IEnumerable ids); - Task> GetManyByProjectIdAsync(Guid projectId); + Task> GetManyByProjectIdAsync(Guid projectId, Guid userId, AccessClientType accessType); Task GetByIdAsync(Guid id); Task CreateAsync(Secret secret); Task UpdateAsync(Secret secret); Task SoftDeleteManyByIdAsync(IEnumerable ids); + Task HardDeleteManyByIdAsync(IEnumerable ids); + Task RestoreManyByIdAsync(IEnumerable ids); + Task> ImportAsync(IEnumerable secrets); } diff --git a/src/Core/SecretsManager/Repositories/IServiceAccountRepository.cs b/src/Core/SecretsManager/Repositories/IServiceAccountRepository.cs index 924a18f0b3..740d597ecf 100644 --- a/src/Core/SecretsManager/Repositories/IServiceAccountRepository.cs +++ b/src/Core/SecretsManager/Repositories/IServiceAccountRepository.cs @@ -11,4 +11,5 @@ public interface IServiceAccountRepository Task ReplaceAsync(ServiceAccount serviceAccount); Task UserHasReadAccessToServiceAccount(Guid id, Guid userId); Task UserHasWriteAccessToServiceAccount(Guid id, Guid userId); + Task> GetManyByOrganizationIdWriteAccessAsync(Guid organizationId, Guid userId, AccessClientType accessType); } diff --git a/src/Core/Services/IDnsResolverService.cs b/src/Core/Services/IDnsResolverService.cs new file mode 100644 index 0000000000..17e4edee92 --- /dev/null +++ b/src/Core/Services/IDnsResolverService.cs @@ -0,0 +1,6 @@ +namespace Bit.Core.Services; + +public interface IDnsResolverService +{ + Task ResolveAsync(string domain, string txtRecord, CancellationToken cancellationToken = default); +} diff --git a/src/Core/Services/IEventService.cs b/src/Core/Services/IEventService.cs index 68c0acfc6c..c22757f9c8 100644 --- a/src/Core/Services/IEventService.cs +++ b/src/Core/Services/IEventService.cs @@ -1,6 +1,7 @@ using Bit.Core.Entities; using Bit.Core.Entities.Provider; using Bit.Core.Enums; +using Bit.Core.SecretsManager.Entities; namespace Bit.Core.Services; @@ -23,4 +24,7 @@ public interface IEventService Task LogProviderUserEventAsync(ProviderUser providerUser, EventType type, DateTime? date = null); Task LogProviderUsersEventAsync(IEnumerable<(ProviderUser, EventType, DateTime?)> events); Task LogProviderOrganizationEventAsync(ProviderOrganization providerOrganization, EventType type, DateTime? date = null); + Task LogOrganizationDomainEventAsync(OrganizationDomain organizationDomain, EventType type, DateTime? date = null); + Task LogOrganizationDomainEventAsync(OrganizationDomain organizationDomain, EventType type, EventSystemUser systemUser, DateTime? date = null); + Task LogServiceAccountSecretEventAsync(Guid serviceAccountId, Secret secret, EventType type, DateTime? date = null); } diff --git a/src/Core/Services/IMailService.cs b/src/Core/Services/IMailService.cs index 3af89108c6..9c86a445d5 100644 --- a/src/Core/Services/IMailService.cs +++ b/src/Core/Services/IMailService.cs @@ -13,7 +13,6 @@ public interface IMailService Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail); Task SendChangeEmailEmailAsync(string newEmailAddress, string token); Task SendTwoFactorEmailAsync(string email, string token); - Task SendNewDeviceLoginTwoFactorEmailAsync(string email, string token); Task SendNoMasterPasswordHintEmailAsync(string email); Task SendMasterPasswordHintEmailAsync(string email, string hint); Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, ExpiringToken token); @@ -54,4 +53,5 @@ public interface IMailService Task SendOTPEmailAsync(string email, string token); Task SendFailedLoginAttemptsEmailAsync(string email, DateTime utcNow, string ip); Task SendFailedTwoFactorAttemptsEmailAsync(string email, DateTime utcNow, string ip); + Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable adminEmails, string organizationId, string domainName); } diff --git a/src/Core/Services/IOrganizationDomainService.cs b/src/Core/Services/IOrganizationDomainService.cs new file mode 100644 index 0000000000..87e7668ea4 --- /dev/null +++ b/src/Core/Services/IOrganizationDomainService.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.Services; + +public interface IOrganizationDomainService +{ + Task ValidateOrganizationsDomainAsync(); + Task OrganizationDomainMaintenanceAsync(); +} diff --git a/src/Core/Services/IOrganizationService.cs b/src/Core/Services/IOrganizationService.cs index 5383c1a3e3..8082ed0f44 100644 --- a/src/Core/Services/IOrganizationService.cs +++ b/src/Core/Services/IOrganizationService.cs @@ -20,7 +20,6 @@ public interface IOrganizationService Task> SignUpAsync(OrganizationSignup organizationSignup, bool provider = false); Task> SignUpAsync(OrganizationLicense license, User owner, string ownerKey, string collectionName, string publicKey, string privateKey); - Task UpdateLicenseAsync(Guid organizationId, OrganizationLicense license); Task DeleteAsync(Organization organization); Task EnableAsync(Guid organizationId, DateTime? expirationDate); Task DisableAsync(Guid organizationId, DateTime? expirationDate); @@ -70,5 +69,5 @@ public interface IOrganizationService Task RestoreUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser, IUserService userService); Task>> RestoreUsersAsync(Guid organizationId, IEnumerable organizationUserIds, Guid? restoringUserId, IUserService userService); - Task GetOccupiedSeatCount(Organization organization); + Task ReplaceAndUpdateCacheAsync(Organization org, EventType? orgEvent = null); } diff --git a/src/Core/Services/IUserService.cs b/src/Core/Services/IUserService.cs index 620e9264f7..fd66feed35 100644 --- a/src/Core/Services/IUserService.cs +++ b/src/Core/Services/IUserService.cs @@ -19,7 +19,7 @@ public interface IUserService Task RegisterUserAsync(User user, string masterPassword, string token, Guid? orgUserId); Task RegisterUserAsync(User user); Task SendMasterPasswordHintAsync(string email); - Task SendTwoFactorEmailAsync(User user, bool isBecauseNewDeviceLogin = false); + Task SendTwoFactorEmailAsync(User user); Task VerifyTwoFactorEmailAsync(User user, string token); Task StartWebAuthnRegistrationAsync(User user); Task DeleteWebAuthnKeyAsync(User user, int id); @@ -76,6 +76,4 @@ public interface IUserService Task SendOTPAsync(User user); Task VerifyOTPAsync(User user, string token); Task VerifySecretAsync(User user, string secret); - Task Needs2FABecauseNewDeviceAsync(User user, string deviceIdentifier, string grantType); - bool CanEditDeviceVerificationSettings(User user); } diff --git a/src/Core/Services/Implementations/DnsResolverService.cs b/src/Core/Services/Implementations/DnsResolverService.cs new file mode 100644 index 0000000000..c47bd2e588 --- /dev/null +++ b/src/Core/Services/Implementations/DnsResolverService.cs @@ -0,0 +1,21 @@ +using Bit.Core.Exceptions; +using DnsClient; + +namespace Bit.Core.Services; + +public class DnsResolverService : IDnsResolverService +{ + public async Task ResolveAsync(string domain, string txtRecord, CancellationToken cancellationToken = default) + { + var lookup = new LookupClient(); + var result = await lookup.QueryAsync(new DnsQuestion(domain, QueryType.TXT), cancellationToken); + if (!result.HasError) + { + return result.Answers.TxtRecords() + .Select(t => t?.EscapedText?.FirstOrDefault()) + .Any(t => t == txtRecord); + } + + throw new DnsQueryException(result.ErrorMessage); + } +} diff --git a/src/Core/Services/Implementations/EmergencyAccessService.cs b/src/Core/Services/Implementations/EmergencyAccessService.cs index e48000b525..f5ddf7adfa 100644 --- a/src/Core/Services/Implementations/EmergencyAccessService.cs +++ b/src/Core/Services/Implementations/EmergencyAccessService.cs @@ -323,7 +323,6 @@ public class EmergencyAccessService : IEmergencyAccessService grantor.Key = key; // Disable TwoFactor providers since they will otherwise block logins grantor.SetTwoFactorProviders(new Dictionary()); - grantor.UnknownDeviceVerificationEnabled = false; await _userRepository.ReplaceAsync(grantor); // Remove grantor from all organizations unless Owner diff --git a/src/Core/Services/Implementations/EventService.cs b/src/Core/Services/Implementations/EventService.cs index 82b6658e0b..cf74767be1 100644 --- a/src/Core/Services/Implementations/EventService.cs +++ b/src/Core/Services/Implementations/EventService.cs @@ -5,6 +5,7 @@ using Bit.Core.Enums; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations; using Bit.Core.Repositories; +using Bit.Core.SecretsManager.Entities; using Bit.Core.Settings; namespace Bit.Core.Services; @@ -181,7 +182,7 @@ public class EventService : IEventService continue; } - eventMessages.Add(new EventMessage(_currentContext) + var e = new EventMessage(_currentContext) { OrganizationId = group.OrganizationId, GroupId = group.Id, @@ -190,12 +191,20 @@ public class EventService : IEventService ProviderId = await GetProviderIdAsync(group.OrganizationId), SystemUser = systemUser, Date = date.GetValueOrDefault(DateTime.UtcNow) - }); + }; + + if (systemUser is EventSystemUser.SCIM) + { + // System user only used for SCIM logs in this method + // and we want event logs to report server instead of unknown + e.DeviceType = DeviceType.Server; + } + + eventMessages.Add(e); } await _eventWriteService.CreateManyAsync(eventMessages); } - public async Task LogPolicyEventAsync(Policy policy, EventType type, DateTime? date = null) { var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync(); @@ -247,7 +256,7 @@ public class EventService : IEventService continue; } - eventMessages.Add(new EventMessage(_currentContext) + var e = new EventMessage(_currentContext) { OrganizationId = organizationUser.OrganizationId, UserId = organizationUser.UserId, @@ -257,7 +266,16 @@ public class EventService : IEventService ActingUserId = _currentContext?.UserId, Date = date.GetValueOrDefault(DateTime.UtcNow), SystemUser = systemUser - }); + }; + + if (systemUser is EventSystemUser.SCIM) + { + // System user only used for SCIM logs in this method + // and we want event logs to report server instead of unknown + e.DeviceType = DeviceType.Server; + } + + eventMessages.Add(e); } await _eventWriteService.CreateManyAsync(eventMessages); @@ -331,6 +349,68 @@ public class EventService : IEventService await _eventWriteService.CreateAsync(e); } + public async Task LogOrganizationDomainEventAsync(OrganizationDomain organizationDomain, EventType type, + DateTime? date = null) + { + var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync(); + if (!CanUseEvents(orgAbilities, organizationDomain.OrganizationId)) + { + return; + } + + var e = new EventMessage(_currentContext) + { + OrganizationId = organizationDomain.OrganizationId, + Type = type, + ActingUserId = _currentContext?.UserId, + DomainName = organizationDomain.DomainName, + Date = date.GetValueOrDefault(DateTime.UtcNow) + }; + await _eventWriteService.CreateAsync(e); + } + + public async Task LogOrganizationDomainEventAsync(OrganizationDomain organizationDomain, EventType type, + EventSystemUser systemUser, + DateTime? date = null) + { + var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync(); + if (!CanUseEvents(orgAbilities, organizationDomain.OrganizationId)) + { + return; + } + + var e = new EventMessage(_currentContext) + { + OrganizationId = organizationDomain.OrganizationId, + Type = type, + ActingUserId = _currentContext?.UserId, + DomainName = organizationDomain.DomainName, + SystemUser = systemUser, + Date = date.GetValueOrDefault(DateTime.UtcNow), + DeviceType = DeviceType.Server + }; + await _eventWriteService.CreateAsync(e); + } + + public async Task LogServiceAccountSecretEventAsync(Guid serviceAccountId, Secret secret, EventType type, DateTime? date = null) + { + var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync(); + if (!CanUseEvents(orgAbilities, secret.OrganizationId)) + { + return; + } + + var e = new EventMessage(_currentContext) + { + OrganizationId = secret.OrganizationId, + Type = type, + SecretId = secret.Id, + ServiceAccountId = serviceAccountId, + Date = date.GetValueOrDefault(DateTime.UtcNow) + }; + await _eventWriteService.CreateAsync(e); + } + private async Task GetProviderIdAsync(Guid? orgId) { if (_currentContext == null || !orgId.HasValue) @@ -354,7 +434,7 @@ public class EventService : IEventService private bool CanUseEvents(IDictionary orgAbilities, Guid orgId) { return orgAbilities != null && orgAbilities.ContainsKey(orgId) && - orgAbilities[orgId].Enabled && orgAbilities[orgId].UseEvents; + orgAbilities[orgId].Enabled && orgAbilities[orgId].UseEvents; } private bool CanUseProviderEvents(IDictionary providerAbilities, Guid providerId) diff --git a/src/Core/Services/Implementations/HandlebarsMailService.cs b/src/Core/Services/Implementations/HandlebarsMailService.cs index 3688c8f153..89edb7f097 100644 --- a/src/Core/Services/Implementations/HandlebarsMailService.cs +++ b/src/Core/Services/Implementations/HandlebarsMailService.cs @@ -113,21 +113,6 @@ public class HandlebarsMailService : IMailService await _mailDeliveryService.SendEmailAsync(message); } - public async Task SendNewDeviceLoginTwoFactorEmailAsync(string email, string token) - { - var message = CreateDefaultMessage("New Device Login Verification Code", email); - var model = new EmailTokenViewModel - { - Token = token, - WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash, - SiteName = _globalSettings.SiteName - }; - await AddMessageContentAsync(message, "NewDeviceLoginTwoFactorEmail", model); - message.MetaData.Add("SendGridBypassListManagement", true); - message.Category = "TwoFactorEmail"; - await _mailDeliveryService.SendEmailAsync(message); - } - public async Task SendMasterPasswordHintEmailAsync(string email, string hint) { var message = CreateDefaultMessage("Your Master Password Hint", email); @@ -881,6 +866,19 @@ public class HandlebarsMailService : IMailService await _mailDeliveryService.SendEmailAsync(message); } + public async Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable adminEmails, string organizationId, string domainName) + { + var message = CreateDefaultMessage("Domain not verified", adminEmails); + var model = new OrganizationDomainUnverifiedViewModel + { + Url = $"{_globalSettings.BaseServiceUri.VaultWithHash}/organizations/{organizationId}/settings/domain-verification", + DomainName = domainName + }; + await AddMessageContentAsync(message, "OrganizationDomainUnverified", model); + message.Category = "UnverifiedOrganizationDomain"; + await _mailDeliveryService.SendEmailAsync(message); + } + private static string GetUserIdentifier(string email, string userName) { return string.IsNullOrEmpty(userName) ? email : CoreHelpers.SanitizeForEmail(userName, false); diff --git a/src/Core/Services/Implementations/OrganizationDomainService.cs b/src/Core/Services/Implementations/OrganizationDomainService.cs new file mode 100644 index 0000000000..ba342ce034 --- /dev/null +++ b/src/Core/Services/Implementations/OrganizationDomainService.cs @@ -0,0 +1,139 @@ +using Bit.Core.Enums; +using Bit.Core.Repositories; +using Bit.Core.Settings; +using Microsoft.Extensions.Logging; + +namespace Bit.Core.Services; + +public class OrganizationDomainService : IOrganizationDomainService +{ + private readonly IOrganizationDomainRepository _domainRepository; + private readonly IOrganizationUserRepository _organizationUserRepository; + private readonly IDnsResolverService _dnsResolverService; + private readonly IEventService _eventService; + private readonly IMailService _mailService; + private readonly ILogger _logger; + private readonly IGlobalSettings _globalSettings; + + public OrganizationDomainService( + IOrganizationDomainRepository domainRepository, + IOrganizationUserRepository organizationUserRepository, + IDnsResolverService dnsResolverService, + IEventService eventService, + IMailService mailService, + ILogger logger, + IGlobalSettings globalSettings) + { + _domainRepository = domainRepository; + _organizationUserRepository = organizationUserRepository; + _dnsResolverService = dnsResolverService; + _eventService = eventService; + _mailService = mailService; + _logger = logger; + _globalSettings = globalSettings; + } + + public async Task ValidateOrganizationsDomainAsync() + { + //Date should be set 1 hour behind to ensure it selects all domains that should be verified + var runDate = DateTime.UtcNow.AddHours(-1); + + var verifiableDomains = await _domainRepository.GetManyByNextRunDateAsync(runDate); + + _logger.LogInformation(Constants.BypassFiltersEventId, "Validating {verifiableDomainsCount} domains.", verifiableDomains.Count); + + foreach (var domain in verifiableDomains) + { + try + { + _logger.LogInformation(Constants.BypassFiltersEventId, "Attempting verification for organization {OrgId} with domain {Domain}", domain.OrganizationId, domain.DomainName); + + var status = await _dnsResolverService.ResolveAsync(domain.DomainName, domain.Txt); + if (status) + { + _logger.LogInformation(Constants.BypassFiltersEventId, "Successfully validated domain"); + + //update entry on OrganizationDomain table + domain.SetLastCheckedDate(); + domain.SetVerifiedDate(); + domain.SetJobRunCount(); + await _domainRepository.ReplaceAsync(domain); + + await _eventService.LogOrganizationDomainEventAsync(domain, EventType.OrganizationDomain_Verified, + EventSystemUser.DomainVerification); + } + else + { + //update entry on OrganizationDomain table + domain.SetLastCheckedDate(); + domain.SetJobRunCount(); + domain.SetNextRunDate(_globalSettings.DomainVerification.VerificationInterval); + await _domainRepository.ReplaceAsync(domain); + + await _eventService.LogOrganizationDomainEventAsync(domain, EventType.OrganizationDomain_NotVerified, + EventSystemUser.DomainVerification); + _logger.LogInformation(Constants.BypassFiltersEventId, "Verification for organization {OrgId} with domain {Domain} failed", + domain.OrganizationId, domain.DomainName); + } + } + catch (Exception ex) + { + //update entry on OrganizationDomain table + domain.SetLastCheckedDate(); + domain.SetJobRunCount(); + domain.SetNextRunDate(_globalSettings.DomainVerification.VerificationInterval); + await _domainRepository.ReplaceAsync(domain); + + await _eventService.LogOrganizationDomainEventAsync(domain, EventType.OrganizationDomain_NotVerified, + EventSystemUser.DomainVerification); + + _logger.LogError(ex, "Verification for organization {OrgId} with domain {Domain} threw an exception: {errorMessage}", + domain.OrganizationId, domain.DomainName, ex.Message); + } + } + } + + public async Task OrganizationDomainMaintenanceAsync() + { + try + { + //Get domains that have not been verified within 72 hours + var expiredDomains = await _domainRepository.GetExpiredOrganizationDomainsAsync(); + + _logger.LogInformation(Constants.BypassFiltersEventId, + "Attempting email reminder for {expiredDomainCount} expired domains.", expiredDomains.Count); + + foreach (var domain in expiredDomains) + { + //get admin emails of organization + var adminEmails = await GetAdminEmailsAsync(domain.OrganizationId); + + //Send email to administrators + if (adminEmails.Count > 0) + { + await _mailService.SendUnverifiedOrganizationDomainEmailAsync(adminEmails, + domain.OrganizationId.ToString(), domain.DomainName); + } + + _logger.LogInformation(Constants.BypassFiltersEventId, "Expired domain: {domainName}", domain.DomainName); + } + //delete domains that have not been verified within 7 days + var status = await _domainRepository.DeleteExpiredAsync(_globalSettings.DomainVerification.ExpirationPeriod); + _logger.LogInformation(Constants.BypassFiltersEventId, "Delete status {status}", status); + } + catch (Exception ex) + { + _logger.LogError(ex, "Organization domain maintenance failed"); + } + } + + private async Task> GetAdminEmailsAsync(Guid organizationId) + { + var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId); + var emailList = orgUsers.Where(o => o.Type <= OrganizationUserType.Admin + || o.GetPermissions()?.ManageSso == true) + .Select(a => a.Email).Distinct().ToList(); + + return emailList; + } +} diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index b2564a9432..fd81abf57c 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -39,7 +39,6 @@ public class OrganizationService : IOrganizationService private readonly ISsoUserRepository _ssoUserRepository; private readonly IReferenceEventService _referenceEventService; private readonly IGlobalSettings _globalSettings; - private readonly ITaxRateRepository _taxRateRepository; private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository; private readonly IOrganizationConnectionRepository _organizationConnectionRepository; private readonly ICurrentContext _currentContext; @@ -68,7 +67,6 @@ public class OrganizationService : IOrganizationService ISsoUserRepository ssoUserRepository, IReferenceEventService referenceEventService, IGlobalSettings globalSettings, - ITaxRateRepository taxRateRepository, IOrganizationApiKeyRepository organizationApiKeyRepository, IOrganizationConnectionRepository organizationConnectionRepository, ICurrentContext currentContext, @@ -96,7 +94,6 @@ public class OrganizationService : IOrganizationService _ssoUserRepository = ssoUserRepository; _referenceEventService = referenceEventService; _globalSettings = globalSettings; - _taxRateRepository = taxRateRepository; _organizationApiKeyRepository = organizationApiKeyRepository; _organizationConnectionRepository = organizationConnectionRepository; _currentContext = currentContext; @@ -119,7 +116,7 @@ public class OrganizationService : IOrganizationService paymentMethodType, paymentToken); if (updated) { - await ReplaceAndUpdateCache(organization); + await ReplaceAndUpdateCacheAsync(organization); } } @@ -205,7 +202,7 @@ public class OrganizationService : IOrganizationService (newPlan.HasAdditionalSeatsOption ? upgrade.AdditionalSeats : 0)); if (!organization.Seats.HasValue || organization.Seats.Value > newPlanSeats) { - var occupiedSeats = await GetOccupiedSeatCount(organization); + var occupiedSeats = await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); if (occupiedSeats > newPlanSeats) { throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " + @@ -346,7 +343,7 @@ public class OrganizationService : IOrganizationService organization.Enabled = success; organization.PublicKey = upgrade.PublicKey; organization.PrivateKey = upgrade.PrivateKey; - await ReplaceAndUpdateCache(organization); + await ReplaceAndUpdateCacheAsync(organization); if (success) { await _referenceEventService.RaiseEventAsync( @@ -392,7 +389,7 @@ public class OrganizationService : IOrganizationService PlanType = plan.Type, Storage = storageAdjustmentGb, }); - await ReplaceAndUpdateCache(organization); + await ReplaceAndUpdateCacheAsync(organization); return secret; } @@ -451,7 +448,7 @@ public class OrganizationService : IOrganizationService organization.MaxAutoscaleSeats = maxAutoscaleSeats; - await ReplaceAndUpdateCache(organization); + await ReplaceAndUpdateCacheAsync(organization); } public async Task AdjustSeatsAsync(Guid organizationId, int seatAdjustment, DateTime? prorationDate = null) @@ -513,7 +510,7 @@ public class OrganizationService : IOrganizationService if (!organization.Seats.HasValue || organization.Seats.Value > newSeatTotal) { - var occupiedSeats = await GetOccupiedSeatCount(organization); + var occupiedSeats = await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); if (occupiedSeats > newSeatTotal) { throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " + @@ -531,7 +528,7 @@ public class OrganizationService : IOrganizationService PreviousSeats = organization.Seats }); organization.Seats = (short?)newSeatTotal; - await ReplaceAndUpdateCache(organization); + await ReplaceAndUpdateCacheAsync(organization); if (organization.Seats.HasValue && organization.MaxAutoscaleSeats.HasValue && organization.Seats == organization.MaxAutoscaleSeats) { @@ -698,21 +695,10 @@ public class OrganizationService : IOrganizationService OrganizationLicense license, User owner, string ownerKey, string collectionName, string publicKey, string privateKey) { - if (license?.LicenseType != null && license.LicenseType != LicenseType.Organization) + var canUse = license.CanUse(_globalSettings, _licensingService, out var exception); + if (!canUse) { - throw new BadRequestException("Premium licenses cannot be applied to an organization. " - + "Upload this license from your personal account settings page."); - } - - if (license == null || !_licensingService.VerifyLicense(license)) - { - throw new BadRequestException("Invalid license."); - } - - if (!license.CanUse(_globalSettings)) - { - throw new BadRequestException("Invalid license. Make sure your license allows for on-premise " + - "hosting of organizations and that the installation id matches your current installation."); + throw new BadRequestException(exception); } if (license.PlanType != PlanType.Custom && @@ -845,172 +831,6 @@ public class OrganizationService : IOrganizationService } } - public async Task UpdateLicenseAsync(Guid organizationId, OrganizationLicense license) - { - var organization = await GetOrgById(organizationId); - if (organization == null) - { - throw new NotFoundException(); - } - - if (!_globalSettings.SelfHosted) - { - throw new InvalidOperationException("Licenses require self hosting."); - } - - if (license?.LicenseType != null && license.LicenseType != LicenseType.Organization) - { - throw new BadRequestException("Premium licenses cannot be applied to an organization. " - + "Upload this license from your personal account settings page."); - } - - if (license == null || !_licensingService.VerifyLicense(license)) - { - throw new BadRequestException("Invalid license."); - } - - if (!license.CanUse(_globalSettings)) - { - throw new BadRequestException("Invalid license. Make sure your license allows for on-premise " + - "hosting of organizations and that the installation id matches your current installation."); - } - - var enabledOrgs = await _organizationRepository.GetManyByEnabledAsync(); - if (enabledOrgs.Any(o => string.Equals(o.LicenseKey, license.LicenseKey) && o.Id != organizationId)) - { - throw new BadRequestException("License is already in use by another organization."); - } - - if (license.Seats.HasValue && - (!organization.Seats.HasValue || organization.Seats.Value > license.Seats.Value)) - { - var occupiedSeats = await GetOccupiedSeatCount(organization); - if (occupiedSeats > license.Seats.Value) - { - throw new BadRequestException($"Your organization currently has {occupiedSeats} seats filled. " + - $"Your new license only has ({license.Seats.Value}) seats. Remove some users."); - } - } - - if (license.MaxCollections.HasValue && (!organization.MaxCollections.HasValue || - organization.MaxCollections.Value > license.MaxCollections.Value)) - { - var collectionCount = await _collectionRepository.GetCountByOrganizationIdAsync(organization.Id); - if (collectionCount > license.MaxCollections.Value) - { - throw new BadRequestException($"Your organization currently has {collectionCount} collections. " + - $"Your new license allows for a maximum of ({license.MaxCollections.Value}) collections. " + - "Remove some collections."); - } - } - - if (!license.UseGroups && organization.UseGroups) - { - var groups = await _groupRepository.GetManyByOrganizationIdAsync(organization.Id); - if (groups.Count > 0) - { - throw new BadRequestException($"Your organization currently has {groups.Count} groups. " + - $"Your new license does not allow for the use of groups. Remove all groups."); - } - } - - if (!license.UsePolicies && organization.UsePolicies) - { - var policies = await _policyRepository.GetManyByOrganizationIdAsync(organization.Id); - if (policies.Any(p => p.Enabled)) - { - throw new BadRequestException($"Your organization currently has {policies.Count} enabled " + - $"policies. Your new license does not allow for the use of policies. Disable all policies."); - } - } - - if (!license.UseSso && organization.UseSso) - { - var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id); - if (ssoConfig != null && ssoConfig.Enabled) - { - throw new BadRequestException($"Your organization currently has a SSO configuration. " + - $"Your new license does not allow for the use of SSO. Disable your SSO configuration."); - } - } - - if (!license.UseKeyConnector && organization.UseKeyConnector) - { - var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id); - if (ssoConfig != null && ssoConfig.GetData().KeyConnectorEnabled) - { - throw new BadRequestException($"Your organization currently has Key Connector enabled. " + - $"Your new license does not allow for the use of Key Connector. Disable your Key Connector."); - } - } - - if (!license.UseScim && organization.UseScim) - { - var scimConnections = await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organization.Id, - OrganizationConnectionType.Scim); - if (scimConnections != null && scimConnections.Any(c => c.GetConfig()?.Enabled == true)) - { - throw new BadRequestException("Your new plan does not allow the SCIM feature. " + - "Disable your SCIM configuration."); - } - } - - if (!license.UseCustomPermissions && organization.UseCustomPermissions) - { - var organizationCustomUsers = - await _organizationUserRepository.GetManyByOrganizationAsync(organization.Id, - OrganizationUserType.Custom); - if (organizationCustomUsers.Any()) - { - throw new BadRequestException("Your new plan does not allow the Custom Permissions feature. " + - "Disable your Custom Permissions configuration."); - } - } - - if (!license.UseResetPassword && organization.UseResetPassword) - { - var resetPasswordPolicy = - await _policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.ResetPassword); - if (resetPasswordPolicy != null && resetPasswordPolicy.Enabled) - { - throw new BadRequestException("Your new license does not allow the Password Reset feature. " - + "Disable your Password Reset policy."); - } - } - - var dir = $"{_globalSettings.LicenseDirectory}/organization"; - Directory.CreateDirectory(dir); - await using var fs = new FileStream(Path.Combine(dir, $"{organization.Id}.json"), FileMode.Create); - await JsonSerializer.SerializeAsync(fs, license, JsonHelpers.Indented); - - organization.Name = license.Name; - organization.BusinessName = license.BusinessName; - organization.BillingEmail = license.BillingEmail; - organization.PlanType = license.PlanType; - organization.Seats = license.Seats; - organization.MaxCollections = license.MaxCollections; - organization.UseGroups = license.UseGroups; - organization.UseDirectory = license.UseDirectory; - organization.UseEvents = license.UseEvents; - organization.UseTotp = license.UseTotp; - organization.Use2fa = license.Use2fa; - organization.UseApi = license.UseApi; - organization.UsePolicies = license.UsePolicies; - organization.UseSso = license.UseSso; - organization.UseKeyConnector = license.UseKeyConnector; - organization.UseScim = license.UseScim; - organization.UseResetPassword = license.UseResetPassword; - organization.SelfHost = license.SelfHost; - organization.UsersGetPremium = license.UsersGetPremium; - organization.UseCustomPermissions = license.UseCustomPermissions; - organization.Plan = license.Plan; - organization.Enabled = license.Enabled; - organization.ExpirationDate = license.Expires; - organization.LicenseKey = license.LicenseKey; - organization.RevisionDate = DateTime.UtcNow; - await ReplaceAndUpdateCache(organization); - } - public async Task DeleteAsync(Organization organization) { await ValidateDeleteOrganizationAsync(organization); @@ -1040,7 +860,7 @@ public class OrganizationService : IOrganizationService org.Enabled = true; org.ExpirationDate = expirationDate; org.RevisionDate = DateTime.UtcNow; - await ReplaceAndUpdateCache(org); + await ReplaceAndUpdateCacheAsync(org); } } @@ -1052,7 +872,7 @@ public class OrganizationService : IOrganizationService org.Enabled = false; org.ExpirationDate = expirationDate; org.RevisionDate = DateTime.UtcNow; - await ReplaceAndUpdateCache(org); + await ReplaceAndUpdateCacheAsync(org); // TODO: send email to owners? } @@ -1065,7 +885,7 @@ public class OrganizationService : IOrganizationService { org.ExpirationDate = expirationDate; org.RevisionDate = DateTime.UtcNow; - await ReplaceAndUpdateCache(org); + await ReplaceAndUpdateCacheAsync(org); } } @@ -1075,7 +895,7 @@ public class OrganizationService : IOrganizationService if (org != null && !org.Enabled) { org.Enabled = true; - await ReplaceAndUpdateCache(org); + await ReplaceAndUpdateCacheAsync(org); } } @@ -1095,7 +915,7 @@ public class OrganizationService : IOrganizationService } } - await ReplaceAndUpdateCache(organization, EventType.Organization_Updated); + await ReplaceAndUpdateCacheAsync(organization, EventType.Organization_Updated); if (updateBilling && !string.IsNullOrWhiteSpace(organization.GatewayCustomerId)) { @@ -1195,7 +1015,7 @@ public class OrganizationService : IOrganizationService organizationId, invites.SelectMany(i => i.invite.Emails), false), StringComparer.InvariantCultureIgnoreCase); if (organization.Seats.HasValue) { - var occupiedSeats = await GetOccupiedSeatCount(organization); + var occupiedSeats = await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); var availableSeats = organization.Seats.Value - occupiedSeats; newSeatsRequired = invites.Sum(i => i.invite.Emails.Count()) - existingEmails.Count() - availableSeats; } @@ -2046,7 +1866,7 @@ public class OrganizationService : IOrganizationService var enoughSeatsAvailable = true; if (organization.Seats.HasValue) { - var occupiedSeats = await GetOccupiedSeatCount(organization); + var occupiedSeats = await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); seatsAvailable = organization.Seats.Value - occupiedSeats; enoughSeatsAvailable = seatsAvailable >= usersToAdd.Count; } @@ -2215,7 +2035,7 @@ public class OrganizationService : IOrganizationService return devices.Where(d => !string.IsNullOrWhiteSpace(d.PushToken)).Select(d => d.Id.ToString()); } - private async Task ReplaceAndUpdateCache(Organization org, EventType? orgEvent = null) + public async Task ReplaceAndUpdateCacheAsync(Organization org, EventType? orgEvent = null) { await _organizationRepository.ReplaceAsync(org); await _applicationCacheService.UpsertOrganizationAbilityAsync(org); @@ -2465,7 +2285,7 @@ public class OrganizationService : IOrganizationService } var organization = await _organizationRepository.GetByIdAsync(organizationUser.OrganizationId); - var occupiedSeats = await GetOccupiedSeatCount(organization); + var occupiedSeats = await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); var availableSeats = organization.Seats.GetValueOrDefault(0) - occupiedSeats; if (availableSeats < 1) { @@ -2493,7 +2313,7 @@ public class OrganizationService : IOrganizationService } var organization = await _organizationRepository.GetByIdAsync(organizationId); - var occupiedSeats = await GetOccupiedSeatCount(organization); + var occupiedSeats = await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id); var availableSeats = organization.Seats.GetValueOrDefault(0) - occupiedSeats; var newSeatsRequired = organizationUserIds.Count() - availableSeats; await AutoAddSeatsAsync(organization, newSeatsRequired, DateTime.UtcNow); @@ -2608,10 +2428,4 @@ public class OrganizationService : IOrganizationService return status; } - - public async Task GetOccupiedSeatCount(Organization organization) - { - var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organization.Id); - return orgUsers.Count(ou => ou.OccupiesOrganizationSeat); - } } diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index 3045797490..659ace42b7 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -46,7 +46,6 @@ public class UserService : UserManager, IUserService, IDisposable private readonly IGlobalSettings _globalSettings; private readonly IOrganizationService _organizationService; private readonly IProviderUserRepository _providerUserRepository; - private readonly IDeviceRepository _deviceRepository; private readonly IStripeSyncService _stripeSyncService; public UserService( @@ -77,7 +76,6 @@ public class UserService : UserManager, IUserService, IDisposable IGlobalSettings globalSettings, IOrganizationService organizationService, IProviderUserRepository providerUserRepository, - IDeviceRepository deviceRepository, IStripeSyncService stripeSyncService) : base( store, @@ -113,7 +111,6 @@ public class UserService : UserManager, IUserService, IDisposable _globalSettings = globalSettings; _organizationService = organizationService; _providerUserRepository = providerUserRepository; - _deviceRepository = deviceRepository; _stripeSyncService = stripeSyncService; } @@ -353,7 +350,7 @@ public class UserService : UserManager, IUserService, IDisposable await _mailService.SendMasterPasswordHintEmailAsync(email, user.MasterPasswordHint); } - public async Task SendTwoFactorEmailAsync(User user, bool isBecauseNewDeviceLogin = false) + public async Task SendTwoFactorEmailAsync(User user) { var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email); if (provider == null || provider.MetaData == null || !provider.MetaData.ContainsKey("Email")) @@ -365,14 +362,7 @@ public class UserService : UserManager, IUserService, IDisposable var token = await base.GenerateUserTokenAsync(user, TokenOptions.DefaultEmailProvider, "2faEmail:" + email); - if (isBecauseNewDeviceLogin) - { - await _mailService.SendNewDeviceLoginTwoFactorEmailAsync(email, token); - } - else - { - await _mailService.SendTwoFactorEmailAsync(email, token); - } + await _mailService.SendTwoFactorEmailAsync(email, token); } public async Task VerifyTwoFactorEmailAsync(User user, string token) @@ -1478,36 +1468,4 @@ public class UserService : UserManager, IUserService, IDisposable ? await VerifyOTPAsync(user, secret) : await CheckPasswordAsync(user, secret); } - - public async Task Needs2FABecauseNewDeviceAsync(User user, string deviceIdentifier, string grantType) - { - return CanEditDeviceVerificationSettings(user) - && user.UnknownDeviceVerificationEnabled - && grantType != "authorization_code" - && await IsNewDeviceAndNotTheFirstOneAsync(user, deviceIdentifier); - } - - public bool CanEditDeviceVerificationSettings(User user) - { - return _globalSettings.TwoFactorAuth.EmailOnNewDeviceLogin - && user.EmailVerified - && !user.UsesKeyConnector - && !(user.GetTwoFactorProviders()?.Any() ?? false); - } - - private async Task IsNewDeviceAndNotTheFirstOneAsync(User user, string deviceIdentifier) - { - if (user == null) - { - return default; - } - - var devices = await _deviceRepository.GetManyByUserIdAsync(user.Id); - if (!devices.Any()) - { - return false; - } - - return !devices.Any(d => d.Identifier == deviceIdentifier); - } } diff --git a/src/Core/Services/NoopImplementations/NoopEventService.cs b/src/Core/Services/NoopImplementations/NoopEventService.cs index 7f9d02b949..0f23933b77 100644 --- a/src/Core/Services/NoopImplementations/NoopEventService.cs +++ b/src/Core/Services/NoopImplementations/NoopEventService.cs @@ -1,6 +1,7 @@ using Bit.Core.Entities; using Bit.Core.Entities.Provider; using Bit.Core.Enums; +using Bit.Core.SecretsManager.Entities; namespace Bit.Core.Services; @@ -68,6 +69,19 @@ public class NoopEventService : IEventService return Task.FromResult(0); } + public Task LogOrganizationDomainEventAsync(OrganizationDomain organizationDomain, EventType type, + DateTime? date = null) + { + return Task.FromResult(0); + } + + public Task LogOrganizationDomainEventAsync(OrganizationDomain organizationDomain, EventType type, + EventSystemUser systemUser, + DateTime? date = null) + { + return Task.FromResult(0); + } + public Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type, DateTime? date = null) { return Task.FromResult(0); @@ -94,4 +108,9 @@ public class NoopEventService : IEventService return Task.FromResult(0); } + public Task LogServiceAccountSecretEventAsync(Guid serviceAccountId, Secret secret, EventType type, + DateTime? date = null) + { + return Task.FromResult(0); + } } diff --git a/src/Core/Services/NoopImplementations/NoopMailService.cs b/src/Core/Services/NoopImplementations/NoopMailService.cs index cee8c91f42..9d7b4698ae 100644 --- a/src/Core/Services/NoopImplementations/NoopMailService.cs +++ b/src/Core/Services/NoopImplementations/NoopMailService.cs @@ -72,11 +72,6 @@ public class NoopMailService : IMailService return Task.FromResult(0); } - public Task SendNewDeviceLoginTwoFactorEmailAsync(string email, string token) - { - return Task.CompletedTask; - } - public Task SendWelcomeEmailAsync(User user) { return Task.FromResult(0); @@ -237,4 +232,9 @@ public class NoopMailService : IMailService { return Task.FromResult(0); } + + public Task SendUnverifiedOrganizationDomainEmailAsync(IEnumerable adminEmails, string organizationId, string domainName) + { + return Task.FromResult(0); + } } diff --git a/src/Core/Settings/GlobalSettings.cs b/src/Core/Settings/GlobalSettings.cs index 53261fa2ab..44820c3040 100644 --- a/src/Core/Settings/GlobalSettings.cs +++ b/src/Core/Settings/GlobalSettings.cs @@ -73,10 +73,10 @@ public class GlobalSettings : IGlobalSettings public virtual AppleIapSettings AppleIap { get; set; } = new AppleIapSettings(); public virtual ISsoSettings Sso { get; set; } = new SsoSettings(); public virtual StripeSettings Stripe { get; set; } = new StripeSettings(); - public virtual ITwoFactorAuthSettings TwoFactorAuth { get; set; } = new TwoFactorAuthSettings(); public virtual DistributedIpRateLimitingSettings DistributedIpRateLimiting { get; set; } = new DistributedIpRateLimitingSettings(); public virtual IPasswordlessAuthSettings PasswordlessAuth { get; set; } = new PasswordlessAuthSettings(); + public virtual IDomainVerificationSettings DomainVerification { get; set; } = new DomainVerificationSettings(); public string BuildExternalUri(string explicitValue, string name) { @@ -510,11 +510,6 @@ public class GlobalSettings : IGlobalSettings public int MaxNetworkRetries { get; set; } = 2; } - public class TwoFactorAuthSettings : ITwoFactorAuthSettings - { - public bool EmailOnNewDeviceLogin { get; set; } = false; - } - public class DistributedIpRateLimitingSettings { public bool Enabled { get; set; } = true; @@ -537,4 +532,10 @@ public class GlobalSettings : IGlobalSettings { public bool KnownDevicesOnly { get; set; } = true; } + + public class DomainVerificationSettings : IDomainVerificationSettings + { + public int VerificationInterval { get; set; } = 12; + public int ExpirationPeriod { get; set; } = 7; + } } diff --git a/src/Core/Settings/IDomainVerificationSettings.cs b/src/Core/Settings/IDomainVerificationSettings.cs new file mode 100644 index 0000000000..aac6c70046 --- /dev/null +++ b/src/Core/Settings/IDomainVerificationSettings.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.Settings; + +public interface IDomainVerificationSettings +{ + public int VerificationInterval { get; set; } + public int ExpirationPeriod { get; set; } +} diff --git a/src/Core/Settings/IGlobalSettings.cs b/src/Core/Settings/IGlobalSettings.cs index 9ed58669ca..49d016253d 100644 --- a/src/Core/Settings/IGlobalSettings.cs +++ b/src/Core/Settings/IGlobalSettings.cs @@ -13,8 +13,8 @@ public interface IGlobalSettings IFileStorageSettings Attachment { get; set; } IConnectionStringSettings Storage { get; set; } IBaseServiceUriSettings BaseServiceUri { get; set; } - ITwoFactorAuthSettings TwoFactorAuth { get; set; } ISsoSettings Sso { get; set; } ILogLevelSettings MinLogLevel { get; set; } IPasswordlessAuthSettings PasswordlessAuth { get; set; } + IDomainVerificationSettings DomainVerification { get; set; } } diff --git a/src/Core/Settings/ITwoFactorAuthSettings.cs b/src/Core/Settings/ITwoFactorAuthSettings.cs deleted file mode 100644 index 2e11e65079..0000000000 --- a/src/Core/Settings/ITwoFactorAuthSettings.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Bit.Core.Settings; - -public interface ITwoFactorAuthSettings -{ - bool EmailOnNewDeviceLogin { get; set; } -} diff --git a/src/Core/Utilities/DuoApi.cs b/src/Core/Utilities/DuoApi.cs index b5a3f040d4..662ca643d7 100644 --- a/src/Core/Utilities/DuoApi.cs +++ b/src/Core/Utilities/DuoApi.cs @@ -15,8 +15,9 @@ using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Web; +using Bit.Core.Models.Api.Response.Duo; -namespace Bit.Core.Utilities.Duo; +namespace Bit.Core.Utilities; public class DuoApi { @@ -27,6 +28,8 @@ public class DuoApi private readonly string _ikey; private readonly string _skey; + private readonly HttpClient _httpClient = new(); + public DuoApi(string ikey, string skey, string host) { _ikey = ikey; @@ -92,11 +95,6 @@ public class DuoApi return string.Concat("Basic ", Encode64(auth)); } - public string ApiCall(string method, string path, Dictionary parameters = null) - { - return ApiCall(method, path, parameters, 0, out var statusCode); - } - /// The request timeout, in milliseconds. /// Specify 0 to use the system-default timeout. Use caution if /// you choose to specify a custom timeout - some API @@ -104,8 +102,7 @@ public class DuoApi /// return a response until an out-of-band authentication process /// has completed. In some cases, this may take as much as a /// small number of minutes. - public string ApiCall(string method, string path, Dictionary parameters, int timeout, - out HttpStatusCode statusCode) + private async Task<(string result, HttpStatusCode statusCode)> ApiCall(string method, string path, Dictionary parameters, int timeout) { if (parameters == null) { @@ -121,58 +118,39 @@ public class DuoApi query = "?" + canonParams; } } - var url = string.Format("{0}://{1}{2}{3}", UrlScheme, _host, path, query); + var url = $"{UrlScheme}://{_host}{path}{query}"; var dateString = RFC822UtcNow(); var auth = Sign(method, path, canonParams, dateString); - var request = (HttpWebRequest)WebRequest.Create(url); - request.Method = method; - request.Accept = "application/json"; + var request = new HttpRequestMessage + { + Method = new HttpMethod(method), + RequestUri = new Uri(url), + }; request.Headers.Add("Authorization", auth); request.Headers.Add("X-Duo-Date", dateString); - request.UserAgent = UserAgent; + request.Headers.UserAgent.ParseAdd(UserAgent); + + if (timeout > 0) + { + _httpClient.Timeout = TimeSpan.FromMilliseconds(timeout); + } if (method.Equals("POST") || method.Equals("PUT")) { - var data = Encoding.UTF8.GetBytes(canonParams); - request.ContentType = "application/x-www-form-urlencoded"; - request.ContentLength = data.Length; - using (var requestStream = request.GetRequestStream()) - { - requestStream.Write(data, 0, data.Length); - } - } - if (timeout > 0) - { - request.Timeout = timeout; + request.Content = new StringContent(canonParams, Encoding.UTF8, "application/x-www-form-urlencoded"); } - // Do the request and process the result. - HttpWebResponse response; - try - { - response = (HttpWebResponse)request.GetResponse(); - } - catch (WebException ex) - { - response = (HttpWebResponse)ex.Response; - if (response == null) - { - throw; - } - } - using (var reader = new StreamReader(response.GetResponseStream())) - { - statusCode = response.StatusCode; - return reader.ReadToEnd(); - } + var response = await _httpClient.SendAsync(request); + var result = await response.Content.ReadAsStringAsync(); + var statusCode = response.StatusCode; + return (result, statusCode); } - public T JSONApiCall(string method, string path, Dictionary parameters = null) - where T : class + public async Task JSONApiCall(string method, string path, Dictionary parameters = null) { - return JSONApiCall(method, path, parameters, 0); + return await JSONApiCall(method, path, parameters, 0); } /// The request timeout, in milliseconds. @@ -182,27 +160,18 @@ public class DuoApi /// return a response until an out-of-band authentication process /// has completed. In some cases, this may take as much as a /// small number of minutes. - public T JSONApiCall(string method, string path, Dictionary parameters, int timeout) - where T : class + private async Task JSONApiCall(string method, string path, Dictionary parameters, int timeout) { - var res = ApiCall(method, path, parameters, timeout, out var statusCode); + var (res, statusCode) = await ApiCall(method, path, parameters, timeout); try { - // TODO: We should deserialize this into our own DTO and not work on dictionaries. - var dict = JsonSerializer.Deserialize>(res); - if (dict["stat"].ToString() == "OK") + var obj = JsonSerializer.Deserialize(res); + if (obj.Stat == "OK") { - return JsonSerializer.Deserialize(dict["response"].ToString()); + return obj.Response; } - var check = ToNullableInt(dict["code"].ToString()); - var code = check.GetValueOrDefault(0); - var messageDetail = string.Empty; - if (dict.ContainsKey("message_detail")) - { - messageDetail = dict["message_detail"].ToString(); - } - throw new ApiException(code, (int)statusCode, dict["message"].ToString(), messageDetail); + throw new ApiException(obj.Code ?? 0, (int)statusCode, obj.Message, obj.MessageDetail); } catch (ApiException) { diff --git a/src/Core/packages.lock.json b/src/Core/packages.lock.json index 3d8b2c9d00..3d85dc3785 100644 --- a/src/Core/packages.lock.json +++ b/src/Core/packages.lock.json @@ -55,21 +55,21 @@ }, "Azure.Storage.Blobs": { "type": "Direct", - "requested": "[12.11.0, )", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "requested": "[12.14.1, )", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Queues": { "type": "Direct", - "requested": "[12.9.0, )", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "requested": "[12.12.0, )", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -93,6 +93,15 @@ "System.Xml.XPath.XmlDocument": "4.3.0" } }, + "DnsClient": { + "type": "Direct", + "requested": "[1.7.0, )", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2.AspNet": { "type": "Direct", "requested": "[3.0.1, )", @@ -390,8 +399,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -418,10 +427,10 @@ }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, diff --git a/src/Events/packages.lock.json b/src/Events/packages.lock.json index 5ae9cb53b9..bfb0051b19 100644 --- a/src/Events/packages.lock.json +++ b/src/Events/packages.lock.json @@ -62,8 +62,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -100,28 +100,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -148,6 +148,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2726,10 +2734,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/src/EventsProcessor/packages.lock.json b/src/EventsProcessor/packages.lock.json index 5ae9cb53b9..bfb0051b19 100644 --- a/src/EventsProcessor/packages.lock.json +++ b/src/EventsProcessor/packages.lock.json @@ -62,8 +62,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -100,28 +100,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -148,6 +148,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2726,10 +2734,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/src/Icons/packages.lock.json b/src/Icons/packages.lock.json index e74e2984da..a09e93cb2f 100644 --- a/src/Icons/packages.lock.json +++ b/src/Icons/packages.lock.json @@ -72,8 +72,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -110,28 +110,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -158,6 +158,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2736,10 +2744,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/src/Identity/IdentityServer/BaseRequestValidator.cs b/src/Identity/IdentityServer/BaseRequestValidator.cs index d8830e0867..ccec87fdb6 100644 --- a/src/Identity/IdentityServer/BaseRequestValidator.cs +++ b/src/Identity/IdentityServer/BaseRequestValidator.cs @@ -100,24 +100,20 @@ public abstract class BaseRequestValidator where T : class return; } - var (isTwoFactorRequired, requires2FABecauseNewDevice, twoFactorOrganization) = await RequiresTwoFactorAsync(user, request); + var (isTwoFactorRequired, twoFactorOrganization) = await RequiresTwoFactorAsync(user, request); if (isTwoFactorRequired) { // Just defaulting it var twoFactorProviderType = TwoFactorProviderType.Authenticator; if (!twoFactorRequest || !Enum.TryParse(twoFactorProvider, out twoFactorProviderType)) { - await BuildTwoFactorResultAsync(user, twoFactorOrganization, context, requires2FABecauseNewDevice); + await BuildTwoFactorResultAsync(user, twoFactorOrganization, context); return; } - BeforeVerifyTwoFactor(user, twoFactorProviderType, requires2FABecauseNewDevice); - var verified = await VerifyTwoFactor(user, twoFactorOrganization, twoFactorProviderType, twoFactorToken); - AfterVerifyTwoFactor(user, twoFactorProviderType, requires2FABecauseNewDevice); - if ((!verified || isBot) && twoFactorProviderType != TwoFactorProviderType.Remember) { await UpdateFailedAuthDetailsAsync(user, true, !validatorContext.KnownDevice); @@ -128,7 +124,7 @@ public abstract class BaseRequestValidator where T : class { // Delay for brute force. await Task.Delay(2000); - await BuildTwoFactorResultAsync(user, twoFactorOrganization, context, requires2FABecauseNewDevice); + await BuildTwoFactorResultAsync(user, twoFactorOrganization, context); return; } } @@ -201,7 +197,7 @@ public abstract class BaseRequestValidator where T : class await SetSuccessResult(context, user, claims, customResponse); } - protected async Task BuildTwoFactorResultAsync(User user, Organization organization, T context, bool requires2FABecauseNewDevice) + protected async Task BuildTwoFactorResultAsync(User user, Organization organization, T context) { var providerKeys = new List(); var providers = new Dictionary>(); @@ -226,23 +222,8 @@ public abstract class BaseRequestValidator where T : class if (!enabledProviders.Any()) { - if (!requires2FABecauseNewDevice) - { - await BuildErrorResultAsync("No two-step providers enabled.", false, context, user); - return; - } - - var emailProvider = new TwoFactorProvider - { - MetaData = new Dictionary { ["Email"] = user.Email.ToLowerInvariant() }, - Enabled = true - }; - enabledProviders.Add(new KeyValuePair( - TwoFactorProviderType.Email, emailProvider)); - user.SetTwoFactorProviders(new Dictionary - { - [TwoFactorProviderType.Email] = emailProvider - }); + await BuildErrorResultAsync("No two-step providers enabled.", false, context, user); + return; } foreach (var provider in enabledProviders) @@ -262,7 +243,7 @@ public abstract class BaseRequestValidator where T : class if (enabledProviders.Count() == 1 && enabledProviders.First().Key == TwoFactorProviderType.Email) { // Send email now if this is their only 2FA method - await _userService.SendTwoFactorEmailAsync(user, requires2FABecauseNewDevice); + await _userService.SendTwoFactorEmailAsync(user); } } @@ -298,12 +279,12 @@ public abstract class BaseRequestValidator where T : class protected abstract void SetErrorResult(T context, Dictionary customResponse); - private async Task> RequiresTwoFactorAsync(User user, ValidatedTokenRequest request) + private async Task> RequiresTwoFactorAsync(User user, ValidatedTokenRequest request) { if (request.GrantType == "client_credentials") { // Do not require MFA for api key logins - return new Tuple(false, false, null); + return new Tuple(false, null); } var individualRequired = _userManager.SupportsUserTwoFactor && @@ -325,17 +306,7 @@ public abstract class BaseRequestValidator where T : class } } - var requires2FA = individualRequired || firstEnabledOrg != null; - var requires2FABecauseNewDevice = !requires2FA - && - await _userService.Needs2FABecauseNewDeviceAsync( - user, - GetDeviceFromRequest(request)?.Identifier, - request.GrantType); - - requires2FA = requires2FA || requires2FABecauseNewDevice; - - return new Tuple(requires2FA, requires2FABecauseNewDevice, firstEnabledOrg); + return new Tuple(individualRequired || firstEnabledOrg != null, firstEnabledOrg); } private async Task IsValidAuthTypeAsync(User user, string grantType) @@ -413,29 +384,6 @@ public abstract class BaseRequestValidator where T : class }; } - private void BeforeVerifyTwoFactor(User user, TwoFactorProviderType type, bool requires2FABecauseNewDevice) - { - if (type == TwoFactorProviderType.Email && requires2FABecauseNewDevice) - { - user.SetTwoFactorProviders(new Dictionary - { - [TwoFactorProviderType.Email] = new TwoFactorProvider - { - MetaData = new Dictionary { ["Email"] = user.Email.ToLowerInvariant() }, - Enabled = true - } - }); - } - } - - private void AfterVerifyTwoFactor(User user, TwoFactorProviderType type, bool requires2FABecauseNewDevice) - { - if (type == TwoFactorProviderType.Email && requires2FABecauseNewDevice) - { - user.ClearTwoFactorProviders(); - } - } - private async Task VerifyTwoFactor(User user, Organization organization, TwoFactorProviderType type, string token) { diff --git a/src/Identity/packages.lock.json b/src/Identity/packages.lock.json index 1921dc5475..cb0378ca95 100644 --- a/src/Identity/packages.lock.json +++ b/src/Identity/packages.lock.json @@ -71,8 +71,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -109,28 +109,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -157,6 +157,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2748,10 +2756,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs index 7d1231190e..44e67f261b 100644 --- a/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs +++ b/src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs @@ -38,6 +38,7 @@ public static class DapperServiceCollectionExtensions services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); if (selfHosted) { diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs new file mode 100644 index 0000000000..c46c994a33 --- /dev/null +++ b/src/Infrastructure.Dapper/Repositories/OrganizationDomainRepository.cs @@ -0,0 +1,110 @@ +using System.Data; +using Bit.Core.Entities; +using Bit.Core.Models.Data.Organizations; +using Bit.Core.Repositories; +using Bit.Core.Settings; +using Dapper; +using Microsoft.Data.SqlClient; + +namespace Bit.Infrastructure.Dapper.Repositories; + +public class OrganizationDomainRepository : Repository, IOrganizationDomainRepository +{ + public OrganizationDomainRepository(GlobalSettings globalSettings) + : this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString) + { } + + public OrganizationDomainRepository(string connectionString, string readOnlyConnectionString) + : base(connectionString, readOnlyConnectionString) + { } + + public async Task> GetClaimedDomainsByDomainNameAsync(string domainName) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[OrganizationDomain_ReadByClaimedDomain]", + new { DomainName = domainName }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } + + public async Task> GetDomainsByOrganizationIdAsync(Guid orgId) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[OrganizationDomain_ReadByOrganizationId]", + new { OrganizationId = orgId }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } + + public async Task> GetManyByNextRunDateAsync(DateTime date) + { + using var connection = new SqlConnection(ConnectionString); + var results = await connection.QueryAsync( + $"[{Schema}].[OrganizationDomain_ReadByNextRunDate]", + new { Date = date }, commandType: CommandType.StoredProcedure + ); + + return results.ToList(); + } + + public async Task GetOrganizationDomainSsoDetailsAsync(string email) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection + .QueryAsync( + $"[{Schema}].[OrganizationDomainSsoDetails_ReadByEmail]", + new { Email = email }, + commandType: CommandType.StoredProcedure); + + return results.SingleOrDefault(); + } + } + + public async Task GetDomainByOrgIdAndDomainNameAsync(Guid orgId, string domainName) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection + .QueryAsync( + $"[{Schema}].[OrganizationDomain_ReadDomainByOrgIdAndDomainName]", + new { OrganizationId = orgId, DomainName = domainName }, + commandType: CommandType.StoredProcedure); + + return results.SingleOrDefault(); + } + } + + public async Task> GetExpiredOrganizationDomainsAsync() + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection + .QueryAsync( + $"[{Schema}].[OrganizationDomain_ReadIfExpired]", + null, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } + + public async Task DeleteExpiredAsync(int expirationPeriod) + { + using (var connection = new SqlConnection(ConnectionString)) + { + return await connection.ExecuteAsync( + $"[{Schema}].[OrganizationDomain_DeleteIfExpired]", + new { ExpirationPeriod = expirationPeriod }, + commandType: CommandType.StoredProcedure) > 0; + } + } +} diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationRepository.cs index c10903e116..52f486212f 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationRepository.cs @@ -94,4 +94,44 @@ public class OrganizationRepository : Repository, IOrganizat return results.ToList(); } } + + public async Task GetByLicenseKeyAsync(string licenseKey) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var result = await connection.QueryAsync( + "[dbo].[Organization_ReadByLicenseKey]", + new { LicenseKey = licenseKey }, + commandType: CommandType.StoredProcedure); + + return result.SingleOrDefault(); + } + } + + public async Task GetSelfHostedOrganizationDetailsById(Guid id) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var result = await connection.QueryMultipleAsync( + "[dbo].[Organization_ReadSelfHostedDetailsById]", + new { Id = id }, + commandType: CommandType.StoredProcedure); + + var selfHostOrganization = await result.ReadSingleOrDefaultAsync(); + if (selfHostOrganization == null) + { + return null; + } + + selfHostOrganization.OccupiedSeatCount = await result.ReadSingleAsync(); + selfHostOrganization.CollectionCount = await result.ReadSingleAsync(); + selfHostOrganization.GroupCount = await result.ReadSingleAsync(); + selfHostOrganization.OrganizationUsers = await result.ReadAsync(); + selfHostOrganization.Policies = await result.ReadAsync(); + selfHostOrganization.SsoConfig = await result.ReadFirstOrDefaultAsync(); + selfHostOrganization.ScimConnections = await result.ReadAsync(); + + return selfHostOrganization; + } + } } diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs index ef3d5bfbbd..462f1ba065 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs @@ -86,6 +86,19 @@ public class OrganizationUserRepository : Repository, IO } } + public async Task GetOccupiedSeatCountByOrganizationIdAsync(Guid organizationId) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var result = await connection.ExecuteScalarAsync( + "[dbo].[OrganizationUser_ReadOccupiedSeatCountByOrganizationId]", + new { OrganizationId = organizationId }, + commandType: CommandType.StoredProcedure); + + return result; + } + } + public async Task> SelectKnownEmailsAsync(Guid organizationId, IEnumerable emails, bool onlyRegisteredUsers) { diff --git a/src/Infrastructure.Dapper/SecretsManager/Repositories/ApiKeyRepository.cs b/src/Infrastructure.Dapper/SecretsManager/Repositories/ApiKeyRepository.cs index 8a13eb52c6..c362ae2369 100644 --- a/src/Infrastructure.Dapper/SecretsManager/Repositories/ApiKeyRepository.cs +++ b/src/Infrastructure.Dapper/SecretsManager/Repositories/ApiKeyRepository.cs @@ -42,4 +42,13 @@ public class ApiKeyRepository : Repository, IApiKeyRepository return results.ToList(); } + + public async Task DeleteManyAsync(IEnumerable objs) + { + using var connection = new SqlConnection(ConnectionString); + await connection.QueryAsync( + $"[{Schema}].[ApiKey_DeleteByIds]", + new { Ids = objs.Select(obj => obj.Id).ToGuidIdArrayTVP() }, + commandType: CommandType.StoredProcedure); + } } diff --git a/src/Infrastructure.Dapper/packages.lock.json b/src/Infrastructure.Dapper/packages.lock.json index 4909ebfa4b..7b050ad0f3 100644 --- a/src/Infrastructure.Dapper/packages.lock.json +++ b/src/Infrastructure.Dapper/packages.lock.json @@ -51,8 +51,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -89,28 +89,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -132,6 +132,14 @@ "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": { "type": "Transitive", "resolved": "3.0.1", @@ -2555,10 +2563,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs index d40c770da1..2ec4fca7e8 100644 --- a/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs +++ b/src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs @@ -73,6 +73,7 @@ public static class EntityFrameworkServiceCollectionExtensions services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); if (selfHosted) { diff --git a/src/Infrastructure.EntityFramework/Models/Organization.cs b/src/Infrastructure.EntityFramework/Models/Organization.cs index c1969cab0c..fca548545c 100644 --- a/src/Infrastructure.EntityFramework/Models/Organization.cs +++ b/src/Infrastructure.EntityFramework/Models/Organization.cs @@ -8,11 +8,13 @@ public class Organization : Core.Entities.Organization public virtual ICollection OrganizationUsers { get; set; } public virtual ICollection Groups { get; set; } public virtual ICollection Policies { get; set; } + public virtual ICollection Collections { get; set; } public virtual ICollection SsoConfigs { get; set; } public virtual ICollection SsoUsers { get; set; } public virtual ICollection Transactions { get; set; } public virtual ICollection ApiKeys { get; set; } public virtual ICollection Connections { get; set; } + public virtual ICollection Domains { get; set; } } public class OrganizationMapperProfile : Profile diff --git a/src/Infrastructure.EntityFramework/Models/OrganizationDomain.cs b/src/Infrastructure.EntityFramework/Models/OrganizationDomain.cs new file mode 100644 index 0000000000..bef7ba2af6 --- /dev/null +++ b/src/Infrastructure.EntityFramework/Models/OrganizationDomain.cs @@ -0,0 +1,16 @@ +using AutoMapper; + +namespace Bit.Infrastructure.EntityFramework.Models; + +public class OrganizationDomain : Core.Entities.OrganizationDomain +{ + public virtual Organization Organization { get; set; } +} + +public class OrganizationDomainMapperProfile : Profile +{ + public OrganizationDomainMapperProfile() + { + CreateMap().ReverseMap(); + } +} diff --git a/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs b/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs index 4368c97b4f..e782960d89 100644 --- a/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs +++ b/src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs @@ -21,6 +21,8 @@ public class DatabaseContext : DbContext public DbSet UserProjectAccessPolicy { get; set; } public DbSet GroupProjectAccessPolicy { get; set; } public DbSet ServiceAccountProjectAccessPolicy { get; set; } + public DbSet UserServiceAccountAccessPolicy { get; set; } + public DbSet GroupServiceAccountAccessPolicy { get; set; } public DbSet ApiKeys { get; set; } public DbSet Ciphers { get; set; } public DbSet Collections { get; set; } @@ -54,6 +56,7 @@ public class DatabaseContext : DbContext public DbSet Transactions { get; set; } public DbSet Users { get; set; } public DbSet AuthRequests { get; set; } + public DbSet OrganizationDomains { get; set; } protected override void OnModelCreating(ModelBuilder builder) { @@ -93,6 +96,7 @@ public class DatabaseContext : DbContext var eOrganizationApiKey = builder.Entity(); var eOrganizationConnection = builder.Entity(); var eAuthRequest = builder.Entity(); + var eOrganizationDomain = builder.Entity(); eCipher.Property(c => c.Id).ValueGeneratedNever(); eCollection.Property(c => c.Id).ValueGeneratedNever(); @@ -114,6 +118,7 @@ public class DatabaseContext : DbContext eOrganizationApiKey.Property(c => c.Id).ValueGeneratedNever(); eOrganizationConnection.Property(c => c.Id).ValueGeneratedNever(); eAuthRequest.Property(ar => ar.Id).ValueGeneratedNever(); + eOrganizationDomain.Property(ar => ar.Id).ValueGeneratedNever(); eCollectionCipher.HasKey(cc => new { cc.CollectionId, cc.CipherId }); eCollectionUser.HasKey(cu => new { cu.CollectionId, cu.OrganizationUserId }); @@ -165,6 +170,7 @@ public class DatabaseContext : DbContext eOrganizationApiKey.ToTable(nameof(OrganizationApiKey)); eOrganizationConnection.ToTable(nameof(OrganizationConnection)); eAuthRequest.ToTable(nameof(AuthRequest)); + eOrganizationDomain.ToTable(nameof(OrganizationDomain)); ConfigureDateTimeUtcQueries(builder); } diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs new file mode 100644 index 0000000000..8b453d5e5e --- /dev/null +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationDomainRepository.cs @@ -0,0 +1,140 @@ +using System.Net.Mail; +using AutoMapper; +using Bit.Core.Enums; +using Bit.Core.Models.Data.Organizations; +using Bit.Core.Repositories; +using Bit.Infrastructure.EntityFramework.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Bit.Infrastructure.EntityFramework.Repositories; + +public class OrganizationDomainRepository : Repository, IOrganizationDomainRepository +{ + public OrganizationDomainRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) + : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.OrganizationDomains) + { + } + + public async Task> GetClaimedDomainsByDomainNameAsync( + string domainName) + { + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + var claimedDomains = await dbContext.OrganizationDomains + .Where(x => x.DomainName == domainName + && x.VerifiedDate != null) + .AsNoTracking() + .ToListAsync(); + return Mapper.Map>(claimedDomains); + } + + public async Task> GetDomainsByOrganizationIdAsync(Guid orgId) + { + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + var domains = await dbContext.OrganizationDomains + .Where(x => x.OrganizationId == orgId) + .AsNoTracking() + .ToListAsync(); + return Mapper.Map>(domains); + } + + public async Task> GetManyByNextRunDateAsync(DateTime date) + { + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + + var domains = await dbContext.OrganizationDomains + .Where(x => x.VerifiedDate == null + && x.JobRunCount != 3 + && x.NextRunDate.Year == date.Year + && x.NextRunDate.Month == date.Month + && x.NextRunDate.Day == date.Day + && x.NextRunDate.Hour == date.Hour) + .AsNoTracking() + .ToListAsync(); + + //Get records that have ignored/failed by the background service + var pastDomains = dbContext.OrganizationDomains + .AsEnumerable() + .Where(x => (date - x.NextRunDate).TotalHours > 36 + && x.VerifiedDate == null + && x.JobRunCount != 3) + .ToList(); + + var results = domains.Union(pastDomains); + + return Mapper.Map>(results); + } + + public async Task GetOrganizationDomainSsoDetailsAsync(string email) + { + var domainName = new MailAddress(email).Host; + + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + var ssoDetails = await (from o in dbContext.Organizations + from od in o.Domains + join s in dbContext.SsoConfigs on o.Id equals s.OrganizationId into sJoin + from s in sJoin.DefaultIfEmpty() + join p in dbContext.Policies.Where(p => p.Type == PolicyType.RequireSso) on o.Id + equals p.OrganizationId into pJoin + from p in pJoin.DefaultIfEmpty() + where od.DomainName == domainName && o.Enabled + select new OrganizationDomainSsoDetailsData + { + OrganizationId = o.Id, + OrganizationName = o.Name, + SsoAvailable = o.SsoConfigs.Any(sc => sc.Enabled), + SsoRequired = p != null && p.Enabled, + OrganizationIdentifier = o.Identifier, + VerifiedDate = od.VerifiedDate, + PolicyType = p.Type, + DomainName = od.DomainName + }) + .AsNoTracking() + .SingleOrDefaultAsync(); + + return ssoDetails; + } + + public async Task GetDomainByOrgIdAndDomainNameAsync(Guid orgId, string domainName) + { + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + var domain = await dbContext.OrganizationDomains + .Where(x => x.OrganizationId == orgId && x.DomainName == domainName) + .AsNoTracking() + .FirstOrDefaultAsync(); + + return Mapper.Map(domain); + } + + public async Task> GetExpiredOrganizationDomainsAsync() + { + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + + //Get domains that have not been verified after 72 hours + var domains = dbContext.OrganizationDomains + .AsEnumerable() + .Where(x => (DateTime.UtcNow - x.CreationDate).Days == 4 + && x.VerifiedDate == null) + .ToList(); + + return Mapper.Map>(domains); + } + + public async Task DeleteExpiredAsync(int expirationPeriod) + { + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + + var expiredDomains = await dbContext.OrganizationDomains + .Where(x => x.LastCheckedDate < DateTime.UtcNow.AddDays(-expirationPeriod)) + .ToListAsync(); + dbContext.OrganizationDomains.RemoveRange(expiredDomains); + return await dbContext.SaveChangesAsync() > 0; + } +} diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs index 970a2da730..7f0028d25b 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs @@ -1,9 +1,10 @@ using AutoMapper; +using Bit.Core.Enums; using Bit.Core.Models.Data.Organizations; using Bit.Core.Repositories; -using Bit.Infrastructure.EntityFramework.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using Organization = Bit.Infrastructure.EntityFramework.Models.Organization; namespace Bit.Infrastructure.EntityFramework.Repositories; @@ -139,4 +140,40 @@ public class OrganizationRepository : Repository GetByLicenseKeyAsync(string licenseKey) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var organization = await GetDbSet(dbContext) + .FirstOrDefaultAsync(o => o.LicenseKey == licenseKey); + + return organization; + } + } + + public async Task GetSelfHostedOrganizationDetailsById(Guid id) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var organization = await GetDbSet(dbContext).FindAsync(id); + if (organization == null) + { + return null; + } + + var selfHostOrganization = Mapper.Map(organization); + + selfHostOrganization.OccupiedSeatCount = + organization.OrganizationUsers.Count(ou => ou.Status >= OrganizationUserStatusType.Invited); + selfHostOrganization.CollectionCount = organization.Collections?.Count ?? 0; + selfHostOrganization.GroupCount = organization?.Groups.Count ?? 0; + selfHostOrganization.SsoConfig = organization.SsoConfigs.SingleOrDefault(); + selfHostOrganization.ScimConnections = organization.Connections.Where(c => c.Type == OrganizationConnectionType.Scim); + + return selfHostOrganization; + } + } } diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs index 6fe13f245d..8deae8e14d 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs @@ -197,6 +197,12 @@ public class OrganizationUserRepository : Repository GetOccupiedSeatCountByOrganizationIdAsync(Guid organizationId) + { + var query = new OrganizationUserReadOccupiedSeatCountByOrganizationIdQuery(organizationId); + return await GetCountFromQuery(query); + } + public async Task GetCountByOrganizationIdAsync(Guid organizationId) { var query = new OrganizationUserReadCountByOrganizationIdQuery(organizationId); diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/OrganizationUserReadOccupiedSeatCountByOrganizationIdQuery.cs b/src/Infrastructure.EntityFramework/Repositories/Queries/OrganizationUserReadOccupiedSeatCountByOrganizationIdQuery.cs new file mode 100644 index 0000000000..47b50617a0 --- /dev/null +++ b/src/Infrastructure.EntityFramework/Repositories/Queries/OrganizationUserReadOccupiedSeatCountByOrganizationIdQuery.cs @@ -0,0 +1,22 @@ +using Bit.Core.Enums; +using Bit.Infrastructure.EntityFramework.Models; + +namespace Bit.Infrastructure.EntityFramework.Repositories.Queries; + +public class OrganizationUserReadOccupiedSeatCountByOrganizationIdQuery : IQuery +{ + private readonly Guid _organizationId; + + public OrganizationUserReadOccupiedSeatCountByOrganizationIdQuery(Guid organizationId) + { + _organizationId = organizationId; + } + + public IQueryable Run(DatabaseContext dbContext) + { + var query = from ou in dbContext.OrganizationUsers + where ou.OrganizationId == _organizationId && ou.Status >= OrganizationUserStatusType.Invited + select ou; + return query; + } +} diff --git a/src/Infrastructure.EntityFramework/Repositories/Queries/OrganizationUserUserViewQuery.cs b/src/Infrastructure.EntityFramework/Repositories/Queries/OrganizationUserUserViewQuery.cs index 49dc3a385d..2f36743dc0 100644 --- a/src/Infrastructure.EntityFramework/Repositories/Queries/OrganizationUserUserViewQuery.cs +++ b/src/Infrastructure.EntityFramework/Repositories/Queries/OrganizationUserUserViewQuery.cs @@ -19,6 +19,7 @@ public class OrganizationUserUserDetailsViewQuery : IQuery>(apiKeys); } + + public async Task DeleteManyAsync(IEnumerable objs) + { + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + var entities = objs.Select(obj => Mapper.Map(obj)); + dbContext.RemoveRange(entities); + await dbContext.SaveChangesAsync(); + } } diff --git a/src/Infrastructure.EntityFramework/packages.lock.json b/src/Infrastructure.EntityFramework/packages.lock.json index 38a0d36178..5a1316b527 100644 --- a/src/Infrastructure.EntityFramework/packages.lock.json +++ b/src/Infrastructure.EntityFramework/packages.lock.json @@ -126,8 +126,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -164,28 +164,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -207,6 +207,14 @@ "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": { "type": "Transitive", "resolved": "3.0.1", @@ -2728,10 +2736,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/src/Notifications/packages.lock.json b/src/Notifications/packages.lock.json index f50b74f911..42fef74678 100644 --- a/src/Notifications/packages.lock.json +++ b/src/Notifications/packages.lock.json @@ -83,8 +83,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -121,28 +121,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -169,6 +169,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2776,10 +2784,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index fef0eb4857..2d50e48b21 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -124,6 +124,7 @@ public static class ServiceCollectionExtensions services.AddScoped(); services.AddScoped(); services.AddLoginServices(); + services.AddScoped(); } public static void AddTokenizers(this IServiceCollection services) @@ -173,6 +174,7 @@ public static class ServiceCollectionExtensions services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddTokenizers(); if (CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ConnectionString) && @@ -339,7 +341,7 @@ public static class ServiceCollectionExtensions { RequireDigit = false, RequireLowercase = false, - RequiredLength = 8, + RequiredLength = 12, RequireNonAlphanumeric = false, RequireUppercase = false }; diff --git a/src/SharedWeb/packages.lock.json b/src/SharedWeb/packages.lock.json index 53edf4ea45..fef8c5d43e 100644 --- a/src/SharedWeb/packages.lock.json +++ b/src/SharedWeb/packages.lock.json @@ -62,8 +62,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -100,28 +100,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -148,6 +148,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2726,10 +2734,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/src/Sql/SecretsManager/dbo/Stored Procedures/ApiKey/ApiKey_DeleteByIds.sql b/src/Sql/SecretsManager/dbo/Stored Procedures/ApiKey/ApiKey_DeleteByIds.sql new file mode 100644 index 0000000000..a0c1f2ace2 --- /dev/null +++ b/src/Sql/SecretsManager/dbo/Stored Procedures/ApiKey/ApiKey_DeleteByIds.sql @@ -0,0 +1,23 @@ +CREATE PROCEDURE [dbo].[ApiKey_DeleteByIds] + @Ids [dbo].[GuidIdArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + DECLARE @BatchSize INT = 100 + + WHILE @BatchSize > 0 + BEGIN + BEGIN TRANSACTION ApiKey_DeleteMany + + DELETE TOP(@BatchSize) AK + FROM + [dbo].[ApiKey] AK + INNER JOIN + @Ids I ON I.Id = AK.Id + + SET @BatchSize = @@ROWCOUNT + + COMMIT TRANSACTION ApiKey_DeleteMany + END +END diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index 57aa460852..42cb031a88 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -128,22 +128,29 @@ + + + + + + + @@ -438,6 +445,7 @@ + @@ -447,5 +455,24 @@ + + + - \ No newline at end of file + + + + + + + + + + + + + + + + + diff --git a/src/Sql/dbo/Stored Procedures/Event_Create.sql b/src/Sql/dbo/Stored Procedures/Event_Create.sql index 99525ed0d5..cd3dd6b6e9 100644 --- a/src/Sql/dbo/Stored Procedures/Event_Create.sql +++ b/src/Sql/dbo/Stored Procedures/Event_Create.sql @@ -16,7 +16,10 @@ @DeviceType SMALLINT, @IpAddress VARCHAR(50), @Date DATETIME2(7), - @SystemUser TINYINT = null + @SystemUser TINYINT = null, + @DomainName VARCHAR(256), + @SecretId UNIQUEIDENTIFIER = null, + @ServiceAccountId UNIQUEIDENTIFIER = null AS BEGIN SET NOCOUNT ON @@ -40,7 +43,10 @@ BEGIN [DeviceType], [IpAddress], [Date], - [SystemUser] + [SystemUser], + [DomainName], + [SecretId], + [ServiceAccountId] ) VALUES ( @@ -61,6 +67,9 @@ BEGIN @DeviceType, @IpAddress, @Date, - @SystemUser + @SystemUser, + @DomainName, + @SecretId, + @ServiceAccountId ) END diff --git a/src/Sql/dbo/Stored Procedures/Event_ReadyById.sql b/src/Sql/dbo/Stored Procedures/Event_ReadyById.sql deleted file mode 100644 index 71e3b28660..0000000000 --- a/src/Sql/dbo/Stored Procedures/Event_ReadyById.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE PROCEDURE [dbo].[Event_ReadById] - @Id UNIQUEIDENTIFIER -AS -BEGIN - SET NOCOUNT ON - - SELECT - * - FROM - [dbo].[Event] - WHERE - [Id] = @Id -END diff --git a/src/Sql/dbo/Stored Procedures/Group_ReadCountByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/Group_ReadCountByOrganizationId.sql new file mode 100644 index 0000000000..141143ee73 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Group_ReadCountByOrganizationId.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[Group_ReadCountByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + COUNT(1) + FROM + [dbo].[Group] + WHERE + [OrganizationId] = @OrganizationId +END diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomainSsoDetails_ReadByEmail.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomainSsoDetails_ReadByEmail.sql new file mode 100644 index 0000000000..b1e4ae7679 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomainSsoDetails_ReadByEmail.sql @@ -0,0 +1,31 @@ +CREATE PROCEDURE [dbo].[OrganizationDomainSsoDetails_ReadByEmail] + @Email NVARCHAR(256) +AS +BEGIN + SET NOCOUNT ON + + DECLARE @Domain NVARCHAR(256) + + SELECT @Domain = SUBSTRING(@Email, CHARINDEX( '@', @Email) + 1, LEN(@Email)) + + SELECT + O.Id AS OrganizationId, + O.[Name] AS OrganizationName, + S.Enabled AS SsoAvailable, + P.Enabled AS SsoRequired, + O.Identifier AS OrganizationIdentifier, + OD.VerifiedDate, + P.[Type] AS PolicyType, + OD.DomainName + FROM + [dbo].[OrganizationView] O + INNER JOIN [dbo].[OrganizationDomainView] OD + ON O.Id = OD.OrganizationId + LEFT JOIN [dbo].[PolicyView] P + ON O.Id = P.OrganizationId + LEFT JOIN [dbo].[Ssoconfig] S + ON O.Id = S.OrganizationId + WHERE OD.DomainName = @Domain + AND O.Enabled = 1 + AND (P.Id is NULL OR (P.Id IS NOT NULL AND P.[Type] = 4)) -- SSO Type +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_Create.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_Create.sql new file mode 100644 index 0000000000..af6f5848a0 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_Create.sql @@ -0,0 +1,39 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @OrganizationId UNIQUEIDENTIFIER, + @Txt VARCHAR(MAX), + @DomainName NVARCHAR(255), + @CreationDate DATETIME2(7), + @VerifiedDate DATETIME2(7), + @LastCheckedDate DATETIME2(7), + @NextRunDate DATETIME2(7), + @JobRunCount TINYINT +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[OrganizationDomain] + ( + [Id], + [OrganizationId], + [Txt], + [DomainName], + [CreationDate], + [VerifiedDate], + [LastCheckedDate], + [NextRunDate], + [JobRunCount] + ) + VALUES + ( + @Id, + @OrganizationId, + @Txt, + @DomainName, + @CreationDate, + @VerifiedDate, + @LastCheckedDate, + @NextRunDate, + @JobRunCount + ) +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_DeleteById.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_DeleteById.sql new file mode 100644 index 0000000000..e7637b95b5 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_DeleteById.sql @@ -0,0 +1,12 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_DeleteById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + DELETE + FROM + [dbo].[OrganizationDomain] + WHERE + [Id] = @Id +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_DeleteIfExpired.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_DeleteIfExpired.sql new file mode 100644 index 0000000000..3e42b9bc66 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_DeleteIfExpired.sql @@ -0,0 +1,10 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_DeleteIfExpired] + @ExpirationPeriod TINYINT +AS +BEGIN + SET NOCOUNT OFF + + DELETE FROM [dbo].[OrganizationDomain] + WHERE DATEDIFF(DAY, [LastCheckedDate], GETUTCDATE()) >= @ExpirationPeriod + AND [VerifiedDate] IS NULL +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_OrganizationDeleted.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_OrganizationDeleted.sql new file mode 100644 index 0000000000..8800a31c21 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_OrganizationDeleted.sql @@ -0,0 +1,12 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_OrganizationDeleted] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + DELETE + FROM + [dbo].[OrganizationDomain] + WHERE + [OrganizationId] = @OrganizationId +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByClaimedDomain.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByClaimedDomain.sql new file mode 100644 index 0000000000..3dd2424375 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByClaimedDomain.sql @@ -0,0 +1,15 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_ReadByClaimedDomain] + @DomainName NVARCHAR(255) +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[OrganizationDomain] + WHERE + [DomainName] = @DomainName + AND + [VerifiedDate] IS NOT NULL +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadById.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadById.sql new file mode 100644 index 0000000000..ae6f98bfd8 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadById.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_ReadById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[OrganizationDomain] + WHERE + [Id] = @Id +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByNextRunDate.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByNextRunDate.sql new file mode 100644 index 0000000000..7dc51c77d0 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByNextRunDate.sql @@ -0,0 +1,25 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_ReadByNextRunDate] + @Date DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[OrganizationDomain] + WHERE [VerifiedDate] IS NULL + AND [JobRunCount] != 3 + AND DATEPART(year, [NextRunDate]) = DATEPART(year, @Date) + AND DATEPART(month, [NextRunDate]) = DATEPART(month, @Date) + AND DATEPART(day, [NextRunDate]) = DATEPART(day, @Date) + AND DATEPART(hour, [NextRunDate]) = DATEPART(hour, @Date) + UNION + SELECT + * + FROM + [dbo].[OrganizationDomain] + WHERE DATEDIFF(hour, [NextRunDate], @Date) > 36 + AND [VerifiedDate] IS NULL + AND [JobRunCount] != 3 +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByOrganizationId.sql new file mode 100644 index 0000000000..e9e723ab6c --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadByOrganizationId.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_ReadByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[OrganizationDomain] + WHERE + [OrganizationId] = @OrganizationId +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadDomainByOrgIdAndDomainName.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadDomainByOrgIdAndDomainName.sql new file mode 100644 index 0000000000..6cc19f965d --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadDomainByOrgIdAndDomainName.sql @@ -0,0 +1,16 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_ReadDomainByOrgIdAndDomainName] + @OrganizationId UNIQUEIDENTIFIER, + @DomainName NVARCHAR(255) +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationDomain] +WHERE + [OrganizationId] = @OrganizationId + AND + [DomainName] = @DomainName +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadIfExpired.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadIfExpired.sql new file mode 100644 index 0000000000..b2a1a9d3fb --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_ReadIfExpired.sql @@ -0,0 +1,14 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_ReadIfExpired] +AS +BEGIN + SET NOCOUNT OFF + + SELECT + * + FROM + [dbo].[OrganizationDomain] + WHERE + DATEDIFF(DAY, [CreationDate], GETUTCDATE()) = 4 --Get domains that have not been verified after 3 days (72 hours) + AND + [VerifiedDate] IS NULL +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationDomain_Update.sql b/src/Sql/dbo/Stored Procedures/OrganizationDomain_Update.sql new file mode 100644 index 0000000000..7318f9afb9 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationDomain_Update.sql @@ -0,0 +1,28 @@ +CREATE PROCEDURE [dbo].[OrganizationDomain_Update] + @Id UNIQUEIDENTIFIER OUTPUT, + @OrganizationId UNIQUEIDENTIFIER, + @Txt VARCHAR(MAX), + @DomainName NVARCHAR(255), + @CreationDate DATETIME2(7), + @VerifiedDate DATETIME2(7), + @LastCheckedDate DATETIME2(7), + @NextRunDate DATETIME2(7), + @JobRunCount TINYINT +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[OrganizationDomain] + SET + [OrganizationId] = @OrganizationId, + [Txt] = @Txt, + [DomainName] = @DomainName, + [CreationDate] = @CreationDate, + [VerifiedDate] = @VerifiedDate, + [LastCheckedDate] = @LastCheckedDate + [NextRunDate] = @NextRunDate, + [JobRunCount] = @JobRunCount + WHERE + [Id] = @Id +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadOccupiedSeatCountByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadOccupiedSeatCountByOrganizationId.sql new file mode 100644 index 0000000000..892ed51012 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadOccupiedSeatCountByOrganizationId.sql @@ -0,0 +1,14 @@ +CREATE PROCEDURE [dbo].[OrganizationUser_ReadOccupiedSeatCountByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + COUNT(1) + FROM + [dbo].[OrganizationUserView] + WHERE + OrganizationId = @OrganizationId + AND Status >= 0 --Invited +END diff --git a/src/Sql/dbo/Stored Procedures/Organization_DeleteById.sql b/src/Sql/dbo/Stored Procedures/Organization_DeleteById.sql index c54a08c029..2c911ea76d 100644 --- a/src/Sql/dbo/Stored Procedures/Organization_DeleteById.sql +++ b/src/Sql/dbo/Stored Procedures/Organization_DeleteById.sql @@ -60,6 +60,7 @@ BEGIN EXEC [dbo].[OrganizationApiKey_OrganizationDeleted] @Id EXEC [dbo].[OrganizationConnection_OrganizationDeleted] @Id EXEC [dbo].[OrganizationSponsorship_OrganizationDeleted] @Id + EXEC [dbo].[OrganizationDomain_OrganizationDeleted] @Id DELETE FROM diff --git a/src/Sql/dbo/Stored Procedures/Organization_ReadByLicenseKey.sql b/src/Sql/dbo/Stored Procedures/Organization_ReadByLicenseKey.sql new file mode 100644 index 0000000000..c189aedd30 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Organization_ReadByLicenseKey.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[Organization_ReadByLicenseKey] + @LicenseKey VARCHAR (100) +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationView] +WHERE + [LicenseKey] = @LicenseKey +END diff --git a/src/Sql/dbo/Stored Procedures/Organization_ReadSelfHostedDetailsById.sql b/src/Sql/dbo/Stored Procedures/Organization_ReadSelfHostedDetailsById.sql new file mode 100644 index 0000000000..6db79e0b2e --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Organization_ReadSelfHostedDetailsById.sql @@ -0,0 +1,15 @@ +CREATE PROCEDURE [dbo].[Organization_ReadSelfHostedDetailsById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[Organization_ReadById] @Id + EXEC [dbo].[OrganizationUser_ReadOccupiedSeatCountByOrganizationId] @Id + EXEC [dbo].[Collection_ReadCountByOrganizationId] @Id + EXEC [dbo].[Group_ReadCountByOrganizationId] @Id + EXEC [dbo].[OrganizationUser_ReadByOrganizationId] @Id, NULL + EXEC [dbo].[Policy_ReadByOrganizationId] @Id + EXEC [dbo].[SsoConfig_ReadByOrganizationId] @Id + EXEC [dbo].[OrganizationConnection_ReadByOrganizationIdType] @Id, 2 --Scim connection type +END diff --git a/src/Sql/dbo/Tables/Event.sql b/src/Sql/dbo/Tables/Event.sql index 9972f41294..1932f103f5 100644 --- a/src/Sql/dbo/Tables/Event.sql +++ b/src/Sql/dbo/Tables/Event.sql @@ -17,6 +17,9 @@ [ProviderUserId] UNIQUEIDENTIFIER NULL, [ProviderOrganizationId] UNIQUEIDENTIFIER NULL, [SystemUser] TINYINT NULL, + [DomainName] VARCHAR(256) NULL, + [SecretId] UNIQUEIDENTIFIER NULL, + [ServiceAccountId] UNIQUEIDENTIFIER NULL, CONSTRAINT [PK_Event] PRIMARY KEY CLUSTERED ([Id] ASC) ); diff --git a/src/Sql/dbo/Tables/OrganizationDomain.sql b/src/Sql/dbo/Tables/OrganizationDomain.sql new file mode 100644 index 0000000000..d7585167a6 --- /dev/null +++ b/src/Sql/dbo/Tables/OrganizationDomain.sql @@ -0,0 +1,15 @@ +CREATE TABLE [dbo].[OrganizationDomain] ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [OrganizationId] UNIQUEIDENTIFIER NOT NULL, + [Txt] VARCHAR(MAX) NOT NULL, + [DomainName] NVARCHAR(255) NOT NULL, + [CreationDate] DATETIME2(7) NOT NULL, + [VerifiedDate] DATETIME2(7) NULL, + [LastCheckedDate] DATETIME2(7) NULL, + [NextRunDate] DATETIME2(7) NOT NULL, + [JobRunCount] TINYINT NOT NULL + CONSTRAINT [PK_OrganizationDomain] PRIMARY KEY CLUSTERED ([Id] ASC), + CONSTRAINT [FK_OrganzationDomain_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]) +); + +GO \ No newline at end of file diff --git a/src/Sql/dbo/Views/OrganizationDomainView.sql b/src/Sql/dbo/Views/OrganizationDomainView.sql new file mode 100644 index 0000000000..c5d484866d --- /dev/null +++ b/src/Sql/dbo/Views/OrganizationDomainView.sql @@ -0,0 +1,6 @@ +CREATE VIEW [dbo].[OrganizationDomainView] +AS +SELECT + * +FROM + [dbo].[OrganizationDomain] \ No newline at end of file diff --git a/src/Sql/dbo/Views/OrganizationUserUserDetailsView.sql b/src/Sql/dbo/Views/OrganizationUserUserDetailsView.sql index 4bc43f79ae..f2d7739769 100644 --- a/src/Sql/dbo/Views/OrganizationUserUserDetailsView.sql +++ b/src/Sql/dbo/Views/OrganizationUserUserDetailsView.sql @@ -6,6 +6,7 @@ SELECT OU.[OrganizationId], U.[Name], ISNULL(U.[Email], OU.[Email]) Email, + U.[AvatarColor], U.[TwoFactorProviders], U.[Premium], OU.[Status], diff --git a/src/Sql/dbo_future/Stored Procedures/User_Create.sql b/src/Sql/dbo_future/Stored Procedures/User_Create.sql new file mode 100644 index 0000000000..7139153950 --- /dev/null +++ b/src/Sql/dbo_future/Stored Procedures/User_Create.sql @@ -0,0 +1,120 @@ +CREATE PROCEDURE [dbo].[User_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @Name NVARCHAR(50), + @Email NVARCHAR(256), + @EmailVerified BIT, + @MasterPassword NVARCHAR(300), + @MasterPasswordHint NVARCHAR(50), + @Culture NVARCHAR(10), + @SecurityStamp NVARCHAR(50), + @TwoFactorProviders NVARCHAR(MAX), + @TwoFactorRecoveryCode NVARCHAR(32), + @EquivalentDomains NVARCHAR(MAX), + @ExcludedGlobalEquivalentDomains NVARCHAR(MAX), + @AccountRevisionDate DATETIME2(7), + @Key NVARCHAR(MAX), + @PublicKey NVARCHAR(MAX), + @PrivateKey NVARCHAR(MAX), + @Premium BIT, + @PremiumExpirationDate DATETIME2(7), + @RenewalReminderDate DATETIME2(7), + @Storage BIGINT, + @MaxStorageGb SMALLINT, + @Gateway TINYINT, + @GatewayCustomerId VARCHAR(50), + @GatewaySubscriptionId VARCHAR(50), + @ReferenceData VARCHAR(MAX), + @LicenseKey VARCHAR(100), + @Kdf TINYINT, + @KdfIterations INT, + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @ApiKey VARCHAR(30), + @ForcePasswordReset BIT = 0, + @UsesKeyConnector BIT = 0, + @FailedLoginCount INT = 0, + @LastFailedLoginDate DATETIME2(7), + @AvatarColor VARCHAR(7) = NULL +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[User] + ( + [Id], + [Name], + [Email], + [EmailVerified], + [MasterPassword], + [MasterPasswordHint], + [Culture], + [SecurityStamp], + [TwoFactorProviders], + [TwoFactorRecoveryCode], + [EquivalentDomains], + [ExcludedGlobalEquivalentDomains], + [AccountRevisionDate], + [Key], + [PublicKey], + [PrivateKey], + [Premium], + [PremiumExpirationDate], + [RenewalReminderDate], + [Storage], + [MaxStorageGb], + [Gateway], + [GatewayCustomerId], + [GatewaySubscriptionId], + [ReferenceData], + [LicenseKey], + [Kdf], + [KdfIterations], + [CreationDate], + [RevisionDate], + [ApiKey], + [ForcePasswordReset], + [UsesKeyConnector], + [FailedLoginCount], + [LastFailedLoginDate], + [AvatarColor] + ) + VALUES + ( + @Id, + @Name, + @Email, + @EmailVerified, + @MasterPassword, + @MasterPasswordHint, + @Culture, + @SecurityStamp, + @TwoFactorProviders, + @TwoFactorRecoveryCode, + @EquivalentDomains, + @ExcludedGlobalEquivalentDomains, + @AccountRevisionDate, + @Key, + @PublicKey, + @PrivateKey, + @Premium, + @PremiumExpirationDate, + @RenewalReminderDate, + @Storage, + @MaxStorageGb, + @Gateway, + @GatewayCustomerId, + @GatewaySubscriptionId, + @ReferenceData, + @LicenseKey, + @Kdf, + @KdfIterations, + @CreationDate, + @RevisionDate, + @ApiKey, + @ForcePasswordReset, + @UsesKeyConnector, + @FailedLoginCount, + @LastFailedLoginDate, + @AvatarColor + ) +END diff --git a/src/Sql/dbo_future/Stored Procedures/User_Update.sql b/src/Sql/dbo_future/Stored Procedures/User_Update.sql new file mode 100644 index 0000000000..1ef314af8e --- /dev/null +++ b/src/Sql/dbo_future/Stored Procedures/User_Update.sql @@ -0,0 +1,82 @@ +CREATE PROCEDURE [dbo].[User_Update] + @Id UNIQUEIDENTIFIER, + @Name NVARCHAR(50), + @Email NVARCHAR(256), + @EmailVerified BIT, + @MasterPassword NVARCHAR(300), + @MasterPasswordHint NVARCHAR(50), + @Culture NVARCHAR(10), + @SecurityStamp NVARCHAR(50), + @TwoFactorProviders NVARCHAR(MAX), + @TwoFactorRecoveryCode NVARCHAR(32), + @EquivalentDomains NVARCHAR(MAX), + @ExcludedGlobalEquivalentDomains NVARCHAR(MAX), + @AccountRevisionDate DATETIME2(7), + @Key NVARCHAR(MAX), + @PublicKey NVARCHAR(MAX), + @PrivateKey NVARCHAR(MAX), + @Premium BIT, + @PremiumExpirationDate DATETIME2(7), + @RenewalReminderDate DATETIME2(7), + @Storage BIGINT, + @MaxStorageGb SMALLINT, + @Gateway TINYINT, + @GatewayCustomerId VARCHAR(50), + @GatewaySubscriptionId VARCHAR(50), + @ReferenceData VARCHAR(MAX), + @LicenseKey VARCHAR(100), + @Kdf TINYINT, + @KdfIterations INT, + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @ApiKey VARCHAR(30), + @ForcePasswordReset BIT = 0, + @UsesKeyConnector BIT = 0, + @FailedLoginCount INT, + @LastFailedLoginDate DATETIME2(7), + @AvatarColor VARCHAR(7) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[User] + SET + [Name] = @Name, + [Email] = @Email, + [EmailVerified] = @EmailVerified, + [MasterPassword] = @MasterPassword, + [MasterPasswordHint] = @MasterPasswordHint, + [Culture] = @Culture, + [SecurityStamp] = @SecurityStamp, + [TwoFactorProviders] = @TwoFactorProviders, + [TwoFactorRecoveryCode] = @TwoFactorRecoveryCode, + [EquivalentDomains] = @EquivalentDomains, + [ExcludedGlobalEquivalentDomains] = @ExcludedGlobalEquivalentDomains, + [AccountRevisionDate] = @AccountRevisionDate, + [Key] = @Key, + [PublicKey] = @PublicKey, + [PrivateKey] = @PrivateKey, + [Premium] = @Premium, + [PremiumExpirationDate] = @PremiumExpirationDate, + [RenewalReminderDate] = @RenewalReminderDate, + [Storage] = @Storage, + [MaxStorageGb] = @MaxStorageGb, + [Gateway] = @Gateway, + [GatewayCustomerId] = @GatewayCustomerId, + [GatewaySubscriptionId] = @GatewaySubscriptionId, + [ReferenceData] = @ReferenceData, + [LicenseKey] = @LicenseKey, + [Kdf] = @Kdf, + [KdfIterations] = @KdfIterations, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate, + [ApiKey] = @ApiKey, + [ForcePasswordReset] = @ForcePasswordReset, + [UsesKeyConnector] = @UsesKeyConnector, + [FailedLoginCount] = @FailedLoginCount, + [LastFailedLoginDate] = @LastFailedLoginDate, + [AvatarColor] = @AvatarColor + WHERE + [Id] = @Id +END diff --git a/src/Sql/dbo_future/Tables/User.sql b/src/Sql/dbo_future/Tables/User.sql new file mode 100644 index 0000000000..e2d681d1e2 --- /dev/null +++ b/src/Sql/dbo_future/Tables/User.sql @@ -0,0 +1,49 @@ +CREATE TABLE [dbo].[User] ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [Name] NVARCHAR (50) NULL, + [Email] NVARCHAR (256) NOT NULL, + [EmailVerified] BIT NOT NULL, + [MasterPassword] NVARCHAR (300) NULL, + [MasterPasswordHint] NVARCHAR (50) NULL, + [Culture] NVARCHAR (10) NOT NULL, + [SecurityStamp] NVARCHAR (50) NOT NULL, + [TwoFactorProviders] NVARCHAR (MAX) NULL, + [TwoFactorRecoveryCode] NVARCHAR (32) NULL, + [EquivalentDomains] NVARCHAR (MAX) NULL, + [ExcludedGlobalEquivalentDomains] NVARCHAR (MAX) NULL, + [AccountRevisionDate] DATETIME2 (7) NOT NULL, + [Key] VARCHAR (MAX) NULL, + [PublicKey] VARCHAR (MAX) NULL, + [PrivateKey] VARCHAR (MAX) NULL, + [Premium] BIT NOT NULL, + [PremiumExpirationDate] DATETIME2 (7) NULL, + [RenewalReminderDate] DATETIME2 (7) NULL, + [Storage] BIGINT NULL, + [MaxStorageGb] SMALLINT NULL, + [Gateway] TINYINT NULL, + [GatewayCustomerId] VARCHAR (50) NULL, + [GatewaySubscriptionId] VARCHAR (50) NULL, + [ReferenceData] NVARCHAR (MAX) NULL, + [LicenseKey] VARCHAR (100) NULL, + [Kdf] TINYINT NOT NULL, + [KdfIterations] INT NOT NULL, + [CreationDate] DATETIME2 (7) NOT NULL, + [RevisionDate] DATETIME2 (7) NOT NULL, + [ApiKey] VARCHAR (30) NOT NULL, + [ForcePasswordReset] BIT NOT NULL, + [UsesKeyConnector] BIT NOT NULL, + [FailedLoginCount] INT CONSTRAINT [D_User_FailedLoginCount] DEFAULT ((0)) NOT NULL, + [LastFailedLoginDate] DATETIME2 (7) NULL, + [AvatarColor] VARCHAR(7) NULL, + CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC) +); + + +GO +CREATE UNIQUE NONCLUSTERED INDEX [IX_User_Email] + ON [dbo].[User]([Email] ASC); + +GO +CREATE NONCLUSTERED INDEX [IX_User_Premium_PremiumExpirationDate_RenewalReminderDate] + ON [dbo].[User]([Premium] ASC, [PremiumExpirationDate] ASC, [RenewalReminderDate] ASC); + diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTest.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTest.cs index 9d42b67e8b..305d249738 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTest.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTest.cs @@ -1,9 +1,11 @@ -using System.Net.Http.Headers; +using System.Net; +using System.Net.Http.Headers; using Bit.Api.IntegrationTest.Factories; -using Bit.Api.IntegrationTest.Helpers; +using Bit.Api.IntegrationTest.SecretsManager.Enums; +using Bit.Api.Models.Response; using Bit.Api.SecretsManager.Models.Request; using Bit.Api.SecretsManager.Models.Response; -using Bit.Core.Entities; +using Bit.Core.Enums; using Bit.Core.SecretsManager.Entities; using Bit.Core.SecretsManager.Repositories; using Bit.Test.Common.Helpers; @@ -13,16 +15,17 @@ namespace Bit.Api.IntegrationTest.SecretsManager.Controllers; public class AccessPoliciesControllerTest : IClassFixture, IAsyncLifetime { + private const string _mockEncryptedString = + "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98sp4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; + private readonly IAccessPolicyRepository _accessPolicyRepository; private readonly HttpClient _client; private readonly ApiApplicationFactory _factory; - - private const string _mockEncryptedString = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98sp4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; - private readonly IProjectRepository _projectRepository; private readonly IServiceAccountRepository _serviceAccountRepository; - private Organization _organization = null!; + private string _email = null!; + private SecretsManagerOrganizationHelper _organizationHelper = null!; public AccessPoliciesControllerTest(ApiApplicationFactory factory) { @@ -35,46 +38,105 @@ public class AccessPoliciesControllerTest : IClassFixture public async Task InitializeAsync() { - var ownerEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com"; - var tokens = await _factory.LoginWithNewAccount(ownerEmail); - var (organization, _) = - await OrganizationTestHelpers.SignUpAsync(_factory, ownerEmail: ownerEmail, billingEmail: ownerEmail); - _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token); - _organization = organization; + _email = $"integration-test{Guid.NewGuid()}@bitwarden.com"; + await _factory.LoginWithNewAccount(_email); + _organizationHelper = new SecretsManagerOrganizationHelper(_factory, _email); } - public Task DisposeAsync() => Task.CompletedTask; - - [Fact] - public async Task CreateProjectAccessPolicies() + public Task DisposeAsync() { - var initialProject = await _projectRepository.CreateAsync(new Project + _client.Dispose(); + return Task.CompletedTask; + } + + private async Task LoginAsync(string email) + { + var tokens = await _factory.LoginAsync(email); + _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task CreateProjectAccessPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + + var project = await _projectRepository.CreateAsync(new Project { - OrganizationId = _organization.Id, - Name = _mockEncryptedString + OrganizationId = org.Id, + Name = _mockEncryptedString, }); - var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { - OrganizationId = _organization.Id, - Name = _mockEncryptedString + OrganizationId = org.Id, + Name = _mockEncryptedString, }); var request = new AccessPoliciesCreateRequest { ServiceAccountAccessPolicyRequests = new List { - new() { GranteeId = initialServiceAccount.Id, Read = true, Write = true } - } + new() { GranteeId = serviceAccount.Id, Read = true, Write = true }, + }, }; - var response = await _client.PostAsJsonAsync($"/projects/{initialProject.Id}/access-policies", request); + var response = await _client.PostAsJsonAsync($"/projects/{project.Id}/access-policies", request); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task CreateProjectAccessPolicies(PermissionType permissionType) + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + var project = await _projectRepository.CreateAsync(new Project + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var request = new AccessPoliciesCreateRequest + { + ServiceAccountAccessPolicyRequests = new List + { + new() { GranteeId = serviceAccount.Id, Read = true, Write = true }, + }, + }; + + var response = await _client.PostAsJsonAsync($"/projects/{project.Id}/access-policies", request); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); Assert.NotNull(result); - Assert.Equal(initialServiceAccount.Id, result!.ServiceAccountAccessPolicies.First().ServiceAccountId); + Assert.Equal(serviceAccount.Id, result!.ServiceAccountAccessPolicies.First().ServiceAccountId); Assert.True(result.ServiceAccountAccessPolicies.First().Read); Assert.True(result.ServiceAccountAccessPolicies.First().Write); AssertHelper.AssertRecent(result.ServiceAccountAccessPolicies.First().RevisionDate); @@ -91,15 +153,103 @@ public class AccessPoliciesControllerTest : IClassFixture } [Fact] - public async Task UpdateAccessPolicy() + public async Task CreateProjectAccessPolicies_NoPermission() { - var initData = await SetupAccessPolicyRequest(); + // Create a new account as a user + var (org, _) = await _organizationHelper.Initialize(true, true); + var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var project = await _projectRepository.CreateAsync(new Project + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var request = new AccessPoliciesCreateRequest + { + ServiceAccountAccessPolicyRequests = new List + { + new() { GranteeId = serviceAccount.Id, Read = true, Write = true }, + }, + }; + + var response = await _client.PostAsJsonAsync($"/projects/{project.Id}/access-policies", request); + + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task UpdateAccessPolicy_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + var initData = await SetupAccessPolicyRequest(org.Id); const bool expectedRead = true; const bool expectedWrite = false; var request = new AccessPolicyUpdateRequest { Read = expectedRead, Write = expectedWrite }; - var response = await _client.PutAsJsonAsync($"/access-policies/{initData.InitialAccessPolicyId}", request); + var response = await _client.PutAsJsonAsync($"/access-policies/{initData.AccessPolicyId}", request); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task UpdateAccessPolicy_NoPermission() + { + // Create a new account as a user + await _organizationHelper.Initialize(true, true); + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var initData = await SetupAccessPolicyRequest(orgUser.OrganizationId); + + const bool expectedRead = true; + const bool expectedWrite = false; + var request = new AccessPolicyUpdateRequest { Read = expectedRead, Write = expectedWrite }; + + var response = await _client.PutAsJsonAsync($"/access-policies/{initData.AccessPolicyId}", request); + + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task UpdateAccessPolicy(PermissionType permissionType) + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + var initData = await SetupAccessPolicyRequest(org.Id); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = initData.ProjectId, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + + const bool expectedRead = true; + const bool expectedWrite = false; + var request = new AccessPolicyUpdateRequest { Read = expectedRead, Write = expectedWrite }; + + var response = await _client.PutAsJsonAsync($"/access-policies/{initData.AccessPolicyId}", request); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); @@ -116,45 +266,142 @@ public class AccessPoliciesControllerTest : IClassFixture AssertHelper.AssertRecent(updatedAccessPolicy.RevisionDate); } + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task DeleteAccessPolicy_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + var initData = await SetupAccessPolicyRequest(org.Id); + + var response = await _client.DeleteAsync($"/access-policies/{initData.AccessPolicyId}"); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } [Fact] - public async Task DeleteAccessPolicy() + public async Task DeleteAccessPolicy_NoPermission() { - var initData = await SetupAccessPolicyRequest(); + // Create a new account as a user + await _organizationHelper.Initialize(true, true); + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); - var response = await _client.DeleteAsync($"/access-policies/{initData.InitialAccessPolicyId}"); + var initData = await SetupAccessPolicyRequest(orgUser.OrganizationId); + + var response = await _client.DeleteAsync($"/access-policies/{initData.AccessPolicyId}"); + + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task DeleteAccessPolicy(PermissionType permissionType) + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + var initData = await SetupAccessPolicyRequest(org.Id); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = initData.ProjectId, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + + var response = await _client.DeleteAsync($"/access-policies/{initData.AccessPolicyId}"); response.EnsureSuccessStatusCode(); - var test = await _accessPolicyRepository.GetByIdAsync(initData.InitialAccessPolicyId); + var test = await _accessPolicyRepository.GetByIdAsync(initData.AccessPolicyId); Assert.Null(test); } [Fact] public async Task GetProjectAccessPolicies_ReturnsEmpty() { - var initialProject = await _projectRepository.CreateAsync(new Project + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + var project = await _projectRepository.CreateAsync(new Project { - OrganizationId = _organization.Id, + OrganizationId = org.Id, Name = _mockEncryptedString, }); - var response = await _client.GetAsync($"/projects/{initialProject.Id}/access-policies"); + var response = await _client.GetAsync($"/projects/{project.Id}/access-policies"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); Assert.NotNull(result); Assert.Empty(result!.UserAccessPolicies); - Assert.Empty(result!.GroupAccessPolicies); - Assert.Empty(result!.ServiceAccountAccessPolicies); + Assert.Empty(result.GroupAccessPolicies); + Assert.Empty(result.ServiceAccountAccessPolicies); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task GetProjectAccessPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + + var initData = await SetupAccessPolicyRequest(org.Id); + + var response = await _client.GetAsync($"/projects/{initData.ProjectId}/access-policies"); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] - public async Task GetProjectAccessPolicies() + public async Task GetProjectAccessPolicies_NoPermission() { - var initData = await SetupAccessPolicyRequest(); + // Create a new account as a user + await _organizationHelper.Initialize(true, true); + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); - var response = await _client.GetAsync($"/projects/{initData.InitialProjectId}/access-policies"); + var initData = await SetupAccessPolicyRequest(orgUser.OrganizationId); + + var response = await _client.GetAsync($"/projects/{initData.ProjectId}/access-policies"); + + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task GetProjectAccessPolicies(PermissionType permissionType) + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + var initData = await SetupAccessPolicyRequest(org.Id); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = initData.ProjectId, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + + var response = await _client.GetAsync($"/projects/{initData.ProjectId}/access-policies"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); @@ -163,44 +410,675 @@ public class AccessPoliciesControllerTest : IClassFixture Assert.Single(result!.ServiceAccountAccessPolicies); } - private async Task SetupAccessPolicyRequest() + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task GetPeoplePotentialGrantees_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) { - var initialProject = await _projectRepository.CreateAsync(new Project + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + + var response = + await _client.GetAsync( + $"/organizations/{org.Id}/access-policies/people/potential-grantees"); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task GetPeoplePotentialGrantees_Success(PermissionType permissionType) + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + if (permissionType == PermissionType.RunAsUserWithPermission) { - OrganizationId = _organization.Id, + var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + } + + var response = + await _client.GetAsync( + $"/organizations/{org.Id}/access-policies/people/potential-grantees"); + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync>(); + + Assert.NotNull(result?.Data); + Assert.NotEmpty(result!.Data); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task GetServiceAccountPotentialGrantees_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + + var response = + await _client.GetAsync( + $"/organizations/{org.Id}/access-policies/service-accounts/potential-grantees"); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetServiceAccountPotentialGrantees_OnlyReturnsServiceAccountsWithWriteAccess() + { + // Create a new account as a user + var (org, _) = await _organizationHelper.Initialize(true, true); + var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, Name = _mockEncryptedString, }); - var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + + var response = + await _client.GetAsync( + $"/organizations/{org.Id}/access-policies/service-accounts/potential-grantees"); + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync>(); + + Assert.NotNull(result?.Data); + Assert.Empty(result!.Data); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task GetServiceAccountsPotentialGrantees_Success(PermissionType permissionType) + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { - OrganizationId = _organization.Id, + OrganizationId = org.Id, Name = _mockEncryptedString, }); - var initialAccessPolicy = await _accessPolicyRepository.CreateManyAsync( + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + await _accessPolicyRepository.CreateManyAsync( + new List + { + new UserServiceAccountAccessPolicy + { + GrantedServiceAccountId = serviceAccount.Id, + OrganizationUserId = orgUser.Id, + Read = true, + Write = true, + }, + }); + } + + var response = + await _client.GetAsync( + $"/organizations/{org.Id}/access-policies/service-accounts/potential-grantees"); + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync>(); + + Assert.NotNull(result?.Data); + Assert.NotEmpty(result!.Data); + Assert.Equal(serviceAccount.Id.ToString(), result.Data.First(x => x.Id == serviceAccount.Id.ToString()).Id); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task GetProjectPotentialGrantees_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + + var response = + await _client.GetAsync( + $"/organizations/{org.Id}/access-policies/projects/potential-grantees"); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetProjectPotentialGrantees_OnlyReturnsProjectsWithWriteAccess() + { + // Create a new account as a user + var (org, _) = await _organizationHelper.Initialize(true, true); + var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + await _projectRepository.CreateAsync(new Project { OrganizationId = org.Id, Name = _mockEncryptedString }); + + + var response = + await _client.GetAsync( + $"/organizations/{org.Id}/access-policies/projects/potential-grantees"); + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync>(); + + Assert.NotNull(result?.Data); + Assert.Empty(result!.Data); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task GetProjectPotentialGrantees_Success(PermissionType permissionType) + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + var project = await _projectRepository.CreateAsync(new Project + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + await _accessPolicyRepository.CreateManyAsync( + new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }); + } + + var response = + await _client.GetAsync( + $"/organizations/{org.Id}/access-policies/projects/potential-grantees"); + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync>(); + + Assert.NotNull(result?.Data); + Assert.NotEmpty(result!.Data); + Assert.Equal(project.Id.ToString(), result.Data.First(x => x.Id == project.Id.ToString()).Id); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task CreateServiceAccountAccessPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, orgUser) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var request = new AccessPoliciesCreateRequest + { + UserAccessPolicyRequests = new List + { + new() { GranteeId = orgUser.Id, Read = true, Write = true }, + }, + }; + + var response = + await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies", request); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task CreateServiceAccountAccessPolicies(PermissionType permissionType) + { + var (org, orgUser) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + var ownerOrgUserId = orgUser.Id; + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var request = new AccessPoliciesCreateRequest + { + UserAccessPolicyRequests = new List + { + new() { GranteeId = orgUser.Id, Read = true, Write = true }, + }, + }; + + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, newOrgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + var accessPolicies = new List + { + new UserServiceAccountAccessPolicy + { + GrantedServiceAccountId = serviceAccount.Id, + OrganizationUserId = newOrgUser.Id, + Read = true, + Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + + request = new AccessPoliciesCreateRequest + { + UserAccessPolicyRequests = new List + { + new() { GranteeId = ownerOrgUserId, Read = true, Write = true }, + }, + }; + } + + + var response = + await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies", request); + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync(); + + Assert.NotNull(result); + Assert.Equal(ownerOrgUserId, + result!.UserAccessPolicies.First(ap => ap.OrganizationUserId == ownerOrgUserId).OrganizationUserId); + Assert.True(result.UserAccessPolicies.First().Read); + Assert.True(result.UserAccessPolicies.First().Write); + AssertHelper.AssertRecent(result.UserAccessPolicies.First().RevisionDate); + AssertHelper.AssertRecent(result.UserAccessPolicies.First().CreationDate); + + var createdAccessPolicy = + await _accessPolicyRepository.GetByIdAsync(result.UserAccessPolicies.First().Id); + Assert.NotNull(createdAccessPolicy); + Assert.Equal(result.UserAccessPolicies.First().Read, createdAccessPolicy!.Read); + Assert.Equal(result.UserAccessPolicies.First().Write, createdAccessPolicy.Write); + Assert.Equal(result.UserAccessPolicies.First().Id, createdAccessPolicy.Id); + AssertHelper.AssertRecent(createdAccessPolicy.CreationDate); + AssertHelper.AssertRecent(createdAccessPolicy.RevisionDate); + } + + [Fact] + public async Task CreateServiceAccountAccessPolicies_NoPermission() + { + // Create a new account as a user + var (org, _) = await _organizationHelper.Initialize(true, true); + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var request = new AccessPoliciesCreateRequest + { + UserAccessPolicyRequests = new List + { + new() { GranteeId = orgUser.Id, Read = true, Write = true }, + }, + }; + + var response = + await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies", request); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task GetServiceAccountAccessPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + var initData = await SetupAccessPolicyRequest(org.Id); + + var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/access-policies"); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetServiceAccountAccessPolicies_ReturnsEmpty() + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/access-policies"); + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync(); + + Assert.NotNull(result); + Assert.Empty(result!.UserAccessPolicies); + Assert.Empty(result.GroupAccessPolicies); + } + + [Fact] + public async Task GetServiceAccountAccessPolicies_NoPermission() + { + // Create a new account as a user + await _organizationHelper.Initialize(true, true); + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var initData = await SetupAccessPolicyRequest(orgUser.OrganizationId); + + var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/access-policies"); + + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task GetServiceAccountAccessPolicies(PermissionType permissionType) + { + var (org, owerOrgUser) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + var initData = await SetupAccessPolicyRequest(org.Id); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + var accessPolicies = new List + { + new UserServiceAccountAccessPolicy + { + GrantedServiceAccountId = initData.ServiceAccountId, + OrganizationUserId = orgUser.Id, + Read = true, + Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + + var policies = new List + { + new UserServiceAccountAccessPolicy + { + GrantedServiceAccountId = initData.ServiceAccountId, + OrganizationUserId = owerOrgUser.Id, + Read = true, + Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(policies); + + var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/access-policies"); + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync(); + + Assert.NotNull(result?.UserAccessPolicies); + Assert.NotEmpty(result!.UserAccessPolicies); + Assert.Equal(owerOrgUser.Id, + result.UserAccessPolicies.First(x => x.OrganizationUserId == owerOrgUser.Id).OrganizationUserId); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task CreateServiceAccountGrantedPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var request = new List { new() { GrantedId = new Guid() } }; + + var response = + await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/granted-policies", request); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task CreateServiceAccountGrantedPolicies(PermissionType permissionType) + { + var (org, orgUser) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + var ownerOrgUserId = orgUser.Id; + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var project = await _projectRepository.CreateAsync(new Project + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var request = + new List { new() { GrantedId = project.Id, Read = true, Write = true } }; + + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, newOrgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = project.Id, OrganizationUserId = newOrgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + + + var response = + await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/granted-policies", request); + response.EnsureSuccessStatusCode(); + + var result = await response.Content + .ReadFromJsonAsync>(); + + Assert.NotNull(result); + Assert.NotEmpty(result!.Data); + Assert.Equal(project.Id, result.Data.First().GrantedProjectId); + + var createdAccessPolicy = + await _accessPolicyRepository.GetByIdAsync(result.Data.First().Id); + Assert.NotNull(createdAccessPolicy); + Assert.Equal(result.Data.First().Read, createdAccessPolicy!.Read); + Assert.Equal(result.Data.First().Write, createdAccessPolicy.Write); + Assert.Equal(result.Data.First().Id, createdAccessPolicy.Id); + AssertHelper.AssertRecent(createdAccessPolicy.CreationDate); + AssertHelper.AssertRecent(createdAccessPolicy.RevisionDate); + } + + [Fact] + public async Task CreateServiceAccountGrantedPolicies_NoPermission() + { + // Create a new account as a user + var (org, _) = await _organizationHelper.Initialize(true, true); + var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var project = await _projectRepository.CreateAsync(new Project + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var request = + new List { new() { GrantedId = project.Id, Read = true, Write = true } }; + + var response = + await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/granted-policies", request); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task GetServiceAccountGrantedPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + var initData = await SetupAccessPolicyRequest(org.Id); + + var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/granted-policies"); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetServiceAccountGrantedPolicies_ReturnsEmpty() + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/granted-policies"); + response.EnsureSuccessStatusCode(); + + var result = await response.Content + .ReadFromJsonAsync>(); + + Assert.NotNull(result); + Assert.Empty(result!.Data); + } + + [Fact] + public async Task GetServiceAccountGrantedPolicies_NoPermission_ReturnsEmpty() + { + // Create a new account as a user + await _organizationHelper.Initialize(true, true); + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var initData = await SetupAccessPolicyRequest(orgUser.OrganizationId); + + var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/granted-policies"); + + var result = await response.Content + .ReadFromJsonAsync>(); + + Assert.NotNull(result); + Assert.Empty(result!.Data); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task GetServiceAccountGrantedPolicies(PermissionType permissionType) + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + var initData = await SetupAccessPolicyRequest(org.Id); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = initData.ProjectId, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + + var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/granted-policies"); + response.EnsureSuccessStatusCode(); + + var result = await response.Content + .ReadFromJsonAsync>(); + + Assert.NotNull(result?.Data); + Assert.NotEmpty(result!.Data); + Assert.Equal(initData.ServiceAccountId, result.Data.First().ServiceAccountId); + Assert.NotNull(result.Data.First().ServiceAccountName); + Assert.NotNull(result.Data.First().GrantedProjectName); + } + + private async Task SetupAccessPolicyRequest(Guid organizationId) + { + var project = await _projectRepository.CreateAsync(new Project + { + OrganizationId = organizationId, + Name = _mockEncryptedString, + }); + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = organizationId, + Name = _mockEncryptedString, + }); + + var accessPolicy = await _accessPolicyRepository.CreateManyAsync( new List { new ServiceAccountProjectAccessPolicy { - Read = true, - Write = true, - ServiceAccountId = initialServiceAccount.Id, - GrantedProjectId = initialProject.Id, - } + Read = true, Write = true, ServiceAccountId = serviceAccount.Id, GrantedProjectId = project.Id, + }, }); return new RequestSetupData { - InitialProjectId = initialProject.Id, - InitialServiceAccountId = initialServiceAccount.Id, - InitialAccessPolicyId = initialAccessPolicy.First().Id, + ProjectId = project.Id, + ServiceAccountId = serviceAccount.Id, + AccessPolicyId = accessPolicy.First().Id, }; } private class RequestSetupData { - public Guid InitialProjectId { get; set; } - public Guid InitialAccessPolicyId { get; set; } - public Guid InitialServiceAccountId { get; set; } + public Guid ProjectId { get; set; } + public Guid AccessPolicyId { get; set; } + public Guid ServiceAccountId { get; set; } } } diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/ProjectsControllerTest.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/ProjectsControllerTest.cs index 1b6db5c9c9..fa9dccc939 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/ProjectsControllerTest.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/ProjectsControllerTest.cs @@ -1,9 +1,11 @@ using System.Net; using System.Net.Http.Headers; using Bit.Api.IntegrationTest.Factories; +using Bit.Api.IntegrationTest.SecretsManager.Enums; using Bit.Api.Models.Response; using Bit.Api.SecretsManager.Models.Request; using Bit.Api.SecretsManager.Models.Response; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.SecretsManager.Entities; using Bit.Core.SecretsManager.Repositories; @@ -20,6 +22,7 @@ public class ProjectsControllerTest : IClassFixture, IAsy private readonly HttpClient _client; private readonly ApiApplicationFactory _factory; private readonly IProjectRepository _projectRepository; + private readonly IAccessPolicyRepository _accessPolicyRepository; private string _email = null!; private SecretsManagerOrganizationHelper _organizationHelper = null!; @@ -29,6 +32,7 @@ public class ProjectsControllerTest : IClassFixture, IAsy _factory = factory; _client = _factory.CreateClient(); _projectRepository = _factory.GetService(); + _accessPolicyRepository = _factory.GetService(); } public async Task InitializeAsync() @@ -64,21 +68,28 @@ public class ProjectsControllerTest : IClassFixture, IAsy } [Fact] - public async Task ListByOrganization_Success() + public async Task ListByOrganization_UserWithoutPermission_EmptyList() { var (org, _) = await _organizationHelper.Initialize(true, true); - await LoginAsync(_email); + var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); - var projectIds = new List(); - for (var i = 0; i < 3; i++) - { - var project = await _projectRepository.CreateAsync(new Project - { - OrganizationId = org.Id, - Name = _mockEncryptedString - }); - projectIds.Add(project.Id); - } + await CreateProjectsAsync(org.Id); + + var response = await _client.GetAsync($"/organizations/{org.Id}/projects"); + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync>(); + Assert.NotNull(result); + Assert.Empty(result!.Data); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task ListByOrganization_Success(PermissionType permissionType) + { + var (projectIds, org) = await SetupProjectsWithAccessAsync(permissionType); var response = await _client.GetAsync($"/organizations/{org.Id}/projects"); response.EnsureSuccessStatusCode(); @@ -104,11 +115,22 @@ public class ProjectsControllerTest : IClassFixture, IAsy Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } - [Fact] - public async Task Create_Success() + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task Create_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, adminOrgUser) = await _organizationHelper.Initialize(true, true); await LoginAsync(_email); + var orgUserId = adminOrgUser.Id; + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + orgUserId = orgUser.Id; + } + var request = new ProjectCreateRequestModel { Name = _mockEncryptedString }; var response = await _client.PostAsJsonAsync($"/organizations/{org.Id}/projects", request); @@ -126,6 +148,17 @@ public class ProjectsControllerTest : IClassFixture, IAsy AssertHelper.AssertRecent(createdProject.RevisionDate); AssertHelper.AssertRecent(createdProject.CreationDate); Assert.Null(createdProject.DeletedDate); + + // Check permissions have been bootstrapped. + var accessPolicies = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(createdProject.Id); + Assert.NotNull(accessPolicies); + var ap = (UserProjectAccessPolicy)accessPolicies.First(); + Assert.Equal(createdProject.Id, ap.GrantedProjectId); + Assert.Equal(orgUserId, ap.OrganizationUserId); + Assert.True(ap.Read); + Assert.True(ap.Write); + AssertHelper.AssertRecent(ap.CreationDate); + AssertHelper.AssertRecent(ap.RevisionDate); } [Theory] @@ -140,34 +173,28 @@ public class ProjectsControllerTest : IClassFixture, IAsy var initialProject = await _projectRepository.CreateAsync(new Project { OrganizationId = org.Id, - Name = _mockEncryptedString + Name = _mockEncryptedString, }); - var mockEncryptedString2 = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; + var mockEncryptedString2 = + "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; var request = new ProjectCreateRequestModel { Name = mockEncryptedString2 }; var response = await _client.PutAsJsonAsync($"/projects/{initialProject.Id}", request); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } - [Fact] - public async Task Update_Success() + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task Update_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); - await LoginAsync(_email); + var initialProject = await SetupProjectWithAccessAsync(permissionType); - var initialProject = await _projectRepository.CreateAsync(new Project - { - OrganizationId = org.Id, - Name = _mockEncryptedString - }); + var mockEncryptedString2 = + "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; - var mockEncryptedString2 = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; - - var request = new ProjectUpdateRequestModel - { - Name = mockEncryptedString2 - }; + var request = new ProjectUpdateRequestModel { Name = mockEncryptedString2 }; var response = await _client.PutAsJsonAsync($"/projects/{initialProject.Id}", request); response.EnsureSuccessStatusCode(); @@ -180,21 +207,21 @@ public class ProjectsControllerTest : IClassFixture, IAsy Assert.NotNull(result); Assert.Equal(request.Name, updatedProject.Name); AssertHelper.AssertRecent(updatedProject.RevisionDate); - AssertHelper.AssertRecent(updatedProject.CreationDate); Assert.Null(updatedProject.DeletedDate); Assert.NotEqual(initialProject.Name, updatedProject.Name); Assert.NotEqual(initialProject.RevisionDate, updatedProject.RevisionDate); } [Fact] - public async Task Update_NonExistingProject_Throws_NotFound() + public async Task Update_NonExistingProject_NotFound() { await _organizationHelper.Initialize(true, true); await LoginAsync(_email); var request = new ProjectUpdateRequestModel { - Name = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg=", + Name = + "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg=", }; var response = await _client.PutAsJsonAsync("/projects/c53de509-4581-402c-8cbd-f26d2c516fba", request); @@ -203,7 +230,7 @@ public class ProjectsControllerTest : IClassFixture, IAsy } [Fact] - public async Task Update_MissingAccessPolicy_Throws_NotFound() + public async Task Update_MissingAccessPolicy_NotFound() { var (org, _) = await _organizationHelper.Initialize(true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); @@ -212,12 +239,13 @@ public class ProjectsControllerTest : IClassFixture, IAsy var project = await _projectRepository.CreateAsync(new Project { OrganizationId = org.Id, - Name = _mockEncryptedString + Name = _mockEncryptedString, }); var request = new ProjectUpdateRequestModel { - Name = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg=", + Name = + "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg=", }; var response = await _client.PutAsJsonAsync($"/projects/{project.Id}", request); @@ -237,10 +265,11 @@ public class ProjectsControllerTest : IClassFixture, IAsy var project = await _projectRepository.CreateAsync(new Project { OrganizationId = org.Id, - Name = _mockEncryptedString + Name = _mockEncryptedString, }); - var mockEncryptedString2 = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; + var mockEncryptedString2 = + "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; var request = new ProjectCreateRequestModel { Name = mockEncryptedString2 }; var response = await _client.PutAsJsonAsync($"/projects/{project.Id}", request); @@ -248,27 +277,7 @@ public class ProjectsControllerTest : IClassFixture, IAsy } [Fact] - public async Task Get_Success() - { - var (org, _) = await _organizationHelper.Initialize(true, true); - await LoginAsync(_email); - - var createdProject = await _projectRepository.CreateAsync(new Project - { - OrganizationId = org.Id, - Name = _mockEncryptedString - }); - - var response = await _client.GetAsync($"/projects/{createdProject.Id}"); - response.EnsureSuccessStatusCode(); - var result = await response.Content.ReadFromJsonAsync(); - Assert.Equal(createdProject.Name, result!.Name); - Assert.Equal(createdProject.RevisionDate, result.RevisionDate); - Assert.Equal(createdProject.CreationDate, result.CreationDate); - } - - [Fact] - public async Task Get_MissingAccessPolicy_Throws_NotFound() + public async Task Get_MissingAccessPolicy_NotFound() { var (org, _) = await _organizationHelper.Initialize(true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); @@ -277,13 +286,28 @@ public class ProjectsControllerTest : IClassFixture, IAsy var createdProject = await _projectRepository.CreateAsync(new Project { OrganizationId = org.Id, - Name = _mockEncryptedString + Name = _mockEncryptedString, }); var response = await _client.GetAsync($"/projects/{createdProject.Id}"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task Get_Success(PermissionType permissionType) + { + var project = await SetupProjectWithAccessAsync(permissionType); + + var response = await _client.GetAsync($"/projects/{project.Id}"); + response.EnsureSuccessStatusCode(); + var result = await response.Content.ReadFromJsonAsync(); + Assert.Equal(project.Name, result!.Name); + Assert.Equal(project.RevisionDate, result.RevisionDate); + Assert.Equal(project.CreationDate, result.CreationDate); + } + [Theory] [InlineData(false, false)] [InlineData(true, false)] @@ -293,53 +317,124 @@ public class ProjectsControllerTest : IClassFixture, IAsy var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); await LoginAsync(_email); - var projectIds = new List(); - for (var i = 0; i < 3; i++) - { - var project = await _projectRepository.CreateAsync(new Project - { - OrganizationId = org.Id, - Name = _mockEncryptedString, - }); - projectIds.Add(project.Id); - } + var projectIds = await CreateProjectsAsync(org.Id); var response = await _client.PostAsync("/projects/delete", JsonContent.Create(projectIds)); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] - public async Task Delete_Success() + public async Task Delete_MissingAccessPolicy_AccessDenied() { var (org, _) = await _organizationHelper.Initialize(true, true); - await LoginAsync(_email); + var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); - var projectIds = new List(); - for (var i = 0; i < 3; i++) - { - var project = await _projectRepository.CreateAsync(new Project - { - OrganizationId = org.Id, - Name = _mockEncryptedString, - }); - projectIds.Add(project.Id); - } + var projectIds = await CreateProjectsAsync(org.Id); + + var response = await _client.PostAsync("/projects/delete", JsonContent.Create(projectIds)); + + var results = await response.Content.ReadFromJsonAsync>(); + Assert.NotNull(results); + Assert.Equal(projectIds.OrderBy(x => x), + results!.Data.Select(x => x.Id).OrderBy(x => x)); + Assert.All(results.Data, item => Assert.Equal("access denied", item.Error)); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task Delete_Success(PermissionType permissionType) + { + var (projectIds, _) = await SetupProjectsWithAccessAsync(permissionType); var response = await _client.PostAsync("/projects/delete", JsonContent.Create(projectIds)); response.EnsureSuccessStatusCode(); var results = await response.Content.ReadFromJsonAsync>(); Assert.NotNull(results); - - var index = 0; - foreach (var result in results!.Data) - { - Assert.Equal(projectIds[index], result.Id); - Assert.Null(result.Error); - index++; - } + Assert.Equal(projectIds.OrderBy(x => x), + results!.Data.Select(x => x.Id).OrderBy(x => x)); + Assert.DoesNotContain(results.Data, x => x.Error != null); var projects = await _projectRepository.GetManyByIds(projectIds); Assert.Empty(projects); } + + private async Task> CreateProjectsAsync(Guid orgId, int numberToCreate = 3) + { + var projectIds = new List(); + for (var i = 0; i < numberToCreate; i++) + { + var project = await _projectRepository.CreateAsync(new Project + { + OrganizationId = orgId, + Name = _mockEncryptedString, + }); + projectIds.Add(project.Id); + } + + return projectIds; + } + + private async Task<(List, Organization)> SetupProjectsWithAccessAsync(PermissionType permissionType, + int projectsToCreate = 3) + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + var projectIds = await CreateProjectsAsync(org.Id, projectsToCreate); + + if (permissionType == PermissionType.RunAsAdmin) + { + return (projectIds, org); + } + + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var accessPolicies = projectIds.Select(projectId => new UserProjectAccessPolicy + { + GrantedProjectId = projectId, + OrganizationUserId = orgUser.Id, + Read = true, + Write = true, + }) + .Cast() + .ToList(); + + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + + return (projectIds, org); + } + + private async Task SetupProjectWithAccessAsync(PermissionType permissionType) + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + var initialProject = await _projectRepository.CreateAsync(new Project + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + if (permissionType == PermissionType.RunAsAdmin) + { + return initialProject; + } + + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = initialProject.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + + return initialProject; + } } diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTest.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTest.cs index 81ece16d3b..83036a41a6 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTest.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTest.cs @@ -1,9 +1,11 @@ using System.Net; using System.Net.Http.Headers; using Bit.Api.IntegrationTest.Factories; +using Bit.Api.IntegrationTest.SecretsManager.Enums; using Bit.Api.Models.Response; using Bit.Api.SecretsManager.Models.Request; using Bit.Api.SecretsManager.Models.Response; +using Bit.Core.Enums; using Bit.Core.SecretsManager.Entities; using Bit.Core.SecretsManager.Repositories; using Bit.Test.Common.Helpers; @@ -20,6 +22,7 @@ public class SecretsControllerTest : IClassFixture, IAsyn private readonly ApiApplicationFactory _factory; private readonly ISecretRepository _secretRepository; private readonly IProjectRepository _projectRepository; + private readonly IAccessPolicyRepository _accessPolicyRepository; private string _email = null!; private SecretsManagerOrganizationHelper _organizationHelper = null!; @@ -30,6 +33,7 @@ public class SecretsControllerTest : IClassFixture, IAsyn _client = _factory.CreateClient(); _secretRepository = _factory.GetService(); _projectRepository = _factory.GetService(); + _accessPolicyRepository = _factory.GetService(); } public async Task InitializeAsync() @@ -64,12 +68,36 @@ public class SecretsControllerTest : IClassFixture, IAsyn Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } - [Fact] - public async Task ListByOrganization_Owner_Success() + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task ListByOrganization_Success(PermissionType permissionType) { - var (org, _) = await _organizationHelper.Initialize(true, true); + var (org, orgUserOwner) = await _organizationHelper.Initialize(true, true); await LoginAsync(_email); + var project = await _projectRepository.CreateAsync(new Project + { + Id = new Guid(), + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + var secretIds = new List(); for (var i = 0; i < 3; i++) { @@ -78,7 +106,9 @@ public class SecretsControllerTest : IClassFixture, IAsyn OrganizationId = org.Id, Key = _mockEncryptedString, Value = _mockEncryptedString, - Note = _mockEncryptedString + Note = _mockEncryptedString, + Projects = new List { project } + }); secretIds.Add(secret.Id); } @@ -113,7 +143,7 @@ public class SecretsControllerTest : IClassFixture, IAsyn } [Fact] - public async Task Create_Owner_Success() + public async Task CreateWithoutProject_RunAsAdmin_Success() { var (org, _) = await _organizationHelper.Initialize(true, true); await LoginAsync(_email); @@ -147,11 +177,33 @@ public class SecretsControllerTest : IClassFixture, IAsyn } [Fact] - public async Task CreateWithProject_Owner_Success() + public async Task CreateWithoutProject_RunAsUser_NotFound() { var (org, _) = await _organizationHelper.Initialize(true, true); + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var request = new SecretCreateRequestModel + { + Key = _mockEncryptedString, + Value = _mockEncryptedString, + Note = _mockEncryptedString + }; + + var response = await _client.PostAsJsonAsync($"/organizations/{org.Id}/secrets", request); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task CreateWithProject_Success(PermissionType permissionType) + { + var (org, orgAdminUser) = await _organizationHelper.Initialize(true, true); await LoginAsync(_email); + AccessClientType accessType = AccessClientType.NoAccessCheck; + var project = await _projectRepository.CreateAsync(new Project() { Id = new Guid(), @@ -159,6 +211,25 @@ public class SecretsControllerTest : IClassFixture, IAsyn Name = _mockEncryptedString }); + var orgUserId = (Guid)orgAdminUser.UserId; + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + accessType = AccessClientType.User; + + var accessPolicies = new List + { + new Core.SecretsManager.Entities.UserProjectAccessPolicy + { + GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id , Read = true, Write = true, + }, + }; + orgUserId = (Guid)orgUser.UserId; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + var secretRequest = new SecretCreateRequestModel() { Key = _mockEncryptedString, @@ -170,7 +241,7 @@ public class SecretsControllerTest : IClassFixture, IAsyn secretResponse.EnsureSuccessStatusCode(); var secretResult = await secretResponse.Content.ReadFromJsonAsync(); - var secret = (await _secretRepository.GetManyByProjectIdAsync(project.Id)).First(); + var secret = (await _secretRepository.GetManyByProjectIdAsync(project.Id, orgUserId, accessType)).First(); Assert.NotNull(secretResult); Assert.Equal(secret.Id.ToString(), secretResult!.Id); @@ -203,18 +274,48 @@ public class SecretsControllerTest : IClassFixture, IAsyn Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } - [Fact] - public async Task Get_Owner_Success() + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task Get_Success(PermissionType permissionType) { var (org, _) = await _organizationHelper.Initialize(true, true); await LoginAsync(_email); + var project = await _projectRepository.CreateAsync(new Project() + { + Id = new Guid(), + OrganizationId = org.Id, + Name = _mockEncryptedString + }); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + else + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.Admin, true); + await LoginAsync(email); + } + var secret = await _secretRepository.CreateAsync(new Secret { OrganizationId = org.Id, Key = _mockEncryptedString, Value = _mockEncryptedString, - Note = _mockEncryptedString + Note = _mockEncryptedString, + Projects = new List { project } }); var response = await _client.GetAsync($"/secrets/{secret.Id}"); @@ -255,25 +356,51 @@ public class SecretsControllerTest : IClassFixture, IAsyn Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } - [Fact] - public async Task Update_Owner_Success() + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task Update_Success(PermissionType permissionType) { var (org, _) = await _organizationHelper.Initialize(true, true); await LoginAsync(_email); + var project = await _projectRepository.CreateAsync(new Project() + { + Id = new Guid(), + OrganizationId = org.Id, + Name = _mockEncryptedString + }); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + var secret = await _secretRepository.CreateAsync(new Secret { OrganizationId = org.Id, Key = _mockEncryptedString, Value = _mockEncryptedString, - Note = _mockEncryptedString + Note = _mockEncryptedString, + Projects = permissionType == PermissionType.RunAsUserWithPermission ? new List() { project } : null }); var request = new SecretUpdateRequestModel() { Key = _mockEncryptedString, Value = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg=", - Note = _mockEncryptedString + Note = _mockEncryptedString, + ProjectIds = permissionType == PermissionType.RunAsUserWithPermission ? new Guid[] { project.Id } : null }; var response = await _client.PutAsJsonAsync($"/secrets/{secret.Id}", request); @@ -316,16 +443,41 @@ public class SecretsControllerTest : IClassFixture, IAsyn }); var secretIds = new[] { secret.Id }; - var response = await _client.PostAsJsonAsync("/secrets/delete", secretIds); + var response = await _client.PostAsJsonAsync($"/secrets/{org.Id}/delete", secretIds); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } - [Fact] - public async Task Delete_Owner_Success() + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task Delete_Success(PermissionType permissionType) { var (org, _) = await _organizationHelper.Initialize(true, true); await LoginAsync(_email); + var project = await _projectRepository.CreateAsync(new Project() + { + Id = new Guid(), + OrganizationId = org.Id, + Name = _mockEncryptedString + }); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + + var secretIds = new List(); for (var i = 0; i < 3; i++) { @@ -334,12 +486,13 @@ public class SecretsControllerTest : IClassFixture, IAsyn OrganizationId = org.Id, Key = _mockEncryptedString, Value = _mockEncryptedString, - Note = _mockEncryptedString + Note = _mockEncryptedString, + Projects = new List() { project } }); secretIds.Add(secret.Id); } - var response = await _client.PostAsJsonAsync("/secrets/delete", secretIds); + var response = await _client.PostAsJsonAsync($"/secrets/delete", secretIds); response.EnsureSuccessStatusCode(); var results = await response.Content.ReadFromJsonAsync>(); diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsTrashControllerTest.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsTrashControllerTest.cs new file mode 100644 index 0000000000..97dd827a8c --- /dev/null +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsTrashControllerTest.cs @@ -0,0 +1,227 @@ +using System.Net; +using System.Net.Http.Headers; +using Bit.Api.IntegrationTest.Factories; +using Bit.Api.SecretsManager.Models.Response; +using Bit.Core.Enums; +using Bit.Core.SecretsManager.Repositories; +using Xunit; +using Secret = Bit.Core.SecretsManager.Entities.Secret; + +namespace Bit.Api.IntegrationTest.SecretsManager.Controllers; + +public class SecretsTrashControllerTest : IClassFixture, IAsyncLifetime +{ + private readonly string _mockEncryptedString = + "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98sp4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; + + private readonly HttpClient _client; + private readonly ApiApplicationFactory _factory; + private readonly ISecretRepository _secretRepository; + + private string _email = null!; + private SecretsManagerOrganizationHelper _organizationHelper = null!; + + public SecretsTrashControllerTest(ApiApplicationFactory factory) + { + _factory = factory; + _client = _factory.CreateClient(); + _secretRepository = _factory.GetService(); + } + + public async Task InitializeAsync() + { + _email = $"integration-test{Guid.NewGuid()}@bitwarden.com"; + await _factory.LoginWithNewAccount(_email); + _organizationHelper = new SecretsManagerOrganizationHelper(_factory, _email); + } + + public Task DisposeAsync() + { + _client.Dispose(); + return Task.CompletedTask; + } + + private async Task LoginAsync(string email) + { + var tokens = await _factory.LoginAsync(email); + _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task ListByOrganization_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + + var response = await _client.GetAsync($"/secrets/{org.Id}/trash"); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task ListByOrganization_NotAdmin_Unauthorized() + { + var (org, _) = await _organizationHelper.Initialize(true, true); + var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var response = await _client.GetAsync($"/secrets/{org.Id}/trash"); + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task ListByOrganization_Success() + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + await _secretRepository.CreateAsync(new Secret + { + OrganizationId = org.Id, + Key = _mockEncryptedString, + Value = _mockEncryptedString, + DeletedDate = DateTime.Now, + }); + + await _secretRepository.CreateAsync(new Secret + { + OrganizationId = org.Id, + Key = _mockEncryptedString, + Value = _mockEncryptedString, + }); + + var response = await _client.GetAsync($"/secrets/{org.Id}/trash"); + + response.EnsureSuccessStatusCode(); + var result = await response.Content.ReadFromJsonAsync(); + Assert.Single(result!.Secrets); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task Empty_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + + var ids = new List { Guid.NewGuid() }; + var response = await _client.PostAsJsonAsync($"/secrets/{org.Id}/trash/empty", ids); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task Empty_NotAdmin_Unauthorized() + { + var (org, _) = await _organizationHelper.Initialize(true, true); + var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var ids = new List { Guid.NewGuid() }; + var response = await _client.PostAsJsonAsync($"/secrets/{org.Id}/trash/empty", ids); + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task Empty_Invalid_NotFound() + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + var secret = await _secretRepository.CreateAsync(new Secret + { + OrganizationId = org.Id, + Key = _mockEncryptedString, + Value = _mockEncryptedString + }); + + var ids = new List { secret.Id }; + var response = await _client.PostAsJsonAsync($"/secrets/{org.Id}/trash/empty", ids); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task Empty_Success() + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + var secret = await _secretRepository.CreateAsync(new Secret + { + OrganizationId = org.Id, + Key = _mockEncryptedString, + Value = _mockEncryptedString, + DeletedDate = DateTime.Now, + }); + + var ids = new List { secret.Id }; + var response = await _client.PostAsJsonAsync($"/secrets/{org.Id}/trash/empty", ids); + response.EnsureSuccessStatusCode(); + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + public async Task Restore_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets) + { + var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets); + await LoginAsync(_email); + + var ids = new List { Guid.NewGuid() }; + var response = await _client.PostAsJsonAsync($"/secrets/{org.Id}/trash/restore", ids); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task Restore_NotAdmin_Unauthorized() + { + var (org, _) = await _organizationHelper.Initialize(true, true); + var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + var ids = new List { Guid.NewGuid() }; + var response = await _client.PostAsJsonAsync($"/secrets/{org.Id}/trash/restore", ids); + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task Restore_Invalid_NotFound() + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + var secret = await _secretRepository.CreateAsync(new Secret + { + OrganizationId = org.Id, + Key = _mockEncryptedString, + Value = _mockEncryptedString + }); + + var ids = new List { secret.Id }; + var response = await _client.PostAsJsonAsync($"/secrets/{org.Id}/trash/restore", ids); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task Restore_Success() + { + var (org, _) = await _organizationHelper.Initialize(true, true); + await LoginAsync(_email); + + var secret = await _secretRepository.CreateAsync(new Secret + { + OrganizationId = org.Id, + Key = _mockEncryptedString, + Value = _mockEncryptedString, + DeletedDate = DateTime.Now, + }); + + var ids = new List { secret.Id }; + var response = await _client.PostAsJsonAsync($"/secrets/{org.Id}/trash/restore", ids); + response.EnsureSuccessStatusCode(); + } +} diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs index 97373422d8..32cae25037 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs @@ -1,6 +1,7 @@ using System.Net; using System.Net.Http.Headers; using Bit.Api.IntegrationTest.Factories; +using Bit.Api.IntegrationTest.SecretsManager.Enums; using Bit.Api.Models.Response; using Bit.Api.SecretsManager.Models.Request; using Bit.Api.SecretsManager.Models.Response; @@ -21,9 +22,11 @@ public class ServiceAccountsControllerTest : IClassFixture(); _accessPolicyRepository = _factory.GetService(); + _apiKeyRepository = _factory.GetService(); } public async Task InitializeAsync() @@ -426,6 +430,130 @@ public class ServiceAccountsControllerTest : IClassFixture { + new UserServiceAccountAccessPolicy + { + GrantedServiceAccountId = serviceAccount.Id, + OrganizationUserId = orgUser.Id, + Write = false, + Read = true, + }, + }); + } + + var accessToken = await _apiKeyRepository.CreateAsync(new ApiKey + { + ServiceAccountId = org.Id, + Name = _mockEncryptedString, + ExpireAt = DateTime.UtcNow.AddDays(30), + }); + + var request = new RevokeAccessTokensRequest + { + Ids = new[] { accessToken.Id }, + }; + + var response = await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-tokens/revoke", request); + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task RevokeAccessToken_Success(PermissionType permissionType) + { + var (org, _) = await _organizationHelper.Initialize(true, true); + + var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount + { + OrganizationId = org.Id, + Name = _mockEncryptedString, + }); + + if (permissionType == PermissionType.RunAsAdmin) + { + await LoginAsync(_email); + } + else + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await LoginAsync(email); + + await _accessPolicyRepository.CreateManyAsync(new List { + new UserServiceAccountAccessPolicy + { + GrantedServiceAccountId = serviceAccount.Id, + OrganizationUserId = orgUser.Id, + Write = true, + Read = true, + }, + }); + } + + var accessToken = await _apiKeyRepository.CreateAsync(new ApiKey + { + ServiceAccountId = org.Id, + Name = _mockEncryptedString, + ExpireAt = DateTime.UtcNow.AddDays(30), + }); + + var request = new RevokeAccessTokensRequest + { + Ids = new[] { accessToken.Id }, + }; + + var response = await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-tokens/revoke", request); + response.EnsureSuccessStatusCode(); + } + private async Task CreateUserPolicyAsync(Guid userId, Guid serviceAccountId, bool read, bool write) { var policy = new UserServiceAccountAccessPolicy diff --git a/test/Api.IntegrationTest/SecretsManager/Enums/PermissionType.cs b/test/Api.IntegrationTest/SecretsManager/Enums/PermissionType.cs new file mode 100644 index 0000000000..7f1c4d7b99 --- /dev/null +++ b/test/Api.IntegrationTest/SecretsManager/Enums/PermissionType.cs @@ -0,0 +1,7 @@ +namespace Bit.Api.IntegrationTest.SecretsManager.Enums; + +public enum PermissionType +{ + RunAsAdmin, + RunAsUserWithPermission, +} diff --git a/test/Api.IntegrationTest/packages.lock.json b/test/Api.IntegrationTest/packages.lock.json index 2e0ce267e0..97e7c7b97d 100644 --- a/test/Api.IntegrationTest/packages.lock.json +++ b/test/Api.IntegrationTest/packages.lock.json @@ -122,8 +122,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -170,28 +170,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -235,6 +235,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -3189,10 +3197,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/test/Api.Test/Controllers/GroupsControllerTests.cs b/test/Api.Test/Controllers/GroupsControllerTests.cs index 5d700b4e36..b7f3b0cd36 100644 --- a/test/Api.Test/Controllers/GroupsControllerTests.cs +++ b/test/Api.Test/Controllers/GroupsControllerTests.cs @@ -29,7 +29,7 @@ public class GroupsControllerTests await sutProvider.GetDependency().Received(1).CreateGroupAsync( Arg.Is(g => g.OrganizationId == organization.Id && g.Name == groupRequestModel.Name && - g.AccessAll == groupRequestModel.AccessAll && g.ExternalId == groupRequestModel.ExternalId), + g.AccessAll == groupRequestModel.AccessAll), organization, Arg.Any>(), Arg.Any>()); @@ -38,7 +38,6 @@ public class GroupsControllerTests Assert.Equal(groupRequestModel.Name, response.Name); Assert.Equal(organization.Id.ToString(), response.OrganizationId); Assert.Equal(groupRequestModel.AccessAll, response.AccessAll); - Assert.Equal(groupRequestModel.ExternalId, response.ExternalId); } [Theory] @@ -57,7 +56,7 @@ public class GroupsControllerTests await sutProvider.GetDependency().Received(1).UpdateGroupAsync( Arg.Is(g => g.OrganizationId == organization.Id && g.Name == groupRequestModel.Name && - g.AccessAll == groupRequestModel.AccessAll && g.ExternalId == groupRequestModel.ExternalId), + g.AccessAll == groupRequestModel.AccessAll), Arg.Is(o => o.Id == organization.Id), Arg.Any>(), Arg.Any>()); @@ -66,6 +65,5 @@ public class GroupsControllerTests Assert.Equal(groupRequestModel.Name, response.Name); Assert.Equal(organization.Id.ToString(), response.OrganizationId); Assert.Equal(groupRequestModel.AccessAll, response.AccessAll); - Assert.Equal(groupRequestModel.ExternalId, response.ExternalId); } } diff --git a/test/Api.Test/Controllers/OrganizationDomainControllerTests.cs b/test/Api.Test/Controllers/OrganizationDomainControllerTests.cs new file mode 100644 index 0000000000..678b38776c --- /dev/null +++ b/test/Api.Test/Controllers/OrganizationDomainControllerTests.cs @@ -0,0 +1,268 @@ +using Bit.Api.Controllers; +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.Exceptions; +using Bit.Core.Models.Data.Organizations; +using Bit.Core.OrganizationFeatures.OrganizationDomains.Interfaces; +using Bit.Core.Repositories; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using NSubstitute.ReturnsExtensions; +using Xunit; +using Organization = Bit.Core.Entities.Organization; +using OrganizationDomain = Bit.Core.Entities.OrganizationDomain; + +namespace Bit.Api.Test.Controllers; + +[ControllerCustomize(typeof(OrganizationDomainController))] +[SutProviderCustomize] +public class OrganizationDomainControllerTests +{ + [Theory, BitAutoData] + public async Task Get_ShouldThrowUnauthorized_WhenOrgIdCannotManageSso(Guid orgId, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(false); + + var requestAction = async () => await sutProvider.Sut.Get(orgId.ToString()); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task Get_ShouldNotFound_WhenOrganizationDoesNotExist(Guid orgId, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(orgId).ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.Get(orgId.ToString()); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task Get_ShouldReturnOrganizationDomainList_WhenOrgIdIsValid(Guid orgId, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(orgId).Returns(new Organization()); + sutProvider.GetDependency() + .GetDomainsByOrganizationId(orgId).Returns(new List + { + new() + { + Id = Guid.NewGuid(), + OrganizationId = orgId, + CreationDate = DateTime.UtcNow.AddDays(-7), + DomainName = "test.com", + Txt = "btw+12342" + } + }); + + var result = await sutProvider.Sut.Get(orgId.ToString()); + + Assert.IsType>(result); + Assert.Equal(orgId.ToString(), result.Data.Select(x => x.OrganizationId).FirstOrDefault()); + } + + [Theory, BitAutoData] + public async Task GetByOrgIdAndId_ShouldThrowUnauthorized_WhenOrgIdCannotManageSso(Guid orgId, Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(false); + + var requestAction = async () => await sutProvider.Sut.Get(orgId.ToString(), id.ToString()); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task GetByOrgIdAndId_ShouldThrowNotFound_WhenOrganizationDoesNotExist(Guid orgId, Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(orgId).ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.Get(orgId.ToString(), id.ToString()); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task GetByOrgIdAndId_ShouldThrowNotFound_WhenOrganizationDomainEntryNotExist(Guid orgId, Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(orgId).Returns(new Organization()); + sutProvider.GetDependency().GetOrganizationDomainById(id).ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.Get(orgId.ToString(), id.ToString()); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task Get_ShouldReturnOrganizationDomain_WhenOrgIdAndIdAreValid(Guid orgId, Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(orgId).Returns(new Organization()); + sutProvider.GetDependency().GetOrganizationDomainById(id) + .Returns(new OrganizationDomain + { + Id = Guid.NewGuid(), + OrganizationId = orgId, + CreationDate = DateTime.UtcNow.AddDays(-7), + DomainName = "test.com", + Txt = "btw+12342" + }); + + var result = await sutProvider.Sut.Get(orgId.ToString(), id.ToString()); + + Assert.IsType(result); + Assert.Equal(orgId.ToString(), result.OrganizationId); + } + + [Theory, BitAutoData] + public async Task Post_ShouldThrowUnauthorized_OrgIdCannotManageSso(Guid orgId, OrganizationDomainRequestModel model, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(false); + + var requestAction = async () => await sutProvider.Sut.Post(orgId.ToString(), model); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task Post_ShouldThrowNotFound_WhenOrganizationDoesNotExist(Guid orgId, OrganizationDomainRequestModel model, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(orgId).ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.Post(orgId.ToString(), model); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task Post_ShouldCreateEntry_WhenRequestIsValid(Guid orgId, OrganizationDomainRequestModel model, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(orgId).Returns(new Organization()); + sutProvider.GetDependency().CreateAsync(Arg.Any()) + .Returns(new OrganizationDomain()); + + var result = await sutProvider.Sut.Post(orgId.ToString(), model); + + await sutProvider.GetDependency().ReceivedWithAnyArgs(1) + .CreateAsync(Arg.Any()); + Assert.IsType(result); + } + + [Theory, BitAutoData] + public async Task Verify_ShouldThrowUnauthorized_WhenOrgIdCannotManageSso(Guid orgId, Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(false); + + var requestAction = async () => await sutProvider.Sut.Verify(orgId.ToString(), id.ToString()); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task Verify_ShouldThrowNotFound_WhenOrganizationDoesNotExist(Guid orgId, Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(orgId).ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.Verify(orgId.ToString(), id.ToString()); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task Verify_WhenRequestIsValid(Guid orgId, Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(orgId).Returns(new Organization()); + sutProvider.GetDependency().VerifyOrganizationDomain(id) + .Returns(new OrganizationDomain()); + + var result = await sutProvider.Sut.Verify(orgId.ToString(), id.ToString()); + + await sutProvider.GetDependency().Received(1) + .VerifyOrganizationDomain(id); + Assert.IsType(result); + } + + [Theory, BitAutoData] + public async Task RemoveDomain_ShouldThrowUnauthorized_OrgIdCannotManageSso(Guid orgId, Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(false); + + var requestAction = async () => await sutProvider.Sut.RemoveDomain(orgId.ToString(), id.ToString()); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task RemoveDomain_ShouldThrowNotFound_WhenOrganizationDoesNotExist(Guid orgId, Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(orgId).ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.RemoveDomain(orgId.ToString(), id.ToString()); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task RemoveDomain_WhenRequestIsValid(Guid orgId, Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency().ManageSso(orgId).Returns(true); + sutProvider.GetDependency().GetByIdAsync(orgId).Returns(new Organization()); + + await sutProvider.Sut.RemoveDomain(orgId.ToString(), id.ToString()); + + await sutProvider.GetDependency().Received(1) + .DeleteAsync(id); + } + + [Theory, BitAutoData] + public async Task GetOrgDomainSsoDetails_ShouldThrowNotFound_WhenEmailHasNotClaimedDomain( + OrganizationDomainSsoDetailsRequestModel model, SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetOrganizationDomainSsoDetailsAsync(model.Email).ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.GetOrgDomainSsoDetails(model); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task GetOrgDomainSsoDetails_ShouldReturnOrganizationDomainSsoDetails_WhenEmailHasClaimedDomain( + OrganizationDomainSsoDetailsRequestModel model, OrganizationDomainSsoDetailsData ssoDetailsData, SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetOrganizationDomainSsoDetailsAsync(model.Email).Returns(ssoDetailsData); + + var result = await sutProvider.Sut.GetOrgDomainSsoDetails(model); + + Assert.IsType(result); + } +} diff --git a/test/Api.Test/Controllers/OrganizationsControllerTests.cs b/test/Api.Test/Controllers/OrganizationsControllerTests.cs index f056beea80..0933a6ae53 100644 --- a/test/Api.Test/Controllers/OrganizationsControllerTests.cs +++ b/test/Api.Test/Controllers/OrganizationsControllerTests.cs @@ -32,6 +32,8 @@ public class OrganizationsControllerTests : IDisposable private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository; private readonly ICloudGetOrganizationLicenseQuery _cloudGetOrganizationLicenseQuery; private readonly ICreateOrganizationApiKeyCommand _createOrganizationApiKeyCommand; + private readonly IUpdateOrganizationLicenseCommand _updateOrganizationLicenseCommand; + private readonly IOrganizationDomainRepository _organizationDomainRepository; private readonly OrganizationsController _sut; @@ -52,11 +54,13 @@ public class OrganizationsControllerTests : IDisposable _userService = Substitute.For(); _cloudGetOrganizationLicenseQuery = Substitute.For(); _createOrganizationApiKeyCommand = Substitute.For(); + _updateOrganizationLicenseCommand = Substitute.For(); _sut = new OrganizationsController(_organizationRepository, _organizationUserRepository, _policyRepository, _organizationService, _userService, _paymentService, _currentContext, _ssoConfigRepository, _ssoConfigService, _getOrganizationApiKeyQuery, _rotateOrganizationApiKeyCommand, - _createOrganizationApiKeyCommand, _organizationApiKeyRepository, _cloudGetOrganizationLicenseQuery, _globalSettings); + _createOrganizationApiKeyCommand, _organizationApiKeyRepository, _updateOrganizationLicenseCommand, + _cloudGetOrganizationLicenseQuery, _globalSettings); } public void Dispose() diff --git a/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs b/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs index 1a12a5a7e2..9a3757fb36 100644 --- a/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs +++ b/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs @@ -1,8 +1,16 @@ using Bit.Api.SecretsManager.Controllers; using Bit.Api.SecretsManager.Models.Request; +using Bit.Api.Test.SecretsManager.Enums; +using Bit.Core.Context; +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.Repositories; using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces; using Bit.Core.SecretsManager.Entities; using Bit.Core.SecretsManager.Repositories; +using Bit.Core.Services; +using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.Helpers; @@ -14,14 +22,94 @@ namespace Bit.Api.Test.SecretsManager.Controllers; [ControllerCustomize(typeof(AccessPoliciesController))] [SutProviderCustomize] +[ProjectCustomize] [JsonDocumentCustomize] public class AccessPoliciesControllerTests { - [Theory] - [BitAutoData] - public async void GetAccessPoliciesByProject_ReturnsEmptyList(SutProvider sutProvider, - Guid id) + private const int _overMax = 16; + + private static AccessPoliciesCreateRequest AddRequestsOverMax(AccessPoliciesCreateRequest request) { + var newRequests = new List(); + for (var i = 0; i < _overMax; i++) + { + newRequests.Add(new AccessPolicyRequest { GranteeId = new Guid(), Read = true, Write = true }); + } + + request.UserAccessPolicyRequests = newRequests; + return request; + } + + private static List AddRequestsOverMax(List request) + { + for (var i = 0; i < _overMax; i++) + { + request.Add(new GrantedAccessPolicyRequest { GrantedId = new Guid() }); + } + + return request; + } + + private static void SetupAdmin(SutProvider sutProvider, Guid organizationId) + { + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(true); + } + + private static void SetupUserWithPermission(SutProvider sutProvider, Guid organizationId) + { + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(false); + sutProvider.GetDependency().OrganizationUser(default).ReturnsForAnyArgs(true); + } + + private static void SetupUserWithoutPermission(SutProvider sutProvider, + Guid organizationId) + { + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(false); + sutProvider.GetDependency().OrganizationUser(default).ReturnsForAnyArgs(true); + } + + private static void SetupPermission(SutProvider sutProvider, + PermissionType permissionType, Guid orgId) + { + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, orgId); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, orgId); + break; + } + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetProjectAccessPolicies_ReturnsEmptyList( + PermissionType permissionType, + SutProvider sutProvider, + Guid id, Project data) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(data); + + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, data.OrganizationId); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, data.OrganizationId); + sutProvider.GetDependency().UserHasWriteAccessToProject(default, default) + .ReturnsForAnyArgs(true); + break; + } + var result = await sutProvider.Sut.GetProjectAccessPoliciesAsync(id); await sutProvider.GetDependency().Received(1) @@ -34,9 +122,45 @@ public class AccessPoliciesControllerTests [Theory] [BitAutoData] - public async void GetAccessPoliciesByProject_Success(SutProvider sutProvider, Guid id, + public async void GetProjectAccessPolicies_UserWithoutPermission_Throws( + SutProvider sutProvider, + Guid id, + Project data) + { + SetupUserWithoutPermission(sutProvider, data.OrganizationId); + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(data); + sutProvider.GetDependency().UserHasWriteAccessToProject(default, default) + .ReturnsForAnyArgs(false); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetProjectAccessPoliciesAsync(id)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetManyByGrantedProjectIdAsync(Arg.Any()); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetProjectAccessPolicies_Success( + PermissionType permissionType, + SutProvider sutProvider, + Guid id, + Project data, UserProjectAccessPolicy resultAccessPolicy) { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(data); + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, data.OrganizationId); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, data.OrganizationId); + sutProvider.GetDependency().UserHasWriteAccessToProject(default, default) + .ReturnsForAnyArgs(true); + break; + } + sutProvider.GetDependency().GetManyByGrantedProjectIdAsync(default) .ReturnsForAnyArgs(new List { resultAccessPolicy }); @@ -52,36 +176,550 @@ public class AccessPoliciesControllerTests [Theory] [BitAutoData] - public async void CreateAccessPolicies_Success(SutProvider sutProvider, Guid id, - UserProjectAccessPolicy data, AccessPoliciesCreateRequest request) + public async void GetProjectAccessPolicies_ProjectsExist_UserWithoutPermission_Throws( + SutProvider sutProvider, + Guid id, + Project data, + UserProjectAccessPolicy resultAccessPolicy) { - sutProvider.GetDependency().CreateAsync(default) - .ReturnsForAnyArgs(new List { data }); - var result = await sutProvider.Sut.CreateProjectAccessPoliciesAsync(id, request); - await sutProvider.GetDependency().Received(1) - .CreateAsync(Arg.Any>()); + SetupUserWithoutPermission(sutProvider, data.OrganizationId); + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(data); + sutProvider.GetDependency().UserHasWriteAccessToProject(default, default) + .ReturnsForAnyArgs(false); + + sutProvider.GetDependency().GetManyByGrantedProjectIdAsync(default) + .ReturnsForAnyArgs(new List { resultAccessPolicy }); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetProjectAccessPoliciesAsync(id)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetManyByGrantedProjectIdAsync(Arg.Any()); } + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetServiceAccountAccessPolicies_ReturnsEmptyList( + PermissionType permissionType, + SutProvider sutProvider, + Guid id, ServiceAccount data) + { + sutProvider.GetDependency().GetByIdAsync(data.Id).ReturnsForAnyArgs(data); + + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, data.OrganizationId); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, data.OrganizationId); + sutProvider.GetDependency() + .UserHasWriteAccessToServiceAccount(default, default) + .ReturnsForAnyArgs(true); + break; + } + + var result = await sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id); + + await sutProvider.GetDependency().Received(1) + .GetManyByGrantedServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id))); + + Assert.Empty(result.UserAccessPolicies); + Assert.Empty(result.GroupAccessPolicies); + } [Theory] [BitAutoData] - public async void UpdateAccessPolicies_Success(SutProvider sutProvider, Guid id, - UserProjectAccessPolicy data, AccessPolicyUpdateRequest request) + public async void GetServiceAccountAccessPolicies_UserWithoutPermission_Throws( + SutProvider sutProvider, + Guid id, + ServiceAccount data) { - sutProvider.GetDependency().UpdateAsync(default, default, default) + SetupUserWithoutPermission(sutProvider, data.OrganizationId); + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(data); + sutProvider.GetDependency().UserHasWriteAccessToServiceAccount(default, default) + .ReturnsForAnyArgs(false); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetManyByGrantedServiceAccountIdAsync(Arg.Any()); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetServiceAccountAccessPolicies_Success( + PermissionType permissionType, + SutProvider sutProvider, + Guid id, + ServiceAccount data, + UserServiceAccountAccessPolicy resultAccessPolicy) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(data); + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, data.OrganizationId); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, data.OrganizationId); + sutProvider.GetDependency() + .UserHasWriteAccessToServiceAccount(default, default) + .ReturnsForAnyArgs(true); + break; + } + + sutProvider.GetDependency().GetManyByGrantedServiceAccountIdAsync(default) + .ReturnsForAnyArgs(new List { resultAccessPolicy }); + + var result = await sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id); + + await sutProvider.GetDependency().Received(1) + .GetManyByGrantedServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id))); + + Assert.Empty(result.GroupAccessPolicies); + Assert.NotEmpty(result.UserAccessPolicies); + } + + [Theory] + [BitAutoData] + public async void GetServiceAccountAccessPolicies_ServiceAccountExists_UserWithoutPermission_Throws( + SutProvider sutProvider, + Guid id, + ServiceAccount data, + UserServiceAccountAccessPolicy resultAccessPolicy) + { + SetupUserWithoutPermission(sutProvider, data.OrganizationId); + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(data); + sutProvider.GetDependency().UserHasWriteAccessToServiceAccount(default, default) + .ReturnsForAnyArgs(false); + + sutProvider.GetDependency().GetManyByGrantedServiceAccountIdAsync(default) + .ReturnsForAnyArgs(new List { resultAccessPolicy }); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetManyByGrantedServiceAccountIdAsync(Arg.Any()); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetServiceAccountGrantedPolicies_ReturnsEmptyList( + PermissionType permissionType, + SutProvider sutProvider, + Guid id, ServiceAccount data) + { + sutProvider.GetDependency().GetByIdAsync(data.Id).ReturnsForAnyArgs(data); + + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, data.OrganizationId); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, data.OrganizationId); + sutProvider.GetDependency() + .UserHasWriteAccessToServiceAccount(default, default) + .ReturnsForAnyArgs(true); + break; + } + + var result = await sutProvider.Sut.GetServiceAccountGrantedPoliciesAsync(id); + + await sutProvider.GetDependency().Received(1) + .GetManyByServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), Arg.Any(), + Arg.Any()); + + Assert.Empty(result.Data); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetServiceAccountGrantedPolicies_Success( + PermissionType permissionType, + SutProvider sutProvider, + Guid id, + ServiceAccount data, + ServiceAccountProjectAccessPolicy resultAccessPolicy) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(data); + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, data.OrganizationId); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, data.OrganizationId); + break; + } + + sutProvider.GetDependency().GetManyByServiceAccountIdAsync(default, default, default) + .ReturnsForAnyArgs(new List { resultAccessPolicy }); + + var result = await sutProvider.Sut.GetServiceAccountGrantedPoliciesAsync(id); + + await sutProvider.GetDependency().Received(1) + .GetManyByServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), Arg.Any(), + Arg.Any()); + + Assert.NotEmpty(result.Data); + } + + [Theory] + [BitAutoData] + public async void CreateProjectAccessPolicies_RequestMoreThanMax_Throws( + SutProvider sutProvider, + Guid id, + Project mockProject, + UserProjectAccessPolicy data, + AccessPoliciesCreateRequest request) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(mockProject); + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency().CreateManyAsync(default, default, default) + .ReturnsForAnyArgs(new List { data }); + + request = AddRequestsOverMax(request); + + await Assert.ThrowsAsync(() => + sutProvider.Sut.CreateProjectAccessPoliciesAsync(id, request)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .CreateManyAsync(Arg.Any>(), Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData] + public async void CreateProjectAccessPolicies_Success( + SutProvider sutProvider, + Guid id, + Project mockProject, + UserProjectAccessPolicy data, + AccessPoliciesCreateRequest request) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(mockProject); + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency().CreateManyAsync(default, default, default) + .ReturnsForAnyArgs(new List { data }); + + await sutProvider.Sut.CreateProjectAccessPoliciesAsync(id, request); + + await sutProvider.GetDependency().Received(1) + .CreateManyAsync(Arg.Any>(), Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData] + public async void CreateServiceAccountAccessPolicies_RequestMoreThanMax_Throws( + SutProvider sutProvider, + Guid id, + ServiceAccount serviceAccount, + UserServiceAccountAccessPolicy data, + AccessPoliciesCreateRequest request) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(serviceAccount); + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency() + .CreateManyAsync(default, default, default) + .ReturnsForAnyArgs(new List { data }); + + request = AddRequestsOverMax(request); + + await Assert.ThrowsAsync(() => + sutProvider.Sut.CreateServiceAccountAccessPoliciesAsync(id, request)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .CreateManyAsync(Arg.Any>(), Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData] + public async void CreateServiceAccountAccessPolicies_Success( + SutProvider sutProvider, + Guid id, + ServiceAccount serviceAccount, + UserServiceAccountAccessPolicy data, + AccessPoliciesCreateRequest request) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(serviceAccount); + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency() + .CreateManyAsync(default, default, default) + .ReturnsForAnyArgs(new List { data }); + + await sutProvider.Sut.CreateServiceAccountAccessPoliciesAsync(id, request); + + await sutProvider.GetDependency().Received(1) + .CreateManyAsync(Arg.Any>(), Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData] + public async void CreateServiceAccountGrantedPolicies_RequestMoreThanMax_Throws( + SutProvider sutProvider, + Guid id, + ServiceAccount serviceAccount, + ServiceAccountProjectAccessPolicy data, + List request) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(serviceAccount); + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency() + .CreateManyAsync(default, default, default) + .ReturnsForAnyArgs(new List { data }); + + request = AddRequestsOverMax(request); + + await Assert.ThrowsAsync(() => + sutProvider.Sut.CreateServiceAccountGrantedPoliciesAsync(id, request)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .CreateManyAsync(Arg.Any>(), Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData] + public async void CreateServiceAccountGrantedPolicies_Success( + SutProvider sutProvider, + Guid id, + ServiceAccount serviceAccount, + ServiceAccountProjectAccessPolicy data, + List request) + { + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(serviceAccount); + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency() + .CreateManyAsync(default, default, default) + .ReturnsForAnyArgs(new List { data }); + + await sutProvider.Sut.CreateServiceAccountGrantedPoliciesAsync(id, request); + + await sutProvider.GetDependency().Received(1) + .CreateManyAsync(Arg.Any>(), Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData] + public async void UpdateAccessPolicies_Success( + SutProvider sutProvider, + Guid id, + UserProjectAccessPolicy data, + AccessPolicyUpdateRequest request) + { + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency().UpdateAsync(default, default, default, default) .ReturnsForAnyArgs(data); - var result = await sutProvider.Sut.UpdateAccessPolicyAsync(id, request); + + await sutProvider.Sut.UpdateAccessPolicyAsync(id, request); + await sutProvider.GetDependency().Received(1) - .UpdateAsync(Arg.Any(), Arg.Is(request.Read), Arg.Is(request.Write)); + .UpdateAsync(Arg.Any(), Arg.Is(request.Read), Arg.Is(request.Write), Arg.Any()); } [Theory] [BitAutoData] public async void DeleteAccessPolicies_Success(SutProvider sutProvider, Guid id) { - sutProvider.GetDependency().DeleteAsync(default).ReturnsNull(); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency().DeleteAsync(default, default).ReturnsNull(); + await sutProvider.Sut.DeleteAccessPolicyAsync(id); + await sutProvider.GetDependency().Received(1) - .DeleteAsync(Arg.Any()); + .DeleteAsync(Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetPeoplePotentialGrantees_ReturnsEmptyList( + PermissionType permissionType, + SutProvider sutProvider, + Guid id) + { + SetupPermission(sutProvider, permissionType, id); + var result = await sutProvider.Sut.GetPeoplePotentialGranteesAsync(id); + + await sutProvider.GetDependency().Received(1) + .GetManyByOrganizationIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id))); + + await sutProvider.GetDependency().Received(1) + .GetManyDetailsByOrganizationAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id))); + + Assert.Empty(result.Data); + } + + [Theory] + [BitAutoData] + public async void GetPeoplePotentialGrantees_UserWithoutPermission_Throws( + SutProvider sutProvider, + Guid id) + { + sutProvider.GetDependency().OrganizationAdmin(id).Returns(false); + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(false); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetPeoplePotentialGranteesAsync(id)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetManyByOrganizationIdAsync(Arg.Any()); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetManyDetailsByOrganizationAsync(Arg.Any()); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetManyByOrganizationIdWriteAccessAsync(Arg.Any(), Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetPeoplePotentialGrantees_Success( + PermissionType permissionType, + SutProvider sutProvider, + Guid id, + Group mockGroup) + { + SetupPermission(sutProvider, permissionType, id); + sutProvider.GetDependency().GetManyByOrganizationIdAsync(default) + .ReturnsForAnyArgs(new List { mockGroup }); + + var result = await sutProvider.Sut.GetPeoplePotentialGranteesAsync(id); + + await sutProvider.GetDependency().Received(1) + .GetManyByOrganizationIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id))); + + await sutProvider.GetDependency().Received(1) + .GetManyDetailsByOrganizationAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id))); + + Assert.NotEmpty(result.Data); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetServiceAccountsPotentialGrantees_ReturnsEmptyList( + PermissionType permissionType, + SutProvider sutProvider, + Guid id) + { + SetupPermission(sutProvider, permissionType, id); + var result = await sutProvider.Sut.GetServiceAccountsPotentialGranteesAsync(id); + + await sutProvider.GetDependency().Received(1) + .GetManyByOrganizationIdWriteAccessAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Any()); + + Assert.Empty(result.Data); + } + + [Theory] + [BitAutoData] + public async void GetServiceAccountsPotentialGranteesAsync_UserWithoutPermission_Throws( + SutProvider sutProvider, + Guid id) + { + sutProvider.GetDependency().OrganizationAdmin(id).Returns(false); + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(false); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetServiceAccountsPotentialGranteesAsync(id)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetManyByOrganizationIdWriteAccessAsync(Arg.Any(), Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetServiceAccountsPotentialGranteesAsync_Success( + PermissionType permissionType, + SutProvider sutProvider, + Guid id, + ServiceAccount mockServiceAccount) + { + SetupPermission(sutProvider, permissionType, id); + sutProvider.GetDependency() + .GetManyByOrganizationIdWriteAccessAsync(default, default, default) + .ReturnsForAnyArgs(new List { mockServiceAccount }); + + var result = await sutProvider.Sut.GetServiceAccountsPotentialGranteesAsync(id); + + await sutProvider.GetDependency().Received(1) + .GetManyByOrganizationIdWriteAccessAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Any()); + + Assert.NotEmpty(result.Data); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetProjectPotentialGrantees_ReturnsEmptyList( + PermissionType permissionType, + SutProvider sutProvider, + Guid id) + { + SetupPermission(sutProvider, permissionType, id); + var result = await sutProvider.Sut.GetProjectPotentialGranteesAsync(id); + + await sutProvider.GetDependency().Received(1) + .GetManyByOrganizationIdWriteAccessAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Any()); + + Assert.Empty(result.Data); + } + + [Theory] + [BitAutoData] + public async void GetProjectPotentialGrantees_UserWithoutPermission_Throws( + SutProvider sutProvider, + Guid id) + { + sutProvider.GetDependency().OrganizationAdmin(id).Returns(false); + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(false); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetProjectPotentialGranteesAsync(id)); + + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .GetManyByOrganizationIdWriteAccessAsync(Arg.Any(), Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetProjectPotentialGrantees_Success( + PermissionType permissionType, + SutProvider sutProvider, + Guid id, + Project mockProject) + { + SetupPermission(sutProvider, permissionType, id); + sutProvider.GetDependency() + .GetManyByOrganizationIdWriteAccessAsync(default, default, default) + .ReturnsForAnyArgs(new List { mockProject }); + + var result = await sutProvider.Sut.GetProjectPotentialGranteesAsync(id); + + await sutProvider.GetDependency().Received(1) + .GetManyByOrganizationIdWriteAccessAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Any()); + + Assert.NotEmpty(result.Data); } } diff --git a/test/Api.Test/SecretsManager/Controllers/ProjectsControllerTests.cs b/test/Api.Test/SecretsManager/Controllers/ProjectsControllerTests.cs index 747d213269..39ba7bdba7 100644 --- a/test/Api.Test/SecretsManager/Controllers/ProjectsControllerTests.cs +++ b/test/Api.Test/SecretsManager/Controllers/ProjectsControllerTests.cs @@ -1,10 +1,17 @@ using Bit.Api.SecretsManager.Controllers; +using Bit.Api.SecretsManager.Models.Request; +using Bit.Api.Test.SecretsManager.Enums; +using Bit.Core.Context; +using Bit.Core.Enums; +using Bit.Core.Exceptions; using Bit.Core.SecretsManager.Commands.Projects.Interfaces; using Bit.Core.SecretsManager.Entities; +using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; +using Bit.Test.Common.Helpers; using NSubstitute; using Xunit; @@ -16,28 +23,226 @@ namespace Bit.Api.Test.SecretsManager.Controllers; [JsonDocumentCustomize] public class ProjectsControllerTests { + private static void SetupAdmin(SutProvider sutProvider, Guid organizationId) + { + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(true); + } + + private static void SetupUserWithPermission(SutProvider sutProvider, Guid organizationId) + { + sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(false); + sutProvider.GetDependency().OrganizationUser(default).ReturnsForAnyArgs(true); + } + + [Theory] + [BitAutoData] + public async void ListByOrganization_SmNotEnabled_Throws(SutProvider sutProvider, Guid data) + { + sutProvider.GetDependency().AccessSecretsManager(data).Returns(false); + + await Assert.ThrowsAsync(() => sutProvider.Sut.ListByOrganizationAsync(data)); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void ListByOrganization_ReturnsEmptyList(PermissionType permissionType, + SutProvider sutProvider, Guid data) + { + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, data); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, data); + break; + } + + var result = await sutProvider.Sut.ListByOrganizationAsync(data); + + await sutProvider.GetDependency().Received(1) + .GetManyByOrganizationIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data)), Arg.Any(), + Arg.Any()); + Assert.Empty(result.Data); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void ListByOrganization_Success(PermissionType permissionType, + SutProvider sutProvider, Guid data, Project mockProject) + { + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, data); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, data); + break; + } + + sutProvider.GetDependency().GetManyByOrganizationIdAsync(default, default, default) + .ReturnsForAnyArgs(new List { mockProject }); + + var result = await sutProvider.Sut.ListByOrganizationAsync(data); + + await sutProvider.GetDependency().Received(1) + .GetManyByOrganizationIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data)), Arg.Any(), + Arg.Any()); + Assert.NotEmpty(result.Data); + Assert.Single(result.Data); + } + + [Theory] + [BitAutoData] + public async void Create_SmNotEnabled_Throws(SutProvider sutProvider, Guid orgId, + ProjectCreateRequestModel data) + { + sutProvider.GetDependency().AccessSecretsManager(orgId).Returns(false); + + await Assert.ThrowsAsync(() => sutProvider.Sut.CreateAsync(orgId, data)); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .CreateAsync(Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void Create_Success(PermissionType permissionType, SutProvider sutProvider, + Guid orgId, ProjectCreateRequestModel data) + { + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, orgId); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, orgId); + break; + } + + var resultProject = data.ToProject(orgId); + sutProvider.GetDependency().CreateAsync(default, default) + .ReturnsForAnyArgs(resultProject); + + await sutProvider.Sut.CreateAsync(orgId, data); + + await sutProvider.GetDependency().Received(1) + .CreateAsync(Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void Update_Success(PermissionType permissionType, SutProvider sutProvider, + Guid orgId, ProjectUpdateRequestModel data) + { + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, orgId); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, orgId); + break; + } + + var resultProject = data.ToProject(orgId); + sutProvider.GetDependency().UpdateAsync(default, default) + .ReturnsForAnyArgs(resultProject); + + await sutProvider.Sut.UpdateAsync(orgId, data); + + await sutProvider.GetDependency().Received(1) + .UpdateAsync(Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData] + public async void Get_SmNotEnabled_Throws(SutProvider sutProvider, Guid data) + { + sutProvider.GetDependency().AccessSecretsManager(data).Returns(false); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetAsync(data)); + } + + [Theory] + [BitAutoData] + public async void Get_ThrowsNotFound(SutProvider sutProvider, Guid data) + { + sutProvider.GetDependency().AccessSecretsManager(data).Returns(true); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetAsync(data)); + } + + [Theory] + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void Get_Success(PermissionType permissionType, SutProvider sutProvider, + Guid orgId, Guid data) + { + switch (permissionType) + { + case PermissionType.RunAsAdmin: + SetupAdmin(sutProvider, orgId); + break; + case PermissionType.RunAsUserWithPermission: + SetupUserWithPermission(sutProvider, orgId); + sutProvider.GetDependency() + .UserHasReadAccessToProject(Arg.Is(data), Arg.Any()).ReturnsForAnyArgs(true); + break; + } + + sutProvider.GetDependency().GetByIdAsync(Arg.Is(data)) + .ReturnsForAnyArgs(new Project { Id = data, OrganizationId = orgId }); + + await sutProvider.Sut.GetAsync(data); + + await sutProvider.GetDependency().Received(1) + .GetByIdAsync(Arg.Is(data)); + } + + [Theory] + [BitAutoData] + public async void Get_UserWithoutPermission_Throws(SutProvider sutProvider, Guid orgId, + Guid data) + { + SetupUserWithPermission(sutProvider, orgId); + sutProvider.GetDependency().UserHasReadAccessToProject(Arg.Is(data), Arg.Any()) + .ReturnsForAnyArgs(false); + + sutProvider.GetDependency().GetByIdAsync(Arg.Is(data)) + .ReturnsForAnyArgs(new Project { Id = data, OrganizationId = orgId }); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetAsync(data)); + } + [Theory] [BitAutoData] public async void BulkDeleteProjects_Success(SutProvider sutProvider, List data) { sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); - var ids = data.Select(project => project.Id)?.ToList(); - var mockResult = new List>(); - foreach (var project in data) - { - mockResult.Add(new Tuple(project, "")); - } + var ids = data.Select(project => project.Id).ToList(); + var mockResult = data.Select(project => new Tuple(project, "")).ToList(); + sutProvider.GetDependency().DeleteProjects(ids, default).ReturnsForAnyArgs(mockResult); var results = await sutProvider.Sut.BulkDeleteAsync(ids); await sutProvider.GetDependency().Received(1) - .DeleteProjects(Arg.Is(ids), Arg.Any()); + .DeleteProjects(Arg.Is(ids), Arg.Any()); Assert.Equal(data.Count, results.Data.Count()); } [Theory] [BitAutoData] - public async void BulkDeleteProjects_NoGuids_ThrowsArgumentNullException(SutProvider sutProvider) + public async void BulkDeleteProjects_NoGuids_ThrowsArgumentNullException( + SutProvider sutProvider) { sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid()); await Assert.ThrowsAsync(() => sutProvider.Sut.BulkDeleteAsync(new List())); diff --git a/test/Api.Test/SecretsManager/Controllers/SecretsControllerTests.cs b/test/Api.Test/SecretsManager/Controllers/SecretsControllerTests.cs index aff105abed..ab020f7514 100644 --- a/test/Api.Test/SecretsManager/Controllers/SecretsControllerTests.cs +++ b/test/Api.Test/SecretsManager/Controllers/SecretsControllerTests.cs @@ -1,10 +1,13 @@ using Bit.Api.SecretsManager.Controllers; using Bit.Api.SecretsManager.Models.Request; +using Bit.Api.Test.SecretsManager.Enums; 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.Repositories; +using Bit.Core.Services; using Bit.Core.Test.SecretsManager.AutoFixture.SecretsFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -22,33 +25,50 @@ public class SecretsControllerTests { [Theory] [BitAutoData] - public async void GetSecretsByOrganization_ReturnsEmptyList(SutProvider sutProvider, Guid id) + public async void GetSecretsByOrganization_ReturnsEmptyList(SutProvider sutProvider, Guid id, Guid organizationId, Guid userId, AccessClientType accessType) { sutProvider.GetDependency().AccessSecretsManager(id).Returns(true); + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + var result = await sutProvider.Sut.ListByOrganizationAsync(id); await sutProvider.GetDependency().Received(1) - .GetManyByOrganizationIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id))); + .GetManyByOrganizationIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), userId, accessType); Assert.Empty(result.Secrets); } [Theory] - [BitAutoData] - public async void GetSecretsByOrganization_Success(SutProvider sutProvider, Secret resultSecret) + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetSecretsByOrganization_Success(PermissionType permissionType, SutProvider sutProvider, Core.SecretsManager.Entities.Secret resultSecret, Guid organizationId, Guid userId, Core.SecretsManager.Entities.Project mockProject, AccessClientType accessType) { sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(true); - sutProvider.GetDependency().GetManyByOrganizationIdAsync(default).ReturnsForAnyArgs(new List { resultSecret }); + sutProvider.GetDependency().GetManyByOrganizationIdAsync(default, default, default).ReturnsForAnyArgs(new List { resultSecret }); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + + if (permissionType == PermissionType.RunAsAdmin) + { + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(true); + } + else + { + resultSecret.Projects = new List() { mockProject }; + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(false); + sutProvider.GetDependency().UserHasReadAccessToProject(mockProject.Id, userId).Returns(true); + } + var result = await sutProvider.Sut.ListByOrganizationAsync(resultSecret.OrganizationId); await sutProvider.GetDependency().Received(1) - .GetManyByOrganizationIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(resultSecret.OrganizationId))); + .GetManyByOrganizationIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(resultSecret.OrganizationId)), userId, accessType); } [Theory] [BitAutoData] - public async void GetSecretsByOrganization_AccessDenied_Throws(SutProvider sutProvider, Secret resultSecret) + public async void GetSecretsByOrganization_AccessDenied_Throws(SutProvider sutProvider, Core.SecretsManager.Entities.Secret resultSecret) { sutProvider.GetDependency().AccessSecretsManager(default).ReturnsForAnyArgs(false); @@ -64,11 +84,29 @@ public class SecretsControllerTests } [Theory] - [BitAutoData] - public async void GetSecret_Success(SutProvider sutProvider, Secret resultSecret) + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void GetSecret_Success(PermissionType permissionType, SutProvider sutProvider, Secret resultSecret, Guid userId, Guid organizationId, Project mockProject) { + sutProvider.GetDependency().AccessSecretsManager(organizationId).Returns(true); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + mockProject.OrganizationId = organizationId; + resultSecret.Projects = new List() { mockProject }; + resultSecret.OrganizationId = organizationId; + sutProvider.GetDependency().GetByIdAsync(default).ReturnsForAnyArgs(resultSecret); + if (permissionType == PermissionType.RunAsAdmin) + { + resultSecret.OrganizationId = organizationId; + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(true); + } + else + { + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(false); + sutProvider.GetDependency().UserHasReadAccessToProject(mockProject.Id, userId).Returns(true); + } + var result = await sutProvider.Sut.GetAsync(resultSecret.Id); await sutProvider.GetDependency().Received(1) @@ -76,46 +114,89 @@ public class SecretsControllerTests } [Theory] - [BitAutoData] - public async void CreateSecret_Success(SutProvider sutProvider, SecretCreateRequestModel data, Guid organizationId) + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void CreateSecret_Success(PermissionType permissionType, SutProvider sutProvider, SecretCreateRequestModel data, Guid organizationId, Project mockProject, Guid userId) { var resultSecret = data.ToSecret(organizationId); + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + + if (permissionType == PermissionType.RunAsAdmin) + { + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(true); + } + else + { + resultSecret.Projects = new List() { mockProject }; + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(false); + sutProvider.GetDependency().UserHasReadAccessToProject(mockProject.Id, userId).Returns(true); + } sutProvider.GetDependency().AccessSecretsManager(organizationId).Returns(true); - sutProvider.GetDependency().CreateAsync(default).ReturnsForAnyArgs(resultSecret); + sutProvider.GetDependency().CreateAsync(default, userId).ReturnsForAnyArgs(resultSecret); var result = await sutProvider.Sut.CreateAsync(organizationId, data); await sutProvider.GetDependency().Received(1) - .CreateAsync(Arg.Any()); + .CreateAsync(Arg.Any(), userId); } [Theory] - [BitAutoData] - public async void UpdateSecret_Success(SutProvider sutProvider, SecretUpdateRequestModel data, Guid secretId) + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void UpdateSecret_Success(PermissionType permissionType, SutProvider sutProvider, SecretUpdateRequestModel data, Guid secretId, Guid organizationId, Guid userId, Project mockProject) { + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + + if (permissionType == PermissionType.RunAsAdmin) + { + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(true); + } + else + { + data.ProjectIds = new Guid[] { mockProject.Id }; + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(false); + sutProvider.GetDependency().UserHasReadAccessToProject(mockProject.Id, userId).Returns(true); + } + var resultSecret = data.ToSecret(secretId); - sutProvider.GetDependency().UpdateAsync(default).ReturnsForAnyArgs(resultSecret); + sutProvider.GetDependency().UpdateAsync(default, userId).ReturnsForAnyArgs(resultSecret); - var result = await sutProvider.Sut.UpdateAsync(secretId, data); + var result = await sutProvider.Sut.UpdateSecretAsync(secretId, data); await sutProvider.GetDependency().Received(1) - .UpdateAsync(Arg.Any()); + .UpdateAsync(Arg.Any(), userId); } [Theory] - [BitAutoData] - public async void BulkDeleteSecret_Success(SutProvider sutProvider, List data) + [BitAutoData(PermissionType.RunAsAdmin)] + [BitAutoData(PermissionType.RunAsUserWithPermission)] + public async void BulkDeleteSecret_Success(PermissionType permissionType, SutProvider sutProvider, List data, Guid organizationId, Guid userId, Project mockProject) { + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); + + if (permissionType == PermissionType.RunAsAdmin) + { + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(true); + } + else + { + data.FirstOrDefault().Projects = new List() { mockProject }; + sutProvider.GetDependency().OrganizationAdmin(organizationId).Returns(false); + sutProvider.GetDependency().UserHasReadAccessToProject(mockProject.Id, userId).Returns(true); + } + + var ids = data.Select(secret => secret.Id).ToList(); var mockResult = new List>(); + foreach (var secret in data) { mockResult.Add(new Tuple(secret, "")); } - sutProvider.GetDependency().DeleteSecrets(ids).ReturnsForAnyArgs(mockResult); + sutProvider.GetDependency().DeleteSecrets(ids, userId).ReturnsForAnyArgs(mockResult); var results = await sutProvider.Sut.BulkDeleteAsync(ids); await sutProvider.GetDependency().Received(1) - .DeleteSecrets(Arg.Is(ids)); + .DeleteSecrets(Arg.Is(ids), userId); Assert.Equal(data.Count, results.Data.Count()); } @@ -123,6 +204,7 @@ public class SecretsControllerTests [BitAutoData] public async void BulkDeleteSecret_NoGuids_ThrowsArgumentNullException(SutProvider sutProvider) { + sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(new Guid()); await Assert.ThrowsAsync(() => sutProvider.Sut.BulkDeleteAsync(new List())); } } diff --git a/test/Api.Test/SecretsManager/Enums/PermissionType.cs b/test/Api.Test/SecretsManager/Enums/PermissionType.cs new file mode 100644 index 0000000000..e411859fc8 --- /dev/null +++ b/test/Api.Test/SecretsManager/Enums/PermissionType.cs @@ -0,0 +1,7 @@ +namespace Bit.Api.Test.SecretsManager.Enums; + +public enum PermissionType +{ + RunAsAdmin, + RunAsUserWithPermission, +} diff --git a/test/Api.Test/packages.lock.json b/test/Api.Test/packages.lock.json index 2f5a90cb9b..6ca00e3af9 100644 --- a/test/Api.Test/packages.lock.json +++ b/test/Api.Test/packages.lock.json @@ -132,8 +132,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -180,28 +180,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -245,6 +245,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -3068,10 +3076,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/test/Billing.Test/packages.lock.json b/test/Billing.Test/packages.lock.json index d6d99f2705..f9b1fef1f8 100644 --- a/test/Billing.Test/packages.lock.json +++ b/test/Billing.Test/packages.lock.json @@ -132,8 +132,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -170,28 +170,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -235,6 +235,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -3498,10 +3506,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/test/Common/packages.lock.json b/test/Common/packages.lock.json index a404c169d9..ca1cee8430 100644 --- a/test/Common/packages.lock.json +++ b/test/Common/packages.lock.json @@ -120,8 +120,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -158,28 +158,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -218,6 +218,14 @@ "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": { "type": "Transitive", "resolved": "2.1.1", @@ -2789,10 +2797,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/test/Core.Test/Models/Data/SelfHostedOrganizationDetailsTests.cs b/test/Core.Test/Models/Data/SelfHostedOrganizationDetailsTests.cs new file mode 100644 index 0000000000..02c35151c2 --- /dev/null +++ b/test/Core.Test/Models/Data/SelfHostedOrganizationDetailsTests.cs @@ -0,0 +1,379 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Models.Business; +using Bit.Core.Models.Data; +using Bit.Core.Models.Data.Organizations; +using Bit.Core.Models.OrganizationConnectionConfigs; +using Bit.Core.Test.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using Xunit; + +namespace Bit.Core.Test.Models.Data; + +public class SelfHostedOrganizationDetailsTests +{ + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_Success(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.True(result); + Assert.True(string.IsNullOrEmpty(exception)); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_OccupiedSeatCount_ExceedsLicense_Fail(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.Seats = 1; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.False(result); + Assert.Contains("Remove some users", exception); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_MaxCollections_ExceedsLicense_Fail(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.MaxCollections = 1; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.False(result); + Assert.Contains("Remove some collections", exception); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_Groups_NotAllowedByLicense_Fail(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseGroups = false; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.False(result); + Assert.Contains("Your new license does not allow for the use of groups", exception); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_Policies_NotAllowedByLicense_Fail(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UsePolicies = false; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.False(result); + Assert.Contains("Your new license does not allow for the use of policies", exception); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_DisabledPolicies_NotAllowedByLicense_Success(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UsePolicies = false; + ((List)orgDetails.Policies).ForEach(p => p.Enabled = false); + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.True(result); + Assert.True(string.IsNullOrEmpty(exception)); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_Sso_NotAllowedByLicense_Fail(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseSso = false; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.False(result); + Assert.Contains("Your new license does not allow for the use of SSO", exception); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_DisabledSso_NotAllowedByLicense_Success(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseSso = false; + orgDetails.SsoConfig.Enabled = false; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.True(result); + Assert.True(string.IsNullOrEmpty(exception)); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_NoSso_NotAllowedByLicense_Success(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseSso = false; + orgDetails.SsoConfig = null; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.True(result); + Assert.True(string.IsNullOrEmpty(exception)); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_KeyConnector_NotAllowedByLicense_Fail(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseKeyConnector = false; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.False(result); + Assert.Contains("Your new license does not allow for the use of Key Connector", exception); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_DisabledKeyConnector_NotAllowedByLicense_Success(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseKeyConnector = false; + orgDetails.SsoConfig.SetData(new SsoConfigurationData() { KeyConnectorEnabled = false }); + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.True(result); + Assert.True(string.IsNullOrEmpty(exception)); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_NoSsoKeyConnector_NotAllowedByLicense_Success(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseKeyConnector = false; + orgDetails.SsoConfig = null; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.True(result); + Assert.True(string.IsNullOrEmpty(exception)); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_Scim_NotAllowedByLicense_Fail(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseScim = false; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.False(result); + Assert.Contains("Your new plan does not allow the SCIM feature", exception); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_DisabledScim_NotAllowedByLicense_Success(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseScim = false; + ((List>)orgDetails.ScimConnections) + .ForEach(c => c.SetConfig(new ScimConfig() { Enabled = false })); + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.True(result); + Assert.True(string.IsNullOrEmpty(exception)); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_NoScimConfig_NotAllowedByLicense_Success(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseScim = false; + orgDetails.ScimConnections = null; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.True(result); + Assert.True(string.IsNullOrEmpty(exception)); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_CustomPermissions_NotAllowedByLicense_Fail(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseCustomPermissions = false; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.False(result); + Assert.Contains("Your new plan does not allow the Custom Permissions feature", exception); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_NoCustomPermissions_NotAllowedByLicense_Success(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseCustomPermissions = false; + ((List)orgDetails.OrganizationUsers).ForEach(ou => ou.Type = OrganizationUserType.User); + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.True(result); + Assert.True(string.IsNullOrEmpty(exception)); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_ResetPassword_NotAllowedByLicense_Fail(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseResetPassword = false; + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.False(result); + Assert.Contains("Your new license does not allow the Password Reset feature", exception); + } + + [Theory] + [BitAutoData] + [OrganizationLicenseCustomize] + public async Task ValidateForOrganization_DisabledResetPassword_NotAllowedByLicense_Success(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + var (orgDetails, orgLicense) = GetOrganizationAndLicense(orgUsers, policies, ssoConfig, scimConnections, license); + orgLicense.UseResetPassword = false; + ((List)orgDetails.Policies).ForEach(p => p.Enabled = false); + + var result = orgDetails.CanUseLicense(license, out var exception); + + Assert.True(result); + Assert.True(string.IsNullOrEmpty(exception)); + } + + private (SelfHostedOrganizationDetails organization, OrganizationLicense license) GetOrganizationAndLicense(List orgUsers, + List policies, SsoConfig ssoConfig, List> scimConnections, OrganizationLicense license) + { + // The default state is that all features are used by Org and allowed by License + // Each test then toggles on/off as necessary + policies.ForEach(p => p.Enabled = true); + policies.First().Type = PolicyType.ResetPassword; + + ssoConfig.Enabled = true; + ssoConfig.SetData(new SsoConfigurationData() + { + KeyConnectorEnabled = true + }); + + var enabledScimConfig = new ScimConfig() { Enabled = true }; + scimConnections.ForEach(c => c.Config = enabledScimConfig); + + orgUsers.First().Type = OrganizationUserType.Custom; + + var organization = new SelfHostedOrganizationDetails() + { + OccupiedSeatCount = 10, + CollectionCount = 5, + GroupCount = 5, + OrganizationUsers = orgUsers, + Policies = policies, + SsoConfig = ssoConfig, + ScimConnections = scimConnections, + + UsePolicies = true, + UseSso = true, + UseKeyConnector = true, + UseScim = true, + UseGroups = true, + UseDirectory = true, + UseEvents = true, + UseTotp = true, + Use2fa = true, + UseApi = true, + UseResetPassword = true, + SelfHost = true, + UsersGetPremium = true, + UseCustomPermissions = true, + }; + + license.Enabled = true; + license.PlanType = PlanType.EnterpriseAnnually; + license.Seats = 10; + license.MaxCollections = 5; + license.UsePolicies = true; + license.UseSso = true; + license.UseKeyConnector = true; + license.UseScim = true; + license.UseGroups = true; + license.UseEvents = true; + license.UseDirectory = true; + license.UseTotp = true; + license.Use2fa = true; + license.UseApi = true; + license.UseResetPassword = true; + license.MaxStorageGb = 1; + license.SelfHost = true; + license.UsersGetPremium = true; + license.UseCustomPermissions = true; + license.Version = 11; + license.Issued = DateTime.Now; + license.Expires = DateTime.Now.AddYears(1); + + return (organization, license); + } +} diff --git a/test/Core.Test/OrganizationFeatures/OrganizationDomains/CreateOrganizationDomainCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationDomains/CreateOrganizationDomainCommandTests.cs new file mode 100644 index 0000000000..70773d38e6 --- /dev/null +++ b/test/Core.Test/OrganizationFeatures/OrganizationDomains/CreateOrganizationDomainCommandTests.cs @@ -0,0 +1,130 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.OrganizationFeatures.OrganizationDomains; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using NSubstitute.ExceptionExtensions; +using NSubstitute.ReturnsExtensions; +using Xunit; + +namespace Bit.Core.Test.OrganizationFeatures.OrganizationDomains; + +[SutProviderCustomize] +public class CreateOrganizationDomainCommandTests +{ + [Theory, BitAutoData] + public async Task CreateAsync_ShouldCreateOrganizationDomainAndLogEvent_WhenDetailsAreValid(OrganizationDomain orgDomain, SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetClaimedDomainsByDomainNameAsync(orgDomain.DomainName) + .Returns(new List()); + sutProvider.GetDependency() + .GetDomainByOrgIdAndDomainNameAsync(orgDomain.OrganizationId, orgDomain.DomainName) + .ReturnsNull(); + sutProvider.GetDependency() + .ResolveAsync(orgDomain.DomainName, orgDomain.Txt) + .Returns(false); + orgDomain.SetNextRunDate(12); + sutProvider.GetDependency() + .CreateAsync(orgDomain) + .Returns(orgDomain); + + + var result = await sutProvider.Sut.CreateAsync(orgDomain); + + Assert.Equal(orgDomain.Id, result.Id); + Assert.Equal(orgDomain.OrganizationId, result.OrganizationId); + Assert.NotNull(result.LastCheckedDate); + Assert.Equal(orgDomain.NextRunDate, result.NextRunDate); + await sutProvider.GetDependency().Received(1) + .LogOrganizationDomainEventAsync(Arg.Any(), EventType.OrganizationDomain_Added); + await sutProvider.GetDependency().Received(1) + .LogOrganizationDomainEventAsync(Arg.Any(), Arg.Is(x => x == EventType.OrganizationDomain_NotVerified)); + } + + [Theory, BitAutoData] + public async Task CreateAsync_ShouldThrowConflictException_WhenDomainIsClaimed(OrganizationDomain orgDomain, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetClaimedDomainsByDomainNameAsync(orgDomain.DomainName) + .Returns(new List() + { + orgDomain + }); + + var requestAction = async () => await sutProvider.Sut.CreateAsync(orgDomain); + + var exception = await Assert.ThrowsAsync(requestAction); + Assert.Contains("The domain is not available to be claimed.", exception.Message); + } + + [Theory, BitAutoData] + public async Task CreateAsync_ShouldThrowConflictException_WhenEntryIsDuplicatedForOrganization(OrganizationDomain orgDomain, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetClaimedDomainsByDomainNameAsync(orgDomain.DomainName) + .Returns(new List()); + sutProvider.GetDependency() + .GetDomainByOrgIdAndDomainNameAsync(orgDomain.OrganizationId, orgDomain.DomainName) + .Returns(orgDomain); + + var requestAction = async () => await sutProvider.Sut.CreateAsync(orgDomain); + + var exception = await Assert.ThrowsAsync(requestAction); + Assert.Contains("A domain already exists for this organization.", exception.Message); + } + + [Theory, BitAutoData] + public async Task CreateAsync_ShouldNotSetVerifiedDate_WhenDomainCannotBeResolved(OrganizationDomain orgDomain, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetClaimedDomainsByDomainNameAsync(orgDomain.DomainName) + .Returns(new List()); + sutProvider.GetDependency() + .GetDomainByOrgIdAndDomainNameAsync(orgDomain.OrganizationId, orgDomain.DomainName) + .ReturnsNull(); + sutProvider.GetDependency() + .ResolveAsync(orgDomain.DomainName, orgDomain.Txt) + .Throws(new DnsQueryException("")); + sutProvider.GetDependency() + .CreateAsync(orgDomain) + .Returns(orgDomain); + + await sutProvider.Sut.CreateAsync(orgDomain); + + Assert.Null(orgDomain.VerifiedDate); + } + + [Theory, BitAutoData] + public async Task CreateAsync_ShouldSetVerifiedDateAndLogEvent_WhenDomainIsResolved(OrganizationDomain orgDomain, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetClaimedDomainsByDomainNameAsync(orgDomain.DomainName) + .Returns(new List()); + sutProvider.GetDependency() + .GetDomainByOrgIdAndDomainNameAsync(orgDomain.OrganizationId, orgDomain.DomainName) + .ReturnsNull(); + sutProvider.GetDependency() + .ResolveAsync(orgDomain.DomainName, orgDomain.Txt) + .Returns(true); + sutProvider.GetDependency() + .CreateAsync(orgDomain) + .Returns(orgDomain); + + var result = await sutProvider.Sut.CreateAsync(orgDomain); + + Assert.NotNull(result.VerifiedDate); + await sutProvider.GetDependency().Received(1) + .LogOrganizationDomainEventAsync(Arg.Any(), EventType.OrganizationDomain_Added); + await sutProvider.GetDependency().Received(1) + .LogOrganizationDomainEventAsync(Arg.Any(), Arg.Is(x => x == EventType.OrganizationDomain_Verified)); + } +} diff --git a/test/Core.Test/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommandTests.cs new file mode 100644 index 0000000000..b9201d35af --- /dev/null +++ b/test/Core.Test/OrganizationFeatures/OrganizationDomains/DeleteOrganizationDomainCommandTests.cs @@ -0,0 +1,47 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.OrganizationFeatures.OrganizationDomains; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using NSubstitute.ReturnsExtensions; +using Xunit; + +namespace Bit.Core.Test.OrganizationFeatures.OrganizationDomains; + +[SutProviderCustomize] +public class DeleteOrganizationDomainCommandTests +{ + [Theory, BitAutoData] + public async Task DeleteAsync_ShouldThrowNotFoundException_WhenIdDoesNotExist(Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency().GetByIdAsync(id).ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.DeleteAsync(id); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task DeleteAsync_Success(Guid id, SutProvider sutProvider) + { + var expected = new OrganizationDomain + { + Id = id, + OrganizationId = Guid.NewGuid(), + DomainName = "Test Domain", + Txt = "btw+test18383838383" + }; + sutProvider.GetDependency().GetByIdAsync(id).Returns(expected); + + await sutProvider.Sut.DeleteAsync(id); + + await sutProvider.GetDependency().Received(1).DeleteAsync(expected); + await sutProvider.GetDependency().Received(1) + .LogOrganizationDomainEventAsync(Arg.Any(), EventType.OrganizationDomain_Removed); + } +} diff --git a/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQueryTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQueryTests.cs new file mode 100644 index 0000000000..0cb77d243a --- /dev/null +++ b/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByIdQueryTests.cs @@ -0,0 +1,22 @@ +using Bit.Core.OrganizationFeatures.OrganizationDomains; +using Bit.Core.Repositories; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.OrganizationFeatures.OrganizationDomains; + +[SutProviderCustomize] +public class GetOrganizationDomainByIdQueryTests +{ + [Theory, BitAutoData] + public async Task GetOrganizationDomainById_CallsGetByIdAsync(Guid id, + SutProvider sutProvider) + { + await sutProvider.Sut.GetOrganizationDomainById(id); + + await sutProvider.GetDependency().Received(1) + .GetByIdAsync(id); + } +} diff --git a/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQueryTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQueryTests.cs new file mode 100644 index 0000000000..964a3a76d4 --- /dev/null +++ b/test/Core.Test/OrganizationFeatures/OrganizationDomains/GetOrganizationDomainByOrganizationIdQueryTests.cs @@ -0,0 +1,22 @@ +using Bit.Core.OrganizationFeatures.OrganizationDomains; +using Bit.Core.Repositories; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.OrganizationFeatures.OrganizationDomains; + +[SutProviderCustomize] +public class GetOrganizationDomainByOrganizationIdQueryTests +{ + [Theory, BitAutoData] + public async Task GetDomainsByOrganizationId_CallsGetDomainsByOrganizationIdAsync(Guid orgId, + SutProvider sutProvider) + { + await sutProvider.Sut.GetDomainsByOrganizationId(orgId); + + await sutProvider.GetDependency().Received(1) + .GetDomainsByOrganizationIdAsync(orgId); + } +} diff --git a/test/Core.Test/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs new file mode 100644 index 0000000000..54783982ad --- /dev/null +++ b/test/Core.Test/OrganizationFeatures/OrganizationDomains/VerifyOrganizationDomainCommandTests.cs @@ -0,0 +1,135 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.OrganizationFeatures.OrganizationDomains; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using NSubstitute.ReceivedExtensions; +using NSubstitute.ReturnsExtensions; +using Xunit; + +namespace Bit.Core.Test.OrganizationFeatures.OrganizationDomains; + +[SutProviderCustomize] +public class VerifyOrganizationDomainCommandTests +{ + [Theory, BitAutoData] + public async Task VerifyOrganizationDomain_ShouldThrowNotFound_WhenDomainDoesNotExist(Guid id, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetByIdAsync(id) + .ReturnsNull(); + + var requestAction = async () => await sutProvider.Sut.VerifyOrganizationDomain(id); + + await Assert.ThrowsAsync(requestAction); + } + + [Theory, BitAutoData] + public async Task VerifyOrganizationDomain_ShouldThrowConflict_WhenDomainHasBeenClaimed(Guid id, + SutProvider sutProvider) + { + var expected = new OrganizationDomain + { + Id = id, + OrganizationId = Guid.NewGuid(), + DomainName = "Test Domain", + Txt = "btw+test18383838383" + }; + expected.SetVerifiedDate(); + sutProvider.GetDependency() + .GetByIdAsync(id) + .Returns(expected); + + var requestAction = async () => await sutProvider.Sut.VerifyOrganizationDomain(id); + + var exception = await Assert.ThrowsAsync(requestAction); + Assert.Contains("Domain has already been verified.", exception.Message); + } + + [Theory, BitAutoData] + public async Task VerifyOrganizationDomain_ShouldThrowConflict_WhenDomainHasBeenClaimedByAnotherOrganization(Guid id, + SutProvider sutProvider) + { + var expected = new OrganizationDomain + { + Id = id, + OrganizationId = Guid.NewGuid(), + DomainName = "Test Domain", + Txt = "btw+test18383838383" + }; + sutProvider.GetDependency() + .GetByIdAsync(id) + .Returns(expected); + sutProvider.GetDependency() + .GetClaimedDomainsByDomainNameAsync(expected.DomainName) + .Returns(new List { expected }); + + var requestAction = async () => await sutProvider.Sut.VerifyOrganizationDomain(id); + + var exception = await Assert.ThrowsAsync(requestAction); + Assert.Contains("The domain is not available to be claimed.", exception.Message); + } + + [Theory, BitAutoData] + public async Task VerifyOrganizationDomain_ShouldVerifyDomainUpdateAndLogEvent_WhenTxtRecordExists(Guid id, + SutProvider sutProvider) + { + var expected = new OrganizationDomain + { + Id = id, + OrganizationId = Guid.NewGuid(), + DomainName = "Test Domain", + Txt = "btw+test18383838383" + }; + sutProvider.GetDependency() + .GetByIdAsync(id) + .Returns(expected); + sutProvider.GetDependency() + .GetClaimedDomainsByDomainNameAsync(expected.DomainName) + .Returns(new List()); + sutProvider.GetDependency() + .ResolveAsync(expected.DomainName, Arg.Any()) + .Returns(true); + + var result = await sutProvider.Sut.VerifyOrganizationDomain(id); + + Assert.NotNull(result.VerifiedDate); + await sutProvider.GetDependency().Received(1) + .ReplaceAsync(Arg.Any()); + await sutProvider.GetDependency().Received(1) + .LogOrganizationDomainEventAsync(Arg.Any(), EventType.OrganizationDomain_Verified); + } + + [Theory, BitAutoData] + public async Task VerifyOrganizationDomain_ShouldNotSetVerifiedDate_WhenTxtRecordDoesNotExist(Guid id, + SutProvider sutProvider) + { + var expected = new OrganizationDomain + { + Id = id, + OrganizationId = Guid.NewGuid(), + DomainName = "Test Domain", + Txt = "btw+test18383838383" + }; + sutProvider.GetDependency() + .GetByIdAsync(id) + .Returns(expected); + sutProvider.GetDependency() + .GetClaimedDomainsByDomainNameAsync(expected.DomainName) + .Returns(new List()); + sutProvider.GetDependency() + .ResolveAsync(expected.DomainName, Arg.Any()) + .Returns(false); + + var result = await sutProvider.Sut.VerifyOrganizationDomain(id); + + Assert.Null(result.VerifiedDate); + await sutProvider.GetDependency().Received(1) + .LogOrganizationDomainEventAsync(Arg.Any(), EventType.OrganizationDomain_NotVerified); + } +} diff --git a/test/Core.Test/Services/EmergencyAccessServiceTests.cs b/test/Core.Test/Services/EmergencyAccessServiceTests.cs index 110aa4a25c..60cacf79fa 100644 --- a/test/Core.Test/Services/EmergencyAccessServiceTests.cs +++ b/test/Core.Test/Services/EmergencyAccessServiceTests.cs @@ -138,11 +138,10 @@ public class EmergencyAccessServiceTests } [Theory, BitAutoData] - public async Task PasswordAsync_Disables_2FA_Providers_And_Unknown_Device_Verification_On_The_Grantor( + public async Task PasswordAsync_Disables_2FA_Providers_On_The_Grantor( SutProvider sutProvider, User requestingUser, User grantor) { grantor.UsesKeyConnector = true; - grantor.UnknownDeviceVerificationEnabled = true; grantor.SetTwoFactorProviders(new Dictionary { [TwoFactorProviderType.Email] = new TwoFactorProvider @@ -164,7 +163,6 @@ public class EmergencyAccessServiceTests await sutProvider.Sut.PasswordAsync(Guid.NewGuid(), requestingUser, "blablahash", "blablakey"); - Assert.False(grantor.UnknownDeviceVerificationEnabled); Assert.Empty(grantor.GetTwoFactorProviders()); await sutProvider.GetDependency().Received().ReplaceAsync(grantor); } diff --git a/test/Core.Test/Services/EventServiceTests.cs b/test/Core.Test/Services/EventServiceTests.cs index cd3498f306..8454f05f5f 100644 --- a/test/Core.Test/Services/EventServiceTests.cs +++ b/test/Core.Test/Services/EventServiceTests.cs @@ -71,19 +71,26 @@ public class EventServiceTests await sutProvider.Sut.LogGroupEventAsync(group, eventType, eventSystemUser, date); + var eventMessage = new EventMessage() + { + IpAddress = ipAddress, + DeviceType = deviceType, + OrganizationId = group.OrganizationId, + GroupId = group.Id, + Type = eventType, + ActingUserId = actingUserId, + ProviderId = providerId, + Date = date, + SystemUser = eventSystemUser + }; + + if (eventSystemUser is EventSystemUser.SCIM) + { + eventMessage.DeviceType = DeviceType.Server; + } + var expected = new List() { - new EventMessage() - { - IpAddress = ipAddress, - DeviceType = deviceType, - OrganizationId = group.OrganizationId, - GroupId = group.Id, - Type = eventType, - ActingUserId = actingUserId, - ProviderId = providerId, - Date = date, - SystemUser = eventSystemUser - } + eventMessage }; await sutProvider.GetDependency().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(expected, new[] { "IdempotencyId" }))); @@ -143,7 +150,7 @@ public class EventServiceTests [Theory, BitAutoData] public async Task LogOrganizationUserEvent_WithEventSystemUser_LogsRequiredInfo(OrganizationUser orgUser, EventType eventType, EventSystemUser eventSystemUser, DateTime date, - Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider sutProvider) + Guid actingUserId, Guid providerId, string ipAddress, SutProvider sutProvider) { var orgAbilities = new Dictionary() { @@ -153,7 +160,6 @@ public class EventServiceTests sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().IpAddress.Returns(ipAddress); sutProvider.GetDependency().ProviderIdForOrg(Arg.Any()).Returns(providerId); - sutProvider.GetDependency().DeviceType.Returns(deviceType); await sutProvider.Sut.LogOrganizationUserEventAsync(orgUser, eventType, eventSystemUser, date); @@ -161,7 +167,7 @@ public class EventServiceTests new EventMessage() { IpAddress = ipAddress, - DeviceType = deviceType, + DeviceType = DeviceType.Server, OrganizationId = orgUser.OrganizationId, UserId = orgUser.UserId, OrganizationUserId = orgUser.Id, diff --git a/test/Core.Test/Services/OrganizationDomainServiceTests.cs b/test/Core.Test/Services/OrganizationDomainServiceTests.cs new file mode 100644 index 0000000000..b6f299b3a6 --- /dev/null +++ b/test/Core.Test/Services/OrganizationDomainServiceTests.cs @@ -0,0 +1,83 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using Xunit; + +namespace Bit.Core.Test.Services; + +[SutProviderCustomize] +public class OrganizationDomainServiceTests +{ + + [Theory, BitAutoData] + public async Task ValidateOrganizationsDomainAsync_CallsDnsResolverServiceAndReplace(SutProvider sutProvider) + { + var domains = new List + { + new() + { + Id = Guid.NewGuid(), + OrganizationId = Guid.NewGuid(), + CreationDate = DateTime.UtcNow, + DomainName = "test.com", + Txt = "btw+12345", + }, + new() + { + Id = Guid.NewGuid(), + OrganizationId = Guid.NewGuid(), + CreationDate = DateTime.UtcNow, + DomainName = "test2.com", + Txt = "btw+6789" + } + }; + sutProvider.GetDependency().GetManyByNextRunDateAsync(default) + .ReturnsForAnyArgs(domains); + + await sutProvider.Sut.ValidateOrganizationsDomainAsync(); + + await sutProvider.GetDependency().ReceivedWithAnyArgs(2) + .ResolveAsync(default, default); + await sutProvider.GetDependency().ReceivedWithAnyArgs(2) + .ReplaceAsync(default); + await sutProvider.GetDependency().ReceivedWithAnyArgs(2) + .LogOrganizationDomainEventAsync(default, EventType.OrganizationDomain_NotVerified, + EventSystemUser.DomainVerification); + } + + [Theory, BitAutoData] + public async Task OrganizationDomainMaintenanceAsync_CallsDeleteExpiredAsync_WhenExpiredDomainsExist( + SutProvider sutProvider) + { + var expiredDomains = new List + { + new() + { + Id = Guid.NewGuid(), + OrganizationId = Guid.NewGuid(), + CreationDate = DateTime.UtcNow, + DomainName = "test.com", + Txt = "btw+12345", + }, + new() + { + Id = Guid.NewGuid(), + OrganizationId = Guid.NewGuid(), + CreationDate = DateTime.UtcNow, + DomainName = "test2.com", + Txt = "btw+6789" + } + }; + sutProvider.GetDependency().GetExpiredOrganizationDomainsAsync() + .Returns(expiredDomains); + + await sutProvider.Sut.OrganizationDomainMaintenanceAsync(); + + await sutProvider.GetDependency().ReceivedWithAnyArgs(1) + .DeleteExpiredAsync(7); + } +} diff --git a/test/Core.Test/Services/UserServiceTests.cs b/test/Core.Test/Services/UserServiceTests.cs index 52d40e5781..3257f1feea 100644 --- a/test/Core.Test/Services/UserServiceTests.cs +++ b/test/Core.Test/Services/UserServiceTests.cs @@ -95,37 +95,6 @@ public class UserServiceTests .SendTwoFactorEmailAsync(email, token); } - [Theory, BitAutoData] - public async Task SendTwoFactorEmailBecauseNewDeviceLoginAsync_Success(SutProvider sutProvider, User user) - { - var email = user.Email.ToLowerInvariant(); - var token = "thisisatokentocompare"; - - var userTwoFactorTokenProvider = Substitute.For>(); - userTwoFactorTokenProvider - .CanGenerateTwoFactorTokenAsync(Arg.Any>(), user) - .Returns(Task.FromResult(true)); - userTwoFactorTokenProvider - .GenerateAsync("2faEmail:" + email, Arg.Any>(), user) - .Returns(Task.FromResult(token)); - - sutProvider.Sut.RegisterTokenProvider("Email", userTwoFactorTokenProvider); - - user.SetTwoFactorProviders(new Dictionary - { - [TwoFactorProviderType.Email] = new TwoFactorProvider - { - MetaData = new Dictionary { ["Email"] = email }, - Enabled = true - } - }); - await sutProvider.Sut.SendTwoFactorEmailAsync(user, true); - - await sutProvider.GetDependency() - .Received(1) - .SendNewDeviceLoginTwoFactorEmailAsync(email, token); - } - [Theory, BitAutoData] public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderOnUser(SutProvider sutProvider, User user) { @@ -164,199 +133,6 @@ public class UserServiceTests await Assert.ThrowsAsync("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user)); } - [Theory, BitAutoData] - public async Task Needs2FABecauseNewDeviceAsync_ReturnsTrue(SutProvider sutProvider, User user) - { - user.EmailVerified = true; - user.TwoFactorProviders = null; - user.UnknownDeviceVerificationEnabled = true; - const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc"; - const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a"; - - sutProvider.GetDependency() - .GetManyByUserIdAsync(user.Id) - .Returns(Task.FromResult>(new List - { - new Device { Identifier = deviceIdInRepo } - })); - - - sutProvider.GetDependency().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true); - - Assert.True(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password")); - } - - [Theory, BitAutoData] - public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_GranType_Is_AuthorizationCode(SutProvider sutProvider, User user) - { - user.EmailVerified = true; - user.TwoFactorProviders = null; - const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc"; - const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a"; - - sutProvider.GetDependency() - .GetManyByUserIdAsync(user.Id) - .Returns(Task.FromResult>(new List - { - new Device { Identifier = deviceIdInRepo } - })); - - Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "authorization_code")); - } - - [Theory, BitAutoData] - public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_Email_Is_Not_Verified(SutProvider sutProvider, User user) - { - user.EmailVerified = false; - user.TwoFactorProviders = null; - const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc"; - const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a"; - - sutProvider.GetDependency() - .GetManyByUserIdAsync(user.Id) - .Returns(Task.FromResult>(new List - { - new Device { Identifier = deviceIdInRepo } - })); - - Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password")); - } - - [Theory, BitAutoData] - public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_Is_The_First_Device(SutProvider sutProvider, User user) - { - user.EmailVerified = true; - user.TwoFactorProviders = null; - const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc"; - - sutProvider.GetDependency() - .GetManyByUserIdAsync(user.Id) - .Returns(Task.FromResult>(new List())); - - Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password")); - } - - [Theory, BitAutoData] - public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_DeviceId_Is_Already_In_Repo(SutProvider sutProvider, User user) - { - user.EmailVerified = true; - user.TwoFactorProviders = null; - const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc"; - - sutProvider.GetDependency() - .GetManyByUserIdAsync(user.Id) - .Returns(Task.FromResult>(new List - { - new Device { Identifier = deviceIdToCheck } - })); - - Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password")); - } - - [Theory, BitAutoData] - public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_GlobalSettings_2FA_EmailOnNewDeviceLogin_Is_Disabled(SutProvider sutProvider, User user) - { - user.EmailVerified = true; - user.TwoFactorProviders = null; - const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc"; - const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a"; - - sutProvider.GetDependency() - .GetManyByUserIdAsync(user.Id) - .Returns(Task.FromResult>(new List - { - new Device { Identifier = deviceIdInRepo } - })); - - sutProvider.GetDependency().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(false); - - Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password")); - } - - [Theory, BitAutoData] - public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_UnknownDeviceVerification_Is_Disabled(SutProvider sutProvider, User user) - { - user.EmailVerified = true; - user.TwoFactorProviders = null; - user.UnknownDeviceVerificationEnabled = false; - const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc"; - const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a"; - - sutProvider.GetDependency() - .GetManyByUserIdAsync(user.Id) - .Returns(Task.FromResult>(new List - { - new Device { Identifier = deviceIdInRepo } - })); - - sutProvider.GetDependency().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true); - - Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password")); - } - - [Theory, BitAutoData] - public void CanEditDeviceVerificationSettings_ReturnsTrue(SutProvider sutProvider, User user) - { - user.EmailVerified = true; - user.TwoFactorProviders = null; - - sutProvider.GetDependency().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true); - - Assert.True(sutProvider.Sut.CanEditDeviceVerificationSettings(user)); - } - - [Theory, BitAutoData] - public void CanEditDeviceVerificationSettings_ReturnsFalse_When_GlobalSettings_2FA_EmailOnNewDeviceLogin_Is_Disabled(SutProvider sutProvider, User user) - { - user.EmailVerified = true; - user.TwoFactorProviders = null; - - sutProvider.GetDependency().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(false); - - Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user)); - } - - [Theory, BitAutoData] - public void CanEditDeviceVerificationSettings_ReturnsFalse_When_Email_Is_Not_Verified(SutProvider sutProvider, User user) - { - user.EmailVerified = false; - user.TwoFactorProviders = null; - - sutProvider.GetDependency().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true); - - Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user)); - } - - [Theory, BitAutoData] - public void CanEditDeviceVerificationSettings_ReturnsFalse_When_User_Uses_Key_Connector(SutProvider sutProvider, User user) - { - user.EmailVerified = true; - user.TwoFactorProviders = null; - user.UsesKeyConnector = true; - - sutProvider.GetDependency().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true); - - Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user)); - } - - [Theory, BitAutoData] - public void CanEditDeviceVerificationSettings_ReturnsFalse_When_User_Has_A_2FA_Already_Set_Up(SutProvider sutProvider, User user) - { - user.EmailVerified = true; - user.SetTwoFactorProviders(new Dictionary - { - [TwoFactorProviderType.Email] = new TwoFactorProvider - { - MetaData = new Dictionary { ["Email"] = "asdfasf" }, - Enabled = true - } - }); - - sutProvider.GetDependency().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true); - - Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user)); - } - [Theory, BitAutoData] public async void HasPremiumFromOrganization_Returns_False_If_No_Orgs(SutProvider sutProvider, User user) { diff --git a/test/Core.Test/packages.lock.json b/test/Core.Test/packages.lock.json index a978e6e840..f955a523a2 100644 --- a/test/Core.Test/packages.lock.json +++ b/test/Core.Test/packages.lock.json @@ -136,8 +136,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -174,28 +174,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -234,6 +234,14 @@ "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": { "type": "Transitive", "resolved": "2.1.1", @@ -2817,10 +2825,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/test/Icons.Test/packages.lock.json b/test/Icons.Test/packages.lock.json index 9c973218a5..9748de731c 100644 --- a/test/Icons.Test/packages.lock.json +++ b/test/Icons.Test/packages.lock.json @@ -113,8 +113,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -151,28 +151,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -216,6 +216,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2911,10 +2919,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/test/Identity.IntegrationTest/packages.lock.json b/test/Identity.IntegrationTest/packages.lock.json index 35c0b92c62..e1cbb115a1 100644 --- a/test/Identity.IntegrationTest/packages.lock.json +++ b/test/Identity.IntegrationTest/packages.lock.json @@ -143,8 +143,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -181,28 +181,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -246,6 +246,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -3136,10 +3144,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/test/Identity.Test/packages.lock.json b/test/Identity.Test/packages.lock.json index f1ceb6d592..40b0b4f5d0 100644 --- a/test/Identity.Test/packages.lock.json +++ b/test/Identity.Test/packages.lock.json @@ -132,8 +132,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -170,28 +170,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -235,6 +235,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -3003,10 +3011,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/test/Infrastructure.EFIntegration.Test/packages.lock.json b/test/Infrastructure.EFIntegration.Test/packages.lock.json index 385dc20ec8..6c4e327eba 100644 --- a/test/Infrastructure.EFIntegration.Test/packages.lock.json +++ b/test/Infrastructure.EFIntegration.Test/packages.lock.json @@ -143,8 +143,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -181,28 +181,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -246,6 +246,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -2993,10 +3001,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/test/Infrastructure.IntegrationTest/packages.lock.json b/test/Infrastructure.IntegrationTest/packages.lock.json index 0049afce28..87b67248d9 100644 --- a/test/Infrastructure.IntegrationTest/packages.lock.json +++ b/test/Infrastructure.IntegrationTest/packages.lock.json @@ -134,8 +134,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -172,28 +172,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -220,6 +220,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2841,10 +2849,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/test/IntegrationTestCommon/Factories/WebApplicationFactoryBase.cs b/test/IntegrationTestCommon/Factories/WebApplicationFactoryBase.cs index d582d22cb5..e2546ac5a7 100644 --- a/test/IntegrationTestCommon/Factories/WebApplicationFactoryBase.cs +++ b/test/IntegrationTestCommon/Factories/WebApplicationFactoryBase.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using NoopRepos = Bit.Core.Repositories.Noop; namespace Bit.IntegrationTestCommon.Factories; @@ -50,7 +51,14 @@ public abstract class WebApplicationFactoryBase : WebApplicationFactory { "globalSettings:postgreSql:connectionString", "Host=localhost;Username=test;Password=test;Database=test" }, // Clear the redis connection string for distributed caching, forcing an in-memory implementation - { "globalSettings:redis:connectionString", ""} + { "globalSettings:redis:connectionString", ""}, + + // Clear Storage + { "globalSettings:attachment:connectionString", null}, + { "globalSettings:events:connectionString", null}, + { "globalSettings:send:connectionString", null}, + { "globalSettings:notifications:connectionString", null}, + { "globalSettings:storage:connectionString", null}, }); }); @@ -97,6 +105,28 @@ public abstract class WebApplicationFactoryBase : WebApplicationFactory services.Remove(captchaValidationService); services.AddSingleton(); + // Disable blocking + var blockingService = services.First(sd => sd.ServiceType == typeof(IBlockIpService)); + services.Remove(blockingService); + services.AddSingleton(); + + // TODO: Install and use azurite in CI pipeline + var installationDeviceRepository = + services.First(sd => sd.ServiceType == typeof(IInstallationDeviceRepository)); + services.Remove(installationDeviceRepository); + services.AddSingleton(); + + // TODO: Install and use azurite in CI pipeline + var metaDataRepository = + services.First(sd => sd.ServiceType == typeof(IMetaDataRepository)); + services.Remove(metaDataRepository); + services.AddSingleton(); + + // TODO: Install and use azurite in CI pipeline + var referenceEventService = services.First(sd => sd.ServiceType == typeof(IReferenceEventService)); + services.Remove(referenceEventService); + services.AddSingleton(); + // Our Rate limiter works so well that it begins to fail tests unless we carve out // one whitelisted ip. We should still test the rate limiter though and they should change the Ip // to something that is NOT whitelisted diff --git a/test/IntegrationTestCommon/packages.lock.json b/test/IntegrationTestCommon/packages.lock.json index fc0eb7b3a1..df6bebf4ed 100644 --- a/test/IntegrationTestCommon/packages.lock.json +++ b/test/IntegrationTestCommon/packages.lock.json @@ -119,8 +119,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -157,28 +157,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -222,6 +222,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -3122,10 +3130,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/util/Migrator/DbScripts/2022-11-03_00_OrganizationDomainInit.sql b/util/Migrator/DbScripts/2022-11-03_00_OrganizationDomainInit.sql new file mode 100644 index 0000000000..94c04c3217 --- /dev/null +++ b/util/Migrator/DbScripts/2022-11-03_00_OrganizationDomainInit.sql @@ -0,0 +1,285 @@ +-- Create Organization Domain table +IF OBJECT_ID('[dbo].[OrganizationDomain]') IS NOT NULL +BEGIN + DROP TABLE [dbo].[OrganizationDomain] +END +GO + +IF OBJECT_ID('[dbo].[OrganizationDomain]') IS NULL +BEGIN +CREATE TABLE [dbo].[OrganizationDomain] ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [OrganizationId] UNIQUEIDENTIFIER NOT NULL, + [Txt] VARCHAR(MAX) NOT NULL, + [DomainName] NVARCHAR(255) NOT NULL, + [CreationDate] DATETIME2(7) NOT NULL, + [VerifiedDate] DATETIME2(7) NULL, + [LastCheckedDate] DATETIME2(7) NULL, + [NextRunDate] DATETIME2(7) NOT NULL, + [JobRunCount] TINYINT NOT NULL + CONSTRAINT [PK_OrganizationDomain] PRIMARY KEY CLUSTERED ([Id] ASC), + CONSTRAINT [FK_OrganzationDomain_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]) +) +END +GO + +-- Create View +CREATE OR ALTER VIEW [dbo].[OrganizationDomainView] +AS +SELECT + * +FROM + [dbo].[OrganizationDomain] +GO + +-- Organization Domain CRUD SPs +-- Create +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @OrganizationId UNIQUEIDENTIFIER, + @Txt VARCHAR(MAX), + @DomainName NVARCHAR(255), + @CreationDate DATETIME2(7), + @VerifiedDate DATETIME2(7), + @LastCheckedDate DATETIME2(7), + @NextRunDate DATETIME2(7), + @JobRunCount TINYINT +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[OrganizationDomain] + ( + [Id], + [OrganizationId], + [Txt], + [DomainName], + [CreationDate], + [VerifiedDate], + [LastCheckedDate], + [NextRunDate], + [JobRunCount] + ) + VALUES + ( + @Id, + @OrganizationId, + @Txt, + @DomainName, + @CreationDate, + @VerifiedDate, + @LastCheckedDate, + @NextRunDate, + @JobRunCount + ) +END +GO + +--Update +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_Update] + @Id UNIQUEIDENTIFIER OUTPUT, + @OrganizationId UNIQUEIDENTIFIER, + @Txt VARCHAR(MAX), + @DomainName NVARCHAR(255), + @CreationDate DATETIME2(7), + @VerifiedDate DATETIME2(7), + @LastCheckedDate DATETIME2(7), + @NextRunDate DATETIME2(7), + @JobRunCount TINYINT +AS +BEGIN + SET NOCOUNT ON + +UPDATE + [dbo].[OrganizationDomain] +SET + [OrganizationId] = @OrganizationId, + [Txt] = @Txt, + [DomainName] = @DomainName, + [CreationDate] = @CreationDate, + [VerifiedDate] = @VerifiedDate, + [LastCheckedDate] = @LastCheckedDate, + [NextRunDate] = @NextRunDate, + [JobRunCount] = @JobRunCount +WHERE + [Id] = @Id +END +GO + +--Read +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_ReadById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationDomain] +WHERE + [Id] = @Id +END +GO + +--Delete +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_DeleteById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +DELETE +FROM + [dbo].[OrganizationDomain] +WHERE + [Id] = @Id +END +GO + +-- SP to get claimed domain by domain name +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_ReadByClaimedDomain] + @DomainName NVARCHAR(255) +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationDomain] +WHERE + [DomainName] = @DomainName + AND + [VerifiedDate] IS NOT NULL +END +GO + +-- SP to get domains by OrganizationId +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_ReadByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationDomain] +WHERE + [OrganizationId] = @OrganizationId +END +GO + +--SP to get domain by organizationId and domainName +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_ReadDomainByOrgIdAndDomainName] + @OrganizationId UNIQUEIDENTIFIER, + @DomainName NVARCHAR(255) +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationDomain] +WHERE + [OrganizationId] = @OrganizationId + AND + [DomainName] = @DomainName +END +GO + +--SP Read by nextRunDate +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_ReadByNextRunDate] + @Date DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationDomain] +WHERE [VerifiedDate] IS NULL + AND [JobRunCount] != 3 + AND DATEPART(year, [NextRunDate]) = DATEPART(year, @Date) + AND DATEPART(month, [NextRunDate]) = DATEPART(month, @Date) + AND DATEPART(day, [NextRunDate]) = DATEPART(day, @Date) + AND DATEPART(hour, [NextRunDate]) = DATEPART(hour, @Date) +UNION +SELECT + * +FROM + [dbo].[OrganizationDomain] +WHERE DATEDIFF(hour, [NextRunDate], @Date) > 36 + AND [VerifiedDate] IS NULL + AND [JobRunCount] != 3 +END +GO + +-- SP to get all domains that have not been verified within 72 hours +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_ReadIfExpired] +AS +BEGIN + SET NOCOUNT OFF + +SELECT + * +FROM + [dbo].[OrganizationDomain] +WHERE + DATEDIFF(DAY, [CreationDate], GETUTCDATE()) >= 4 --Get domains that have not been verified after 3 days (72 hours) + AND + [VerifiedDate] IS NULL +END +GO + +-- SP to delete domains that have been left unverified for 7 days +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_DeleteIfExpired] + @ExpirationPeriod TINYINT +AS +BEGIN + SET NOCOUNT OFF + +DELETE FROM [dbo].[OrganizationDomain] +WHERE DATEDIFF(DAY, [LastCheckedDate], GETUTCDATE()) >= @ExpirationPeriod + AND [VerifiedDate] IS NULL +END +GO + +-- SP to get Organization SSO Provider details by Email +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomainSsoDetails_ReadByEmail] + @Email NVARCHAR(256) +AS +BEGIN + SET NOCOUNT ON + + DECLARE @Domain NVARCHAR(256) + +SELECT @Domain = SUBSTRING(@Email, CHARINDEX( '@', @Email) + 1, LEN(@Email)) + +SELECT + O.Id AS OrganizationId, + O.[Name] AS OrganizationName, + O.UseSso AS SsoAvailable, + P.Enabled AS SsoRequired, + O.Identifier AS OrganizationIdentifier, + OD.VerifiedDate, + P.[Type] AS PolicyType, + OD.DomainName +FROM + [dbo].[OrganizationView] O + INNER JOIN [dbo].[OrganizationDomainView] OD +ON O.Id = OD.OrganizationId + -- use left join instead of inner join so that results + -- come back even if org doesn't have a policy yet for + -- requiring SSO + LEFT JOIN [dbo].[PolicyView] P + ON O.Id = P.OrganizationId +WHERE OD.DomainName = @Domain + AND O.Enabled = 1 + -- Handle null results + AND (P.Id is NULL OR (P.Id IS NOT NULL AND P.[Type] = 4)) -- SSO Type +END +GO \ No newline at end of file diff --git a/util/Migrator/DbScripts/2022-12-08_00_EventsDomainName.sql b/util/Migrator/DbScripts/2022-12-08_00_EventsDomainName.sql new file mode 100644 index 0000000000..c04bf06916 --- /dev/null +++ b/util/Migrator/DbScripts/2022-12-08_00_EventsDomainName.sql @@ -0,0 +1,95 @@ +--Add column DomainName to Event table +IF COL_LENGTH('[dbo].[Event]', 'DomainName') IS NULL + BEGIN + ALTER TABLE + [dbo].[Event] + ADD + [DomainName] VARCHAR(256) NULL; + END +GO + +-- Recreate EventView +IF OBJECT_ID('[dbo].[EventView]') IS NOT NULL +BEGIN + DROP VIEW [dbo].[EventView] +END +GO + +CREATE VIEW [dbo].[EventView] +AS +SELECT + * +FROM + [dbo].[Event] +GO + +CREATE OR ALTER PROCEDURE [dbo].[Event_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @Type INT, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @InstallationId UNIQUEIDENTIFIER, + @ProviderId UNIQUEIDENTIFIER, + @CipherId UNIQUEIDENTIFIER, + @CollectionId UNIQUEIDENTIFIER, + @PolicyId UNIQUEIDENTIFIER, + @GroupId UNIQUEIDENTIFIER, + @OrganizationUserId UNIQUEIDENTIFIER, + @ProviderUserId UNIQUEIDENTIFIER, + @ProviderOrganizationId UNIQUEIDENTIFIER = null, + @ActingUserId UNIQUEIDENTIFIER, + @DeviceType SMALLINT, + @IpAddress VARCHAR(50), + @Date DATETIME2(7), + @SystemUser TINYINT = null, + @DomainName VARCHAR(256) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[Event] + ( + [Id], + [Type], + [UserId], + [OrganizationId], + [InstallationId], + [ProviderId], + [CipherId], + [CollectionId], + [PolicyId], + [GroupId], + [OrganizationUserId], + [ProviderUserId], + [ProviderOrganizationId], + [ActingUserId], + [DeviceType], + [IpAddress], + [Date], + [SystemUser], + [DomainName] + ) + VALUES + ( + @Id, + @Type, + @UserId, + @OrganizationId, + @InstallationId, + @ProviderId, + @CipherId, + @CollectionId, + @PolicyId, + @GroupId, + @OrganizationUserId, + @ProviderUserId, + @ProviderOrganizationId, + @ActingUserId, + @DeviceType, + @IpAddress, + @Date, + @SystemUser, + @DomainName + ) +END +GO \ No newline at end of file diff --git a/util/Migrator/DbScripts/2023-01-18_00_FixOrganizationDeleteOrgDomain.sql b/util/Migrator/DbScripts/2023-01-18_00_FixOrganizationDeleteOrgDomain.sql new file mode 100644 index 0000000000..f7bafad474 --- /dev/null +++ b/util/Migrator/DbScripts/2023-01-18_00_FixOrganizationDeleteOrgDomain.sql @@ -0,0 +1,114 @@ +-- Create OrganizationDomain delete by OrganizationId +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_OrganizationDeleted] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +DELETE +FROM + [dbo].[OrganizationDomain] +WHERE + [OrganizationId] = @OrganizationId +END +GO + +-- Update Organization Delete By Id to include OrganizationDomain_OrganizationDeleted +CREATE OR ALTER PROCEDURE [dbo].[Organization_DeleteById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationId] @Id + + DECLARE @BatchSize INT = 100 + WHILE @BatchSize > 0 +BEGIN +BEGIN TRANSACTION Organization_DeleteById_Ciphers + + DELETE TOP(@BatchSize) + FROM + [dbo].[Cipher] + WHERE + [UserId] IS NULL + AND [OrganizationId] = @Id + + SET @BatchSize = @@ROWCOUNT + + COMMIT TRANSACTION Organization_DeleteById_Ciphers +END + +BEGIN TRANSACTION Organization_DeleteById + +DELETE +FROM + [dbo].[SsoUser] +WHERE + [OrganizationId] = @Id + +DELETE +FROM + [dbo].[SsoConfig] +WHERE + [OrganizationId] = @Id + +DELETE CU + FROM + [dbo].[CollectionUser] CU + INNER JOIN + [dbo].[OrganizationUser] OU ON [CU].[OrganizationUserId] = [OU].[Id] + WHERE + [OU].[OrganizationId] = @Id + +DELETE +FROM + [dbo].[OrganizationUser] +WHERE + [OrganizationId] = @Id + +DELETE +FROM + [dbo].[ProviderOrganization] +WHERE + [OrganizationId] = @Id + + EXEC [dbo].[OrganizationApiKey_OrganizationDeleted] @Id + EXEC [dbo].[OrganizationConnection_OrganizationDeleted] @Id + EXEC [dbo].[OrganizationSponsorship_OrganizationDeleted] @Id + EXEC [dbo].[OrganizationDomain_OrganizationDeleted] @Id + +DELETE +FROM + [dbo].[Project] +WHERE + [OrganizationId] = @Id + +DELETE +FROM + [dbo].[Secret] +WHERE + [OrganizationId] = @Id + +DELETE AK + FROM + [dbo].[ApiKey] AK + INNER JOIN + [dbo].[ServiceAccount] SA ON [AK].[ServiceAccountId] = [SA].[Id] + WHERE + [SA].[OrganizationId] = @Id + +DELETE +FROM + [dbo].[ServiceAccount] +WHERE + [OrganizationId] = @Id + +DELETE +FROM + [dbo].[Organization] +WHERE + [Id] = @Id + + COMMIT TRANSACTION Organization_DeleteById +END diff --git a/util/Migrator/DbScripts/2023-01-23_00_AddAvatarToOrganizationUsers.sql b/util/Migrator/DbScripts/2023-01-23_00_AddAvatarToOrganizationUsers.sql new file mode 100644 index 0000000000..b96b0fa9b0 --- /dev/null +++ b/util/Migrator/DbScripts/2023-01-23_00_AddAvatarToOrganizationUsers.sql @@ -0,0 +1,32 @@ +CREATE OR ALTER VIEW [dbo].[OrganizationUserUserDetailsView] +AS +SELECT + OU.[Id], + OU.[UserId], + OU.[OrganizationId], + U.[Name], + ISNULL(U.[Email], OU.[Email]) Email, + U.[AvatarColor], + U.[TwoFactorProviders], + U.[Premium], + OU.[Status], + OU.[Type], + OU.[AccessAll], + OU.[ExternalId], + SU.[ExternalId] SsoExternalId, + OU.[Permissions], + OU.[ResetPasswordKey], + U.[UsesKeyConnector] +FROM + [dbo].[OrganizationUser] OU +LEFT JOIN + [dbo].[User] U ON U.[Id] = OU.[UserId] +LEFT JOIN + [dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId] +GO + +IF OBJECT_ID('[dbo].[OrganizationUserUserDetails_ReadByOrganizationId]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserUserDetails_ReadByOrganizationId]'; +END +GO diff --git a/util/Migrator/DbScripts/2023-02-14_00_RevokeApiKeys.sql b/util/Migrator/DbScripts/2023-02-14_00_RevokeApiKeys.sql new file mode 100644 index 0000000000..044b920b53 --- /dev/null +++ b/util/Migrator/DbScripts/2023-02-14_00_RevokeApiKeys.sql @@ -0,0 +1,23 @@ +CREATE OR ALTER PROCEDURE [dbo].[ApiKey_DeleteByIds] + @Ids [dbo].[GuidIdArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + DECLARE @BatchSize INT = 100 + + WHILE @BatchSize > 0 + BEGIN + BEGIN TRANSACTION ApiKey_DeleteMany + + DELETE TOP(@BatchSize) AK + FROM + [dbo].[ApiKey] AK + INNER JOIN + @Ids I ON I.Id = AK.Id + + SET @BatchSize = @@ROWCOUNT + + COMMIT TRANSACTION ApiKey_DeleteMany + END +END diff --git a/util/Migrator/DbScripts/2023-02-16_00_SecretsManagerEvent.sql b/util/Migrator/DbScripts/2023-02-16_00_SecretsManagerEvent.sql new file mode 100644 index 0000000000..14a6873695 --- /dev/null +++ b/util/Migrator/DbScripts/2023-02-16_00_SecretsManagerEvent.sql @@ -0,0 +1,100 @@ +IF COL_LENGTH('[dbo].[Event]', 'SecretId') IS NULL +BEGIN + ALTER TABLE + [dbo].[Event] + ADD + [SecretId] UNIQUEIDENTIFIER NULL +END +GO + +IF COL_LENGTH('[dbo].[Event]', 'ServiceAccountId') IS NULL +BEGIN + ALTER TABLE + [dbo].[Event] + ADD + [ServiceAccountId] UNIQUEIDENTIFIER NULL +END +GO + +IF OBJECT_ID('[dbo].[EventView]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[EventView]'; +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[Event_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @Type INT, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @InstallationId UNIQUEIDENTIFIER, + @ProviderId UNIQUEIDENTIFIER, + @CipherId UNIQUEIDENTIFIER, + @CollectionId UNIQUEIDENTIFIER, + @PolicyId UNIQUEIDENTIFIER, + @GroupId UNIQUEIDENTIFIER, + @OrganizationUserId UNIQUEIDENTIFIER, + @ProviderUserId UNIQUEIDENTIFIER, + @ProviderOrganizationId UNIQUEIDENTIFIER = null, + @ActingUserId UNIQUEIDENTIFIER, + @DeviceType SMALLINT, + @IpAddress VARCHAR(50), + @Date DATETIME2(7), + @SystemUser TINYINT = null, + @DomainName VARCHAR(256), + @SecretId UNIQUEIDENTIFIER = null, + @ServiceAccountId UNIQUEIDENTIFIER = null +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[Event] + ( + [Id], + [Type], + [UserId], + [OrganizationId], + [InstallationId], + [ProviderId], + [CipherId], + [CollectionId], + [PolicyId], + [GroupId], + [OrganizationUserId], + [ProviderUserId], + [ProviderOrganizationId], + [ActingUserId], + [DeviceType], + [IpAddress], + [Date], + [SystemUser], + [DomainName], + [SecretId], + [ServiceAccountId] + ) + VALUES + ( + @Id, + @Type, + @UserId, + @OrganizationId, + @InstallationId, + @ProviderId, + @CipherId, + @CollectionId, + @PolicyId, + @GroupId, + @OrganizationUserId, + @ProviderUserId, + @ProviderOrganizationId, + @ActingUserId, + @DeviceType, + @IpAddress, + @Date, + @SystemUser, + @DomainName, + @SecretId, + @ServiceAccountId + ) +END +GO diff --git a/util/Migrator/DbScripts/2023-02-16_00_SelfHostedOrganizationDetails.sql b/util/Migrator/DbScripts/2023-02-16_00_SelfHostedOrganizationDetails.sql new file mode 100644 index 0000000000..49323874cc --- /dev/null +++ b/util/Migrator/DbScripts/2023-02-16_00_SelfHostedOrganizationDetails.sql @@ -0,0 +1,62 @@ +CREATE OR ALTER PROCEDURE [dbo].[Group_ReadCountByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + COUNT(1) + FROM + [dbo].[Group] + WHERE + [OrganizationId] = @OrganizationId +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_ReadOccupiedSeatCountByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + COUNT(1) + FROM + [dbo].[OrganizationUserView] + WHERE + OrganizationId = @OrganizationId + AND Status >= 0 --Invited +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[Organization_ReadByLicenseKey] + @LicenseKey VARCHAR (100) +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationView] +WHERE + [LicenseKey] = @LicenseKey +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[Organization_ReadSelfHostedDetailsById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[Organization_ReadById] @Id + EXEC [dbo].[OrganizationUser_ReadOccupiedSeatCountByOrganizationId] @Id + EXEC [dbo].[Collection_ReadCountByOrganizationId] @Id + EXEC [dbo].[Group_ReadCountByOrganizationId] @Id + EXEC [dbo].[OrganizationUser_ReadByOrganizationId] @Id, NULL + EXEC [dbo].[Policy_ReadByOrganizationId] @Id + EXEC [dbo].[SsoConfig_ReadByOrganizationId] @Id + EXEC [dbo].[OrganizationConnection_ReadByOrganizationIdType] @Id, 2 --Scim connection type +END +GO diff --git a/util/Migrator/DbScripts/2023-02-16_FixSsoAvailableOrganizationDomain.sql b/util/Migrator/DbScripts/2023-02-16_FixSsoAvailableOrganizationDomain.sql new file mode 100644 index 0000000000..97a9a227a8 --- /dev/null +++ b/util/Migrator/DbScripts/2023-02-16_FixSsoAvailableOrganizationDomain.sql @@ -0,0 +1,31 @@ +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomainSsoDetails_ReadByEmail] + @Email NVARCHAR(256) +AS +BEGIN + SET NOCOUNT ON + + DECLARE @Domain NVARCHAR(256) + +SELECT @Domain = SUBSTRING(@Email, CHARINDEX( '@', @Email) + 1, LEN(@Email)) + +SELECT + O.Id AS OrganizationId, + O.[Name] AS OrganizationName, + S.Enabled AS SsoAvailable, + P.Enabled AS SsoRequired, + O.Identifier AS OrganizationIdentifier, + OD.VerifiedDate, + P.[Type] AS PolicyType, + OD.DomainName +FROM + [dbo].[OrganizationView] O + INNER JOIN [dbo].[OrganizationDomainView] OD +ON O.Id = OD.OrganizationId + LEFT JOIN [dbo].[PolicyView] P + ON O.Id = P.OrganizationId + LEFT JOIN [dbo].[Ssoconfig] S + ON O.Id = S.OrganizationId +WHERE OD.DomainName = @Domain + AND O.Enabled = 1 + AND (P.Id is NULL OR (P.Id IS NOT NULL AND P.[Type] = 4)) -- SSO Type +END \ No newline at end of file diff --git a/util/Migrator/DbScripts/2023-02-22_FixOrganizationUserUserDetailsViewOutOfSync.sql b/util/Migrator/DbScripts/2023-02-22_FixOrganizationUserUserDetailsViewOutOfSync.sql new file mode 100644 index 0000000000..6e0ed991c4 --- /dev/null +++ b/util/Migrator/DbScripts/2023-02-22_FixOrganizationUserUserDetailsViewOutOfSync.sql @@ -0,0 +1,45 @@ +CREATE OR ALTER VIEW [dbo].[OrganizationUserUserDetailsView] +AS +SELECT + OU.[Id], + OU.[UserId], + OU.[OrganizationId], + U.[Name], + ISNULL(U.[Email], OU.[Email]) Email, + U.[AvatarColor], + U.[TwoFactorProviders], + U.[Premium], + OU.[Status], + OU.[Type], + OU.[AccessAll], + OU.[AccessSecretsManager], + OU.[ExternalId], + SU.[ExternalId] SsoExternalId, + OU.[Permissions], + OU.[ResetPasswordKey], + U.[UsesKeyConnector] +FROM + [dbo].[OrganizationUser] OU +LEFT JOIN + [dbo].[User] U ON U.[Id] = OU.[UserId] +LEFT JOIN + [dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId] +GO + +IF OBJECT_ID('[dbo].[OrganizationUserUserDetails_ReadById]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserUserDetails_ReadById]'; +END +GO + +IF OBJECT_ID('[dbo].[OrganizationUserUserDetails_ReadByOrganizationId]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserUserDetails_ReadByOrganizationId]'; +END +GO + +IF OBJECT_ID('[dbo].[OrganizationUser_ReadByMinimumRole]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUser_ReadByMinimumRole]'; +END +GO diff --git a/util/Migrator/DbScripts/2023-02-22_FixReturningExpiredDomainsAfterSpecifiedPeriod.sql b/util/Migrator/DbScripts/2023-02-22_FixReturningExpiredDomainsAfterSpecifiedPeriod.sql new file mode 100644 index 0000000000..67fef99b84 --- /dev/null +++ b/util/Migrator/DbScripts/2023-02-22_FixReturningExpiredDomainsAfterSpecifiedPeriod.sql @@ -0,0 +1,14 @@ +CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_ReadIfExpired] +AS +BEGIN + SET NOCOUNT OFF + + SELECT + * + FROM + [dbo].[OrganizationDomain] + WHERE + DATEDIFF(DAY, [CreationDate], GETUTCDATE()) = 4 --Get domains that have not been verified after 3 days (72 hours) + AND + [VerifiedDate] IS NULL +END \ No newline at end of file diff --git a/util/Migrator/DbScripts_future/2023-01-FutureMigration.sql b/util/Migrator/DbScripts_future/2023-01-FutureMigration.sql new file mode 100644 index 0000000000..9f725c7be2 --- /dev/null +++ b/util/Migrator/DbScripts_future/2023-01-FutureMigration.sql @@ -0,0 +1,230 @@ +-- Table: User (UnknownDeviceVerificationEnabled) +IF COL_LENGTH('[dbo].[User]', 'UnknownDeviceVerificationEnabled') IS NOT NULL +BEGIN + ALTER TABLE + [dbo].[User] + DROP CONSTRAINT + [D_User_UnknownDeviceVerificationEnabled] + ALTER TABLE + [dbo].[User] + DROP COLUMN + [UnknownDeviceVerificationEnabled] +END +GO + +-- View: User +CREATE OR ALTER VIEW [dbo].[UserView] +AS +SELECT + * +FROM + [dbo].[User] +GO + +-- Stored Procedure: User_Create +CREATE OR ALTER PROCEDURE [dbo].[User_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @Name NVARCHAR(50), + @Email NVARCHAR(256), + @EmailVerified BIT, + @MasterPassword NVARCHAR(300), + @MasterPasswordHint NVARCHAR(50), + @Culture NVARCHAR(10), + @SecurityStamp NVARCHAR(50), + @TwoFactorProviders NVARCHAR(MAX), + @TwoFactorRecoveryCode NVARCHAR(32), + @EquivalentDomains NVARCHAR(MAX), + @ExcludedGlobalEquivalentDomains NVARCHAR(MAX), + @AccountRevisionDate DATETIME2(7), + @Key NVARCHAR(MAX), + @PublicKey NVARCHAR(MAX), + @PrivateKey NVARCHAR(MAX), + @Premium BIT, + @PremiumExpirationDate DATETIME2(7), + @RenewalReminderDate DATETIME2(7), + @Storage BIGINT, + @MaxStorageGb SMALLINT, + @Gateway TINYINT, + @GatewayCustomerId VARCHAR(50), + @GatewaySubscriptionId VARCHAR(50), + @ReferenceData VARCHAR(MAX), + @LicenseKey VARCHAR(100), + @Kdf TINYINT, + @KdfIterations INT, + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @ApiKey VARCHAR(30), + @ForcePasswordReset BIT = 0, + @UsesKeyConnector BIT = 0, + @FailedLoginCount INT = 0, + @LastFailedLoginDate DATETIME2(7), + @AvatarColor VARCHAR(7) = NULL +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[User] + ( + [Id], + [Name], + [Email], + [EmailVerified], + [MasterPassword], + [MasterPasswordHint], + [Culture], + [SecurityStamp], + [TwoFactorProviders], + [TwoFactorRecoveryCode], + [EquivalentDomains], + [ExcludedGlobalEquivalentDomains], + [AccountRevisionDate], + [Key], + [PublicKey], + [PrivateKey], + [Premium], + [PremiumExpirationDate], + [RenewalReminderDate], + [Storage], + [MaxStorageGb], + [Gateway], + [GatewayCustomerId], + [GatewaySubscriptionId], + [ReferenceData], + [LicenseKey], + [Kdf], + [KdfIterations], + [CreationDate], + [RevisionDate], + [ApiKey], + [ForcePasswordReset], + [UsesKeyConnector], + [FailedLoginCount], + [LastFailedLoginDate], + [AvatarColor] + ) + VALUES + ( + @Id, + @Name, + @Email, + @EmailVerified, + @MasterPassword, + @MasterPasswordHint, + @Culture, + @SecurityStamp, + @TwoFactorProviders, + @TwoFactorRecoveryCode, + @EquivalentDomains, + @ExcludedGlobalEquivalentDomains, + @AccountRevisionDate, + @Key, + @PublicKey, + @PrivateKey, + @Premium, + @PremiumExpirationDate, + @RenewalReminderDate, + @Storage, + @MaxStorageGb, + @Gateway, + @GatewayCustomerId, + @GatewaySubscriptionId, + @ReferenceData, + @LicenseKey, + @Kdf, + @KdfIterations, + @CreationDate, + @RevisionDate, + @ApiKey, + @ForcePasswordReset, + @UsesKeyConnector, + @FailedLoginCount, + @LastFailedLoginDate, + @AvatarColor + ) +END +GO + +-- Stored Procedure: User_Update +CREATE OR ALTER PROCEDURE [dbo].[User_Update] + @Id UNIQUEIDENTIFIER, + @Name NVARCHAR(50), + @Email NVARCHAR(256), + @EmailVerified BIT, + @MasterPassword NVARCHAR(300), + @MasterPasswordHint NVARCHAR(50), + @Culture NVARCHAR(10), + @SecurityStamp NVARCHAR(50), + @TwoFactorProviders NVARCHAR(MAX), + @TwoFactorRecoveryCode NVARCHAR(32), + @EquivalentDomains NVARCHAR(MAX), + @ExcludedGlobalEquivalentDomains NVARCHAR(MAX), + @AccountRevisionDate DATETIME2(7), + @Key NVARCHAR(MAX), + @PublicKey NVARCHAR(MAX), + @PrivateKey NVARCHAR(MAX), + @Premium BIT, + @PremiumExpirationDate DATETIME2(7), + @RenewalReminderDate DATETIME2(7), + @Storage BIGINT, + @MaxStorageGb SMALLINT, + @Gateway TINYINT, + @GatewayCustomerId VARCHAR(50), + @GatewaySubscriptionId VARCHAR(50), + @ReferenceData VARCHAR(MAX), + @LicenseKey VARCHAR(100), + @Kdf TINYINT, + @KdfIterations INT, + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @ApiKey VARCHAR(30), + @ForcePasswordReset BIT = 0, + @UsesKeyConnector BIT = 0, + @FailedLoginCount INT, + @LastFailedLoginDate DATETIME2(7), + @AvatarColor VARCHAR(7) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[User] + SET + [Name] = @Name, + [Email] = @Email, + [EmailVerified] = @EmailVerified, + [MasterPassword] = @MasterPassword, + [MasterPasswordHint] = @MasterPasswordHint, + [Culture] = @Culture, + [SecurityStamp] = @SecurityStamp, + [TwoFactorProviders] = @TwoFactorProviders, + [TwoFactorRecoveryCode] = @TwoFactorRecoveryCode, + [EquivalentDomains] = @EquivalentDomains, + [ExcludedGlobalEquivalentDomains] = @ExcludedGlobalEquivalentDomains, + [AccountRevisionDate] = @AccountRevisionDate, + [Key] = @Key, + [PublicKey] = @PublicKey, + [PrivateKey] = @PrivateKey, + [Premium] = @Premium, + [PremiumExpirationDate] = @PremiumExpirationDate, + [RenewalReminderDate] = @RenewalReminderDate, + [Storage] = @Storage, + [MaxStorageGb] = @MaxStorageGb, + [Gateway] = @Gateway, + [GatewayCustomerId] = @GatewayCustomerId, + [GatewaySubscriptionId] = @GatewaySubscriptionId, + [ReferenceData] = @ReferenceData, + [LicenseKey] = @LicenseKey, + [Kdf] = @Kdf, + [KdfIterations] = @KdfIterations, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate, + [ApiKey] = @ApiKey, + [ForcePasswordReset] = @ForcePasswordReset, + [UsesKeyConnector] = @UsesKeyConnector, + [FailedLoginCount] = @FailedLoginCount, + [LastFailedLoginDate] = @LastFailedLoginDate, + [AvatarColor] = @AvatarColor + WHERE + [Id] = @Id +END +GO diff --git a/util/Migrator/Migrator.csproj b/util/Migrator/Migrator.csproj index 954db4a618..c7f00465dd 100644 --- a/util/Migrator/Migrator.csproj +++ b/util/Migrator/Migrator.csproj @@ -5,7 +5,7 @@ - + diff --git a/util/Migrator/packages.lock.json b/util/Migrator/packages.lock.json index 34bf0f9028..3f4398b3a3 100644 --- a/util/Migrator/packages.lock.json +++ b/util/Migrator/packages.lock.json @@ -4,13 +4,13 @@ "net6.0": { "dbup-sqlserver": { "type": "Direct", - "requested": "[4.5.0, )", - "resolved": "4.5.0", - "contentHash": "/4hy4qmbWmtbLJGq8XCH3mtlgMld2G8rbXcjNDhqkq5y6dGZDW03OI4UsnQRxBiTQD5aYOcLuycK1dCJYhkdSw==", + "requested": "[5.0.8, )", + "resolved": "5.0.8", + "contentHash": "b954l5Zgj9qgHtm16SLq2qGLJ0gIZtrWdh6JHoUsCLMHYW+0K2Oevabquw447At4U6X2t4CNuy7ZLHYf/Z/8yg==", "dependencies": { - "Microsoft.Azure.Services.AppAuthentication": "1.3.1", - "System.Data.SqlClient": "4.6.0", - "dbup-core": "4.5.0" + "Microsoft.Azure.Services.AppAuthentication": "1.6.2", + "Microsoft.Data.SqlClient": "5.0.1", + "dbup-core": "5.0.8" } }, "Microsoft.Extensions.Logging": { @@ -69,8 +69,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -107,28 +107,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -152,13 +152,21 @@ }, "dbup-core": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "CR00QMAtHjfeMhwxFC5haoA0q4KZ5s6Y/AdZaT6oFjySik2eFEqVasuLgWSPKSiR7ti3z01BtiR7aD3nVckAsg==", + "resolved": "5.0.8", + "contentHash": "d+3RxJDftcarp1Y7jI78HRdRWRC7VFjM+rB2CFHWDmao6OixuLrqiyEo1DeuMNrWLTR5mmE8p1YTpFOvozI9ZQ==", "dependencies": { - "Microsoft.CSharp": "4.4.0", + "Microsoft.CSharp": "4.7.0", "System.Diagnostics.TraceSource": "4.3.0" } }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -422,10 +430,10 @@ }, "Microsoft.Azure.Services.AppAuthentication": { "type": "Transitive", - "resolved": "1.3.1", - "contentHash": "59CEcmUSlg5nYOzcyhdoUu+EQH4wrjCKj7dNuuPMeIjDCikAON9/KQXTQLfzfWTjDwqHIRptAAj0DTBp25lFcg==", + "resolved": "1.6.2", + "contentHash": "rSQhTv43ionr9rWvE4vxIe/i73XR5hoBYfh7UUgdaVOGW1MZeikR9RmgaJhonTylimCcCuJvrU0zXsSIFOsTGw==", "dependencies": { - "Microsoft.IdentityModel.Clients.ActiveDirectory": "4.3.0", + "Microsoft.IdentityModel.Clients.ActiveDirectory": "5.2.9", "System.Diagnostics.Process": "4.3.0" } }, @@ -715,15 +723,22 @@ }, "Microsoft.IdentityModel.Clients.ActiveDirectory": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "IRXnTCHxwnpnGBHVnTWd8RBJk7nsBsNZVl8j20kh234bP+oBILkt+6Iw5vQg5Q+sZmALt3Oq6X3Kx7qY71XyVw==", + "resolved": "5.2.9", + "contentHash": "WhBAG/9hWiMHIXve4ZgwXP3spRwf7kFFfejf76QA5BvumgnPp8iDkDCiJugzAcpW1YaHB526z1UVxHhVT1E5qw==", "dependencies": { + "Microsoft.CSharp": "4.3.0", "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.Primitives": "4.3.0", "System.Security.Cryptography.X509Certificates": "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": { @@ -777,8 +792,8 @@ }, "Microsoft.NETCore.Targets": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "1.1.3", + "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==" }, "Microsoft.OData.Core": { "type": "Transitive", @@ -953,16 +968,6 @@ "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": { "type": "Transitive", "resolved": "4.3.0", @@ -1055,21 +1060,6 @@ "resolved": "4.3.2", "contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==" }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==" - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==" - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==" - }, "SendGrid": { "type": "Transitive", "resolved": "9.27.0", @@ -1311,29 +1301,69 @@ }, "System.Collections.NonGeneric": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==", + "resolved": "4.3.0", + "contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", "dependencies": { - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Collections.Specialized": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "/HKQyVP0yH1I0YtK7KJL/28snxHNH/bi+0lgk/+MbURF6ULhAE31MDI+NZDerNWu264YbxklXCCygISgm+HMug==", + "resolved": "4.3.0", + "contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==", "dependencies": { - "System.Collections.NonGeneric": "4.0.1", - "System.Globalization": "4.0.11", - "System.Globalization.Extensions": "4.0.1", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" + "System.Collections.NonGeneric": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.ComponentModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.ComponentModel.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.Configuration.ConfigurationManager": { @@ -1357,17 +1387,6 @@ "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": { "type": "Transitive", "resolved": "4.3.0", @@ -1471,24 +1490,23 @@ }, "System.Dynamic.Runtime": { "type": "Transitive", - "resolved": "4.0.11", - "contentHash": "db34f6LHYM0U0JpE+sOmjar27BnqTVkbLJhgfwMpTdgTigG/Hna3m2MYVwnFzGGKnEJk2UXFuoVTr8WUbU91/A==", + "resolved": "4.3.0", + "contentHash": "SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==", "dependencies": { - "System.Collections": "4.0.11", - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.Linq": "4.1.0", - "System.Linq.Expressions": "4.1.0", - "System.ObjectModel": "4.0.12", - "System.Reflection": "4.1.0", - "System.Reflection.Emit": "4.0.1", - "System.Reflection.Emit.ILGeneration": "4.0.1", - "System.Reflection.Primitives": "4.0.1", - "System.Reflection.TypeExtensions": "4.1.0", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Formats.Asn1": { @@ -1951,6 +1969,15 @@ "System.Xml.XmlSerializer": "4.3.0" } }, + "System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.1", + "Microsoft.NETCore.Targets": "1.1.3" + } + }, "System.Reflection": { "type": "Transitive", "resolved": "4.3.0", @@ -2118,6 +2145,18 @@ "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": { "type": "Transitive", "resolved": "4.3.0", @@ -2609,10 +2648,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/util/MySqlMigrations/HelperScripts/2022-11-03_00_OrganizationDomainClaim.sql b/util/MySqlMigrations/HelperScripts/2022-11-03_00_OrganizationDomainClaim.sql new file mode 100644 index 0000000000..49279dac92 --- /dev/null +++ b/util/MySqlMigrations/HelperScripts/2022-11-03_00_OrganizationDomainClaim.sql @@ -0,0 +1,21 @@ +START TRANSACTION; + +CREATE TABLE `OrganizationDomain` ( + `Id` char(36) COLLATE ascii_general_ci NOT NULL, + `OrganizationId` char(36) COLLATE ascii_general_ci NOT NULL, + `Txt` longtext CHARACTER SET utf8mb4 NULL, + `DomainName` varchar(255) CHARACTER SET utf8mb4 NULL, + `CreationDate` datetime(6) NOT NULL, + `VerifiedDate` datetime(6) NULL, + `NextRunDate` datetime(6) NOT NULL, + `NextRunCount` int NOT NULL, + CONSTRAINT `PK_OrganizationDomain` PRIMARY KEY (`Id`), + CONSTRAINT `FK_OrganizationDomain_Organization_OrganizationId` FOREIGN KEY (`OrganizationId`) REFERENCES `Organization` (`Id`) ON DELETE CASCADE +) CHARACTER SET=utf8mb4; + +CREATE INDEX `IX_OrganizationDomain_OrganizationId` ON `OrganizationDomain` (`OrganizationId`); + +INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`) +VALUES ('20221108015516_OrganizationDomainClaim', '6.0.4'); + +COMMIT; \ No newline at end of file diff --git a/util/MySqlMigrations/HelperScripts/2022-11-28_00_OrganizationDomainClaimRenameNextRunCount.sql b/util/MySqlMigrations/HelperScripts/2022-11-28_00_OrganizationDomainClaimRenameNextRunCount.sql new file mode 100644 index 0000000000..49d35fc005 --- /dev/null +++ b/util/MySqlMigrations/HelperScripts/2022-11-28_00_OrganizationDomainClaimRenameNextRunCount.sql @@ -0,0 +1,8 @@ +START TRANSACTION; + +ALTER TABLE `OrganizationDomain` RENAME COLUMN `NextRunCount` TO `JobRunCount`; + +INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`) +VALUES ('20221129004644_OrganizationDomainClaimRenameNextRunCount', '6.0.4'); + +COMMIT; \ No newline at end of file diff --git a/util/MySqlMigrations/HelperScripts/2022-12-08_00_EventsDomainName.sql b/util/MySqlMigrations/HelperScripts/2022-12-08_00_EventsDomainName.sql new file mode 100644 index 0000000000..bba4fe72fb --- /dev/null +++ b/util/MySqlMigrations/HelperScripts/2022-12-08_00_EventsDomainName.sql @@ -0,0 +1,9 @@ +START TRANSACTION; + +ALTER TABLE `Event` ADD `DomainName` longtext CHARACTER SET utf8mb4 NULL; + +INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`) +VALUES ('20221209015017_EventsDomainName', '6.0.4'); + +COMMIT; + diff --git a/util/MySqlMigrations/HelperScripts/2022-12-09_00_OrganizationDomainLastCheckedDate.sql b/util/MySqlMigrations/HelperScripts/2022-12-09_00_OrganizationDomainLastCheckedDate.sql new file mode 100644 index 0000000000..5204026f97 --- /dev/null +++ b/util/MySqlMigrations/HelperScripts/2022-12-09_00_OrganizationDomainLastCheckedDate.sql @@ -0,0 +1,9 @@ +START TRANSACTION; + +ALTER TABLE `OrganizationDomain` ADD `LastCheckedDate` datetime(6) NULL; + +INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`) +VALUES ('20221209192355_OrganizationDomainLastCheckedDate', '6.0.4'); + +COMMIT; + diff --git a/util/MySqlMigrations/Migrations/20221114202829_OrganizationDomainClaim.Designer.cs b/util/MySqlMigrations/Migrations/20221114202829_OrganizationDomainClaim.Designer.cs new file mode 100644 index 0000000000..9df3c72b4e --- /dev/null +++ b/util/MySqlMigrations/Migrations/20221114202829_OrganizationDomainClaim.Designer.cs @@ -0,0 +1,1722 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20221114202829_OrganizationDomainClaim")] + partial class OrganizationDomainClaim + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("varchar(25)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("AuthenticationDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MasterPasswordHash") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("RequestFingerprint") + .HasColumnType("longtext"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ResponseDate") + .HasColumnType("datetime(6)"); + + b.Property("ResponseDeviceId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Attachments") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Favorites") + .HasColumnType("longtext"); + + b.Property("Folders") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Reprompt") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("GranteeId") + .HasColumnType("char(36)"); + + b.Property("GrantorId") + .HasColumnType("char(36)"); + + b.Property("KeyEncrypted") + .HasColumnType("longtext"); + + b.Property("LastNotificationDate") + .HasColumnType("datetime(6)"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("WaitTimeDays") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ActingUserId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("DeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("PolicyId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("ProviderOrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderUserId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ConsumedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("SelfHost") + .HasColumnType("tinyint(1)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("Use2fa") + .HasColumnType("tinyint(1)"); + + b.Property("UseApi") + .HasColumnType("tinyint(1)"); + + b.Property("UseDirectory") + .HasColumnType("tinyint(1)"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UsePolicies") + .HasColumnType("tinyint(1)"); + + b.Property("UseResetPassword") + .HasColumnType("tinyint(1)"); + + b.Property("UseScim") + .HasColumnType("tinyint(1)"); + + b.Property("UseSso") + .HasColumnType("tinyint(1)"); + + b.Property("UseTotp") + .HasColumnType("tinyint(1)"); + + b.Property("UsersGetPremium") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Config") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("NextRunCount") + .HasColumnType("int"); + + b.Property("NextRunDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Txt") + .HasColumnType("longtext"); + + b.Property("VerifiedDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("LastSyncDate") + .HasColumnType("datetime(6)"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("tinyint unsigned"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("ToDelete") + .HasColumnType("tinyint(1)"); + + b.Property("ValidUntil") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BusinessAddress1") + .HasColumnType("longtext"); + + b.Property("BusinessAddress2") + .HasColumnType("longtext"); + + b.Property("BusinessAddress3") + .HasColumnType("longtext"); + + b.Property("BusinessCountry") + .HasColumnType("longtext"); + + b.Property("BusinessName") + .HasColumnType("longtext"); + + b.Property("BusinessTaxNumber") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Settings") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MaxAccessCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Rate") + .HasColumnType("decimal(65,30)"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PaymentMethodType") + .HasColumnType("tinyint unsigned"); + + b.Property("Refunded") + .HasColumnType("tinyint(1)"); + + b.Property("RefundedAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccountRevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailVerified") + .HasColumnType("tinyint(1)"); + + b.Property("EquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("longtext"); + + b.Property("FailedLoginCount") + .HasColumnType("int"); + + b.Property("ForcePasswordReset") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Kdf") + .HasColumnType("tinyint unsigned"); + + b.Property("KdfIterations") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LastFailedLoginDate") + .HasColumnType("datetime(6)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Premium") + .HasColumnType("tinyint(1)"); + + b.Property("PremiumExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RenewalReminderDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20221114202829_OrganizationDomainClaim.cs b/util/MySqlMigrations/Migrations/20221114202829_OrganizationDomainClaim.cs new file mode 100644 index 0000000000..93c5c522b6 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20221114202829_OrganizationDomainClaim.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +public partial class OrganizationDomainClaim : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "OrganizationDomain", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + OrganizationId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Txt = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + DomainName = table.Column(type: "varchar(255)", maxLength: 255, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CreationDate = table.Column(type: "datetime(6)", nullable: false), + VerifiedDate = table.Column(type: "datetime(6)", nullable: true), + NextRunDate = table.Column(type: "datetime(6)", nullable: false), + NextRunCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrganizationDomain", x => x.Id); + table.ForeignKey( + name: "FK_OrganizationDomain_Organization_OrganizationId", + column: x => x.OrganizationId, + principalTable: "Organization", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_OrganizationDomain_OrganizationId", + table: "OrganizationDomain", + column: "OrganizationId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OrganizationDomain"); + } +} diff --git a/util/MySqlMigrations/Migrations/20221129004644_OrganizationDomainClaimRenameNextRunCount.Designer.cs b/util/MySqlMigrations/Migrations/20221129004644_OrganizationDomainClaimRenameNextRunCount.Designer.cs new file mode 100644 index 0000000000..0d0694ef5a --- /dev/null +++ b/util/MySqlMigrations/Migrations/20221129004644_OrganizationDomainClaimRenameNextRunCount.Designer.cs @@ -0,0 +1,1725 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20221129004644_OrganizationDomainClaimRenameNextRunCount")] + partial class OrganizationDomainClaimRenameNextRunCount + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("varchar(25)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("AuthenticationDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MasterPasswordHash") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("RequestFingerprint") + .HasColumnType("longtext"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ResponseDate") + .HasColumnType("datetime(6)"); + + b.Property("ResponseDeviceId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Attachments") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Favorites") + .HasColumnType("longtext"); + + b.Property("Folders") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Reprompt") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("GranteeId") + .HasColumnType("char(36)"); + + b.Property("GrantorId") + .HasColumnType("char(36)"); + + b.Property("KeyEncrypted") + .HasColumnType("longtext"); + + b.Property("LastNotificationDate") + .HasColumnType("datetime(6)"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("WaitTimeDays") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ActingUserId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("DeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("PolicyId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("ProviderOrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderUserId") + .HasColumnType("char(36)"); + + b.Property("SystemUser") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ConsumedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("SelfHost") + .HasColumnType("tinyint(1)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("Use2fa") + .HasColumnType("tinyint(1)"); + + b.Property("UseApi") + .HasColumnType("tinyint(1)"); + + b.Property("UseDirectory") + .HasColumnType("tinyint(1)"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UsePolicies") + .HasColumnType("tinyint(1)"); + + b.Property("UseResetPassword") + .HasColumnType("tinyint(1)"); + + b.Property("UseScim") + .HasColumnType("tinyint(1)"); + + b.Property("UseSso") + .HasColumnType("tinyint(1)"); + + b.Property("UseTotp") + .HasColumnType("tinyint(1)"); + + b.Property("UsersGetPremium") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Config") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("JobRunCount") + .HasColumnType("int"); + + b.Property("NextRunDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Txt") + .HasColumnType("longtext"); + + b.Property("VerifiedDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("LastSyncDate") + .HasColumnType("datetime(6)"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("tinyint unsigned"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("ToDelete") + .HasColumnType("tinyint(1)"); + + b.Property("ValidUntil") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BusinessAddress1") + .HasColumnType("longtext"); + + b.Property("BusinessAddress2") + .HasColumnType("longtext"); + + b.Property("BusinessAddress3") + .HasColumnType("longtext"); + + b.Property("BusinessCountry") + .HasColumnType("longtext"); + + b.Property("BusinessName") + .HasColumnType("longtext"); + + b.Property("BusinessTaxNumber") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Settings") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MaxAccessCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Rate") + .HasColumnType("decimal(65,30)"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PaymentMethodType") + .HasColumnType("tinyint unsigned"); + + b.Property("Refunded") + .HasColumnType("tinyint(1)"); + + b.Property("RefundedAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccountRevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailVerified") + .HasColumnType("tinyint(1)"); + + b.Property("EquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("longtext"); + + b.Property("FailedLoginCount") + .HasColumnType("int"); + + b.Property("ForcePasswordReset") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Kdf") + .HasColumnType("tinyint unsigned"); + + b.Property("KdfIterations") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LastFailedLoginDate") + .HasColumnType("datetime(6)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Premium") + .HasColumnType("tinyint(1)"); + + b.Property("PremiumExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RenewalReminderDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20221129004644_OrganizationDomainClaimRenameNextRunCount.cs b/util/MySqlMigrations/Migrations/20221129004644_OrganizationDomainClaimRenameNextRunCount.cs new file mode 100644 index 0000000000..a6b2f4c10b --- /dev/null +++ b/util/MySqlMigrations/Migrations/20221129004644_OrganizationDomainClaimRenameNextRunCount.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +public partial class OrganizationDomainClaimRenameNextRunCount : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "NextRunCount", + table: "OrganizationDomain", + newName: "JobRunCount"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "JobRunCount", + table: "OrganizationDomain", + newName: "NextRunCount"); + } +} diff --git a/util/MySqlMigrations/Migrations/20221209015017_EventsDomainName.Designer.cs b/util/MySqlMigrations/Migrations/20221209015017_EventsDomainName.Designer.cs new file mode 100644 index 0000000000..2a2ef90cc7 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20221209015017_EventsDomainName.Designer.cs @@ -0,0 +1,1731 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20221209015017_EventsDomainName")] + partial class EventsDomainName + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("varchar(25)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("AuthenticationDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MasterPasswordHash") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("RequestFingerprint") + .HasColumnType("longtext"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ResponseDate") + .HasColumnType("datetime(6)"); + + b.Property("ResponseDeviceId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Attachments") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Favorites") + .HasColumnType("longtext"); + + b.Property("Folders") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Reprompt") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("GranteeId") + .HasColumnType("char(36)"); + + b.Property("GrantorId") + .HasColumnType("char(36)"); + + b.Property("KeyEncrypted") + .HasColumnType("longtext"); + + b.Property("LastNotificationDate") + .HasColumnType("datetime(6)"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("WaitTimeDays") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ActingUserId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("DeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("DomainName") + .HasColumnType("longtext"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("PolicyId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("ProviderOrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderUserId") + .HasColumnType("char(36)"); + + b.Property("SystemUser") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ConsumedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("SelfHost") + .HasColumnType("tinyint(1)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("Use2fa") + .HasColumnType("tinyint(1)"); + + b.Property("UseApi") + .HasColumnType("tinyint(1)"); + + b.Property("UseCustomPermissions") + .HasColumnType("tinyint(1)"); + + b.Property("UseDirectory") + .HasColumnType("tinyint(1)"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UsePolicies") + .HasColumnType("tinyint(1)"); + + b.Property("UseResetPassword") + .HasColumnType("tinyint(1)"); + + b.Property("UseScim") + .HasColumnType("tinyint(1)"); + + b.Property("UseSso") + .HasColumnType("tinyint(1)"); + + b.Property("UseTotp") + .HasColumnType("tinyint(1)"); + + b.Property("UsersGetPremium") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Config") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("JobRunCount") + .HasColumnType("int"); + + b.Property("NextRunDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Txt") + .HasColumnType("longtext"); + + b.Property("VerifiedDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("LastSyncDate") + .HasColumnType("datetime(6)"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("tinyint unsigned"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("ToDelete") + .HasColumnType("tinyint(1)"); + + b.Property("ValidUntil") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BusinessAddress1") + .HasColumnType("longtext"); + + b.Property("BusinessAddress2") + .HasColumnType("longtext"); + + b.Property("BusinessAddress3") + .HasColumnType("longtext"); + + b.Property("BusinessCountry") + .HasColumnType("longtext"); + + b.Property("BusinessName") + .HasColumnType("longtext"); + + b.Property("BusinessTaxNumber") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Settings") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MaxAccessCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Rate") + .HasColumnType("decimal(65,30)"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PaymentMethodType") + .HasColumnType("tinyint unsigned"); + + b.Property("Refunded") + .HasColumnType("tinyint(1)"); + + b.Property("RefundedAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccountRevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailVerified") + .HasColumnType("tinyint(1)"); + + b.Property("EquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("longtext"); + + b.Property("FailedLoginCount") + .HasColumnType("int"); + + b.Property("ForcePasswordReset") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Kdf") + .HasColumnType("tinyint unsigned"); + + b.Property("KdfIterations") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LastFailedLoginDate") + .HasColumnType("datetime(6)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Premium") + .HasColumnType("tinyint(1)"); + + b.Property("PremiumExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RenewalReminderDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20221209015017_EventsDomainName.cs b/util/MySqlMigrations/Migrations/20221209015017_EventsDomainName.cs new file mode 100644 index 0000000000..8b006a63e3 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20221209015017_EventsDomainName.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +public partial class EventsDomainName : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DomainName", + table: "Event", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DomainName", + table: "Event"); + } +} diff --git a/util/MySqlMigrations/Migrations/20221209192355_OrganizationDomainLastCheckedDate.Designer.cs b/util/MySqlMigrations/Migrations/20221209192355_OrganizationDomainLastCheckedDate.Designer.cs new file mode 100644 index 0000000000..5dc49bb675 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20221209192355_OrganizationDomainLastCheckedDate.Designer.cs @@ -0,0 +1,1734 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20221209192355_OrganizationDomainLastCheckedDate")] + partial class OrganizationDomainLastCheckedDate + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("varchar(25)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("AuthenticationDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MasterPasswordHash") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("RequestFingerprint") + .HasColumnType("longtext"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ResponseDate") + .HasColumnType("datetime(6)"); + + b.Property("ResponseDeviceId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Attachments") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Favorites") + .HasColumnType("longtext"); + + b.Property("Folders") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Reprompt") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("GranteeId") + .HasColumnType("char(36)"); + + b.Property("GrantorId") + .HasColumnType("char(36)"); + + b.Property("KeyEncrypted") + .HasColumnType("longtext"); + + b.Property("LastNotificationDate") + .HasColumnType("datetime(6)"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("WaitTimeDays") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ActingUserId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("DeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("DomainName") + .HasColumnType("longtext"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("PolicyId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("ProviderOrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderUserId") + .HasColumnType("char(36)"); + + b.Property("SystemUser") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ConsumedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("SelfHost") + .HasColumnType("tinyint(1)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("Use2fa") + .HasColumnType("tinyint(1)"); + + b.Property("UseApi") + .HasColumnType("tinyint(1)"); + + b.Property("UseCustomPermissions") + .HasColumnType("tinyint(1)"); + + b.Property("UseDirectory") + .HasColumnType("tinyint(1)"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UsePolicies") + .HasColumnType("tinyint(1)"); + + b.Property("UseResetPassword") + .HasColumnType("tinyint(1)"); + + b.Property("UseScim") + .HasColumnType("tinyint(1)"); + + b.Property("UseSso") + .HasColumnType("tinyint(1)"); + + b.Property("UseTotp") + .HasColumnType("tinyint(1)"); + + b.Property("UsersGetPremium") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Config") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("JobRunCount") + .HasColumnType("int"); + + b.Property("LastCheckedDate") + .HasColumnType("datetime(6)"); + + b.Property("NextRunDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Txt") + .HasColumnType("longtext"); + + b.Property("VerifiedDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("LastSyncDate") + .HasColumnType("datetime(6)"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("tinyint unsigned"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("ToDelete") + .HasColumnType("tinyint(1)"); + + b.Property("ValidUntil") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BusinessAddress1") + .HasColumnType("longtext"); + + b.Property("BusinessAddress2") + .HasColumnType("longtext"); + + b.Property("BusinessAddress3") + .HasColumnType("longtext"); + + b.Property("BusinessCountry") + .HasColumnType("longtext"); + + b.Property("BusinessName") + .HasColumnType("longtext"); + + b.Property("BusinessTaxNumber") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Settings") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MaxAccessCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Rate") + .HasColumnType("decimal(65,30)"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PaymentMethodType") + .HasColumnType("tinyint unsigned"); + + b.Property("Refunded") + .HasColumnType("tinyint(1)"); + + b.Property("RefundedAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccountRevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailVerified") + .HasColumnType("tinyint(1)"); + + b.Property("EquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("longtext"); + + b.Property("FailedLoginCount") + .HasColumnType("int"); + + b.Property("ForcePasswordReset") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Kdf") + .HasColumnType("tinyint unsigned"); + + b.Property("KdfIterations") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LastFailedLoginDate") + .HasColumnType("datetime(6)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Premium") + .HasColumnType("tinyint(1)"); + + b.Property("PremiumExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RenewalReminderDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20221209192355_OrganizationDomainLastCheckedDate.cs b/util/MySqlMigrations/Migrations/20221209192355_OrganizationDomainLastCheckedDate.cs new file mode 100644 index 0000000000..f6884cb5ca --- /dev/null +++ b/util/MySqlMigrations/Migrations/20221209192355_OrganizationDomainLastCheckedDate.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +public partial class OrganizationDomainLastCheckedDate : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "LastCheckedDate", + table: "OrganizationDomain", + type: "datetime(6)", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "LastCheckedDate", + table: "OrganizationDomain"); + } +} diff --git a/util/MySqlMigrations/Migrations/20230118225349_RemoveDeviceUnknownVerification.Designer.cs b/util/MySqlMigrations/Migrations/20230118225349_RemoveDeviceUnknownVerification.Designer.cs new file mode 100644 index 0000000000..1cdec6545e --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230118225349_RemoveDeviceUnknownVerification.Designer.cs @@ -0,0 +1,2095 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230118225349_RemoveDeviceUnknownVerification")] + partial class RemoveDeviceUnknownVerification + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Read") + .HasColumnType("tinyint(1)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Write") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ClientSecret") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("varchar(25)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("AuthenticationDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MasterPasswordHash") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("RequestFingerprint") + .HasColumnType("longtext"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ResponseDate") + .HasColumnType("datetime(6)"); + + b.Property("ResponseDeviceId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Attachments") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Favorites") + .HasColumnType("longtext"); + + b.Property("Folders") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Reprompt") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("GranteeId") + .HasColumnType("char(36)"); + + b.Property("GrantorId") + .HasColumnType("char(36)"); + + b.Property("KeyEncrypted") + .HasColumnType("longtext"); + + b.Property("LastNotificationDate") + .HasColumnType("datetime(6)"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("WaitTimeDays") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ActingUserId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("DeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("PolicyId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("ProviderOrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderUserId") + .HasColumnType("char(36)"); + + b.Property("SystemUser") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ConsumedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("SelfHost") + .HasColumnType("tinyint(1)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("Use2fa") + .HasColumnType("tinyint(1)"); + + b.Property("UseApi") + .HasColumnType("tinyint(1)"); + + b.Property("UseCustomPermissions") + .HasColumnType("tinyint(1)"); + + b.Property("UseDirectory") + .HasColumnType("tinyint(1)"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UsePolicies") + .HasColumnType("tinyint(1)"); + + b.Property("UseResetPassword") + .HasColumnType("tinyint(1)"); + + b.Property("UseScim") + .HasColumnType("tinyint(1)"); + + b.Property("UseSecretsManager") + .HasColumnType("tinyint(1)"); + + b.Property("UseSso") + .HasColumnType("tinyint(1)"); + + b.Property("UseTotp") + .HasColumnType("tinyint(1)"); + + b.Property("UsersGetPremium") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Config") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("LastSyncDate") + .HasColumnType("datetime(6)"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("tinyint unsigned"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("ToDelete") + .HasColumnType("tinyint(1)"); + + b.Property("ValidUntil") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Project", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BusinessAddress1") + .HasColumnType("longtext"); + + b.Property("BusinessAddress2") + .HasColumnType("longtext"); + + b.Property("BusinessAddress3") + .HasColumnType("longtext"); + + b.Property("BusinessCountry") + .HasColumnType("longtext"); + + b.Property("BusinessName") + .HasColumnType("longtext"); + + b.Property("BusinessTaxNumber") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Settings") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Note") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MaxAccessCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Rate") + .HasColumnType("decimal(65,30)"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PaymentMethodType") + .HasColumnType("tinyint unsigned"); + + b.Property("Refunded") + .HasColumnType("tinyint(1)"); + + b.Property("RefundedAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccountRevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailVerified") + .HasColumnType("tinyint(1)"); + + b.Property("EquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("longtext"); + + b.Property("FailedLoginCount") + .HasColumnType("int"); + + b.Property("ForcePasswordReset") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Kdf") + .HasColumnType("tinyint unsigned"); + + b.Property("KdfIterations") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LastFailedLoginDate") + .HasColumnType("datetime(6)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Premium") + .HasColumnType("tinyint(1)"); + + b.Property("PremiumExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RenewalReminderDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("char(36)"); + + b.Property("SecretsId") + .HasColumnType("char(36)"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany() + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany() + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20230118225349_RemoveDeviceUnknownVerification.cs b/util/MySqlMigrations/Migrations/20230118225349_RemoveDeviceUnknownVerification.cs new file mode 100644 index 0000000000..06c4daaf3b --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230118225349_RemoveDeviceUnknownVerification.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +public partial class RemoveDeviceUnknownVerification : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UnknownDeviceVerificationEnabled", + table: "User"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UnknownDeviceVerificationEnabled", + table: "User", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + } +} diff --git a/util/MySqlMigrations/Migrations/20230213133250_SecretsManagerEvent.Designer.cs b/util/MySqlMigrations/Migrations/20230213133250_SecretsManagerEvent.Designer.cs new file mode 100644 index 0000000000..fb72ab0d7b --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230213133250_SecretsManagerEvent.Designer.cs @@ -0,0 +1,2138 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230213133250_SecretsManagerEvent")] + partial class SecretsManagerEvent + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("varchar(25)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("AuthenticationDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MasterPasswordHash") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("RequestFingerprint") + .HasColumnType("longtext"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ResponseDate") + .HasColumnType("datetime(6)"); + + b.Property("ResponseDeviceId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Attachments") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Favorites") + .HasColumnType("longtext"); + + b.Property("Folders") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Reprompt") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("GranteeId") + .HasColumnType("char(36)"); + + b.Property("GrantorId") + .HasColumnType("char(36)"); + + b.Property("KeyEncrypted") + .HasColumnType("longtext"); + + b.Property("LastNotificationDate") + .HasColumnType("datetime(6)"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("WaitTimeDays") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ActingUserId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("DeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("PolicyId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("ProviderOrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderUserId") + .HasColumnType("char(36)"); + + b.Property("SecretId") + .HasColumnType("char(36)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + + b.Property("SystemUser") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ConsumedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("SelfHost") + .HasColumnType("tinyint(1)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("Use2fa") + .HasColumnType("tinyint(1)"); + + b.Property("UseApi") + .HasColumnType("tinyint(1)"); + + b.Property("UseCustomPermissions") + .HasColumnType("tinyint(1)"); + + b.Property("UseDirectory") + .HasColumnType("tinyint(1)"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UsePolicies") + .HasColumnType("tinyint(1)"); + + b.Property("UseResetPassword") + .HasColumnType("tinyint(1)"); + + b.Property("UseScim") + .HasColumnType("tinyint(1)"); + + b.Property("UseSecretsManager") + .HasColumnType("tinyint(1)"); + + b.Property("UseSso") + .HasColumnType("tinyint(1)"); + + b.Property("UseTotp") + .HasColumnType("tinyint(1)"); + + b.Property("UsersGetPremium") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Config") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("LastSyncDate") + .HasColumnType("datetime(6)"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("tinyint unsigned"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("ToDelete") + .HasColumnType("tinyint(1)"); + + b.Property("ValidUntil") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("AccessSecretsManager") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BillingPhone") + .HasColumnType("longtext"); + + b.Property("BusinessAddress1") + .HasColumnType("longtext"); + + b.Property("BusinessAddress2") + .HasColumnType("longtext"); + + b.Property("BusinessAddress3") + .HasColumnType("longtext"); + + b.Property("BusinessCountry") + .HasColumnType("longtext"); + + b.Property("BusinessName") + .HasColumnType("longtext"); + + b.Property("BusinessTaxNumber") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Settings") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MaxAccessCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Rate") + .HasColumnType("decimal(65,30)"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PaymentMethodType") + .HasColumnType("tinyint unsigned"); + + b.Property("Refunded") + .HasColumnType("tinyint(1)"); + + b.Property("RefundedAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccountRevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailVerified") + .HasColumnType("tinyint(1)"); + + b.Property("EquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("longtext"); + + b.Property("FailedLoginCount") + .HasColumnType("int"); + + b.Property("ForcePasswordReset") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Kdf") + .HasColumnType("tinyint unsigned"); + + b.Property("KdfIterations") + .HasColumnType("int"); + + b.Property("KdfMemory") + .HasColumnType("int"); + + b.Property("KdfParallelism") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LastEmailChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LastFailedLoginDate") + .HasColumnType("datetime(6)"); + + b.Property("LastKdfChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LastKeyRotationDate") + .HasColumnType("datetime(6)"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Premium") + .HasColumnType("tinyint(1)"); + + b.Property("PremiumExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RenewalReminderDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Read") + .HasColumnType("tinyint(1)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Write") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ClientSecret") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Note") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("char(36)"); + + b.Property("SecretsId") + .HasColumnType("char(36)"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20230213133250_SecretsManagerEvent.cs b/util/MySqlMigrations/Migrations/20230213133250_SecretsManagerEvent.cs new file mode 100644 index 0000000000..91d12c745d --- /dev/null +++ b/util/MySqlMigrations/Migrations/20230213133250_SecretsManagerEvent.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +public partial class SecretsManagerEvent : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SecretId", + table: "Event", + type: "char(36)", + nullable: true, + collation: "ascii_general_ci"); + + migrationBuilder.AddColumn( + name: "ServiceAccountId", + table: "Event", + type: "char(36)", + nullable: true, + collation: "ascii_general_ci"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretId", + table: "Event"); + + migrationBuilder.DropColumn( + name: "ServiceAccountId", + table: "Event"); + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index 1bbdcd7ccd..0ede57ecdc 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -318,6 +318,9 @@ namespace Bit.MySqlMigrations.Migrations b.Property("DeviceType") .HasColumnType("tinyint unsigned"); + b.Property("DomainName") + .HasColumnType("longtext"); + b.Property("GroupId") .HasColumnType("char(36)"); @@ -346,6 +349,12 @@ namespace Bit.MySqlMigrations.Migrations b.Property("ProviderUserId") .HasColumnType("char(36)"); + b.Property("SecretId") + .HasColumnType("char(36)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + b.Property("SystemUser") .HasColumnType("tinyint unsigned"); @@ -705,6 +714,43 @@ namespace Bit.MySqlMigrations.Migrations b.ToTable("OrganizationConnection", (string)null); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("JobRunCount") + .HasColumnType("int"); + + b.Property("LastCheckedDate") + .HasColumnType("datetime(6)"); + + b.Property("NextRunDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Txt") + .HasColumnType("longtext"); + + b.Property("VerifiedDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => { b.Property("Id") @@ -1292,9 +1338,6 @@ namespace Bit.MySqlMigrations.Migrations .HasMaxLength(32) .HasColumnType("varchar(32)"); - b.Property("UnknownDeviceVerificationEnabled") - .HasColumnType("tinyint(1)"); - b.Property("UsesKeyConnector") .HasColumnType("tinyint(1)"); @@ -1783,6 +1826,17 @@ namespace Bit.MySqlMigrations.Migrations b.Navigation("Organization"); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => { b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") @@ -2079,6 +2133,8 @@ namespace Bit.MySqlMigrations.Migrations b.Navigation("Connections"); + b.Navigation("Domains"); + b.Navigation("Groups"); b.Navigation("OrganizationUsers"); diff --git a/util/MySqlMigrations/packages.lock.json b/util/MySqlMigrations/packages.lock.json index 257b0d059c..209f5237d7 100644 --- a/util/MySqlMigrations/packages.lock.json +++ b/util/MySqlMigrations/packages.lock.json @@ -72,8 +72,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -110,28 +110,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -153,6 +153,14 @@ "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": { "type": "Transitive", "resolved": "3.0.1", @@ -2736,10 +2744,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/util/PostgresMigrations/HelperScripts/2022-11-03_00_OrganizationDomainClaim.psql b/util/PostgresMigrations/HelperScripts/2022-11-03_00_OrganizationDomainClaim.psql new file mode 100644 index 0000000000..a493c33733 --- /dev/null +++ b/util/PostgresMigrations/HelperScripts/2022-11-03_00_OrganizationDomainClaim.psql @@ -0,0 +1,21 @@ +START TRANSACTION; + +CREATE TABLE "OrganizationDomain" ( + "Id" uuid NOT NULL, + "OrganizationId" uuid NOT NULL, + "Txt" text NULL, + "DomainName" character varying(255) NULL, + "CreationDate" timestamp with time zone NOT NULL, + "VerifiedDate" timestamp with time zone NULL, + "NextRunDate" timestamp with time zone NOT NULL, + "NextRunCount" integer NOT NULL, + CONSTRAINT "PK_OrganizationDomain" PRIMARY KEY ("Id"), + CONSTRAINT "FK_OrganizationDomain_Organization_OrganizationId" FOREIGN KEY ("OrganizationId") REFERENCES "Organization" ("Id") ON DELETE CASCADE +); + +CREATE INDEX "IX_OrganizationDomain_OrganizationId" ON "OrganizationDomain" ("OrganizationId"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20221108020928_OrganizationDomainClaim', '6.0.4'); + +COMMIT; \ No newline at end of file diff --git a/util/PostgresMigrations/HelperScripts/2022-11-28_00_OrganizationDomainClaimRenameNextRunCount.psql b/util/PostgresMigrations/HelperScripts/2022-11-28_00_OrganizationDomainClaimRenameNextRunCount.psql new file mode 100644 index 0000000000..357a596a2d --- /dev/null +++ b/util/PostgresMigrations/HelperScripts/2022-11-28_00_OrganizationDomainClaimRenameNextRunCount.psql @@ -0,0 +1,8 @@ +START TRANSACTION; + +ALTER TABLE "OrganizationDomain" RENAME COLUMN "NextRunCount" TO "JobRunCount"; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20221129032517_OrganizationDomainClaimRenameNextRunCount', '6.0.4'); + +COMMIT; \ No newline at end of file diff --git a/util/PostgresMigrations/HelperScripts/2022-12-08_00_EventsDomainName.psql b/util/PostgresMigrations/HelperScripts/2022-12-08_00_EventsDomainName.psql new file mode 100644 index 0000000000..5143d998d6 --- /dev/null +++ b/util/PostgresMigrations/HelperScripts/2022-12-08_00_EventsDomainName.psql @@ -0,0 +1,9 @@ +START TRANSACTION; + +ALTER TABLE "Event" ADD "DomainName" text NULL; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20221209020447_EventsDomainName', '6.0.4'); + +COMMIT; + diff --git a/util/PostgresMigrations/HelperScripts/2022-12-09_00_OrganizationDomainLastCheckedDate.psql b/util/PostgresMigrations/HelperScripts/2022-12-09_00_OrganizationDomainLastCheckedDate.psql new file mode 100644 index 0000000000..ff099eee6c --- /dev/null +++ b/util/PostgresMigrations/HelperScripts/2022-12-09_00_OrganizationDomainLastCheckedDate.psql @@ -0,0 +1,9 @@ +START TRANSACTION; + +ALTER TABLE "OrganizationDomain" ADD "LastCheckedDate" timestamp with time zone NULL; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20221209194623_OrganizationDomainLastCheckedDate', '6.0.4'); + +COMMIT; + diff --git a/util/PostgresMigrations/Migrations/20221025033204_PasswordlessAuthRequestAddApprovedColumn.Designer.cs b/util/PostgresMigrations/Migrations/20221025033204_PasswordlessAuthRequestAddApprovedColumn.Designer.cs index 936ab4648c..478ea452e5 100644 --- a/util/PostgresMigrations/Migrations/20221025033204_PasswordlessAuthRequestAddApprovedColumn.Designer.cs +++ b/util/PostgresMigrations/Migrations/20221025033204_PasswordlessAuthRequestAddApprovedColumn.Designer.cs @@ -38,10 +38,10 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("boolean"); b.Property("AuthenticationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Key") .HasColumnType("text"); @@ -67,7 +67,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(50)"); b.Property("ResponseDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("ResponseDeviceId") .HasColumnType("uuid"); @@ -96,13 +96,13 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Data") .HasColumnType("text"); b.Property("DeletedDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Favorites") .HasColumnType("text"); @@ -117,7 +117,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("smallint"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Type") .HasColumnType("smallint"); @@ -140,7 +140,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("ExternalId") .HasMaxLength(300) @@ -153,7 +153,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -231,7 +231,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Identifier") .HasMaxLength(50) @@ -246,7 +246,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(255)"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Type") .HasColumnType("smallint"); @@ -267,7 +267,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Email") .HasMaxLength(256) @@ -283,13 +283,13 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("LastNotificationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("RecoveryInitiatedDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Status") .HasColumnType("smallint"); @@ -324,7 +324,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("Date") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("DeviceType") .HasColumnType("smallint"); @@ -374,13 +374,13 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Name") .HasColumnType("text"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("UserId") .HasColumnType("uuid"); @@ -403,10 +403,10 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(200)"); b.Property("ConsumedDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Data") .HasColumnType("text"); @@ -416,7 +416,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(200)"); b.Property("ExpirationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("SessionId") .HasMaxLength(100) @@ -444,7 +444,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("boolean"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("ExternalId") .HasMaxLength(300) @@ -458,7 +458,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -493,7 +493,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Email") .HasMaxLength(256) @@ -545,13 +545,13 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(30)"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Enabled") .HasColumnType("boolean"); b.Property("ExpirationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Gateway") .HasColumnType("smallint"); @@ -587,7 +587,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(50)"); b.Property("OwnersNotifiedOfAutoscaling") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Plan") .HasMaxLength(50) @@ -606,7 +606,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Seats") .HasColumnType("integer"); @@ -674,7 +674,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Type") .HasColumnType("smallint"); @@ -720,7 +720,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(256)"); b.Property("LastSyncDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("OfferedToEmail") .HasMaxLength(256) @@ -742,7 +742,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("boolean"); b.Property("ValidUntil") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -762,7 +762,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("boolean"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Email") .HasMaxLength(256) @@ -785,7 +785,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Status") .HasColumnType("smallint"); @@ -811,7 +811,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Data") .HasColumnType("text"); @@ -823,7 +823,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Type") .HasColumnType("smallint"); @@ -862,7 +862,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Enabled") .HasColumnType("boolean"); @@ -871,7 +871,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Status") .HasColumnType("smallint"); @@ -890,7 +890,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Key") .HasColumnType("text"); @@ -902,7 +902,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Settings") .HasColumnType("text"); @@ -922,7 +922,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Email") .HasColumnType("text"); @@ -937,7 +937,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Status") .HasColumnType("smallint"); @@ -966,19 +966,19 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("integer"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Data") .HasColumnType("text"); b.Property("DeletionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Disabled") .HasColumnType("boolean"); b.Property("ExpirationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("HideEmail") .HasColumnType("boolean"); @@ -997,7 +997,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(300)"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Type") .HasColumnType("smallint"); @@ -1023,7 +1023,7 @@ namespace Bit.PostgresMigrations.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Data") .HasColumnType("text"); @@ -1035,7 +1035,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -1053,7 +1053,7 @@ namespace Bit.PostgresMigrations.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("ExternalId") .HasMaxLength(50) @@ -1113,7 +1113,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("numeric"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Details") .HasMaxLength(100) @@ -1159,7 +1159,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("uuid"); b.Property("AccountRevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("ApiKey") .IsRequired() @@ -1167,7 +1167,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("character varying(30)"); b.Property("CreationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("Culture") .HasMaxLength(10) @@ -1215,7 +1215,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("LastFailedLoginDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("LicenseKey") .HasMaxLength(100) @@ -1240,7 +1240,7 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("boolean"); b.Property("PremiumExpirationDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("PrivateKey") .HasColumnType("text"); @@ -1252,10 +1252,10 @@ namespace Bit.PostgresMigrations.Migrations .HasColumnType("text"); b.Property("RenewalReminderDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("RevisionDate") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("SecurityStamp") .IsRequired() diff --git a/util/PostgresMigrations/Migrations/20221114192912_OrganizationDomainClaim.Designer.cs b/util/PostgresMigrations/Migrations/20221114192912_OrganizationDomainClaim.Designer.cs new file mode 100644 index 0000000000..bf669a7fae --- /dev/null +++ b/util/PostgresMigrations/Migrations/20221114192912_OrganizationDomainClaim.Designer.cs @@ -0,0 +1,1733 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20221114192912_OrganizationDomainClaim")] + partial class OrganizationDomainClaim + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") + .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("AuthenticationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MasterPasswordHash") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestFingerprint") + .HasColumnType("text"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ResponseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ResponseDeviceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Favorites") + .HasColumnType("text"); + + b.Property("Folders") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Reprompt") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GranteeId") + .HasColumnType("uuid"); + + b.Property("GrantorId") + .HasColumnType("uuid"); + + b.Property("KeyEncrypted") + .HasColumnType("text"); + + b.Property("LastNotificationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitTimeDays") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ActingUserId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceType") + .HasColumnType("smallint"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("PolicyId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("ProviderOrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderUserId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp with time zone"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("SelfHost") + .HasColumnType("boolean"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("Use2fa") + .HasColumnType("boolean"); + + b.Property("UseApi") + .HasColumnType("boolean"); + + b.Property("UseDirectory") + .HasColumnType("boolean"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UsePolicies") + .HasColumnType("boolean"); + + b.Property("UseResetPassword") + .HasColumnType("boolean"); + + b.Property("UseScim") + .HasColumnType("boolean"); + + b.Property("UseSso") + .HasColumnType("boolean"); + + b.Property("UseTotp") + .HasColumnType("boolean"); + + b.Property("UsersGetPremium") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Config") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("NextRunCount") + .HasColumnType("integer"); + + b.Property("NextRunDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Txt") + .HasColumnType("text"); + + b.Property("VerifiedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LastSyncDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("smallint"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("ToDelete") + .HasColumnType("boolean"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MaxAccessCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PaymentMethodType") + .HasColumnType("smallint"); + + b.Property("Refunded") + .HasColumnType("boolean"); + + b.Property("RefundedAmount") + .HasColumnType("numeric"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountRevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("EquivalentDomains") + .HasColumnType("text"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("text"); + + b.Property("FailedLoginCount") + .HasColumnType("integer"); + + b.Property("ForcePasswordReset") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Kdf") + .HasColumnType("smallint"); + + b.Property("KdfIterations") + .HasColumnType("integer"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LastFailedLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Premium") + .HasColumnType("boolean"); + + b.Property("PremiumExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RenewalReminderDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("boolean"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20221114192912_OrganizationDomainClaim.cs b/util/PostgresMigrations/Migrations/20221114192912_OrganizationDomainClaim.cs new file mode 100644 index 0000000000..c94070b19a --- /dev/null +++ b/util/PostgresMigrations/Migrations/20221114192912_OrganizationDomainClaim.cs @@ -0,0 +1,46 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +public partial class OrganizationDomainClaim : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "OrganizationDomain", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + OrganizationId = table.Column(type: "uuid", nullable: false), + Txt = table.Column(type: "text", nullable: true), + DomainName = table.Column(type: "character varying(255)", maxLength: 255, nullable: true), + CreationDate = table.Column(type: "timestamp with time zone", nullable: false), + VerifiedDate = table.Column(type: "timestamp with time zone", nullable: true), + NextRunDate = table.Column(type: "timestamp with time zone", nullable: false), + NextRunCount = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrganizationDomain", x => x.Id); + table.ForeignKey( + name: "FK_OrganizationDomain_Organization_OrganizationId", + column: x => x.OrganizationId, + principalTable: "Organization", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_OrganizationDomain_OrganizationId", + table: "OrganizationDomain", + column: "OrganizationId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OrganizationDomain"); + } +} diff --git a/util/PostgresMigrations/Migrations/20221129032517_OrganizationDomainClaimRenameNextRunCount.Designer.cs b/util/PostgresMigrations/Migrations/20221129032517_OrganizationDomainClaimRenameNextRunCount.Designer.cs new file mode 100644 index 0000000000..b648dde8f0 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20221129032517_OrganizationDomainClaimRenameNextRunCount.Designer.cs @@ -0,0 +1,1736 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20221129032517_OrganizationDomainClaimRenameNextRunCount")] + partial class OrganizationDomainClaimRenameNextRunCount + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") + .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("AuthenticationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MasterPasswordHash") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestFingerprint") + .HasColumnType("text"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ResponseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ResponseDeviceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Favorites") + .HasColumnType("text"); + + b.Property("Folders") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Reprompt") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GranteeId") + .HasColumnType("uuid"); + + b.Property("GrantorId") + .HasColumnType("uuid"); + + b.Property("KeyEncrypted") + .HasColumnType("text"); + + b.Property("LastNotificationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitTimeDays") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ActingUserId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceType") + .HasColumnType("smallint"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("PolicyId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("ProviderOrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderUserId") + .HasColumnType("uuid"); + + b.Property("SystemUser") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp with time zone"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("SelfHost") + .HasColumnType("boolean"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("Use2fa") + .HasColumnType("boolean"); + + b.Property("UseApi") + .HasColumnType("boolean"); + + b.Property("UseDirectory") + .HasColumnType("boolean"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UsePolicies") + .HasColumnType("boolean"); + + b.Property("UseResetPassword") + .HasColumnType("boolean"); + + b.Property("UseScim") + .HasColumnType("boolean"); + + b.Property("UseSso") + .HasColumnType("boolean"); + + b.Property("UseTotp") + .HasColumnType("boolean"); + + b.Property("UsersGetPremium") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Config") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("JobRunCount") + .HasColumnType("integer"); + + b.Property("NextRunDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Txt") + .HasColumnType("text"); + + b.Property("VerifiedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LastSyncDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("smallint"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("ToDelete") + .HasColumnType("boolean"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MaxAccessCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PaymentMethodType") + .HasColumnType("smallint"); + + b.Property("Refunded") + .HasColumnType("boolean"); + + b.Property("RefundedAmount") + .HasColumnType("numeric"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountRevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("EquivalentDomains") + .HasColumnType("text"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("text"); + + b.Property("FailedLoginCount") + .HasColumnType("integer"); + + b.Property("ForcePasswordReset") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Kdf") + .HasColumnType("smallint"); + + b.Property("KdfIterations") + .HasColumnType("integer"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LastFailedLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Premium") + .HasColumnType("boolean"); + + b.Property("PremiumExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RenewalReminderDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("boolean"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20221129032517_OrganizationDomainClaimRenameNextRunCount.cs b/util/PostgresMigrations/Migrations/20221129032517_OrganizationDomainClaimRenameNextRunCount.cs new file mode 100644 index 0000000000..65f845ebe2 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20221129032517_OrganizationDomainClaimRenameNextRunCount.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +public partial class OrganizationDomainClaimRenameNextRunCount : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "NextRunCount", + table: "OrganizationDomain", + newName: "JobRunCount"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "JobRunCount", + table: "OrganizationDomain", + newName: "NextRunCount"); + } +} diff --git a/util/PostgresMigrations/Migrations/20221209020447_EventsDomainName.Designer.cs b/util/PostgresMigrations/Migrations/20221209020447_EventsDomainName.Designer.cs new file mode 100644 index 0000000000..5de4580846 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20221209020447_EventsDomainName.Designer.cs @@ -0,0 +1,1742 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20221209020447_EventsDomainName")] + partial class EventsDomainName + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") + .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("AuthenticationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MasterPasswordHash") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestFingerprint") + .HasColumnType("text"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ResponseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ResponseDeviceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Favorites") + .HasColumnType("text"); + + b.Property("Folders") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Reprompt") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GranteeId") + .HasColumnType("uuid"); + + b.Property("GrantorId") + .HasColumnType("uuid"); + + b.Property("KeyEncrypted") + .HasColumnType("text"); + + b.Property("LastNotificationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitTimeDays") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ActingUserId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceType") + .HasColumnType("smallint"); + + b.Property("DomainName") + .HasColumnType("text"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("PolicyId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("ProviderOrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderUserId") + .HasColumnType("uuid"); + + b.Property("SystemUser") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp with time zone"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("SelfHost") + .HasColumnType("boolean"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("Use2fa") + .HasColumnType("boolean"); + + b.Property("UseApi") + .HasColumnType("boolean"); + + b.Property("UseCustomPermissions") + .HasColumnType("boolean"); + + b.Property("UseDirectory") + .HasColumnType("boolean"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UsePolicies") + .HasColumnType("boolean"); + + b.Property("UseResetPassword") + .HasColumnType("boolean"); + + b.Property("UseScim") + .HasColumnType("boolean"); + + b.Property("UseSso") + .HasColumnType("boolean"); + + b.Property("UseTotp") + .HasColumnType("boolean"); + + b.Property("UsersGetPremium") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Config") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("JobRunCount") + .HasColumnType("integer"); + + b.Property("NextRunDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Txt") + .HasColumnType("text"); + + b.Property("VerifiedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LastSyncDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("smallint"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("ToDelete") + .HasColumnType("boolean"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MaxAccessCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PaymentMethodType") + .HasColumnType("smallint"); + + b.Property("Refunded") + .HasColumnType("boolean"); + + b.Property("RefundedAmount") + .HasColumnType("numeric"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountRevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("EquivalentDomains") + .HasColumnType("text"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("text"); + + b.Property("FailedLoginCount") + .HasColumnType("integer"); + + b.Property("ForcePasswordReset") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Kdf") + .HasColumnType("smallint"); + + b.Property("KdfIterations") + .HasColumnType("integer"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LastFailedLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Premium") + .HasColumnType("boolean"); + + b.Property("PremiumExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RenewalReminderDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("boolean"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20221209020447_EventsDomainName.cs b/util/PostgresMigrations/Migrations/20221209020447_EventsDomainName.cs new file mode 100644 index 0000000000..c5fb22bcf3 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20221209020447_EventsDomainName.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +public partial class EventsDomainName : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DomainName", + table: "Event", + type: "text", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DomainName", + table: "Event"); + } +} diff --git a/util/PostgresMigrations/Migrations/20221209194623_OrganizationDomainLastCheckedDate.Designer.cs b/util/PostgresMigrations/Migrations/20221209194623_OrganizationDomainLastCheckedDate.Designer.cs new file mode 100644 index 0000000000..a0520d308a --- /dev/null +++ b/util/PostgresMigrations/Migrations/20221209194623_OrganizationDomainLastCheckedDate.Designer.cs @@ -0,0 +1,1745 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20221209194623_OrganizationDomainLastCheckedDate")] + partial class OrganizationDomainLastCheckedDate + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") + .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("AuthenticationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MasterPasswordHash") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestFingerprint") + .HasColumnType("text"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ResponseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ResponseDeviceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Favorites") + .HasColumnType("text"); + + b.Property("Folders") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Reprompt") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GranteeId") + .HasColumnType("uuid"); + + b.Property("GrantorId") + .HasColumnType("uuid"); + + b.Property("KeyEncrypted") + .HasColumnType("text"); + + b.Property("LastNotificationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitTimeDays") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ActingUserId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceType") + .HasColumnType("smallint"); + + b.Property("DomainName") + .HasColumnType("text"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("PolicyId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("ProviderOrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderUserId") + .HasColumnType("uuid"); + + b.Property("SystemUser") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp with time zone"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("SelfHost") + .HasColumnType("boolean"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("Use2fa") + .HasColumnType("boolean"); + + b.Property("UseApi") + .HasColumnType("boolean"); + + b.Property("UseCustomPermissions") + .HasColumnType("boolean"); + + b.Property("UseDirectory") + .HasColumnType("boolean"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UsePolicies") + .HasColumnType("boolean"); + + b.Property("UseResetPassword") + .HasColumnType("boolean"); + + b.Property("UseScim") + .HasColumnType("boolean"); + + b.Property("UseSso") + .HasColumnType("boolean"); + + b.Property("UseTotp") + .HasColumnType("boolean"); + + b.Property("UsersGetPremium") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Config") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("JobRunCount") + .HasColumnType("integer"); + + b.Property("LastCheckedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("NextRunDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Txt") + .HasColumnType("text"); + + b.Property("VerifiedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LastSyncDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("smallint"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("ToDelete") + .HasColumnType("boolean"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MaxAccessCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PaymentMethodType") + .HasColumnType("smallint"); + + b.Property("Refunded") + .HasColumnType("boolean"); + + b.Property("RefundedAmount") + .HasColumnType("numeric"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountRevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("EquivalentDomains") + .HasColumnType("text"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("text"); + + b.Property("FailedLoginCount") + .HasColumnType("integer"); + + b.Property("ForcePasswordReset") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Kdf") + .HasColumnType("smallint"); + + b.Property("KdfIterations") + .HasColumnType("integer"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LastFailedLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Premium") + .HasColumnType("boolean"); + + b.Property("PremiumExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RenewalReminderDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("boolean"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20221209194623_OrganizationDomainLastCheckedDate.cs b/util/PostgresMigrations/Migrations/20221209194623_OrganizationDomainLastCheckedDate.cs new file mode 100644 index 0000000000..0c6b7c2fcf --- /dev/null +++ b/util/PostgresMigrations/Migrations/20221209194623_OrganizationDomainLastCheckedDate.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +public partial class OrganizationDomainLastCheckedDate : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "LastCheckedDate", + table: "OrganizationDomain", + type: "timestamp without time zone", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "LastCheckedDate", + table: "OrganizationDomain"); + } +} diff --git a/util/PostgresMigrations/Migrations/20230118224536_RemoveDeviceUnknownVerification.Designer.cs b/util/PostgresMigrations/Migrations/20230118224536_RemoveDeviceUnknownVerification.Designer.cs new file mode 100644 index 0000000000..6f59829cd5 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230118224536_RemoveDeviceUnknownVerification.Designer.cs @@ -0,0 +1,2106 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230118224536_RemoveDeviceUnknownVerification")] + partial class RemoveDeviceUnknownVerification + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") + .HasAnnotation("ProductVersion", "6.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("text"); + + b.Property("Read") + .HasColumnType("boolean"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Write") + .HasColumnType("boolean"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClientSecret") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("AuthenticationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MasterPasswordHash") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestFingerprint") + .HasColumnType("text"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ResponseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ResponseDeviceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Favorites") + .HasColumnType("text"); + + b.Property("Folders") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Reprompt") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GranteeId") + .HasColumnType("uuid"); + + b.Property("GrantorId") + .HasColumnType("uuid"); + + b.Property("KeyEncrypted") + .HasColumnType("text"); + + b.Property("LastNotificationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitTimeDays") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ActingUserId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceType") + .HasColumnType("smallint"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("PolicyId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("ProviderOrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderUserId") + .HasColumnType("uuid"); + + b.Property("SystemUser") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp with time zone"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("SelfHost") + .HasColumnType("boolean"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("Use2fa") + .HasColumnType("boolean"); + + b.Property("UseApi") + .HasColumnType("boolean"); + + b.Property("UseCustomPermissions") + .HasColumnType("boolean"); + + b.Property("UseDirectory") + .HasColumnType("boolean"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UsePolicies") + .HasColumnType("boolean"); + + b.Property("UseResetPassword") + .HasColumnType("boolean"); + + b.Property("UseScim") + .HasColumnType("boolean"); + + b.Property("UseSecretsManager") + .HasColumnType("boolean"); + + b.Property("UseSso") + .HasColumnType("boolean"); + + b.Property("UseTotp") + .HasColumnType("boolean"); + + b.Property("UsersGetPremium") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Config") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LastSyncDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("smallint"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("ToDelete") + .HasColumnType("boolean"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Project", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MaxAccessCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PaymentMethodType") + .HasColumnType("smallint"); + + b.Property("Refunded") + .HasColumnType("boolean"); + + b.Property("RefundedAmount") + .HasColumnType("numeric"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountRevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("character varying(7)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("EquivalentDomains") + .HasColumnType("text"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("text"); + + b.Property("FailedLoginCount") + .HasColumnType("integer"); + + b.Property("ForcePasswordReset") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Kdf") + .HasColumnType("smallint"); + + b.Property("KdfIterations") + .HasColumnType("integer"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LastFailedLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Premium") + .HasColumnType("boolean"); + + b.Property("PremiumExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RenewalReminderDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("uuid"); + + b.Property("SecretsId") + .HasColumnType("uuid"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany() + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany() + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20230118224536_RemoveDeviceUnknownVerification.cs b/util/PostgresMigrations/Migrations/20230118224536_RemoveDeviceUnknownVerification.cs new file mode 100644 index 0000000000..89a4a24ade --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230118224536_RemoveDeviceUnknownVerification.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +public partial class RemoveDeviceUnknownVerification : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UnknownDeviceVerificationEnabled", + table: "User"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UnknownDeviceVerificationEnabled", + table: "User", + type: "boolean", + nullable: false, + defaultValue: false); + } +} diff --git a/util/PostgresMigrations/Migrations/20230213133239_SecretsManagerEvent.Designer.cs b/util/PostgresMigrations/Migrations/20230213133239_SecretsManagerEvent.Designer.cs new file mode 100644 index 0000000000..e71b8e85d5 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230213133239_SecretsManagerEvent.Designer.cs @@ -0,0 +1,2149 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230213133239_SecretsManagerEvent")] + partial class SecretsManagerEvent + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") + .HasAnnotation("ProductVersion", "6.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("AuthenticationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MasterPasswordHash") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestFingerprint") + .HasColumnType("text"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ResponseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ResponseDeviceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Favorites") + .HasColumnType("text"); + + b.Property("Folders") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Reprompt") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GranteeId") + .HasColumnType("uuid"); + + b.Property("GrantorId") + .HasColumnType("uuid"); + + b.Property("KeyEncrypted") + .HasColumnType("text"); + + b.Property("LastNotificationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitTimeDays") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ActingUserId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceType") + .HasColumnType("smallint"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("PolicyId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("ProviderOrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderUserId") + .HasColumnType("uuid"); + + b.Property("SecretId") + .HasColumnType("uuid"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + + b.Property("SystemUser") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp with time zone"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("SelfHost") + .HasColumnType("boolean"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("Use2fa") + .HasColumnType("boolean"); + + b.Property("UseApi") + .HasColumnType("boolean"); + + b.Property("UseCustomPermissions") + .HasColumnType("boolean"); + + b.Property("UseDirectory") + .HasColumnType("boolean"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UsePolicies") + .HasColumnType("boolean"); + + b.Property("UseResetPassword") + .HasColumnType("boolean"); + + b.Property("UseScim") + .HasColumnType("boolean"); + + b.Property("UseSecretsManager") + .HasColumnType("boolean"); + + b.Property("UseSso") + .HasColumnType("boolean"); + + b.Property("UseTotp") + .HasColumnType("boolean"); + + b.Property("UsersGetPremium") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Config") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LastSyncDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("smallint"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("ToDelete") + .HasColumnType("boolean"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessAll") + .HasColumnType("boolean"); + + b.Property("AccessSecretsManager") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BillingPhone") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MaxAccessCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PaymentMethodType") + .HasColumnType("smallint"); + + b.Property("Refunded") + .HasColumnType("boolean"); + + b.Property("RefundedAmount") + .HasColumnType("numeric"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountRevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("character varying(7)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("EquivalentDomains") + .HasColumnType("text"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("text"); + + b.Property("FailedLoginCount") + .HasColumnType("integer"); + + b.Property("ForcePasswordReset") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Kdf") + .HasColumnType("smallint"); + + b.Property("KdfIterations") + .HasColumnType("integer"); + + b.Property("KdfMemory") + .HasColumnType("integer"); + + b.Property("KdfParallelism") + .HasColumnType("integer"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LastEmailChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastFailedLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastKdfChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastKeyRotationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Premium") + .HasColumnType("boolean"); + + b.Property("PremiumExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RenewalReminderDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("boolean"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("text"); + + b.Property("Read") + .HasColumnType("boolean"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Write") + .HasColumnType("boolean"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClientSecret") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("uuid"); + + b.Property("SecretsId") + .HasColumnType("uuid"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20230213133239_SecretsManagerEvent.cs b/util/PostgresMigrations/Migrations/20230213133239_SecretsManagerEvent.cs new file mode 100644 index 0000000000..ff560044df --- /dev/null +++ b/util/PostgresMigrations/Migrations/20230213133239_SecretsManagerEvent.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +public partial class SecretsManagerEvent : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SecretId", + table: "Event", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "ServiceAccountId", + table: "Event", + type: "uuid", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretId", + table: "Event"); + + migrationBuilder.DropColumn( + name: "ServiceAccountId", + table: "Event"); + } +} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index 5a11a8a4a7..061e59f3db 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -322,6 +322,9 @@ namespace Bit.PostgresMigrations.Migrations b.Property("DeviceType") .HasColumnType("smallint"); + b.Property("DomainName") + .HasColumnType("text"); + b.Property("GroupId") .HasColumnType("uuid"); @@ -350,6 +353,12 @@ namespace Bit.PostgresMigrations.Migrations b.Property("ProviderUserId") .HasColumnType("uuid"); + b.Property("SecretId") + .HasColumnType("uuid"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + b.Property("SystemUser") .HasColumnType("smallint"); @@ -710,6 +719,43 @@ namespace Bit.PostgresMigrations.Migrations b.ToTable("OrganizationConnection", (string)null); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DomainName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("JobRunCount") + .HasColumnType("integer"); + + b.Property("LastCheckedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("NextRunDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Txt") + .HasColumnType("text"); + + b.Property("VerifiedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => { b.Property("Id") @@ -1303,9 +1349,6 @@ namespace Bit.PostgresMigrations.Migrations .HasMaxLength(32) .HasColumnType("character varying(32)"); - b.Property("UnknownDeviceVerificationEnabled") - .HasColumnType("boolean"); - b.Property("UsesKeyConnector") .HasColumnType("boolean"); @@ -1794,6 +1837,17 @@ namespace Bit.PostgresMigrations.Migrations b.Navigation("Organization"); }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => { b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") @@ -2090,6 +2144,8 @@ namespace Bit.PostgresMigrations.Migrations b.Navigation("Connections"); + b.Navigation("Domains"); + b.Navigation("Groups"); b.Navigation("OrganizationUsers"); diff --git a/util/PostgresMigrations/packages.lock.json b/util/PostgresMigrations/packages.lock.json index 257b0d059c..209f5237d7 100644 --- a/util/PostgresMigrations/packages.lock.json +++ b/util/PostgresMigrations/packages.lock.json @@ -72,8 +72,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -110,28 +110,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -153,6 +153,14 @@ "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": { "type": "Transitive", "resolved": "3.0.1", @@ -2736,10 +2744,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/util/Setup/Configuration.cs b/util/Setup/Configuration.cs index bfe7b10d31..37afa09c3a 100644 --- a/util/Setup/Configuration.cs +++ b/util/Setup/Configuration.cs @@ -76,13 +76,7 @@ public class Configuration [Description("Nginx Header Content-Security-Policy parameter\n" + "WARNING: Reconfiguring this parameter may break features. By changing this parameter\n" + "you become responsible for maintaining this value.")] - public string NginxHeaderContentSecurityPolicy { get; set; } = "default-src 'self'; " + - "script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; " + - "img-src 'self' data: https://haveibeenpwned.com; " + - "child-src 'self' https://*.duosecurity.com https://*.duofederal.com; " + - "frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; " + - "connect-src 'self' wss://{0} https://api.pwnedpasswords.com " + - "https://api.2fa.directory; object-src 'self' blob:;"; + public string NginxHeaderContentSecurityPolicy { get; set; } [Description("Communicate with the Bitwarden push relay service (push.bitwarden.com) for mobile\n" + "app live sync.")] diff --git a/util/Setup/Context.cs b/util/Setup/Context.cs index f82e5005c6..251c61f626 100644 --- a/util/Setup/Context.cs +++ b/util/Setup/Context.cs @@ -7,6 +7,40 @@ public class Context { private const string ConfigPath = "/bitwarden/config.yml"; + // These track of old CSP default values to correct. + // Do not change these values. + private const string Dec2020ContentSecurityPolicy = "default-src 'self'; style-src 'self' " + + "'unsafe-inline'; img-src 'self' data: https://haveibeenpwned.com https://www.gravatar.com; " + + "child-src 'self' https://*.duosecurity.com; frame-src 'self' https://*.duosecurity.com; " + + "connect-src 'self' wss://{0} https://api.pwnedpasswords.com " + + "https://twofactorauth.org; object-src 'self' blob:;"; + private const string Jan2021ContentSecurityPolicy = "default-src 'self'; style-src 'self' " + + "'unsafe-inline'; img-src 'self' data: https://haveibeenpwned.com https://www.gravatar.com; " + + "child-src 'self' https://*.duosecurity.com https://*.duofederal.com; " + + "frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; " + + "connect-src 'self' wss://{0} https://api.pwnedpasswords.com " + + "https://twofactorauth.org; object-src 'self' blob:;"; + private const string Feb2021ContentSecurityPolicy = "default-src 'self'; style-src 'self' " + + "'unsafe-inline'; img-src 'self' data: https://haveibeenpwned.com https://www.gravatar.com; " + + "child-src 'self' https://*.duosecurity.com https://*.duofederal.com; " + + "frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; " + + "connect-src 'self' wss://{0} https://api.pwnedpasswords.com " + + "https://2fa.directory; object-src 'self' blob:;"; + private const string Jan2023ContentSecurityPolicy = "default-src 'self'; style-src 'self' " + + "'unsafe-inline'; img-src 'self' data: https://haveibeenpwned.com; " + + "child-src 'self' https://*.duosecurity.com https://*.duofederal.com; " + + "frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; " + + "connect-src 'self' wss://{0} https://api.pwnedpasswords.com " + + "https://api.2fa.directory; object-src 'self' blob:;"; + + private string[] _oldCspDefaults = + { + Dec2020ContentSecurityPolicy, + Jan2021ContentSecurityPolicy, + Feb2021ContentSecurityPolicy, + Jan2023ContentSecurityPolicy + }; + public string[] Args { get; set; } public bool Quiet { get; set; } public bool Stub { get; set; } @@ -117,6 +151,13 @@ public class Context .WithNamingConvention(UnderscoredNamingConvention.Instance) .Build(); Config = deserializer.Deserialize(configText); + + // Fix old explicit config assignments of CSP which should be treated as a default value + if (_oldCspDefaults.Any(c => c == Config.NginxHeaderContentSecurityPolicy)) + { + Config.NginxHeaderContentSecurityPolicy = null; + SaveConfiguration(); + } } public void SaveConfiguration() diff --git a/util/Setup/NginxConfigBuilder.cs b/util/Setup/NginxConfigBuilder.cs index 420793cef7..865b8bdd69 100644 --- a/util/Setup/NginxConfigBuilder.cs +++ b/util/Setup/NginxConfigBuilder.cs @@ -4,6 +4,14 @@ public class NginxConfigBuilder { private const string ConfFile = "/bitwarden/nginx/default.conf"; + private const string DefaultContentSecurityPolicy = "default-src 'self'; " + + "script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; " + + "img-src 'self' data: https://haveibeenpwned.com; " + + "child-src 'self' https://*.duosecurity.com https://*.duofederal.com; " + + "frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; " + + "connect-src 'self' wss://{0} https://api.pwnedpasswords.com " + + "https://api.2fa.directory; object-src 'self' blob:;"; + private readonly Context _context; public NginxConfigBuilder(Context context) @@ -72,7 +80,12 @@ public class NginxConfigBuilder Domain = context.Config.Domain; Url = context.Config.Url; RealIps = context.Config.RealIps; - ContentSecurityPolicy = string.Format(context.Config.NginxHeaderContentSecurityPolicy, Domain); + var csp = DefaultContentSecurityPolicy; + if (!string.IsNullOrWhiteSpace(context.Config.NginxHeaderContentSecurityPolicy)) + { + csp = context.Config.NginxHeaderContentSecurityPolicy; + } + ContentSecurityPolicy = string.Format(csp, Domain); if (Ssl) { diff --git a/util/Setup/packages.lock.json b/util/Setup/packages.lock.json index 916391b7b7..3b729e4899 100644 --- a/util/Setup/packages.lock.json +++ b/util/Setup/packages.lock.json @@ -73,8 +73,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -111,28 +111,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -156,21 +156,29 @@ }, "dbup-core": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "CR00QMAtHjfeMhwxFC5haoA0q4KZ5s6Y/AdZaT6oFjySik2eFEqVasuLgWSPKSiR7ti3z01BtiR7aD3nVckAsg==", + "resolved": "5.0.8", + "contentHash": "d+3RxJDftcarp1Y7jI78HRdRWRC7VFjM+rB2CFHWDmao6OixuLrqiyEo1DeuMNrWLTR5mmE8p1YTpFOvozI9ZQ==", "dependencies": { - "Microsoft.CSharp": "4.4.0", + "Microsoft.CSharp": "4.7.0", "System.Diagnostics.TraceSource": "4.3.0" } }, "dbup-sqlserver": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "/4hy4qmbWmtbLJGq8XCH3mtlgMld2G8rbXcjNDhqkq5y6dGZDW03OI4UsnQRxBiTQD5aYOcLuycK1dCJYhkdSw==", + "resolved": "5.0.8", + "contentHash": "b954l5Zgj9qgHtm16SLq2qGLJ0gIZtrWdh6JHoUsCLMHYW+0K2Oevabquw447At4U6X2t4CNuy7ZLHYf/Z/8yg==", "dependencies": { - "Microsoft.Azure.Services.AppAuthentication": "1.3.1", - "System.Data.SqlClient": "4.6.0", - "dbup-core": "4.5.0" + "Microsoft.Azure.Services.AppAuthentication": "1.6.2", + "Microsoft.Data.SqlClient": "5.0.1", + "dbup-core": "5.0.8" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" } }, "Fido2": { @@ -428,10 +436,10 @@ }, "Microsoft.Azure.Services.AppAuthentication": { "type": "Transitive", - "resolved": "1.3.1", - "contentHash": "59CEcmUSlg5nYOzcyhdoUu+EQH4wrjCKj7dNuuPMeIjDCikAON9/KQXTQLfzfWTjDwqHIRptAAj0DTBp25lFcg==", + "resolved": "1.6.2", + "contentHash": "rSQhTv43ionr9rWvE4vxIe/i73XR5hoBYfh7UUgdaVOGW1MZeikR9RmgaJhonTylimCcCuJvrU0zXsSIFOsTGw==", "dependencies": { - "Microsoft.IdentityModel.Clients.ActiveDirectory": "4.3.0", + "Microsoft.IdentityModel.Clients.ActiveDirectory": "5.2.9", "System.Diagnostics.Process": "4.3.0" } }, @@ -721,15 +729,22 @@ }, "Microsoft.IdentityModel.Clients.ActiveDirectory": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "IRXnTCHxwnpnGBHVnTWd8RBJk7nsBsNZVl8j20kh234bP+oBILkt+6Iw5vQg5Q+sZmALt3Oq6X3Kx7qY71XyVw==", + "resolved": "5.2.9", + "contentHash": "WhBAG/9hWiMHIXve4ZgwXP3spRwf7kFFfejf76QA5BvumgnPp8iDkDCiJugzAcpW1YaHB526z1UVxHhVT1E5qw==", "dependencies": { + "Microsoft.CSharp": "4.3.0", "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.Primitives": "4.3.0", "System.Security.Cryptography.X509Certificates": "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": { @@ -783,8 +798,8 @@ }, "Microsoft.NETCore.Targets": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "1.1.3", + "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==" }, "Microsoft.OData.Core": { "type": "Transitive", @@ -959,16 +974,6 @@ "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": { "type": "Transitive", "resolved": "4.3.0", @@ -1061,21 +1066,6 @@ "resolved": "4.3.2", "contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==" }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==" - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==" - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==" - }, "SendGrid": { "type": "Transitive", "resolved": "9.27.0", @@ -1317,29 +1307,69 @@ }, "System.Collections.NonGeneric": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==", + "resolved": "4.3.0", + "contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", "dependencies": { - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Collections.Specialized": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "/HKQyVP0yH1I0YtK7KJL/28snxHNH/bi+0lgk/+MbURF6ULhAE31MDI+NZDerNWu264YbxklXCCygISgm+HMug==", + "resolved": "4.3.0", + "contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==", "dependencies": { - "System.Collections.NonGeneric": "4.0.1", - "System.Globalization": "4.0.11", - "System.Globalization.Extensions": "4.0.1", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" + "System.Collections.NonGeneric": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.ComponentModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.ComponentModel.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.Configuration.ConfigurationManager": { @@ -1363,17 +1393,6 @@ "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": { "type": "Transitive", "resolved": "4.3.0", @@ -1477,24 +1496,23 @@ }, "System.Dynamic.Runtime": { "type": "Transitive", - "resolved": "4.0.11", - "contentHash": "db34f6LHYM0U0JpE+sOmjar27BnqTVkbLJhgfwMpTdgTigG/Hna3m2MYVwnFzGGKnEJk2UXFuoVTr8WUbU91/A==", + "resolved": "4.3.0", + "contentHash": "SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==", "dependencies": { - "System.Collections": "4.0.11", - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.Linq": "4.1.0", - "System.Linq.Expressions": "4.1.0", - "System.ObjectModel": "4.0.12", - "System.Reflection": "4.1.0", - "System.Reflection.Emit": "4.0.1", - "System.Reflection.Emit.ILGeneration": "4.0.1", - "System.Reflection.Primitives": "4.0.1", - "System.Reflection.TypeExtensions": "4.1.0", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Formats.Asn1": { @@ -1957,6 +1975,15 @@ "System.Xml.XmlSerializer": "4.3.0" } }, + "System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.1", + "Microsoft.NETCore.Targets": "1.1.3" + } + }, "System.Reflection": { "type": "Transitive", "resolved": "4.3.0", @@ -2124,6 +2151,18 @@ "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": { "type": "Transitive", "resolved": "4.3.0", @@ -2615,10 +2654,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", @@ -2650,9 +2690,9 @@ "migrator": { "type": "Project", "dependencies": { - "Core": "[2023.1.0, )", + "Core": "[2023.2.0, )", "Microsoft.Extensions.Logging": "[6.0.0, )", - "dbup-sqlserver": "[4.5.0, )" + "dbup-sqlserver": "[5.0.8, )" } } } diff --git a/util/SqlServerEFScaffold/packages.lock.json b/util/SqlServerEFScaffold/packages.lock.json index 34679ed985..3135598017 100644 --- a/util/SqlServerEFScaffold/packages.lock.json +++ b/util/SqlServerEFScaffold/packages.lock.json @@ -72,8 +72,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -120,28 +120,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -168,6 +168,14 @@ "resolved": "2.0.123", "contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ==" }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "2hrXR83b5g6/ZMJOA36hXp4t56yb7G1mF3Hg6IkrHxvtyaoXRn2WVdgDPN3V8+GugOlUBbTWXgPaka4dXw1QIg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Fido2": { "type": "Transitive", "resolved": "3.0.1", @@ -2818,10 +2826,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )", diff --git a/util/SqliteMigrations/Migrations/20230118212950_RemoveDeviceUnknownVerification.Designer.cs b/util/SqliteMigrations/Migrations/20230118212950_RemoveDeviceUnknownVerification.Designer.cs new file mode 100644 index 0000000000..e1000e6a46 --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230118212950_RemoveDeviceUnknownVerification.Designer.cs @@ -0,0 +1,2093 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230118212950_RemoveDeviceUnknownVerification")] + partial class RemoveDeviceUnknownVerification + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.12"); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Read") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Write") + .HasColumnType("INTEGER"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ClientSecret") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ExpireAt") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("TEXT"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("AuthenticationDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHash") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("RequestDeviceType") + .HasColumnType("INTEGER"); + + b.Property("RequestFingerprint") + .HasColumnType("TEXT"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ResponseDate") + .HasColumnType("TEXT"); + + b.Property("ResponseDeviceId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Attachments") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Favorites") + .HasColumnType("TEXT"); + + b.Property("Folders") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Reprompt") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("GranteeId") + .HasColumnType("TEXT"); + + b.Property("GrantorId") + .HasColumnType("TEXT"); + + b.Property("KeyEncrypted") + .HasColumnType("TEXT"); + + b.Property("LastNotificationDate") + .HasColumnType("TEXT"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("WaitTimeDays") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ActingUserId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("DeviceType") + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("InstallationId") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("PolicyId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("ProviderOrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderUserId") + .HasColumnType("TEXT"); + + b.Property("SystemUser") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessAll") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxCollections") + .HasColumnType("INTEGER"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("TEXT"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PlanType") + .HasColumnType("INTEGER"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Seats") + .HasColumnType("INTEGER"); + + b.Property("SelfHost") + .HasColumnType("INTEGER"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("Use2fa") + .HasColumnType("INTEGER"); + + b.Property("UseApi") + .HasColumnType("INTEGER"); + + b.Property("UseCustomPermissions") + .HasColumnType("INTEGER"); + + b.Property("UseDirectory") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.Property("UseGroups") + .HasColumnType("INTEGER"); + + b.Property("UseKeyConnector") + .HasColumnType("INTEGER"); + + b.Property("UsePolicies") + .HasColumnType("INTEGER"); + + b.Property("UseResetPassword") + .HasColumnType("INTEGER"); + + b.Property("UseScim") + .HasColumnType("INTEGER"); + + b.Property("UseSecretsManager") + .HasColumnType("INTEGER"); + + b.Property("UseSso") + .HasColumnType("INTEGER"); + + b.Property("UseTotp") + .HasColumnType("INTEGER"); + + b.Property("UsersGetPremium") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Config") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("LastSyncDate") + .HasColumnType("TEXT"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PlanSponsorshipType") + .HasColumnType("INTEGER"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("ToDelete") + .HasColumnType("INTEGER"); + + b.Property("ValidUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessAll") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ResetPasswordKey") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Project", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BillingEmail") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCount") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeletionDate") + .HasColumnType("TEXT"); + + b.Property("Disabled") + .HasColumnType("INTEGER"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("HideEmail") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MaxAccessCount") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Rate") + .HasColumnType("TEXT"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Amount") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PaymentMethodType") + .HasColumnType("INTEGER"); + + b.Property("Refunded") + .HasColumnType("INTEGER"); + + b.Property("RefundedAmount") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccountRevisionDate") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailVerified") + .HasColumnType("INTEGER"); + + b.Property("EquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("FailedLoginCount") + .HasColumnType("INTEGER"); + + b.Property("ForcePasswordReset") + .HasColumnType("INTEGER"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Kdf") + .HasColumnType("INTEGER"); + + b.Property("KdfIterations") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("LastFailedLoginDate") + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Premium") + .HasColumnType("INTEGER"); + + b.Property("PremiumExpirationDate") + .HasColumnType("TEXT"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RenewalReminderDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("UsesKeyConnector") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("TEXT"); + + b.Property("SecretsId") + .HasColumnType("TEXT"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany() + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany() + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/SqliteMigrations/Migrations/20230118212950_RemoveDeviceUnknownVerification.cs b/util/SqliteMigrations/Migrations/20230118212950_RemoveDeviceUnknownVerification.cs new file mode 100644 index 0000000000..b6ae617a59 --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230118212950_RemoveDeviceUnknownVerification.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations; + +public partial class RemoveDeviceUnknownVerification : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UnknownDeviceVerificationEnabled", + table: "User"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UnknownDeviceVerificationEnabled", + table: "User", + type: "INTEGER", + nullable: false, + defaultValue: false); + } +} diff --git a/util/SqliteMigrations/Migrations/20230213133244_SecretsManagerEvent.Designer.cs b/util/SqliteMigrations/Migrations/20230213133244_SecretsManagerEvent.Designer.cs new file mode 100644 index 0000000000..f791006ba0 --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230213133244_SecretsManagerEvent.Designer.cs @@ -0,0 +1,2136 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230213133244_SecretsManagerEvent")] + partial class SecretsManagerEvent + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.12"); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("TEXT"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("AuthenticationDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHash") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("RequestDeviceType") + .HasColumnType("INTEGER"); + + b.Property("RequestFingerprint") + .HasColumnType("TEXT"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ResponseDate") + .HasColumnType("TEXT"); + + b.Property("ResponseDeviceId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Attachments") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Favorites") + .HasColumnType("TEXT"); + + b.Property("Folders") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Reprompt") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("GranteeId") + .HasColumnType("TEXT"); + + b.Property("GrantorId") + .HasColumnType("TEXT"); + + b.Property("KeyEncrypted") + .HasColumnType("TEXT"); + + b.Property("LastNotificationDate") + .HasColumnType("TEXT"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("WaitTimeDays") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ActingUserId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("DeviceType") + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("InstallationId") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("PolicyId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("ProviderOrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderUserId") + .HasColumnType("TEXT"); + + b.Property("SecretId") + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + + b.Property("SystemUser") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessAll") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxCollections") + .HasColumnType("INTEGER"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("TEXT"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PlanType") + .HasColumnType("INTEGER"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Seats") + .HasColumnType("INTEGER"); + + b.Property("SelfHost") + .HasColumnType("INTEGER"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("Use2fa") + .HasColumnType("INTEGER"); + + b.Property("UseApi") + .HasColumnType("INTEGER"); + + b.Property("UseCustomPermissions") + .HasColumnType("INTEGER"); + + b.Property("UseDirectory") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.Property("UseGroups") + .HasColumnType("INTEGER"); + + b.Property("UseKeyConnector") + .HasColumnType("INTEGER"); + + b.Property("UsePolicies") + .HasColumnType("INTEGER"); + + b.Property("UseResetPassword") + .HasColumnType("INTEGER"); + + b.Property("UseScim") + .HasColumnType("INTEGER"); + + b.Property("UseSecretsManager") + .HasColumnType("INTEGER"); + + b.Property("UseSso") + .HasColumnType("INTEGER"); + + b.Property("UseTotp") + .HasColumnType("INTEGER"); + + b.Property("UsersGetPremium") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Config") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("LastSyncDate") + .HasColumnType("TEXT"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PlanSponsorshipType") + .HasColumnType("INTEGER"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("ToDelete") + .HasColumnType("INTEGER"); + + b.Property("ValidUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessAll") + .HasColumnType("INTEGER"); + + b.Property("AccessSecretsManager") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ResetPasswordKey") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BillingEmail") + .HasColumnType("TEXT"); + + b.Property("BillingPhone") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCount") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeletionDate") + .HasColumnType("TEXT"); + + b.Property("Disabled") + .HasColumnType("INTEGER"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("HideEmail") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MaxAccessCount") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Rate") + .HasColumnType("TEXT"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Amount") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PaymentMethodType") + .HasColumnType("INTEGER"); + + b.Property("Refunded") + .HasColumnType("INTEGER"); + + b.Property("RefundedAmount") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccountRevisionDate") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailVerified") + .HasColumnType("INTEGER"); + + b.Property("EquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("FailedLoginCount") + .HasColumnType("INTEGER"); + + b.Property("ForcePasswordReset") + .HasColumnType("INTEGER"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Kdf") + .HasColumnType("INTEGER"); + + b.Property("KdfIterations") + .HasColumnType("INTEGER"); + + b.Property("KdfMemory") + .HasColumnType("INTEGER"); + + b.Property("KdfParallelism") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("LastEmailChangeDate") + .HasColumnType("TEXT"); + + b.Property("LastFailedLoginDate") + .HasColumnType("TEXT"); + + b.Property("LastKdfChangeDate") + .HasColumnType("TEXT"); + + b.Property("LastKeyRotationDate") + .HasColumnType("TEXT"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Premium") + .HasColumnType("INTEGER"); + + b.Property("PremiumExpirationDate") + .HasColumnType("TEXT"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RenewalReminderDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("INTEGER"); + + b.Property("UsesKeyConnector") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Read") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Write") + .HasColumnType("INTEGER"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("AccessPolicy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ClientSecret") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EncryptedPayload") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ExpireAt") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Scope") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("TEXT"); + + b.Property("SecretsId") + .HasColumnType("TEXT"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/SqliteMigrations/Migrations/20230213133244_SecretsManagerEvent.cs b/util/SqliteMigrations/Migrations/20230213133244_SecretsManagerEvent.cs new file mode 100644 index 0000000000..6c61e3d455 --- /dev/null +++ b/util/SqliteMigrations/Migrations/20230213133244_SecretsManagerEvent.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations; + +public partial class SecretsManagerEvent : Migration +{ + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SecretId", + table: "Event", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "ServiceAccountId", + table: "Event", + type: "TEXT", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SecretId", + table: "Event"); + + migrationBuilder.DropColumn( + name: "ServiceAccountId", + table: "Event"); + } +} diff --git a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs index bba1dd1559..61a1e44731 100644 --- a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -344,6 +344,12 @@ namespace Bit.SqliteMigrations.Migrations b.Property("ProviderUserId") .HasColumnType("TEXT"); + b.Property("SecretId") + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + b.Property("SystemUser") .HasColumnType("INTEGER"); @@ -1290,9 +1296,6 @@ namespace Bit.SqliteMigrations.Migrations .HasMaxLength(32) .HasColumnType("TEXT"); - b.Property("UnknownDeviceVerificationEnabled") - .HasColumnType("INTEGER"); - b.Property("UsesKeyConnector") .HasColumnType("INTEGER"); diff --git a/util/SqliteMigrations/packages.lock.json b/util/SqliteMigrations/packages.lock.json index 257b0d059c..209f5237d7 100644 --- a/util/SqliteMigrations/packages.lock.json +++ b/util/SqliteMigrations/packages.lock.json @@ -72,8 +72,8 @@ }, "Azure.Core": { "type": "Transitive", - "resolved": "1.24.0", - "contentHash": "+/qI1j2oU1S4/nvxb2k/wDsol00iGf1AyJX5g3epV7eOpQEP/2xcgh/cxgKMeFgn3U2fmgSiBnQZdkV+l5y0Uw==", + "resolved": "1.25.0", + "contentHash": "X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA==", "dependencies": { "Microsoft.Bcl.AsyncInterfaces": "1.1.1", "System.Diagnostics.DiagnosticSource": "4.6.0", @@ -110,28 +110,28 @@ }, "Azure.Storage.Blobs": { "type": "Transitive", - "resolved": "12.11.0", - "contentHash": "50eRjIhY7Q1JN7kT2MSawDKCcwSb7uRZUkz00P/BLjSg47gm2hxUYsnJPyvzCHntYMbOWzrvaVQTwYwXabaR5Q==", + "resolved": "12.14.1", + "contentHash": "DvRBWUDMB2LjdRbsBNtz/LiVIYk56hqzSooxx4uq4rCdLj2M+7Vvoa1r+W35Dz6ZXL6p+SNcgEae3oZ+CkPfow==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Text.Json": "4.7.2" } }, "Azure.Storage.Common": { "type": "Transitive", - "resolved": "12.10.0", - "contentHash": "vYkHGzUkdZTace/cDPZLG+Mh/EoPqQuGxDIBOau9D+XWoDPmuUFGk325aXplkFE4JFGpSwoytNYzk/qBCaiHqg==", + "resolved": "12.13.0", + "contentHash": "jDv8xJWeZY2Er9zA6QO25BiGolxg87rItt9CwAp7L/V9EPJeaz8oJydaNL9Wj0+3ncceoMgdiyEv66OF8YUwWQ==", "dependencies": { - "Azure.Core": "1.22.0", + "Azure.Core": "1.25.0", "System.IO.Hashing": "6.0.0" } }, "Azure.Storage.Queues": { "type": "Transitive", - "resolved": "12.9.0", - "contentHash": "jDiyHtsCUCrWNvZW7SjJnJb46UhpdgQrWCbL8aWpapDHlq9LvbvxYpfLh4dfKAz09QiTznLMIU3i+md9+7GzqQ==", + "resolved": "12.12.0", + "contentHash": "PwrfymLYFmmOt6A0vMiDVhBV7RoOAKftzzvrbSM3W9cJKpkxAg57AhM7/wbNb3P8Uq0B73lBurkFiFzWK9PXHg==", "dependencies": { - "Azure.Storage.Common": "12.10.0", + "Azure.Storage.Common": "12.13.0", "System.Memory.Data": "1.0.2", "System.Text.Json": "4.7.2" } @@ -153,6 +153,14 @@ "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": { "type": "Transitive", "resolved": "3.0.1", @@ -2736,10 +2744,11 @@ "AspNetCoreRateLimit": "[4.0.2, )", "AspNetCoreRateLimit.Redis": "[1.0.1, )", "Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.2.1, )", - "Azure.Storage.Blobs": "[12.11.0, )", - "Azure.Storage.Queues": "[12.9.0, )", + "Azure.Storage.Blobs": "[12.14.1, )", + "Azure.Storage.Queues": "[12.12.0, )", "BitPay.Light": "[1.0.1907, )", "Braintree": "[5.12.0, )", + "DnsClient": "[1.7.0, )", "Fido2.AspNet": "[3.0.1, )", "Handlebars.Net": "[2.1.2, )", "IdentityServer4": "[4.1.2, )",