From ee65e7745fac9fded5ec1fe51b38b43b3b784bc7 Mon Sep 17 00:00:00 2001 From: Ping Timeout Date: Mon, 3 Apr 2023 21:14:14 +0800 Subject: [PATCH] Revert "[pull] main from dani-garcia:main (#64)" This reverts commit ade385943bd768f3a402c0bf4541f8ffb76a38c4. --- .env.template | 24 +- .github/workflows/build.yml | 36 +- .github/workflows/hadolint.yml | 2 +- .github/workflows/release.yml | 155 +- .hadolint.yaml | 4 - .pre-commit-config.yaml | 8 +- Cargo.lock | 1340 +++++++---------- Cargo.toml | 85 +- LICENSE.txt | 149 +- README.md | 15 +- clippy.toml | 1 + docker/Dockerfile.j2 | 74 +- docker/Makefile | 4 +- docker/amd64/Dockerfile | 27 +- docker/amd64/Dockerfile.alpine | 25 +- ...{Dockerfile.buildkit => Dockerfile.buildx} | 27 +- ...ildkit.alpine => Dockerfile.buildx.alpine} | 25 +- docker/arm64/Dockerfile | 38 +- docker/arm64/Dockerfile.alpine | 27 +- ...{Dockerfile.buildkit => Dockerfile.buildx} | 38 +- ...ildkit.alpine => Dockerfile.buildx.alpine} | 27 +- docker/armv6/Dockerfile | 39 +- docker/armv6/Dockerfile.alpine | 27 +- ...{Dockerfile.buildkit => Dockerfile.buildx} | 39 +- ...ildkit.alpine => Dockerfile.buildx.alpine} | 27 +- docker/armv7/Dockerfile | 38 +- docker/armv7/Dockerfile.alpine | 27 +- ...{Dockerfile.buildkit => Dockerfile.buildx} | 38 +- ...ildkit.alpine => Dockerfile.buildx.alpine} | 27 +- hooks/arches.sh | 4 - hooks/build | 13 +- hooks/pre_build | 2 +- hooks/push | 54 +- .../down.sql | 0 .../up.sql | 2 - .../down.sql | 0 .../2023-01-11-205851_add_avatar_color/up.sql | 2 - .../2023-01-31-222222_add_argon2/down.sql | 0 .../mysql/2023-01-31-222222_add_argon2/up.sql | 7 - .../down.sql | 0 .../up.sql | 2 - .../down.sql | 0 .../2023-01-11-205851_add_avatar_color/up.sql | 2 - .../2023-01-31-222222_add_argon2/down.sql | 0 .../2023-01-31-222222_add_argon2/up.sql | 7 - .../down.sql | 0 .../up.sql | 2 - .../down.sql | 0 .../2023-01-11-205851_add_avatar_color/up.sql | 2 - .../2023-01-31-222222_add_argon2/down.sql | 0 .../2023-01-31-222222_add_argon2/up.sql | 7 - rust-toolchain | 2 +- rustfmt.toml | 3 + src/api/admin.rs | 158 +- src/api/core/accounts.rs | 196 +-- src/api/core/ciphers.rs | 349 +++-- src/api/core/emergency_access.rs | 42 +- src/api/core/events.rs | 15 +- src/api/core/mod.rs | 1 - src/api/core/organizations.rs | 755 +++------- src/api/core/two_factor/authenticator.rs | 8 +- src/api/core/two_factor/duo.rs | 10 +- src/api/core/two_factor/email.rs | 8 +- src/api/core/two_factor/mod.rs | 39 +- src/api/core/two_factor/webauthn.rs | 20 +- src/api/core/two_factor/yubikey.rs | 22 +- src/api/icons.rs | 28 +- src/api/identity.rs | 52 +- src/api/notifications.rs | 39 +- src/api/web.rs | 28 +- src/auth.rs | 67 +- src/config.rs | 101 +- src/db/models/attachment.rs | 9 +- src/db/models/cipher.rs | 56 +- src/db/models/collection.rs | 165 +- src/db/models/event.rs | 17 +- src/db/models/group.rs | 45 +- src/db/models/mod.rs | 2 +- src/db/models/org_policy.rs | 23 +- src/db/models/organization.rs | 54 +- src/db/models/user.rs | 35 +- src/db/schemas/mysql/schema.rs | 4 - src/db/schemas/postgresql/schema.rs | 4 - src/db/schemas/sqlite/schema.rs | 4 - src/mail.rs | 108 +- src/main.rs | 139 +- src/static/scripts/admin.css | 25 +- src/static/scripts/admin.js | 26 +- src/static/scripts/admin_diagnostics.js | 54 +- src/static/scripts/admin_organizations.js | 28 +- src/static/scripts/admin_settings.js | 102 +- src/static/scripts/admin_users.js | 140 +- src/static/scripts/datatables.css | 29 +- src/static/scripts/datatables.js | 64 +- ...ery-3.6.3.slim.js => jquery-3.6.2.slim.js} | 17 +- src/static/templates/admin/base.hbs | 2 +- src/static/templates/admin/diagnostics.hbs | 9 +- src/static/templates/admin/organizations.hbs | 18 +- src/static/templates/admin/settings.hbs | 10 +- src/static/templates/admin/users.hbs | 21 +- .../templates/email/admin_reset_password.hbs | 6 - .../email/admin_reset_password.html.hbs | 11 - src/util.rs | 15 +- 103 files changed, 2037 insertions(+), 3647 deletions(-) create mode 100644 clippy.toml rename docker/amd64/{Dockerfile.buildkit => Dockerfile.buildx} (86%) rename docker/amd64/{Dockerfile.buildkit.alpine => Dockerfile.buildx.alpine} (87%) rename docker/arm64/{Dockerfile.buildkit => Dockerfile.buildx} (88%) rename docker/arm64/{Dockerfile.buildkit.alpine => Dockerfile.buildx.alpine} (86%) rename docker/armv6/{Dockerfile.buildkit => Dockerfile.buildx} (88%) rename docker/armv6/{Dockerfile.buildkit.alpine => Dockerfile.buildx.alpine} (87%) rename docker/armv7/{Dockerfile.buildkit => Dockerfile.buildx} (88%) rename docker/armv7/{Dockerfile.buildkit.alpine => Dockerfile.buildx.alpine} (86%) delete mode 100644 migrations/mysql/2023-01-06-151600_add_reset_password_support/down.sql delete mode 100644 migrations/mysql/2023-01-06-151600_add_reset_password_support/up.sql delete mode 100644 migrations/mysql/2023-01-11-205851_add_avatar_color/down.sql delete mode 100644 migrations/mysql/2023-01-11-205851_add_avatar_color/up.sql delete mode 100644 migrations/mysql/2023-01-31-222222_add_argon2/down.sql delete mode 100644 migrations/mysql/2023-01-31-222222_add_argon2/up.sql delete mode 100644 migrations/postgresql/2023-01-06-151600_add_reset_password_support/down.sql delete mode 100644 migrations/postgresql/2023-01-06-151600_add_reset_password_support/up.sql delete mode 100644 migrations/postgresql/2023-01-11-205851_add_avatar_color/down.sql delete mode 100644 migrations/postgresql/2023-01-11-205851_add_avatar_color/up.sql delete mode 100644 migrations/postgresql/2023-01-31-222222_add_argon2/down.sql delete mode 100644 migrations/postgresql/2023-01-31-222222_add_argon2/up.sql delete mode 100644 migrations/sqlite/2023-01-06-151600_add_reset_password_support/down.sql delete mode 100644 migrations/sqlite/2023-01-06-151600_add_reset_password_support/up.sql delete mode 100644 migrations/sqlite/2023-01-11-205851_add_avatar_color/down.sql delete mode 100644 migrations/sqlite/2023-01-11-205851_add_avatar_color/up.sql delete mode 100644 migrations/sqlite/2023-01-31-222222_add_argon2/down.sql delete mode 100644 migrations/sqlite/2023-01-31-222222_add_argon2/up.sql rename src/static/scripts/{jquery-3.6.3.slim.js => jquery-3.6.2.slim.js} (99%) delete mode 100644 src/static/templates/email/admin_reset_password.hbs delete mode 100644 src/static/templates/email/admin_reset_password.html.hbs diff --git a/.env.template b/.env.template index 9d6f75a1477..4b323706f1e 100644 --- a/.env.template +++ b/.env.template @@ -259,13 +259,9 @@ ## A comma-separated list means only those users can create orgs: # ORG_CREATION_USERS=admin1@example.com,admin2@example.com -## Token for the admin interface, preferably an Argon2 PCH string -## Vaultwarden has a built-in generator by calling `vaultwarden hash` -## For details see: https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page#secure-the-admin_token +## Token for the admin interface, preferably use a long random string +## One option is to use 'openssl rand -base64 48' ## If not set, the admin panel is disabled -## New Argon2 PHC string -# ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$MmeKRnGK5RW5mJS7h3TOL89GrpLPXJPAtTK8FTqj9HM$DqsstvoSAETl9YhnsXbf43WeaUwJC6JhViIvuPoig78' -## Old plain text string (Will generate warnings in favor of Argon2) # ADMIN_TOKEN=Vy2VyYTTsKPv8W5aEOWUbB/Bt3DEKePbHmI4m9VcemUMS2rEviDowNAFqYi1xjmp ## Enable this to bypass the admin panel security. This option is only @@ -302,9 +298,9 @@ ## This setting applies globally to all users. # INCOMPLETE_2FA_TIME_LIMIT=3 -## Number of server-side passwords hashing iterations for the password hash. -## The default for new users. If changed, it will be updated during login for existing users. -# PASSWORD_ITERATIONS=350000 +## Controls the PBBKDF password iterations to apply on the server +## The change only applies when the password is changed +# PASSWORD_ITERATIONS=100000 ## Controls whether users can set password hints. This setting applies globally to all users. # PASSWORD_HINTS_ALLOWED=true @@ -339,9 +335,6 @@ ## Allow a burst of requests of up to this size, while maintaining the average indicated by `ADMIN_RATELIMIT_SECONDS`. # ADMIN_RATELIMIT_MAX_BURST=3 -## Set the lifetime of admin sessions to this value (in minutes). -# ADMIN_SESSION_LIFETIME=20 - ## Yubico (Yubikey) Settings ## Set your Client ID and Secret Key for Yubikey OTP ## You can generate it here: https://upgrade.yubico.com/getapikey/ @@ -380,7 +373,7 @@ # ROCKET_WORKERS=10 # ROCKET_TLS={certs="/path/to/certs.pem",key="/path/to/key.pem"} -## Mail specific settings, set SMTP_FROM and either SMTP_HOST or USE_SENDMAIL to enable the mail service. +## Mail specific settings, set SMTP_HOST and SMTP_FROM to enable the mail service. ## To make sure the email links are pointing to the correct host, set the DOMAIN variable. ## Note: if SMTP_USERNAME is specified, SMTP_PASSWORD is mandatory # SMTP_HOST=smtp.domain.tld @@ -392,11 +385,6 @@ # SMTP_PASSWORD=password # SMTP_TIMEOUT=15 -# Whether to send mail via the `sendmail` command -# USE_SENDMAIL=false -# Which sendmail command to use. The one found in the $PATH is used if not specified. -# SENDMAIL_COMMAND="/path/to/sendmail" - ## Defaults for SSL is "Plain" and "Login" and nothing for Non-SSL connections. ## Possible values: ["Plain", "Login", "Xoauth2"]. ## Multiple options need to be separated by a comma ','. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e47a58d801e..c8b5a8556cf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,8 +9,6 @@ on: - "Cargo.*" - "build.rs" - "rust-toolchain" - - "rustfmt.toml" - - "diesel.toml" pull_request: paths: - ".github/workflows/build.yml" @@ -19,8 +17,6 @@ on: - "Cargo.*" - "build.rs" - "rust-toolchain" - - "rustfmt.toml" - - "diesel.toml" jobs: build: @@ -30,48 +26,42 @@ jobs: # This is done globally to prevent rebuilds when the RUSTFLAGS env variable changes. env: RUSTFLAGS: "-D warnings" - CARGO_REGISTRIES_CRATES_IO_PROTOCOL: git # Use the old git protocol until it is stable probably in 1.68 or 1.69. MSRV needs to be at this before removed. strategy: fail-fast: false matrix: channel: - "rust-toolchain" # The version defined in rust-toolchain - "msrv" # The supported MSRV + include: + - channel: "msrv" + version: "1.61.0" name: Build and Test ${{ matrix.channel }} steps: # Checkout the repo - name: "Checkout" - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0 # End Checkout the repo - # Install dependencies - name: "Install dependencies Ubuntu" run: sudo apt-get update && sudo apt-get install -y --no-install-recommends openssl sqlite build-essential libmariadb-dev-compat libpq-dev libssl-dev pkg-config # End Install dependencies - # Determine rust-toolchain version - name: Init Variables id: toolchain shell: bash + if: ${{ matrix.channel == 'rust-toolchain' }} run: | - if [[ "${{ matrix.channel }}" == 'rust-toolchain' ]]; then - RUST_TOOLCHAIN="$(cat rust-toolchain)" - elif [[ "${{ matrix.channel }}" == 'msrv' ]]; then - RUST_TOOLCHAIN="$(grep -oP 'rust-version.*"(\K.*?)(?=")' Cargo.toml)" - else - RUST_TOOLCHAIN="${{ matrix.channel }}" - fi + RUST_TOOLCHAIN="$(cat rust-toolchain)" echo "RUST_TOOLCHAIN=${RUST_TOOLCHAIN}" | tee -a "${GITHUB_OUTPUT}" # End Determine rust-toolchain version - - # Only install the clippy and rustfmt components on the default rust-toolchain + # Uses the rust-toolchain file to determine version - name: "Install rust-toolchain version" - uses: dtolnay/rust-toolchain@fc3253060d0c959bea12a59f10f8391454a0b02d # master @ 2023-03-21 - 06:36 GMT+1 + uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb # master @ 2022-10-25 - 21:40 GMT+2 if: ${{ matrix.channel == 'rust-toolchain' }} with: toolchain: "${{steps.toolchain.outputs.RUST_TOOLCHAIN}}" @@ -79,17 +69,17 @@ jobs: # End Uses the rust-toolchain file to determine version - # Install the any other channel to be used for which we do not execute clippy and rustfmt + # Install the MSRV channel to be used - name: "Install MSRV version" - uses: dtolnay/rust-toolchain@fc3253060d0c959bea12a59f10f8391454a0b02d # master @ 2023-03-21 - 06:36 GMT+1 + uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb # master @ 2022-10-25 - 21:40 GMT+2 if: ${{ matrix.channel != 'rust-toolchain' }} with: - toolchain: "${{steps.toolchain.outputs.RUST_TOOLCHAIN}}" + toolchain: ${{ matrix.version }} # End Install the MSRV channel to be used # Enable Rust Caching - - uses: Swatinem/rust-cache@6fd3edff6979b79f87531400ad694fb7f2c84b1f # v2.2.1 + - uses: Swatinem/rust-cache@359a70e43a0bb8a13953b04a90f76428b4959bb6 # v2.2.0 # End Enable Rust Caching @@ -194,7 +184,7 @@ jobs: # Upload artifact to Github Actions - name: "Upload artifact" - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1 if: ${{ matrix.channel == 'rust-toolchain' }} with: name: vaultwarden diff --git a/.github/workflows/hadolint.yml b/.github/workflows/hadolint.yml index b8646a39e64..6598311a2d3 100644 --- a/.github/workflows/hadolint.yml +++ b/.github/workflows/hadolint.yml @@ -13,7 +13,7 @@ jobs: steps: # Checkout the repo - name: Checkout - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0 # End Checkout the repo diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8f2a99526f..b3690ceb23c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,23 +48,11 @@ jobs: ports: - 5000:5000 env: - # Use BuildKit (https://docs.docker.com/build/buildkit/) for better - # build performance and the ability to copy extended file attributes - # (e.g., for executable capabilities) across build phases. - DOCKER_BUILDKIT: 1 + DOCKER_BUILDKIT: 1 # Disabled for now, but we should look at this because it will speedup building! + # DOCKER_REPO/secrets.DOCKERHUB_REPO needs to be 'index.docker.io//' + DOCKER_REPO: ${{ secrets.DOCKERHUB_REPO }} SOURCE_COMMIT: ${{ github.sha }} SOURCE_REPOSITORY_URL: "https://github.com/${{ github.repository }}" - # The *_REPO variables need to be configured as repository variables - # Append `/settings/variables/actions` to your repo url - # DOCKERHUB_REPO needs to be 'index.docker.io//' - # Check for Docker hub credentials in secrets - HAVE_DOCKERHUB_LOGIN: ${{ vars.DOCKERHUB_REPO != '' && secrets.DOCKERHUB_USERNAME != '' && secrets.DOCKERHUB_TOKEN != '' }} - # GHCR_REPO needs to be 'ghcr.io//' - # Check for Github credentials in secrets - HAVE_GHCR_LOGIN: ${{ vars.GHCR_REPO != '' && github.repository_owner != '' && secrets.GITHUB_TOKEN != '' }} - # QUAY_REPO needs to be 'quay.io//' - # Check for Quay.io credentials in secrets - HAVE_QUAY_LOGIN: ${{ vars.QUAY_REPO != '' && secrets.QUAY_USERNAME != '' && secrets.QUAY_TOKEN != '' }} if: ${{ needs.skip_check.outputs.should_skip != 'true' && github.repository == 'dani-garcia/vaultwarden' }} strategy: matrix: @@ -73,10 +61,17 @@ jobs: steps: # Checkout the repo - name: Checkout - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0 with: fetch-depth: 0 + # Login to Docker Hub + - name: Login to Docker Hub + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2.1.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + # Determine Docker Tag - name: Init Variables id: vars @@ -90,146 +85,34 @@ jobs: fi # End Determine Docker Tag - # Login to Docker Hub - - name: Login to Docker Hub - uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2.1.0 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - if: ${{ env.HAVE_DOCKERHUB_LOGIN == 'true' }} - - # Login to GitHub Container Registry - - name: Login to GitHub Container Registry - uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2.1.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - if: ${{ env.HAVE_GHCR_LOGIN == 'true' }} - - # Login to Quay.io - - name: Login to Quay.io - uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2.1.0 - with: - registry: quay.io - username: ${{ secrets.QUAY_USERNAME }} - password: ${{ secrets.QUAY_TOKEN }} - if: ${{ env.HAVE_QUAY_LOGIN == 'true' }} - - # Debian - - # Docker Hub - - name: Build Debian based images (docker.io) - shell: bash - env: - DOCKER_REPO: "${{ vars.DOCKERHUB_REPO }}" - DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}" - run: | - ./hooks/build - if: ${{ matrix.base_image == 'debian' && env.HAVE_DOCKERHUB_LOGIN == 'true' }} - - - name: Push Debian based images (docker.io) - shell: bash - env: - DOCKER_REPO: "${{ vars.DOCKERHUB_REPO }}" - DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}" - run: | - ./hooks/push - if: ${{ matrix.base_image == 'debian' && env.HAVE_DOCKERHUB_LOGIN == 'true' }} - - # GitHub Container Registry - - name: Build Debian based images (ghcr.io) - shell: bash - env: - DOCKER_REPO: "${{ vars.GHCR_REPO }}" - DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}" - run: | - ./hooks/build - if: ${{ matrix.base_image == 'debian' && env.HAVE_GHCR_LOGIN == 'true' }} - - - name: Push Debian based images (ghcr.io) - shell: bash - env: - DOCKER_REPO: "${{ vars.GHCR_REPO }}" - DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}" - run: | - ./hooks/push - if: ${{ matrix.base_image == 'debian' && env.HAVE_GHCR_LOGIN == 'true' }} - - # Quay.io - - name: Build Debian based images (quay.io) + - name: Build Debian based images shell: bash env: - DOCKER_REPO: "${{ vars.QUAY_REPO }}" DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}" run: | ./hooks/build - if: ${{ matrix.base_image == 'debian' && env.HAVE_QUAY_LOGIN == 'true' }} + if: ${{ matrix.base_image == 'debian' }} - - name: Push Debian based images (quay.io) + - name: Push Debian based images shell: bash env: - DOCKER_REPO: "${{ vars.QUAY_REPO }}" DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}" run: | ./hooks/push - if: ${{ matrix.base_image == 'debian' && env.HAVE_QUAY_LOGIN == 'true' }} - - # Alpine - - # Docker Hub - - name: Build Alpine based images (docker.io) - shell: bash - env: - DOCKER_REPO: "${{ vars.DOCKERHUB_REPO }}" - DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}-alpine" - run: | - ./hooks/build - if: ${{ matrix.base_image == 'alpine' && env.HAVE_DOCKERHUB_LOGIN == 'true' }} - - - name: Push Alpine based images (docker.io) - shell: bash - env: - DOCKER_REPO: "${{ vars.DOCKERHUB_REPO }}" - DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}-alpine" - run: | - ./hooks/push - if: ${{ matrix.base_image == 'alpine' && env.HAVE_DOCKERHUB_LOGIN == 'true' }} - - # GitHub Container Registry - - name: Build Alpine based images (ghcr.io) - shell: bash - env: - DOCKER_REPO: "${{ vars.GHCR_REPO }}" - DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}-alpine" - run: | - ./hooks/build - if: ${{ matrix.base_image == 'alpine' && env.HAVE_GHCR_LOGIN == 'true' }} - - - name: Push Alpine based images (ghcr.io) - shell: bash - env: - DOCKER_REPO: "${{ vars.GHCR_REPO }}" - DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}-alpine" - run: | - ./hooks/push - if: ${{ matrix.base_image == 'alpine' && env.HAVE_GHCR_LOGIN == 'true' }} + if: ${{ matrix.base_image == 'debian' }} - # Quay.io - - name: Build Alpine based images (quay.io) + - name: Build Alpine based images shell: bash env: - DOCKER_REPO: "${{ vars.QUAY_REPO }}" DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}-alpine" run: | ./hooks/build - if: ${{ matrix.base_image == 'alpine' && env.HAVE_QUAY_LOGIN == 'true' }} + if: ${{ matrix.base_image == 'alpine' }} - - name: Push Alpine based images (quay.io) + - name: Push Alpine based images shell: bash env: - DOCKER_REPO: "${{ vars.QUAY_REPO }}" DOCKER_TAG: "${{steps.vars.outputs.DOCKER_TAG}}-alpine" run: | ./hooks/push - if: ${{ matrix.base_image == 'alpine' && env.HAVE_QUAY_LOGIN == 'true' }} + if: ${{ matrix.base_image == 'alpine' }} diff --git a/.hadolint.yaml b/.hadolint.yaml index e7387659d56..f1c324b8cfb 100644 --- a/.hadolint.yaml +++ b/.hadolint.yaml @@ -3,9 +3,5 @@ ignored: - DL3008 # disable explicit version for apk install - DL3018 - # disable check for consecutive `RUN` instructions - - DL3059 trustedRegistries: - docker.io - - ghcr.io - - quay.io diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0d7abeb75b0..a8a6824259a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,20 +1,16 @@ --- repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.3.0 hooks: - id: check-yaml - id: check-json - id: check-toml - - id: mixed-line-ending - args: ["--fix=no"] - id: end-of-file-fixer exclude: "(.*js$|.*css$)" - id: check-case-conflict - id: check-merge-conflict - id: detect-private-key - - id: check-symlinks - - id: forbid-submodules - repo: local hooks: - id: fmt @@ -40,5 +36,5 @@ repos: language: system args: ["--features", "sqlite,mysql,postgresql,enable_mimalloc", "--", "-D", "warnings"] types_or: [rust, file] - files: (Cargo.toml|Cargo.lock|rust-toolchain|clippy.toml|.*\.rs$) + files: (Cargo.toml|Cargo.lock|rust-toolchain|.*\.rs$) pass_filenames: false diff --git a/Cargo.lock b/Cargo.lock index df4b04f8eac..665367cf9d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,56 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -41,28 +85,6 @@ dependencies = [ "libc", ] -[[package]] -name = "argon2" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95c2fcf79ad1932ac6269a738109997a83c227c09b75842ae564dc8ede6a861c" -dependencies = [ - "base64ct", - "blake2", - "password-hash", -] - -[[package]] -name = "async-channel" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - [[package]] name = "async-compression" version = "0.3.15" @@ -77,146 +99,36 @@ dependencies = [ "tokio", ] -[[package]] -name = "async-executor" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" -dependencies = [ - "async-lock", - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" -dependencies = [ - "async-channel", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite", - "log", - "parking", - "polling", - "rustix", - "slab", - "socket2", - "waker-fn", -] - -[[package]] -name = "async-lock" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-process" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6381ead98388605d0d9ff86371043b5aa922a3905824244de40dc263a14fcba4" -dependencies = [ - "async-io", - "async-lock", - "autocfg", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "libc", - "signal-hook", - "windows-sys 0.42.0", -] - -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-channel", - "async-global-executor", - "async-io", - "async-lock", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - [[package]] name = "async-stream" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" dependencies = [ "async-stream-impl", "futures-core", - "pin-project-lite", ] [[package]] name = "async-stream-impl" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] -[[package]] -name = "async-task" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" - [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn", ] [[package]] @@ -235,10 +147,15 @@ dependencies = [ ] [[package]] -name = "atomic-waker" -version = "1.1.0" +name = "atty" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] [[package]] name = "autocfg" @@ -247,22 +164,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "base64" -version = "0.13.1" +name = "backtrace" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] [[package]] name = "base64" -version = "0.21.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] -name = "base64ct" -version = "1.6.0" +name = "base64" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" [[package]] name = "binascii" @@ -276,44 +202,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitflags" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - [[package]] name = "block-buffer" -version = "0.10.4" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] -[[package]] -name = "blocking" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" -dependencies = [ - "async-channel", - "async-lock", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", -] - [[package]] name = "brotli" version = "3.3.4" @@ -327,9 +224,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.4" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -337,9 +234,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "byteorder" @@ -349,9 +246,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "cached" @@ -364,7 +261,7 @@ dependencies = [ "cached_proc_macro", "cached_proc_macro_types", "futures", - "hashbrown 0.13.2", + "hashbrown 0.13.1", "instant", "lazy_static", "once_cell", @@ -382,7 +279,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -393,9 +290,9 @@ checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" [[package]] name = "cfg-if" @@ -405,9 +302,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "num-integer", @@ -439,22 +336,23 @@ dependencies = [ ] [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "cipher" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" dependencies = [ - "termcolor", - "unicode-width", + "crypto-common", + "inout", ] [[package]] -name = "concurrent-queue" -version = "2.1.0" +name = "codespan-reporting" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ - "crossbeam-utils", + "termcolor", + "unicode-width", ] [[package]] @@ -463,18 +361,14 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ + "aes-gcm", + "base64 0.20.0", + "hkdf", + "hmac", "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "cookie" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" -dependencies = [ - "percent-encoding", + "rand", + "sha2", + "subtle", "time", "version_check", ] @@ -485,7 +379,7 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e4b6aa369f41f5faa04bb80c9b1f4216ea81646ed6124d76ba5c49a7aafd9cd" dependencies = [ - "cookie 0.16.2", + "cookie", "idna 0.2.3", "log", "publicsuffix", @@ -501,7 +395,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bca9b3c618262fc0c85ecbc814c144e04be9c6eec08b315e7cd1cfbe0bb6ca84" dependencies = [ - "cookie 0.16.2", + "cookie", "idna 0.3.0", "log", "publicsuffix", @@ -529,9 +423,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] @@ -558,9 +452,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] @@ -572,24 +466,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] [[package]] -name = "ctor" -version = "0.1.26" +name = "ctr" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "quote", - "syn 1.0.109", + "cipher", ] [[package]] name = "cxx" -version = "1.0.94" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" dependencies = [ "cc", "cxxbridge-flags", @@ -599,9 +493,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.94" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" dependencies = [ "cc", "codespan-reporting", @@ -609,31 +503,31 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.13", + "syn", ] [[package]] name = "cxxbridge-flags" -version = "1.0.94" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" +checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" [[package]] name = "cxxbridge-macro" -version = "1.0.94" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn", ] [[package]] name = "darling" -version = "0.14.4" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" dependencies = [ "darling_core", "darling_macro", @@ -641,27 +535,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 1.0.109", + "syn", ] [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -691,9 +585,9 @@ checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] name = "devise" -version = "0.4.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6eacefd3f541c66fc61433d65e54e0e46e0a029a819a7dbbc7a7b489e8a85f8" +checksum = "50c7580b072f1c8476148f16e0a0d5dedddab787da98d86c5082c5e9ed8ab595" dependencies = [ "devise_codegen", "devise_core", @@ -701,9 +595,9 @@ dependencies = [ [[package]] name = "devise_codegen" -version = "0.4.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8cf4b8dd484ede80fd5c547592c46c3745a617c8af278e2b72bea86b2dfed6" +checksum = "123c73e7a6e51b05c75fe1a1b2f4e241399ea5740ed810b0e3e6cacd9db5e7b2" dependencies = [ "devise_core", "quote", @@ -711,24 +605,24 @@ dependencies = [ [[package]] name = "devise_core" -version = "0.4.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a" +checksum = "841ef46f4787d9097405cac4e70fb8644fc037b526e8c14054247c0263c400d0" dependencies = [ - "bitflags 2.0.2", + "bitflags", "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.13", + "syn", ] [[package]] name = "diesel" -version = "2.0.3" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4391a22b19c916e50bec4d6140f29bdda3e3bb187223fe6e3ea0b6e4d1021c04" +checksum = "68c186a7418a2aac330bb76cde82f16c36b03a66fb91db32d20214311f9f6545" dependencies = [ - "bitflags 1.3.2", + "bitflags", "byteorder", "chrono", "diesel_derives", @@ -743,14 +637,14 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.0.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad74fdcf086be3d4fdd142f67937678fe60ed431c3b2f08599e7687269410c4" +checksum = "143b758c91dbc3fe1fdcb0dba5bd13276c6a66422f2ef5795b58488248a310aa" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -787,23 +681,23 @@ dependencies = [ [[package]] name = "dotenvy" -version = "0.15.7" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0" [[package]] name = "either" -version = "1.8.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "email-encoding" -version = "0.2.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75" +checksum = "34dd14c63662e0206599796cd5e1ad0268ab2b9d19b868d6050d688eba2bbf98" dependencies = [ - "base64 0.21.0", + "base64 0.13.1", "memchr", ] @@ -818,9 +712,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ "cfg-if", ] @@ -834,28 +728,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "errno" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.45.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", + "syn", ] [[package]] @@ -867,26 +740,20 @@ dependencies = [ "version_check", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "fastrand" -version = "1.9.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] [[package]] name = "fern" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" +checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a" dependencies = [ "log", "syslog", @@ -948,9 +815,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.28" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" dependencies = [ "futures-channel", "futures-core", @@ -963,9 +830,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", "futures-sink", @@ -973,15 +840,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" dependencies = [ "futures-core", "futures-task", @@ -990,47 +857,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-lite" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] name = "futures-timer" @@ -1040,9 +892,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-channel", "futures-core", @@ -1058,22 +910,22 @@ dependencies = [ [[package]] name = "generator" -version = "0.7.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a20a288a94683f5f4da0adecdbe095c94a77c295e514cc6484e9394dd8376e" +checksum = "d266041a359dfa931b370ef684cceb84b166beb14f7f0421f4a6a3d0c446d12e" dependencies = [ "cc", "libc", "log", "rustversion", - "windows 0.44.0", + "windows", ] [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", @@ -1091,22 +943,26 @@ dependencies = [ ] [[package]] -name = "glob" -version = "0.3.1" +name = "ghash" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] [[package]] -name = "gloo-timers" -version = "0.2.6" +name = "gimli" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] +checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "governor" @@ -1128,9 +984,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.16" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" dependencies = [ "bytes", "fnv", @@ -1174,15 +1030,24 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.13.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" [[package]] name = "heck" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] [[package]] name = "hermit-abi" @@ -1194,10 +1059,13 @@ dependencies = [ ] [[package]] -name = "hermit-abi" -version = "0.3.1" +name = "hkdf" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] [[package]] name = "hmac" @@ -1230,9 +1098,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", @@ -1264,9 +1132,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ "bytes", "futures-channel", @@ -1301,16 +1169,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.54" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows 0.46.0", + "winapi", ] [[package]] @@ -1352,9 +1220,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.3" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown 0.12.3", @@ -1368,23 +1236,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" [[package]] -name = "instant" -version = "0.1.12" +name = "inout" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "cfg-if", + "generic-array", ] [[package]] -name = "io-lifetimes" -version = "1.0.9" +name = "instant" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.45.0", + "cfg-if", ] [[package]] @@ -1401,27 +1267,15 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" - -[[package]] -name = "is-terminal" -version = "0.4.6" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.45.0", -] +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "jetscii" @@ -1431,9 +1285,9 @@ checksum = "47f142fe24a9c9944451e8349de0a56af5f3e7226dc46f3ed4d4ecc0b85af75e" [[package]] name = "job_scheduler_ng" -version = "2.0.4" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10bbdf445513bbe53f4666218b7057d265c76fa0b30475e121a6bf05dbaacaae" +checksum = "830ebb8320dcba49a2d081c36677d500bbbf9ab7e0500ddddadf88252bca3891" dependencies = [ "chrono", "cron", @@ -1442,20 +1296,20 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] [[package]] name = "jsonwebtoken" -version = "8.3.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +checksum = "09f4f04699947111ec1733e71778d763555737579e44b85844cae8e1940a1828" dependencies = [ - "base64 0.21.0", + "base64 0.13.1", "pem", "ring", "serde", @@ -1463,15 +1317,6 @@ dependencies = [ "simple_asn1", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -1480,13 +1325,12 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lettre" -version = "0.10.4" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bd09637ae3ec7bd605b8e135e757980b3968430ff2b1a4a94fb7769e50166d" +checksum = "2eabca5e0b4d0e98e7f2243fb5b7520b6af2b65d8f87bcc86f2c75185a6ff243" dependencies = [ - "async-std", "async-trait", - "base64 0.21.0", + "base64 0.13.1", "email-encoding", "email_address", "fastrand", @@ -1494,7 +1338,7 @@ dependencies = [ "futures-util", "hostname", "httpdate", - "idna 0.3.0", + "idna 0.2.3", "mime", "native-tls", "nom", @@ -1509,9 +1353,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.140" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libmimalloc-sys" @@ -1549,12 +1393,6 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" -[[package]] -name = "linux-raw-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" - [[package]] name = "lock_api" version = "0.4.9" @@ -1572,7 +1410,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", - "value-bag", ] [[package]] @@ -1625,9 +1462,9 @@ dependencies = [ [[package]] name = "matches" -version = "0.1.10" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "memchr" @@ -1667,9 +1504,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.17" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "minimal-lexical" @@ -1688,21 +1525,21 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] name = "multer" -version = "2.1.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +checksum = "6ed4198ce7a4cbd2a57af78d28c6fbb57d81ac5f1d6ad79ac6c5587419cbdf22" dependencies = [ "bytes", "encoding_rs", @@ -1712,7 +1549,7 @@ dependencies = [ "log", "memchr", "mime", - "spin 0.9.7", + "spin 0.9.4", "tokio", "tokio-util", "version_check", @@ -1754,9 +1591,9 @@ checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" [[package]] name = "nom" -version = "7.1.3" +version = "7.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" dependencies = [ "memchr", "minimal-lexical", @@ -1797,7 +1634,7 @@ checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -1838,19 +1675,34 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d864c91689fdc196779b98dba0aceac6118594c2df6ee5d943eb6a8df4d107a" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "opaque-debug" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.49" +version = "0.10.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2f106ab837a24e03672c59b1239669a0596406ff657c3c0835b6b7f0f35a33" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" dependencies = [ - "bitflags 1.3.2", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -1861,13 +1713,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn", ] [[package]] @@ -1878,19 +1730,20 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.25.2+1.1.1t" +version = "111.24.0+1.1.1s" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320708a054ad9b3bf314688b5db87cf4d6683d64cfc835e2337924ae62bf4431" +checksum = "3498f259dab01178c6228c6b00dcef0ed2a2d5e20d648c017861227773ea4abd" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.84" +version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a20eace9dc2d82904039cb76dcf50fb1a0bba071cfd1629720b5d6f1ddba0fa" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ + "autocfg", "cc", "libc", "openssl-src", @@ -1904,12 +1757,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - [[package]] name = "parking_lot" version = "0.12.1" @@ -1922,15 +1769,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1942,28 +1789,17 @@ dependencies = [ "regex", ] -[[package]] -name = "password-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - [[package]] name = "paste" -version = "1.0.12" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" [[package]] name = "pear" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec95680a7087503575284e5063e14b694b7a9c0b065e5dceec661e0497127e8" +checksum = "15e44241c5e4c868e3eaa78b7c1848cadd6344ed4f54d029832d32b415a58702" dependencies = [ "inlinable_string", "pear_codegen", @@ -1972,21 +1808,21 @@ dependencies = [ [[package]] name = "pear_codegen" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9661a3a53f93f09f2ea882018e4d7c88f6ff2956d809a276060476fd8c879d3c" +checksum = "82a5ca643c2303ecb740d506539deba189e16f2754040a42901cd8105d0282d0" dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.13", + "syn", ] [[package]] name = "pem" -version = "1.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" dependencies = [ "base64 0.13.1", ] @@ -1999,9 +1835,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.7" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" +checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" dependencies = [ "thiserror", "ucd-trie", @@ -2009,9 +1845,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.7" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15" +checksum = "96504449aa860c8dcde14f9fba5c58dc6658688ca1fe363589d6327b8662c603" dependencies = [ "pest", "pest_generator", @@ -2019,26 +1855,26 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.7" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e" +checksum = "798e0220d1111ae63d66cb66a5dcb3fc2d986d520b98e49e1852bfdb11d7c5e7" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.13", + "syn", ] [[package]] name = "pest_meta" -version = "2.5.7" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e" +checksum = "984298b75898e30a843e278a9f2452c31e349a073a0ce6fd950a12a74464e065" dependencies = [ "once_cell", "pest", - "sha2", + "sha1", ] [[package]] @@ -2104,19 +1940,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] -name = "polling" -version = "2.6.0" +name = "polyval" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e1f879b2998099c2d69ab9605d145d5b661195627eccc680002c4918a7fb6fa" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" dependencies = [ - "autocfg", - "bitflags 1.3.2", "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.45.0", + "cpufeatures", + "opaque-debug", + "universal-hash", ] [[package]] @@ -2143,7 +1975,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.109", + "syn", "version_check", ] @@ -2158,24 +1990,30 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" -version = "1.0.55" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0dd4be24fcdcfeaa12a432d588dc59bbad6cad3510c67e74a2b6b2fc950564" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" dependencies = [ "unicode-ident", ] [[package]] name = "proc-macro2-diagnostics" -version = "0.10.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "606c4ba35817e2922a308af55ad51bab3645b59eae5c570d4a6cf07e36bd493b" +checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn", "version_check", "yansi", ] @@ -2220,18 +2058,18 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.26" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] [[package]] name = "quoted_printable" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a24039f627d8285853cc90dcddf8c1ebfaa91f834566948872b225b9a28ed1b6" +checksum = "20f14e071918cbeefc5edc986a7aa92c425dae244e003a35e1cdddb5ca39b5cb" [[package]] name = "r2d2" @@ -2276,11 +2114,11 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "10.7.0" +version = "10.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -2289,43 +2127,34 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] name = "ref-cast" -version = "1.0.16" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" +checksum = "8c78fb8c9293bcd48ef6fce7b4ca950ceaf21210de6e105a883ee280c0f7b9ed" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.16" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" +checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn", ] [[package]] name = "regex" -version = "1.7.3" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -2343,20 +2172,29 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" dependencies = [ "async-compression", - "base64 0.21.0", + "base64 0.13.1", "bytes", - "cookie 0.16.2", + "cookie", "cookie_store 0.16.1", "encoding_rs", "futures-core", @@ -2374,6 +2212,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "proc-macro-hack", "serde", "serde_json", "serde_urlencoded", @@ -2386,7 +2225,6 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", "web-sys", "winreg", ] @@ -2439,20 +2277,20 @@ dependencies = [ [[package]] name = "rocket" -version = "0.5.0-rc.3" +version = "0.5.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58734f7401ae5cfd129685b48f61182331745b357b96f2367f01aebaf1cc9cc9" +checksum = "98ead083fce4a405feb349cf09abdf64471c6077f14e0ce59364aa90d4b99317" dependencies = [ "async-stream", "async-trait", "atomic", + "atty", "binascii", "bytes", "either", "figment", "futures", "indexmap", - "is-terminal", "log", "memchr", "multer", @@ -2478,9 +2316,9 @@ dependencies = [ [[package]] name = "rocket_codegen" -version = "0.5.0-rc.3" +version = "0.5.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7093353f14228c744982e409259fb54878ba9563d08214f2d880d59ff2fc508b" +checksum = "d6aeb6bb9c61e9cd2c00d70ea267bf36f76a4cc615e5908b349c2f9d93999b47" dependencies = [ "devise", "glob", @@ -2488,17 +2326,17 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn 2.0.13", + "syn", "unicode-xid", ] [[package]] name = "rocket_http" -version = "0.5.0-rc.3" +version = "0.5.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936012c99162a03a67f37f9836d5f938f662e26f2717809761a9ac46432090f4" +checksum = "2ded65d127954de3c12471630bf4b81a2792f065984461e65b91d0fdaafc17a2" dependencies = [ - "cookie 0.17.0", + "cookie", "either", "futures", "http", @@ -2523,45 +2361,16 @@ dependencies = [ ] [[package]] -name = "rpassword" -version = "7.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" -dependencies = [ - "libc", - "rtoolbox", - "winapi", -] - -[[package]] -name = "rtoolbox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "rustix" -version = "0.37.6" +name = "rustc-demangle" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d097081ed288dfe45699b72f5b5d648e5f15d64d900c7080273baa20c16a6849" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.45.0", -] +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" [[package]] name = "rustls" -version = "0.20.8" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" dependencies = [ "log", "ring", @@ -2571,24 +2380,24 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" dependencies = [ - "base64 0.21.0", + "base64 0.13.1", ] [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "same-file" @@ -2605,14 +2414,14 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "scheduled-thread-pool" -version = "0.2.7" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" +checksum = "977a7519bff143a44f842fd07e80ad1329295bd71686457f18e496736f4bf9bf" dependencies = [ "parking_lot", ] @@ -2631,9 +2440,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.5" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" [[package]] name = "sct" @@ -2647,11 +2456,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -2660,9 +2469,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" dependencies = [ "core-foundation-sys", "libc", @@ -2670,15 +2479,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "serde" -version = "1.0.159" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] @@ -2695,20 +2504,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.159" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn", ] [[package]] name = "serde_json" -version = "1.0.95" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "itoa", "ryu", @@ -2769,21 +2578,11 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "signal-hook" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" dependencies = [ "libc", ] @@ -2808,9 +2607,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" dependencies = [ "autocfg", ] @@ -2823,9 +2622,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", @@ -2839,9 +2638,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.7" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0959fd6f767df20b231736396e4f602171e00d95205676286e79d4a4eb67bef" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" [[package]] name = "stable-pattern" @@ -2875,20 +2674,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.13" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -2910,53 +2698,53 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", ] [[package]] name = "termcolor" -version = "1.2.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" dependencies = [ - "cfg-if", "once_cell", ] @@ -2971,9 +2759,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ "itoa", "libc", @@ -2991,9 +2779,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.8" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" dependencies = [ "time-core", ] @@ -3009,19 +2797,20 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.27.0" +version = "1.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" dependencies = [ "autocfg", "bytes", "libc", + "memchr", "mio", "num_cpus", "parking_lot", @@ -3029,25 +2818,25 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn", ] [[package]] name = "tokio-native-tls" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" dependencies = [ "native-tls", "tokio", @@ -3078,9 +2867,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" dependencies = [ "futures-core", "pin-project-lite", @@ -3101,9 +2890,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ "bytes", "futures-core", @@ -3115,9 +2904,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" dependencies = [ "serde", ] @@ -3161,7 +2950,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -3306,15 +3095,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" @@ -3337,6 +3126,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -3363,9 +3162,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.3.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" dependencies = [ "getrandom", ] @@ -3376,26 +3175,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "value-bag" -version = "1.0.0-alpha.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" -dependencies = [ - "ctor", - "version_check", -] - [[package]] name = "vaultwarden" version = "1.0.0" dependencies = [ - "argon2", + "backtrace", "bytes", "cached", "chrono", "chrono-tz", - "cookie 0.16.2", + "cookie", "cookie_store 0.19.0", "dashmap", "data-encoding", @@ -3413,7 +3202,6 @@ dependencies = [ "job_scheduler_ng", "jsonwebtoken", "lettre", - "libmimalloc-sys", "libsqlite3-sys", "log", "mimalloc", @@ -3430,7 +3218,6 @@ dependencies = [ "ring", "rmpv", "rocket", - "rpassword", "semver", "serde", "serde_json", @@ -3443,7 +3230,6 @@ dependencies = [ "url", "uuid", "webauthn-rs", - "which", "yubico", ] @@ -3459,19 +3245,14 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - [[package]] name = "walkdir" -version = "2.3.3" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", + "winapi", "winapi-util", ] @@ -3499,9 +3280,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3509,24 +3290,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if", "js-sys", @@ -3536,9 +3317,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3546,41 +3327,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" - -[[package]] -name = "wasm-streams" -version = "0.2.3" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -3615,17 +3383,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - [[package]] name = "widestring" version = "0.5.1" @@ -3665,20 +3422,15 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.44.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" +checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" -dependencies = [ - "windows-targets", + "windows_aarch64_msvc 0.39.0", + "windows_i686_gnu 0.39.0", + "windows_i686_msvc 0.39.0", + "windows_x86_64_gnu 0.39.0", + "windows_x86_64_msvc 0.39.0", ] [[package]] @@ -3688,79 +3440,85 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_x86_64_msvc 0.42.0", ] [[package]] -name = "windows-sys" -version = "0.45.0" +name = "windows_aarch64_gnullvm" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets", -] +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" [[package]] -name = "windows-targets" -version = "0.42.2" +name = "windows_aarch64_msvc" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] +checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" [[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" +name = "windows_aarch64_msvc" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" [[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" +name = "windows_i686_gnu" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" [[package]] name = "winreg" diff --git a/Cargo.toml b/Cargo.toml index 4a5e689381f..4eab1ef9b7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,12 +3,12 @@ name = "vaultwarden" version = "1.0.0" authors = ["Daniel García "] edition = "2021" -rust-version = "1.66.1" +rust-version = "1.61.0" resolver = "2" repository = "https://github.com/dani-garcia/vaultwarden" readme = "README.md" -license = "AGPL-3.0-only" +license = "GPL-3.0-only" publish = false build = "build.rs" @@ -41,43 +41,43 @@ syslog = "6.0.1" # Needs to be v4 until fern is updated [dependencies] # Logging log = "0.4.17" -fern = { version = "0.6.2", features = ["syslog-6"] } +fern = { version = "0.6.1", features = ["syslog-6"] } tracing = { version = "0.1.37", features = ["log"] } # Needed to have lettre and webauthn-rs trace logging to work +backtrace = "0.3.67" # Logging panics to logfile instead stderr only + # A `dotenv` implementation for Rust -dotenvy = { version = "0.15.7", default-features = false } +dotenvy = { version = "0.15.6", default-features = false } # Lazy initialization -once_cell = "1.17.1" +once_cell = "1.17.0" # Numerical libraries num-traits = "0.2.15" num-derive = "0.3.3" # Web framework -rocket = { version = "0.5.0-rc.3", features = ["tls", "json"], default-features = false } +rocket = { version = "0.5.0-rc.2", features = ["tls", "json"], default-features = false } # WebSockets libraries tokio-tungstenite = "0.18.0" rmpv = "1.0.0" # MessagePack library - -# Concurrent HashMap used for WebSocket messaging and favicons dashmap = "5.4.0" # Async futures -futures = "0.3.28" -tokio = { version = "1.27.0", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time", "signal"] } +futures = "0.3.25" +tokio = { version = "1.24.1", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time", "signal"] } # A generic serialization/deserialization framework -serde = { version = "1.0.159", features = ["derive"] } -serde_json = "1.0.95" +serde = { version = "1.0.152", features = ["derive"] } +serde_json = "1.0.91" # A safe, extensible ORM and Query builder -diesel = { version = "2.0.3", features = ["chrono", "r2d2"] } +diesel = { version = "2.0.2", features = ["chrono", "r2d2"] } diesel_migrations = "2.0.0" diesel_logger = { version = "0.2.0", optional = true } -# Bundled/Static SQLite +# Bundled SQLite libsqlite3-sys = { version = "0.25.2", features = ["bundled"], optional = true } # Crypto-related libraries @@ -85,21 +85,21 @@ rand = { version = "0.8.5", features = ["small_rng"] } ring = "0.16.20" # UUID generation -uuid = { version = "1.3.0", features = ["v4"] } +uuid = { version = "1.2.2", features = ["v4"] } # Date and time libraries -chrono = { version = "0.4.24", features = ["clock", "serde"], default-features = false } +chrono = { version = "0.4.23", features = ["clock", "serde"], default-features = false } chrono-tz = "0.8.1" -time = "0.3.20" +time = "0.3.17" # Job scheduler -job_scheduler_ng = "2.0.4" +job_scheduler_ng = "2.0.3" # Data encoding library Hex/Base32/Base64 data-encoding = "2.3.3" # JWT library -jsonwebtoken = "8.3.0" +jsonwebtoken = "8.2.0" # TOTP library totp-lite = "2.0.0" @@ -110,69 +110,50 @@ yubico = { version = "0.11.0", features = ["online-tokio"], default-features = f # WebAuthn libraries webauthn-rs = "0.3.2" -# Handling of URL's for WebAuthn and favicons +# Handling of URL's for WebAuthn url = "2.3.1" -# Email libraries -lettre = { version = "0.10.3", features = ["smtp-transport", "sendmail-transport", "builder", "serde", "tokio1-native-tls", "hostname", "tracing", "tokio1"], default-features = false } +# Email librariese-Base, Update crates and small change. +lettre = { version = "0.10.1", features = ["smtp-transport", "builder", "serde", "tokio1-native-tls", "hostname", "tracing", "tokio1"], default-features = false } percent-encoding = "2.2.0" # URL encoding library used for URL's in the emails email_address = "0.2.4" -# HTML Template library +# Template library handlebars = { version = "4.3.6", features = ["dir_source"] } -# HTTP client (Used for favicons, version check, DUO and HIBP API) -reqwest = { version = "0.11.16", features = ["stream", "json", "gzip", "brotli", "socks", "cookies", "trust-dns"] } +# HTTP client +reqwest = { version = "0.11.13", features = ["stream", "json", "gzip", "brotli", "socks", "cookies", "trust-dns"] } -# Favicon extraction libraries +# For favicon extraction from main website html5gum = "0.5.2" -regex = { version = "1.7.3", features = ["std", "perf", "unicode-perl"], default-features = false } +regex = { version = "1.7.1", features = ["std", "perf", "unicode-perl"], default-features = false } data-url = "0.2.0" -bytes = "1.4.0" - -# Cache function results (Used for version check and favicon fetching) +bytes = "1.3.0" cached = "0.42.0" # Used for custom short lived cookie jar during favicon extraction cookie = "0.16.2" cookie_store = "0.19.0" -# Used by U2F, JWT and PostgreSQL -openssl = "0.10.48" +# Used by U2F, JWT and Postgres +openssl = "0.10.45" # CLI argument parsing pico-args = "0.5.0" # Macro ident concatenation -paste = "1.0.12" +paste = "1.0.11" governor = "0.5.1" # Check client versions for specific features. -semver = "1.0.17" +semver = "1.0.16" # Allow overriding the default memory allocator # Mainly used for the musl builds, since the default musl malloc is very slow -mimalloc = { version = "=0.1.34", features = ["secure"], default-features = false, optional = true } -libmimalloc-sys = "=0.1.30" -which = "4.4.0" - -# Argon2 library with support for the PHC format -argon2 = "0.5.0" - -# Reading a password from the cli for generating the Argon2id ADMIN_TOKEN -rpassword = "7.2.0" +mimalloc = { version = "0.1.34", features = ["secure"], default-features = false, optional = true } # Strip debuginfo from the release builds # Also enable thin LTO for some optimizations [profile.release] strip = "debuginfo" lto = "thin" - -# Always build argon2 using opt-level 3 -# This is a huge speed improvement during testing -[profile.dev.package.argon2] -opt-level = 3 - -# A little bit of a speedup -[profile.dev] -split-debuginfo = "unpacked" diff --git a/LICENSE.txt b/LICENSE.txt index 0ad25db4bd1..f288702d2fa 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,5 +1,5 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies @@ -7,15 +7,17 @@ Preamble - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. + The GNU General Public License is a free, copyleft license for +software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to +the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free -software for all its users. +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you @@ -24,34 +26,44 @@ them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. @@ -60,7 +72,7 @@ modification follow. 0. Definitions. - "This License" refers to version 3 of the GNU Affero General Public License. + "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. @@ -537,45 +549,35 @@ to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. + 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single +under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General +Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published +GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's +versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. @@ -633,29 +635,40 @@ the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. + GNU General Public License for more details. - You should have received a copy of the GNU Affero General Public License + You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see +For more information on this, and how to apply and follow the GNU GPL, see . + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index dc98d41db29..4a233496be4 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,11 @@ 📢 Note: This project was known as Bitwarden_RS and has been renamed to separate itself from the official Bitwarden server in the hopes of avoiding confusion and trademark/branding issues. Please see [#1642](https://github.com/dani-garcia/vaultwarden/discussions/1642) for more explanation. --- -[![Build](https://github.com/dani-garcia/vaultwarden/actions/workflows/build.yml/badge.svg)](https://github.com/dani-garcia/vaultwarden/actions/workflows/build.yml) -[![ghcr.io](https://img.shields.io/badge/ghcr.io-download-blue)](https://github.com/dani-garcia/vaultwarden/pkgs/container/vaultwarden) + [![Docker Pulls](https://img.shields.io/docker/pulls/vaultwarden/server.svg)](https://hub.docker.com/r/vaultwarden/server) -[![Quay.io](https://img.shields.io/badge/Quay.io-download-blue)](https://quay.io/repository/vaultwarden/server) [![Dependency Status](https://deps.rs/repo/github/dani-garcia/vaultwarden/status.svg)](https://deps.rs/repo/github/dani-garcia/vaultwarden) [![GitHub Release](https://img.shields.io/github/release/dani-garcia/vaultwarden.svg)](https://github.com/dani-garcia/vaultwarden/releases/latest) -[![AGPL-3.0 Licensed](https://img.shields.io/github/license/dani-garcia/vaultwarden.svg)](https://github.com/dani-garcia/vaultwarden/blob/main/LICENSE.txt) +[![GPL-3.0 Licensed](https://img.shields.io/github/license/dani-garcia/vaultwarden.svg)](https://github.com/dani-garcia/vaultwarden/blob/main/LICENSE.txt) [![Matrix Chat](https://img.shields.io/matrix/vaultwarden:matrix.org.svg?logo=matrix)](https://matrix.to/#/#vaultwarden:matrix.org) Image is based on [Rust implementation of Bitwarden API](https://github.com/dani-garcia/vaultwarden). @@ -25,13 +23,12 @@ Image is based on [Rust implementation of Bitwarden API](https://github.com/dani Basically full implementation of Bitwarden API is provided including: * Organizations support - * Attachments and Send + * Attachments * Vault API support * Serving the static files for Vault interface * Website icons API * Authenticator and U2F support * YubiKey and Duo support - * Emergency Access ## Installation Pull the docker image and mount a volume from the host for persistent storage: @@ -42,7 +39,7 @@ docker run -d --name vaultwarden -v /vw-data/:/data/ -p 80:80 vaultwarden/server ``` This will preserve any persistent data under /vw-data/, you can adapt the path to whatever suits you. -**IMPORTANT**: Most modern web browsers, disallow the use of Web Crypto APIs in insecure contexts. In this case, you might get an error like `Cannot read property 'importKey'`. To solve this problem, you need to access the web vault via HTTPS or localhost. +**IMPORTANT**: Some web browsers, like Chrome, disallow the use of Web Crypto APIs in insecure contexts. In this case, you might get an error like `Cannot read property 'importKey'`. To solve this problem, you need to access the web vault from HTTPS. This can be configured in [vaultwarden directly](https://github.com/dani-garcia/vaultwarden/wiki/Enabling-HTTPS) or using a third-party reverse proxy ([some examples](https://github.com/dani-garcia/vaultwarden/wiki/Proxy-examples)). @@ -52,9 +49,9 @@ If you have an available domain name, you can get HTTPS certificates with [Let's See the [vaultwarden wiki](https://github.com/dani-garcia/vaultwarden/wiki) for more information on how to configure and run the vaultwarden server. ## Get in touch -To ask a question, offer suggestions or new features or to get help configuring or installing the software, please use [GitHub Discussions](https://github.com/dani-garcia/vaultwarden/discussions) or [the forum](https://vaultwarden.discourse.group/). +To ask a question, offer suggestions or new features or to get help configuring or installing the software, please [use the forum](https://vaultwarden.discourse.group/). -If you spot any bugs or crashes with vaultwarden itself, please [create an issue](https://github.com/dani-garcia/vaultwarden/issues/). Make sure you are on the latest version and there aren't any similar issues open, though! +If you spot any bugs or crashes with vaultwarden itself, please [create an issue](https://github.com/dani-garcia/vaultwarden/issues/). Make sure there aren't any similar issues open, though! If you prefer to chat, we're usually hanging around at [#vaultwarden:matrix.org](https://matrix.to/#/#vaultwarden:matrix.org) room on Matrix. Feel free to join us! diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000000..f5fcb567141 --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +msrv = "1.61.0" diff --git a/docker/Dockerfile.j2 b/docker/Dockerfile.j2 index 54490ef3fbd..f0f4b2c9e8e 100644 --- a/docker/Dockerfile.j2 +++ b/docker/Dockerfile.j2 @@ -2,42 +2,40 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. -{% set rust_version = "1.68.2" %} -{% set debian_version = "bullseye" %} -{% set alpine_version = "3.17" %} -{% set build_stage_base_image = "rust:%s-%s" % (rust_version, debian_version) %} + +{% set build_stage_base_image = "rust:1.66-bullseye" %} {% if "alpine" in target_file %} {% if "amd64" in target_file %} -{% set build_stage_base_image = "blackdex/rust-musl:x86_64-musl-stable-%s" % rust_version %} -{% set runtime_stage_base_image = "alpine:%s" % alpine_version %} +{% set build_stage_base_image = "blackdex/rust-musl:x86_64-musl-stable-1.66.0" %} +{% set runtime_stage_base_image = "alpine:3.17" %} {% set package_arch_target = "x86_64-unknown-linux-musl" %} {% elif "armv7" in target_file %} -{% set build_stage_base_image = "blackdex/rust-musl:armv7-musleabihf-stable-%s" % rust_version %} -{% set runtime_stage_base_image = "balenalib/armv7hf-alpine:%s" % alpine_version %} +{% set build_stage_base_image = "blackdex/rust-musl:armv7-musleabihf-stable-1.66.0" %} +{% set runtime_stage_base_image = "balenalib/armv7hf-alpine:3.17" %} {% set package_arch_target = "armv7-unknown-linux-musleabihf" %} {% elif "armv6" in target_file %} -{% set build_stage_base_image = "blackdex/rust-musl:arm-musleabi-stable-%s" % rust_version %} -{% set runtime_stage_base_image = "balenalib/rpi-alpine:%s" % alpine_version %} +{% set build_stage_base_image = "blackdex/rust-musl:arm-musleabi-stable-1.66.0" %} +{% set runtime_stage_base_image = "balenalib/rpi-alpine:3.17" %} {% set package_arch_target = "arm-unknown-linux-musleabi" %} {% elif "arm64" in target_file %} -{% set build_stage_base_image = "blackdex/rust-musl:aarch64-musl-stable-%s" % rust_version %} -{% set runtime_stage_base_image = "balenalib/aarch64-alpine:%s" % alpine_version %} +{% set build_stage_base_image = "blackdex/rust-musl:aarch64-musl-stable-1.66.0" %} +{% set runtime_stage_base_image = "balenalib/aarch64-alpine:3.17" %} {% set package_arch_target = "aarch64-unknown-linux-musl" %} {% endif %} {% elif "amd64" in target_file %} -{% set runtime_stage_base_image = "debian:%s-slim" % debian_version %} +{% set runtime_stage_base_image = "debian:bullseye-slim" %} {% elif "arm64" in target_file %} -{% set runtime_stage_base_image = "balenalib/aarch64-debian:%s" % debian_version %} +{% set runtime_stage_base_image = "balenalib/aarch64-debian:bullseye" %} {% set package_arch_name = "arm64" %} {% set package_arch_target = "aarch64-unknown-linux-gnu" %} {% set package_cross_compiler = "aarch64-linux-gnu" %} {% elif "armv6" in target_file %} -{% set runtime_stage_base_image = "balenalib/rpi-debian:%s" % debian_version %} +{% set runtime_stage_base_image = "balenalib/rpi-debian:bullseye" %} {% set package_arch_name = "armel" %} {% set package_arch_target = "arm-unknown-linux-gnueabi" %} {% set package_cross_compiler = "arm-linux-gnueabi" %} {% elif "armv7" in target_file %} -{% set runtime_stage_base_image = "balenalib/armv7hf-debian:%s" % debian_version %} +{% set runtime_stage_base_image = "balenalib/armv7hf-debian:bullseye" %} {% set package_arch_name = "armhf" %} {% set package_arch_target = "armv7-unknown-linux-gnueabihf" %} {% set package_cross_compiler = "arm-linux-gnueabihf" %} @@ -52,7 +50,7 @@ {% else %} {% set package_arch_target_param = "" %} {% endif %} -{% if "buildkit" in target_file %} +{% if "buildx" in target_file %} {% set mount_rust_cache = "--mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry " %} {% else %} {% set mount_rust_cache = "" %} @@ -61,8 +59,8 @@ # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ ####################### VAULT BUILD IMAGE ####################### -{% set vault_version = "v2023.3.0b" %} -{% set vault_image_digest = "sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee" %} +{% set vault_version = "v2022.12.0" %} +{% set vault_image_digest = "sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e" %} # The web-vault digest specifies a particular web-vault build on Docker Hub. # Using the digest instead of the tag name provides better security, # as the digest of an image is immutable, whereas a tag name can later @@ -85,6 +83,8 @@ FROM vaultwarden/web-vault@{{ vault_image_digest }} as vault ########################## BUILD IMAGE ########################## FROM {{ build_stage_base_image }} as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 \ @@ -93,6 +93,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN {{ mount_rust_cache -}} mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal @@ -103,19 +104,21 @@ RUN {{ mount_rust_cache -}} mkdir -pv "${CARGO_HOME}" \ ENV RUSTFLAGS='-Clink-arg=/usr/local/musl/{{ package_arch_target }}/lib/libatomic.a' {% endif %} {% elif "arm" in target_file %} -# Install build dependencies for the {{ package_arch_name }} architecture +# +# Install required build libs for {{ package_arch_name }} architecture. +# hadolint ignore=DL3059 RUN dpkg --add-architecture {{ package_arch_name }} \ && apt-get update \ && apt-get install -y \ --no-install-recommends \ - gcc-{{ package_cross_compiler }} \ + libssl-dev{{ package_arch_prefix }} \ libc6-dev{{ package_arch_prefix }} \ + libpq5{{ package_arch_prefix }} \ + libpq-dev{{ package_arch_prefix }} \ + libmariadb3{{ package_arch_prefix }} \ libmariadb-dev{{ package_arch_prefix }} \ libmariadb-dev-compat{{ package_arch_prefix }} \ - libmariadb3{{ package_arch_prefix }} \ - libpq-dev{{ package_arch_prefix }} \ - libpq5{{ package_arch_prefix }} \ - libssl-dev{{ package_arch_prefix }} \ + gcc-{{ package_cross_compiler }} \ # # Make sure cargo has the right target config && echo '[target.{{ package_arch_target }}]' >> "${CARGO_HOME}/config" \ @@ -127,13 +130,16 @@ ENV CC_{{ package_arch_target | replace("-", "_") }}="/usr/bin/{{ package_cross_ CROSS_COMPILE="1" \ OPENSSL_INCLUDE_DIR="/usr/include/{{ package_cross_compiler }}" \ OPENSSL_LIB_DIR="/usr/lib/{{ package_cross_compiler }}" + {% elif "amd64" in target_file %} -# Install build dependencies +# Install DB packages RUN apt-get update \ && apt-get install -y \ --no-install-recommends \ - libmariadb-dev \ - libpq-dev + libmariadb-dev{{ package_arch_prefix }} \ + libpq-dev{{ package_arch_prefix }} \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* {% endif %} # Creates a dummy project used to grab dependencies @@ -172,6 +178,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN {{ mount_rust_cache -}} cargo build --features ${DB} --release{{ package_arch_target_param }} ######################## RUNTIME IMAGE ######################## @@ -188,6 +195,7 @@ ENV ROCKET_PROFILE="release" \ {% if "amd64" not in target_file %} +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] {% endif %} @@ -195,18 +203,18 @@ RUN [ "cross-build-start" ] RUN mkdir /data \ {% if "alpine" in runtime_stage_base_image %} && apk add --no-cache \ - ca-certificates \ - curl \ openssl \ - tzdata + tzdata \ + curl \ + ca-certificates {% else %} && apt-get update && apt-get install -y \ --no-install-recommends \ + openssl \ ca-certificates \ curl \ libmariadb-dev-compat \ libpq5 \ - openssl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* {% endif %} @@ -214,11 +222,13 @@ RUN mkdir /data \ {% if "armv6" in target_file and "alpine" not in target_file %} # In the Balena Bullseye images for armv6/rpi-debian there is a missing symlink. # This symlink was there in the buster images, and for some reason this is needed. +# hadolint ignore=DL3059 RUN ln -v -s /lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3 {% endif -%} {% if "amd64" not in target_file %} +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] {% endif %} diff --git a/docker/Makefile b/docker/Makefile index d7c0ab80a18..8c939cbabaf 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -8,8 +8,8 @@ all: $(OBJECTS) %/Dockerfile.alpine: Dockerfile.j2 render_template ./render_template "$<" "{\"target_file\":\"$@\"}" > "$@" -%/Dockerfile.buildkit: Dockerfile.j2 render_template +%/Dockerfile.buildx: Dockerfile.j2 render_template ./render_template "$<" "{\"target_file\":\"$@\"}" > "$@" -%/Dockerfile.buildkit.alpine: Dockerfile.j2 render_template +%/Dockerfile.buildx.alpine: Dockerfile.j2 render_template ./render_template "$<" "{\"target_file\":\"$@\"}" > "$@" diff --git a/docker/amd64/Dockerfile b/docker/amd64/Dockerfile index 52513005c9f..88a4583273c 100644 --- a/docker/amd64/Dockerfile +++ b/docker/amd64/Dockerfile @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM rust:1.68.2-bullseye as build +FROM rust:1.66-bullseye as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,16 +39,19 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal -# Install build dependencies +# Install DB packages RUN apt-get update \ && apt-get install -y \ --no-install-recommends \ libmariadb-dev \ - libpq-dev + libpq-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* # Creates a dummy project used to grab dependencies RUN USER=root cargo new --bin /app @@ -75,6 +81,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN cargo build --features ${DB} --release ######################## RUNTIME IMAGE ######################## @@ -91,11 +98,11 @@ ENV ROCKET_PROFILE="release" \ RUN mkdir /data \ && apt-get update && apt-get install -y \ --no-install-recommends \ + openssl \ ca-certificates \ curl \ libmariadb-dev-compat \ libpq5 \ - openssl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/docker/amd64/Dockerfile.alpine b/docker/amd64/Dockerfile.alpine index 189e5747824..07db4098720 100644 --- a/docker/amd64/Dockerfile.alpine +++ b/docker/amd64/Dockerfile.alpine @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:x86_64-musl-stable-1.68.2 as build +FROM blackdex/rust-musl:x86_64-musl-stable-1.66.0 as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,6 +39,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal @@ -71,6 +75,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=x86_64-unknown-linux-musl ######################## RUNTIME IMAGE ######################## @@ -88,10 +93,10 @@ ENV ROCKET_PROFILE="release" \ # Create data folder and Install needed libraries RUN mkdir /data \ && apk add --no-cache \ - ca-certificates \ - curl \ openssl \ - tzdata + tzdata \ + curl \ + ca-certificates VOLUME /data diff --git a/docker/amd64/Dockerfile.buildkit b/docker/amd64/Dockerfile.buildx similarity index 86% rename from docker/amd64/Dockerfile.buildkit rename to docker/amd64/Dockerfile.buildx index c8b159fd28d..9e34a01e086 100644 --- a/docker/amd64/Dockerfile.buildkit +++ b/docker/amd64/Dockerfile.buildx @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM rust:1.68.2-bullseye as build +FROM rust:1.66-bullseye as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,16 +39,19 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal -# Install build dependencies +# Install DB packages RUN apt-get update \ && apt-get install -y \ --no-install-recommends \ libmariadb-dev \ - libpq-dev + libpq-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* # Creates a dummy project used to grab dependencies RUN USER=root cargo new --bin /app @@ -75,6 +81,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release ######################## RUNTIME IMAGE ######################## @@ -91,11 +98,11 @@ ENV ROCKET_PROFILE="release" \ RUN mkdir /data \ && apt-get update && apt-get install -y \ --no-install-recommends \ + openssl \ ca-certificates \ curl \ libmariadb-dev-compat \ libpq5 \ - openssl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/docker/amd64/Dockerfile.buildkit.alpine b/docker/amd64/Dockerfile.buildx.alpine similarity index 87% rename from docker/amd64/Dockerfile.buildkit.alpine rename to docker/amd64/Dockerfile.buildx.alpine index 6c3ab0efaac..8ee0bb1cdeb 100644 --- a/docker/amd64/Dockerfile.buildkit.alpine +++ b/docker/amd64/Dockerfile.buildx.alpine @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:x86_64-musl-stable-1.68.2 as build +FROM blackdex/rust-musl:x86_64-musl-stable-1.66.0 as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,6 +39,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal @@ -71,6 +75,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=x86_64-unknown-linux-musl ######################## RUNTIME IMAGE ######################## @@ -88,10 +93,10 @@ ENV ROCKET_PROFILE="release" \ # Create data folder and Install needed libraries RUN mkdir /data \ && apk add --no-cache \ - ca-certificates \ - curl \ openssl \ - tzdata + tzdata \ + curl \ + ca-certificates VOLUME /data diff --git a/docker/arm64/Dockerfile b/docker/arm64/Dockerfile index 117517aacff..0f0dba16ca4 100644 --- a/docker/arm64/Dockerfile +++ b/docker/arm64/Dockerfile @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM rust:1.68.2-bullseye as build +FROM rust:1.66-bullseye as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,23 +39,26 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal -# Install build dependencies for the arm64 architecture +# +# Install required build libs for arm64 architecture. +# hadolint ignore=DL3059 RUN dpkg --add-architecture arm64 \ && apt-get update \ && apt-get install -y \ --no-install-recommends \ - gcc-aarch64-linux-gnu \ + libssl-dev:arm64 \ libc6-dev:arm64 \ + libpq5:arm64 \ + libpq-dev:arm64 \ + libmariadb3:arm64 \ libmariadb-dev:arm64 \ libmariadb-dev-compat:arm64 \ - libmariadb3:arm64 \ - libpq-dev:arm64 \ - libpq5:arm64 \ - libssl-dev:arm64 \ + gcc-aarch64-linux-gnu \ # # Make sure cargo has the right target config && echo '[target.aarch64-unknown-linux-gnu]' >> "${CARGO_HOME}/config" \ @@ -65,6 +71,7 @@ ENV CC_aarch64_unknown_linux_gnu="/usr/bin/aarch64-linux-gnu-gcc" \ OPENSSL_INCLUDE_DIR="/usr/include/aarch64-linux-gnu" \ OPENSSL_LIB_DIR="/usr/lib/aarch64-linux-gnu" + # Creates a dummy project used to grab dependencies RUN USER=root cargo new --bin /app WORKDIR /app @@ -94,6 +101,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu ######################## RUNTIME IMAGE ######################## @@ -105,20 +113,22 @@ ENV ROCKET_PROFILE="release" \ ROCKET_ADDRESS=0.0.0.0 \ ROCKET_PORT=80 +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apt-get update && apt-get install -y \ --no-install-recommends \ + openssl \ ca-certificates \ curl \ libmariadb-dev-compat \ libpq5 \ - openssl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/docker/arm64/Dockerfile.alpine b/docker/arm64/Dockerfile.alpine index bb2eb65c4e0..d6509ff5560 100644 --- a/docker/arm64/Dockerfile.alpine +++ b/docker/arm64/Dockerfile.alpine @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:aarch64-musl-stable-1.68.2 as build +FROM blackdex/rust-musl:aarch64-musl-stable-1.66.0 as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,6 +39,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal @@ -71,6 +75,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-musl ######################## RUNTIME IMAGE ######################## @@ -84,16 +89,18 @@ ENV ROCKET_PROFILE="release" \ SSL_CERT_DIR=/etc/ssl/certs +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apk add --no-cache \ - ca-certificates \ - curl \ openssl \ - tzdata + tzdata \ + curl \ + ca-certificates +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/docker/arm64/Dockerfile.buildkit b/docker/arm64/Dockerfile.buildx similarity index 88% rename from docker/arm64/Dockerfile.buildkit rename to docker/arm64/Dockerfile.buildx index ebf45fdfc9a..132af16e9cd 100644 --- a/docker/arm64/Dockerfile.buildkit +++ b/docker/arm64/Dockerfile.buildx @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM rust:1.68.2-bullseye as build +FROM rust:1.66-bullseye as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,23 +39,26 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal -# Install build dependencies for the arm64 architecture +# +# Install required build libs for arm64 architecture. +# hadolint ignore=DL3059 RUN dpkg --add-architecture arm64 \ && apt-get update \ && apt-get install -y \ --no-install-recommends \ - gcc-aarch64-linux-gnu \ + libssl-dev:arm64 \ libc6-dev:arm64 \ + libpq5:arm64 \ + libpq-dev:arm64 \ + libmariadb3:arm64 \ libmariadb-dev:arm64 \ libmariadb-dev-compat:arm64 \ - libmariadb3:arm64 \ - libpq-dev:arm64 \ - libpq5:arm64 \ - libssl-dev:arm64 \ + gcc-aarch64-linux-gnu \ # # Make sure cargo has the right target config && echo '[target.aarch64-unknown-linux-gnu]' >> "${CARGO_HOME}/config" \ @@ -65,6 +71,7 @@ ENV CC_aarch64_unknown_linux_gnu="/usr/bin/aarch64-linux-gnu-gcc" \ OPENSSL_INCLUDE_DIR="/usr/include/aarch64-linux-gnu" \ OPENSSL_LIB_DIR="/usr/lib/aarch64-linux-gnu" + # Creates a dummy project used to grab dependencies RUN USER=root cargo new --bin /app WORKDIR /app @@ -94,6 +101,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu ######################## RUNTIME IMAGE ######################## @@ -105,20 +113,22 @@ ENV ROCKET_PROFILE="release" \ ROCKET_ADDRESS=0.0.0.0 \ ROCKET_PORT=80 +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apt-get update && apt-get install -y \ --no-install-recommends \ + openssl \ ca-certificates \ curl \ libmariadb-dev-compat \ libpq5 \ - openssl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/docker/arm64/Dockerfile.buildkit.alpine b/docker/arm64/Dockerfile.buildx.alpine similarity index 86% rename from docker/arm64/Dockerfile.buildkit.alpine rename to docker/arm64/Dockerfile.buildx.alpine index f80c1acd909..b002cee995d 100644 --- a/docker/arm64/Dockerfile.buildkit.alpine +++ b/docker/arm64/Dockerfile.buildx.alpine @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:aarch64-musl-stable-1.68.2 as build +FROM blackdex/rust-musl:aarch64-musl-stable-1.66.0 as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,6 +39,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal @@ -71,6 +75,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=aarch64-unknown-linux-musl ######################## RUNTIME IMAGE ######################## @@ -84,16 +89,18 @@ ENV ROCKET_PROFILE="release" \ SSL_CERT_DIR=/etc/ssl/certs +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apk add --no-cache \ - ca-certificates \ - curl \ openssl \ - tzdata + tzdata \ + curl \ + ca-certificates +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/docker/armv6/Dockerfile b/docker/armv6/Dockerfile index d8643de7d94..8f34ec3200d 100644 --- a/docker/armv6/Dockerfile +++ b/docker/armv6/Dockerfile @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM rust:1.68.2-bullseye as build +FROM rust:1.66-bullseye as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,23 +39,26 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal -# Install build dependencies for the armel architecture +# +# Install required build libs for armel architecture. +# hadolint ignore=DL3059 RUN dpkg --add-architecture armel \ && apt-get update \ && apt-get install -y \ --no-install-recommends \ - gcc-arm-linux-gnueabi \ + libssl-dev:armel \ libc6-dev:armel \ + libpq5:armel \ + libpq-dev:armel \ + libmariadb3:armel \ libmariadb-dev:armel \ libmariadb-dev-compat:armel \ - libmariadb3:armel \ - libpq-dev:armel \ - libpq5:armel \ - libssl-dev:armel \ + gcc-arm-linux-gnueabi \ # # Make sure cargo has the right target config && echo '[target.arm-unknown-linux-gnueabi]' >> "${CARGO_HOME}/config" \ @@ -65,6 +71,7 @@ ENV CC_arm_unknown_linux_gnueabi="/usr/bin/arm-linux-gnueabi-gcc" \ OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabi" \ OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabi" + # Creates a dummy project used to grab dependencies RUN USER=root cargo new --bin /app WORKDIR /app @@ -94,6 +101,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi ######################## RUNTIME IMAGE ######################## @@ -105,24 +113,27 @@ ENV ROCKET_PROFILE="release" \ ROCKET_ADDRESS=0.0.0.0 \ ROCKET_PORT=80 +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apt-get update && apt-get install -y \ --no-install-recommends \ + openssl \ ca-certificates \ curl \ libmariadb-dev-compat \ libpq5 \ - openssl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # In the Balena Bullseye images for armv6/rpi-debian there is a missing symlink. # This symlink was there in the buster images, and for some reason this is needed. +# hadolint ignore=DL3059 RUN ln -v -s /lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3 +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/docker/armv6/Dockerfile.alpine b/docker/armv6/Dockerfile.alpine index d896dce3110..5bc379c23fe 100644 --- a/docker/armv6/Dockerfile.alpine +++ b/docker/armv6/Dockerfile.alpine @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:arm-musleabi-stable-1.68.2 as build +FROM blackdex/rust-musl:arm-musleabi-stable-1.66.0 as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,6 +39,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal @@ -73,6 +77,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=arm-unknown-linux-musleabi ######################## RUNTIME IMAGE ######################## @@ -86,16 +91,18 @@ ENV ROCKET_PROFILE="release" \ SSL_CERT_DIR=/etc/ssl/certs +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apk add --no-cache \ - ca-certificates \ - curl \ openssl \ - tzdata + tzdata \ + curl \ + ca-certificates +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/docker/armv6/Dockerfile.buildkit b/docker/armv6/Dockerfile.buildx similarity index 88% rename from docker/armv6/Dockerfile.buildkit rename to docker/armv6/Dockerfile.buildx index 784ea895e78..18285ee22a0 100644 --- a/docker/armv6/Dockerfile.buildkit +++ b/docker/armv6/Dockerfile.buildx @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM rust:1.68.2-bullseye as build +FROM rust:1.66-bullseye as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,23 +39,26 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal -# Install build dependencies for the armel architecture +# +# Install required build libs for armel architecture. +# hadolint ignore=DL3059 RUN dpkg --add-architecture armel \ && apt-get update \ && apt-get install -y \ --no-install-recommends \ - gcc-arm-linux-gnueabi \ + libssl-dev:armel \ libc6-dev:armel \ + libpq5:armel \ + libpq-dev:armel \ + libmariadb3:armel \ libmariadb-dev:armel \ libmariadb-dev-compat:armel \ - libmariadb3:armel \ - libpq-dev:armel \ - libpq5:armel \ - libssl-dev:armel \ + gcc-arm-linux-gnueabi \ # # Make sure cargo has the right target config && echo '[target.arm-unknown-linux-gnueabi]' >> "${CARGO_HOME}/config" \ @@ -65,6 +71,7 @@ ENV CC_arm_unknown_linux_gnueabi="/usr/bin/arm-linux-gnueabi-gcc" \ OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabi" \ OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabi" + # Creates a dummy project used to grab dependencies RUN USER=root cargo new --bin /app WORKDIR /app @@ -94,6 +101,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi ######################## RUNTIME IMAGE ######################## @@ -105,24 +113,27 @@ ENV ROCKET_PROFILE="release" \ ROCKET_ADDRESS=0.0.0.0 \ ROCKET_PORT=80 +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apt-get update && apt-get install -y \ --no-install-recommends \ + openssl \ ca-certificates \ curl \ libmariadb-dev-compat \ libpq5 \ - openssl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # In the Balena Bullseye images for armv6/rpi-debian there is a missing symlink. # This symlink was there in the buster images, and for some reason this is needed. +# hadolint ignore=DL3059 RUN ln -v -s /lib/ld-linux-armhf.so.3 /lib/ld-linux.so.3 +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/docker/armv6/Dockerfile.buildkit.alpine b/docker/armv6/Dockerfile.buildx.alpine similarity index 87% rename from docker/armv6/Dockerfile.buildkit.alpine rename to docker/armv6/Dockerfile.buildx.alpine index e2f75b7e473..7455205ae97 100644 --- a/docker/armv6/Dockerfile.buildkit.alpine +++ b/docker/armv6/Dockerfile.buildx.alpine @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:arm-musleabi-stable-1.68.2 as build +FROM blackdex/rust-musl:arm-musleabi-stable-1.66.0 as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,6 +39,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal @@ -73,6 +77,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=arm-unknown-linux-musleabi ######################## RUNTIME IMAGE ######################## @@ -86,16 +91,18 @@ ENV ROCKET_PROFILE="release" \ SSL_CERT_DIR=/etc/ssl/certs +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apk add --no-cache \ - ca-certificates \ - curl \ openssl \ - tzdata + tzdata \ + curl \ + ca-certificates +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/docker/armv7/Dockerfile b/docker/armv7/Dockerfile index 654dad06543..1d4553d1d3c 100644 --- a/docker/armv7/Dockerfile +++ b/docker/armv7/Dockerfile @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM rust:1.68.2-bullseye as build +FROM rust:1.66-bullseye as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,23 +39,26 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal -# Install build dependencies for the armhf architecture +# +# Install required build libs for armhf architecture. +# hadolint ignore=DL3059 RUN dpkg --add-architecture armhf \ && apt-get update \ && apt-get install -y \ --no-install-recommends \ - gcc-arm-linux-gnueabihf \ + libssl-dev:armhf \ libc6-dev:armhf \ + libpq5:armhf \ + libpq-dev:armhf \ + libmariadb3:armhf \ libmariadb-dev:armhf \ libmariadb-dev-compat:armhf \ - libmariadb3:armhf \ - libpq-dev:armhf \ - libpq5:armhf \ - libssl-dev:armhf \ + gcc-arm-linux-gnueabihf \ # # Make sure cargo has the right target config && echo '[target.armv7-unknown-linux-gnueabihf]' >> "${CARGO_HOME}/config" \ @@ -65,6 +71,7 @@ ENV CC_armv7_unknown_linux_gnueabihf="/usr/bin/arm-linux-gnueabihf-gcc" \ OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf" \ OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf" + # Creates a dummy project used to grab dependencies RUN USER=root cargo new --bin /app WORKDIR /app @@ -94,6 +101,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf ######################## RUNTIME IMAGE ######################## @@ -105,20 +113,22 @@ ENV ROCKET_PROFILE="release" \ ROCKET_ADDRESS=0.0.0.0 \ ROCKET_PORT=80 +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apt-get update && apt-get install -y \ --no-install-recommends \ + openssl \ ca-certificates \ curl \ libmariadb-dev-compat \ libpq5 \ - openssl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/docker/armv7/Dockerfile.alpine b/docker/armv7/Dockerfile.alpine index 137f84d8d9b..ec26d0fe0c0 100644 --- a/docker/armv7/Dockerfile.alpine +++ b/docker/armv7/Dockerfile.alpine @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:armv7-musleabihf-stable-1.68.2 as build +FROM blackdex/rust-musl:armv7-musleabihf-stable-1.66.0 as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,6 +39,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal @@ -71,6 +75,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-musleabihf ######################## RUNTIME IMAGE ######################## @@ -84,16 +89,18 @@ ENV ROCKET_PROFILE="release" \ SSL_CERT_DIR=/etc/ssl/certs +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apk add --no-cache \ - ca-certificates \ - curl \ openssl \ - tzdata + tzdata \ + curl \ + ca-certificates +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/docker/armv7/Dockerfile.buildkit b/docker/armv7/Dockerfile.buildx similarity index 88% rename from docker/armv7/Dockerfile.buildkit rename to docker/armv7/Dockerfile.buildx index 8e950799779..b51202fbfaf 100644 --- a/docker/armv7/Dockerfile.buildkit +++ b/docker/armv7/Dockerfile.buildx @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM rust:1.68.2-bullseye as build +FROM rust:1.66-bullseye as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,23 +39,26 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal -# Install build dependencies for the armhf architecture +# +# Install required build libs for armhf architecture. +# hadolint ignore=DL3059 RUN dpkg --add-architecture armhf \ && apt-get update \ && apt-get install -y \ --no-install-recommends \ - gcc-arm-linux-gnueabihf \ + libssl-dev:armhf \ libc6-dev:armhf \ + libpq5:armhf \ + libpq-dev:armhf \ + libmariadb3:armhf \ libmariadb-dev:armhf \ libmariadb-dev-compat:armhf \ - libmariadb3:armhf \ - libpq-dev:armhf \ - libpq5:armhf \ - libssl-dev:armhf \ + gcc-arm-linux-gnueabihf \ # # Make sure cargo has the right target config && echo '[target.armv7-unknown-linux-gnueabihf]' >> "${CARGO_HOME}/config" \ @@ -65,6 +71,7 @@ ENV CC_armv7_unknown_linux_gnueabihf="/usr/bin/arm-linux-gnueabihf-gcc" \ OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf" \ OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf" + # Creates a dummy project used to grab dependencies RUN USER=root cargo new --bin /app WORKDIR /app @@ -94,6 +101,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf ######################## RUNTIME IMAGE ######################## @@ -105,20 +113,22 @@ ENV ROCKET_PROFILE="release" \ ROCKET_ADDRESS=0.0.0.0 \ ROCKET_PORT=80 +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apt-get update && apt-get install -y \ --no-install-recommends \ + openssl \ ca-certificates \ curl \ libmariadb-dev-compat \ libpq5 \ - openssl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/docker/armv7/Dockerfile.buildkit.alpine b/docker/armv7/Dockerfile.buildx.alpine similarity index 86% rename from docker/armv7/Dockerfile.buildkit.alpine rename to docker/armv7/Dockerfile.buildx.alpine index b20f6bd2a13..fcb8f8a163d 100644 --- a/docker/armv7/Dockerfile.buildkit.alpine +++ b/docker/armv7/Dockerfile.buildx.alpine @@ -2,6 +2,7 @@ # This file was generated using a Jinja2 template. # Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles. + # Using multistage build: # https://docs.docker.com/develop/develop-images/multistage-build/ # https://whitfin.io/speeding-up-rust-docker-builds/ @@ -15,18 +16,20 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull vaultwarden/web-vault:v2023.3.0b -# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2023.3.0b -# [vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee] +# $ docker pull vaultwarden/web-vault:v2022.12.0 +# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2022.12.0 +# [vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee -# [vaultwarden/web-vault:v2023.3.0b] +# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e +# [vaultwarden/web-vault:v2022.12.0] # -FROM vaultwarden/web-vault@sha256:aa6ba791911a815ea570ec2ddc59992481c6ba8fbb65eed4f7074b463430d3ee as vault +FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:armv7-musleabihf-stable-1.68.2 as build +FROM blackdex/rust-musl:armv7-musleabihf-stable-1.66.0 as build + + # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive \ @@ -36,6 +39,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ CARGO_HOME="/root/.cargo" \ USER="root" + # Create CARGO_HOME folder and don't download rust docs RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry mkdir -pv "${CARGO_HOME}" \ && rustup set profile minimal @@ -71,6 +75,7 @@ RUN touch src/main.rs # Builds again, this time it'll just be # your actual source files being built +# hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=armv7-unknown-linux-musleabihf ######################## RUNTIME IMAGE ######################## @@ -84,16 +89,18 @@ ENV ROCKET_PROFILE="release" \ SSL_CERT_DIR=/etc/ssl/certs +# hadolint ignore=DL3059 RUN [ "cross-build-start" ] # Create data folder and Install needed libraries RUN mkdir /data \ && apk add --no-cache \ - ca-certificates \ - curl \ openssl \ - tzdata + tzdata \ + curl \ + ca-certificates +# hadolint ignore=DL3059 RUN [ "cross-build-end" ] VOLUME /data diff --git a/hooks/arches.sh b/hooks/arches.sh index 96114fae484..c27adf31ada 100644 --- a/hooks/arches.sh +++ b/hooks/arches.sh @@ -1,5 +1,3 @@ -#!/usr/bin/env bash - # The default Debian-based images support these arches for all database backends. arches=( amd64 @@ -7,9 +5,7 @@ arches=( armv7 arm64 ) -export arches if [[ "${DOCKER_TAG}" == *alpine ]]; then distro_suffix=.alpine fi -export distro_suffix diff --git a/hooks/build b/hooks/build index b89a56610b1..96f04d15f71 100755 --- a/hooks/build +++ b/hooks/build @@ -1,8 +1,7 @@ -#!/usr/bin/env bash +#!/bin/bash echo ">>> Building images..." -# shellcheck source=arches.sh source ./hooks/arches.sh if [[ -z "${SOURCE_COMMIT}" ]]; then @@ -24,10 +23,10 @@ LABELS=( # https://github.com/opencontainers/image-spec/blob/master/annotations.md org.opencontainers.image.created="$(date --utc --iso-8601=seconds)" org.opencontainers.image.documentation="https://github.com/dani-garcia/vaultwarden/wiki" - org.opencontainers.image.licenses="AGPL-3.0-only" + org.opencontainers.image.licenses="GPL-3.0-only" org.opencontainers.image.revision="${SOURCE_COMMIT}" org.opencontainers.image.source="${SOURCE_REPOSITORY_URL}" - org.opencontainers.image.url="https://github.com/dani-garcia/vaultwarden" + org.opencontainers.image.url="https://hub.docker.com/r/${DOCKER_REPO#*/}" org.opencontainers.image.version="${SOURCE_VERSION}" ) LABEL_ARGS=() @@ -35,9 +34,9 @@ for label in "${LABELS[@]}"; do LABEL_ARGS+=(--label "${label}") done -# Check if DOCKER_BUILDKIT is set, if so, use the Dockerfile.buildkit as template +# Check if DOCKER_BUILDKIT is set, if so, use the Dockerfile.buildx as template if [[ -n "${DOCKER_BUILDKIT}" ]]; then - buildkit_suffix=.buildkit + buildx_suffix=.buildx fi set -ex @@ -46,6 +45,6 @@ for arch in "${arches[@]}"; do docker build \ "${LABEL_ARGS[@]}" \ -t "${DOCKER_REPO}:${DOCKER_TAG}-${arch}" \ - -f "docker/${arch}/Dockerfile${buildkit_suffix}${distro_suffix}" \ + -f docker/${arch}/Dockerfile${buildx_suffix}${distro_suffix} \ . done diff --git a/hooks/pre_build b/hooks/pre_build index 9829bb5d8ea..6cb50ab1e5f 100755 --- a/hooks/pre_build +++ b/hooks/pre_build @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/bash set -ex diff --git a/hooks/push b/hooks/push index f2a3267f215..de8d24e885a 100755 --- a/hooks/push +++ b/hooks/push @@ -1,6 +1,5 @@ -#!/usr/bin/env bash +#!/bin/bash -# shellcheck source=arches.sh source ./hooks/arches.sh export DOCKER_CLI_EXPERIMENTAL=enabled @@ -42,7 +41,7 @@ LOCAL_REPO="${LOCAL_REGISTRY}/${REPO}" echo ">>> Pushing images to local registry..." -for arch in "${arches[@]}"; do +for arch in ${arches[@]}; do docker_image="${DOCKER_REPO}:${DOCKER_TAG}-${arch}" local_image="${LOCAL_REPO}:${DOCKER_TAG}-${arch}" docker tag "${docker_image}" "${local_image}" @@ -72,9 +71,9 @@ tags=("${DOCKER_REPO}:${DOCKER_TAG}") # to make it easier for users to track the latest release. if [[ "${DOCKER_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then if [[ "${DOCKER_TAG}" == *alpine ]]; then - tags+=("${DOCKER_REPO}:alpine") + tags+=(${DOCKER_REPO}:alpine) else - tags+=("${DOCKER_REPO}:latest") + tags+=(${DOCKER_REPO}:latest) fi fi @@ -92,10 +91,10 @@ declare -A arch_to_platform=( [arm64]="linux/arm64" ) platforms=() -for arch in "${arches[@]}"; do +for arch in ${arches[@]}; do platforms+=("${arch_to_platform[$arch]}") done -platform="$(join "," "${platforms[@]}")" +platforms="$(join "," "${platforms[@]}")" # Run the build, pushing the resulting images and multi-arch manifest list to # Docker Hub. The Dockerfile is read from stdin to avoid sending any build @@ -105,7 +104,46 @@ docker buildx build \ --network host \ --build-arg LOCAL_REPO="${LOCAL_REPO}" \ --build-arg DOCKER_TAG="${DOCKER_TAG}" \ - --platform "${platform}" \ + --platform "${platforms}" \ "${tag_args[@]}" \ --push \ - < ./docker/Dockerfile.buildx + +# Add an extra arch-specific tag for `arm32v6`; Docker can't seem to properly +# auto-select that image on ARMv6 platforms like Raspberry Pi 1 and Zero +# (https://github.com/moby/moby/issues/41017). +# +# Note that we use `arm32v6` instead of `armv6` to be consistent with the +# existing vaultwarden tags, which adhere to the naming conventions of the +# Docker per-architecture repos (e.g., https://hub.docker.com/u/arm32v6). +# Unfortunately, these per-arch repo names aren't always consistent with the +# corresponding platform (OS/arch/variant) IDs, particularly in the case of +# 32-bit ARM arches (e.g., `linux/arm/v6` is used, not `linux/arm32/v6`). +# +# TODO: It looks like this issue should be fixed starting in Docker 20.10.0, +# so this step can be removed once fixed versions are in wider distribution. +# +# Tags: +# +# testing => testing-arm32v6 +# testing-alpine => +# x.y.z => x.y.z-arm32v6, latest-arm32v6 +# x.y.z-alpine => +# +if [[ "${DOCKER_TAG}" != *alpine ]]; then + image="${DOCKER_REPO}":"${DOCKER_TAG}" + + # Fetch the multi-arch manifest list and find the digest of the armv6 image. + filter='.manifests|.[]|select(.platform.architecture=="arm" and .platform.variant=="v6")|.digest' + digest="$(docker manifest inspect "${image}" | jq -r "${filter}")" + + # Pull the armv6 image by digest, retag it, and repush it. + docker pull "${DOCKER_REPO}"@"${digest}" + docker tag "${DOCKER_REPO}"@"${digest}" "${image}"-arm32v6 + docker push "${image}"-arm32v6 + + if [[ "${DOCKER_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then + docker tag "${image}"-arm32v6 "${DOCKER_REPO}:latest"-arm32v6 + docker push "${DOCKER_REPO}:latest"-arm32v6 + fi +fi diff --git a/migrations/mysql/2023-01-06-151600_add_reset_password_support/down.sql b/migrations/mysql/2023-01-06-151600_add_reset_password_support/down.sql deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/migrations/mysql/2023-01-06-151600_add_reset_password_support/up.sql b/migrations/mysql/2023-01-06-151600_add_reset_password_support/up.sql deleted file mode 100644 index 326b310692f..00000000000 --- a/migrations/mysql/2023-01-06-151600_add_reset_password_support/up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE users_organizations -ADD COLUMN reset_password_key TEXT; diff --git a/migrations/mysql/2023-01-11-205851_add_avatar_color/down.sql b/migrations/mysql/2023-01-11-205851_add_avatar_color/down.sql deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/migrations/mysql/2023-01-11-205851_add_avatar_color/up.sql b/migrations/mysql/2023-01-11-205851_add_avatar_color/up.sql deleted file mode 100644 index 8f8e7205123..00000000000 --- a/migrations/mysql/2023-01-11-205851_add_avatar_color/up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE users -ADD COLUMN avatar_color VARCHAR(7); diff --git a/migrations/mysql/2023-01-31-222222_add_argon2/down.sql b/migrations/mysql/2023-01-31-222222_add_argon2/down.sql deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/migrations/mysql/2023-01-31-222222_add_argon2/up.sql b/migrations/mysql/2023-01-31-222222_add_argon2/up.sql deleted file mode 100644 index 35b8189e95e..00000000000 --- a/migrations/mysql/2023-01-31-222222_add_argon2/up.sql +++ /dev/null @@ -1,7 +0,0 @@ -ALTER TABLE users - ADD COLUMN - client_kdf_memory INTEGER DEFAULT NULL; - -ALTER TABLE users - ADD COLUMN - client_kdf_parallelism INTEGER DEFAULT NULL; diff --git a/migrations/postgresql/2023-01-06-151600_add_reset_password_support/down.sql b/migrations/postgresql/2023-01-06-151600_add_reset_password_support/down.sql deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/migrations/postgresql/2023-01-06-151600_add_reset_password_support/up.sql b/migrations/postgresql/2023-01-06-151600_add_reset_password_support/up.sql deleted file mode 100644 index 326b310692f..00000000000 --- a/migrations/postgresql/2023-01-06-151600_add_reset_password_support/up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE users_organizations -ADD COLUMN reset_password_key TEXT; diff --git a/migrations/postgresql/2023-01-11-205851_add_avatar_color/down.sql b/migrations/postgresql/2023-01-11-205851_add_avatar_color/down.sql deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/migrations/postgresql/2023-01-11-205851_add_avatar_color/up.sql b/migrations/postgresql/2023-01-11-205851_add_avatar_color/up.sql deleted file mode 100644 index cf3ef9f77dd..00000000000 --- a/migrations/postgresql/2023-01-11-205851_add_avatar_color/up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE users -ADD COLUMN avatar_color TEXT; diff --git a/migrations/postgresql/2023-01-31-222222_add_argon2/down.sql b/migrations/postgresql/2023-01-31-222222_add_argon2/down.sql deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/migrations/postgresql/2023-01-31-222222_add_argon2/up.sql b/migrations/postgresql/2023-01-31-222222_add_argon2/up.sql deleted file mode 100644 index 35b8189e95e..00000000000 --- a/migrations/postgresql/2023-01-31-222222_add_argon2/up.sql +++ /dev/null @@ -1,7 +0,0 @@ -ALTER TABLE users - ADD COLUMN - client_kdf_memory INTEGER DEFAULT NULL; - -ALTER TABLE users - ADD COLUMN - client_kdf_parallelism INTEGER DEFAULT NULL; diff --git a/migrations/sqlite/2023-01-06-151600_add_reset_password_support/down.sql b/migrations/sqlite/2023-01-06-151600_add_reset_password_support/down.sql deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/migrations/sqlite/2023-01-06-151600_add_reset_password_support/up.sql b/migrations/sqlite/2023-01-06-151600_add_reset_password_support/up.sql deleted file mode 100644 index 326b310692f..00000000000 --- a/migrations/sqlite/2023-01-06-151600_add_reset_password_support/up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE users_organizations -ADD COLUMN reset_password_key TEXT; diff --git a/migrations/sqlite/2023-01-11-205851_add_avatar_color/down.sql b/migrations/sqlite/2023-01-11-205851_add_avatar_color/down.sql deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/migrations/sqlite/2023-01-11-205851_add_avatar_color/up.sql b/migrations/sqlite/2023-01-11-205851_add_avatar_color/up.sql deleted file mode 100644 index cf3ef9f77dd..00000000000 --- a/migrations/sqlite/2023-01-11-205851_add_avatar_color/up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE users -ADD COLUMN avatar_color TEXT; diff --git a/migrations/sqlite/2023-01-31-222222_add_argon2/down.sql b/migrations/sqlite/2023-01-31-222222_add_argon2/down.sql deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/migrations/sqlite/2023-01-31-222222_add_argon2/up.sql b/migrations/sqlite/2023-01-31-222222_add_argon2/up.sql deleted file mode 100644 index 35b8189e95e..00000000000 --- a/migrations/sqlite/2023-01-31-222222_add_argon2/up.sql +++ /dev/null @@ -1,7 +0,0 @@ -ALTER TABLE users - ADD COLUMN - client_kdf_memory INTEGER DEFAULT NULL; - -ALTER TABLE users - ADD COLUMN - client_kdf_parallelism INTEGER DEFAULT NULL; diff --git a/rust-toolchain b/rust-toolchain index 5deab586f9a..b6148bc0a75 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.68.2 +1.66.0 diff --git a/rustfmt.toml b/rustfmt.toml index 1d5e440fe7f..2867b14119d 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,7 @@ +# version = "Two" edition = "2021" max_width = 120 newline_style = "Unix" use_small_heuristics = "Off" +# struct_lit_single_line = false +# overflow_delimited_expr = true diff --git a/src/api/admin.rs b/src/api/admin.rs index 0c56d0fa7c5..6e0c2acf951 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -13,7 +13,7 @@ use rocket::{ }; use crate::{ - api::{core::log_event, ApiResult, EmptyResult, JsonResult, Notify, NumberOrString}, + api::{core::log_event, ApiResult, EmptyResult, JsonResult, Notify, NumberOrString, UpdateType}, auth::{decode_admin, encode_jwt, generate_admin_claims, ClientIp}, config::ConfigBuilder, db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType}, @@ -33,7 +33,6 @@ pub fn routes() -> Vec { routes![ get_users_json, get_user_json, - get_user_by_mail_json, post_admin_login, admin_page, invite_user, @@ -53,8 +52,7 @@ pub fn routes() -> Vec { organizations_overview, delete_organization, diagnostics, - get_diagnostics_config, - resend_user_invite, + get_diagnostics_config ] } @@ -185,7 +183,7 @@ fn post_admin_login(data: Form, cookies: &CookieJar<'_>, ip: ClientIp let cookie = Cookie::build(COOKIE_NAME, jwt) .path(admin_path()) - .max_age(rocket::time::Duration::minutes(CONFIG.admin_session_lifetime())) + .max_age(rocket::time::Duration::minutes(20)) .same_site(SameSite::Strict) .http_only(true) .finish(); @@ -202,19 +200,6 @@ fn post_admin_login(data: Form, cookies: &CookieJar<'_>, ip: ClientIp fn _validate_token(token: &str) -> bool { match CONFIG.admin_token().as_ref() { None => false, - Some(t) if t.starts_with("$argon2") => { - use argon2::password_hash::PasswordVerifier; - match argon2::password_hash::PasswordHash::new(t) { - Ok(h) => { - // NOTE: hash params from `ADMIN_TOKEN` are used instead of what is configured in the `Argon2` instance. - argon2::Argon2::default().verify_password(token.trim().as_ref(), &h).is_ok() - } - Err(e) => { - error!("The configured Argon2 PHC in `ADMIN_TOKEN` is invalid: {e}"); - false - } - } - } Some(t) => crate::crypto::ct_eq(t.trim(), token.trim()), } } @@ -314,9 +299,8 @@ fn logout(cookies: &CookieJar<'_>) -> Redirect { #[get("/users")] async fn get_users_json(_token: AdminToken, mut conn: DbConn) -> Json { - let users = User::get_all(&mut conn).await; - let mut users_json = Vec::with_capacity(users.len()); - for u in users { + let mut users_json = Vec::new(); + for u in User::get_all(&mut conn).await { let mut usr = u.to_json(&mut conn).await; usr["UserEnabled"] = json!(u.enabled); usr["CreatedAt"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT)); @@ -328,9 +312,8 @@ async fn get_users_json(_token: AdminToken, mut conn: DbConn) -> Json { #[get("/users/overview")] async fn users_overview(_token: AdminToken, mut conn: DbConn) -> ApiResult> { - let users = User::get_all(&mut conn).await; - let mut users_json = Vec::with_capacity(users.len()); - for u in users { + let mut users_json = Vec::new(); + for u in User::get_all(&mut conn).await { let mut usr = u.to_json(&mut conn).await; usr["cipher_count"] = json!(Cipher::count_owned_by_user(&u.uuid, &mut conn).await); usr["attachment_count"] = json!(Attachment::count_by_user(&u.uuid, &mut conn).await); @@ -348,18 +331,6 @@ async fn users_overview(_token: AdminToken, mut conn: DbConn) -> ApiResult")] -async fn get_user_by_mail_json(mail: String, _token: AdminToken, mut conn: DbConn) -> JsonResult { - if let Some(u) = User::find_by_mail(&mail, &mut conn).await { - let mut usr = u.to_json(&mut conn).await; - usr["UserEnabled"] = json!(u.enabled); - usr["CreatedAt"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT)); - Ok(Json(usr)) - } else { - err_code!("User doesn't exist", Status::NotFound.code); - } -} - #[get("/users/")] async fn get_user_json(uuid: String, _token: AdminToken, mut conn: DbConn) -> JsonResult { let u = get_user_or_404(&uuid, &mut conn).await?; @@ -370,7 +341,7 @@ async fn get_user_json(uuid: String, _token: AdminToken, mut conn: DbConn) -> Js } #[post("/users//delete")] -async fn delete_user(uuid: String, token: AdminToken, mut conn: DbConn) -> EmptyResult { +async fn delete_user(uuid: String, _token: AdminToken, mut conn: DbConn, ip: ClientIp) -> EmptyResult { let user = get_user_or_404(&uuid, &mut conn).await?; // Get the user_org records before deleting the actual user @@ -384,7 +355,7 @@ async fn delete_user(uuid: String, token: AdminToken, mut conn: DbConn) -> Empty user_org.org_uuid, String::from(ACTING_ADMIN_USER), 14, // Use UnknownBrowser type - &token.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -401,7 +372,7 @@ async fn deauth_user(uuid: String, _token: AdminToken, mut conn: DbConn, nt: Not let save_result = user.save(&mut conn).await; - nt.send_logout(&user, None).await; + nt.send_user_update(UpdateType::LogOut, &user).await; save_result } @@ -415,7 +386,7 @@ async fn disable_user(uuid: String, _token: AdminToken, mut conn: DbConn, nt: No let save_result = user.save(&mut conn).await; - nt.send_logout(&user, None).await; + nt.send_user_update(UpdateType::LogOut, &user).await; save_result } @@ -436,24 +407,6 @@ async fn remove_2fa(uuid: String, _token: AdminToken, mut conn: DbConn) -> Empty user.save(&mut conn).await } -#[post("/users//invite/resend")] -async fn resend_user_invite(uuid: String, _token: AdminToken, mut conn: DbConn) -> EmptyResult { - if let Some(user) = User::find_by_uuid(&uuid, &mut conn).await { - //TODO: replace this with user.status check when it will be available (PR#3397) - if !user.password_hash.is_empty() { - err_code!("User already accepted invitation", Status::BadRequest.code); - } - - if CONFIG.mail_enabled() { - mail::send_invite(&user.email, &user.uuid, None, None, &CONFIG.invitation_org_name(), None).await - } else { - Ok(()) - } - } else { - err_code!("User doesn't exist", Status::NotFound.code); - } -} - #[derive(Deserialize, Debug)] struct UserOrgTypeData { user_type: NumberOrString, @@ -462,7 +415,12 @@ struct UserOrgTypeData { } #[post("/users/org_type", data = "")] -async fn update_user_org_type(data: Json, token: AdminToken, mut conn: DbConn) -> EmptyResult { +async fn update_user_org_type( + data: Json, + _token: AdminToken, + mut conn: DbConn, + ip: ClientIp, +) -> EmptyResult { let data: UserOrgTypeData = data.into_inner(); let mut user_to_edit = @@ -503,7 +461,7 @@ async fn update_user_org_type(data: Json, token: AdminToken, mu data.org_uuid, String::from(ACTING_ADMIN_USER), 14, // Use UnknownBrowser type - &token.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -519,15 +477,11 @@ async fn update_revision_users(_token: AdminToken, mut conn: DbConn) -> EmptyRes #[get("/organizations/overview")] async fn organizations_overview(_token: AdminToken, mut conn: DbConn) -> ApiResult> { - let organizations = Organization::get_all(&mut conn).await; - let mut organizations_json = Vec::with_capacity(organizations.len()); - for o in organizations { + let mut organizations_json = Vec::new(); + for o in Organization::get_all(&mut conn).await { let mut org = o.to_json(); org["user_count"] = json!(UserOrganization::count_by_org(&o.uuid, &mut conn).await); org["cipher_count"] = json!(Cipher::count_by_org(&o.uuid, &mut conn).await); - org["collection_count"] = json!(Collection::count_by_org(&o.uuid, &mut conn).await); - org["group_count"] = json!(Group::count_by_org(&o.uuid, &mut conn).await); - org["event_count"] = json!(Event::count_by_org(&o.uuid, &mut conn).await); org["attachment_count"] = json!(Attachment::count_by_org(&o.uuid, &mut conn).await); org["attachment_size"] = json!(get_display_size(Attachment::size_by_org(&o.uuid, &mut conn).await as i32)); organizations_json.push(org); @@ -558,20 +512,10 @@ struct GitCommit { sha: String, } -#[derive(Deserialize)] -struct TimeApi { - year: u16, - month: u8, - day: u8, - hour: u8, - minute: u8, - seconds: u8, -} - -async fn get_json_api(url: &str) -> Result { - let json_api = get_reqwest_client(); +async fn get_github_api(url: &str) -> Result { + let github_api = get_reqwest_client(); - Ok(json_api.get(url).send().await?.error_for_status()?.json::().await?) + Ok(github_api.get(url).send().await?.error_for_status()?.json::().await?) } async fn has_http_access() -> bool { @@ -591,13 +535,14 @@ async fn get_release_info(has_http_access: bool, running_within_docker: bool) -> // If the HTTP Check failed, do not even attempt to check for new versions since we were not able to connect with github.com anyway. if has_http_access { ( - match get_json_api::("https://api.github.com/repos/dani-garcia/vaultwarden/releases/latest") + match get_github_api::("https://api.github.com/repos/dani-garcia/vaultwarden/releases/latest") .await { Ok(r) => r.tag_name, _ => "-".to_string(), }, - match get_json_api::("https://api.github.com/repos/dani-garcia/vaultwarden/commits/main").await { + match get_github_api::("https://api.github.com/repos/dani-garcia/vaultwarden/commits/main").await + { Ok(mut c) => { c.sha.truncate(8); c.sha @@ -609,7 +554,7 @@ async fn get_release_info(has_http_access: bool, running_within_docker: bool) -> if running_within_docker { "-".to_string() } else { - match get_json_api::( + match get_github_api::( "https://api.github.com/repos/dani-garcia/bw_web_builds/releases/latest", ) .await @@ -624,24 +569,6 @@ async fn get_release_info(has_http_access: bool, running_within_docker: bool) -> } } -async fn get_ntp_time(has_http_access: bool) -> String { - if has_http_access { - if let Ok(ntp_time) = get_json_api::("https://www.timeapi.io/api/Time/current/zone?timeZone=UTC").await - { - return format!( - "{year}-{month:02}-{day:02} {hour:02}:{minute:02}:{seconds:02} UTC", - year = ntp_time.year, - month = ntp_time.month, - day = ntp_time.day, - hour = ntp_time.hour, - minute = ntp_time.minute, - seconds = ntp_time.seconds - ); - } - } - String::from("Unable to fetch NTP time.") -} - #[get("/diagnostics")] async fn diagnostics(_token: AdminToken, ip_header: IpHeader, mut conn: DbConn) -> ApiResult> { use chrono::prelude::*; @@ -670,7 +597,7 @@ async fn diagnostics(_token: AdminToken, ip_header: IpHeader, mut conn: DbConn) // Check if we are able to resolve DNS entries let dns_resolved = match ("github.com", 0).to_socket_addrs().map(|mut i| i.next()) { Ok(Some(a)) => a.ip().to_string(), - _ => "Unable to resolve domain name.".to_string(), + _ => "Could not resolve domain name.".to_string(), }; let (latest_release, latest_commit, latest_web_build) = @@ -687,7 +614,7 @@ async fn diagnostics(_token: AdminToken, ip_header: IpHeader, mut conn: DbConn) "latest_release": latest_release, "latest_commit": latest_commit, "web_vault_enabled": &CONFIG.web_vault_enabled(), - "web_vault_version": web_vault_version.version.trim_start_matches('v'), + "web_vault_version": web_vault_version.version, "latest_web_build": latest_web_build, "running_within_docker": running_within_docker, "docker_base_image": if running_within_docker { docker_base_image() } else { "Not applicable" }, @@ -704,8 +631,7 @@ async fn diagnostics(_token: AdminToken, ip_header: IpHeader, mut conn: DbConn) "host_arch": std::env::consts::ARCH, "host_os": std::env::consts::OS, "server_time_local": Local::now().format("%Y-%m-%d %H:%M:%S %Z").to_string(), - "server_time": Utc::now().format("%Y-%m-%d %H:%M:%S UTC").to_string(), // Run the server date/time check as late as possible to minimize the time difference - "ntp_time": get_ntp_time(has_http_access).await, // Run the ntp check as late as possible to minimize the time difference + "server_time": Utc::now().format("%Y-%m-%d %H:%M:%S UTC").to_string(), // Run the date/time check as the last item to minimize the difference }); let text = AdminTemplateData::new("admin/diagnostics", diagnostics_json).render()?; @@ -738,24 +664,15 @@ async fn backup_db(_token: AdminToken, mut conn: DbConn) -> EmptyResult { } } -pub struct AdminToken { - ip: ClientIp, -} +pub struct AdminToken {} #[rocket::async_trait] impl<'r> FromRequest<'r> for AdminToken { type Error = &'static str; async fn from_request(request: &'r Request<'_>) -> Outcome { - let ip = match ClientIp::from_request(request).await { - Outcome::Success(ip) => ip, - _ => err_handler!("Error getting Client IP"), - }; - if CONFIG.disable_admin_token() { - Outcome::Success(Self { - ip, - }) + Outcome::Success(Self {}) } else { let cookies = request.cookies(); @@ -764,16 +681,19 @@ impl<'r> FromRequest<'r> for AdminToken { None => return Outcome::Failure((Status::Unauthorized, "Unauthorized")), }; + let ip = match ClientIp::from_request(request).await { + Outcome::Success(ip) => ip.ip, + _ => err_handler!("Error getting Client IP"), + }; + if decode_admin(access_token).is_err() { // Remove admin cookie cookies.remove(Cookie::build(COOKIE_NAME, "").path(admin_path()).finish()); - error!("Invalid or expired admin JWT. IP: {}.", &ip.ip); + error!("Invalid or expired admin JWT. IP: {}.", ip); return Outcome::Failure((Status::Unauthorized, "Session expired")); } - Outcome::Success(Self { - ip, - }) + Outcome::Success(Self {}) } } } diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index b8e2c79ac3c..1f932f699b1 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -6,17 +6,12 @@ use crate::{ api::{ core::log_user_event, EmptyResult, JsonResult, JsonUpcase, Notify, NumberOrString, PasswordData, UpdateType, }, - auth::{decode_delete, decode_invite, decode_verify_email, Headers}, + auth::{decode_delete, decode_invite, decode_verify_email, ClientIp, Headers}, crypto, db::{models::*, DbConn}, mail, CONFIG, }; -use rocket::{ - http::Status, - request::{FromRequest, Outcome, Request}, -}; - pub fn routes() -> Vec { routes![ register, @@ -44,8 +39,6 @@ pub fn routes() -> Vec { api_key, rotate_api_key, get_known_device, - get_known_device_from_path, - put_avatar, ] } @@ -55,8 +48,6 @@ pub struct RegisterData { Email: String, Kdf: Option, KdfIterations: Option, - KdfMemory: Option, - KdfParallelism: Option, Key: String, Keys: Option, MasterPasswordHash: String, @@ -161,18 +152,16 @@ pub async fn _register(data: JsonUpcase, mut conn: DbConn) -> Json // Make sure we don't leave a lingering invitation. Invitation::take(&email, &mut conn).await; - if let Some(client_kdf_type) = data.Kdf { - user.client_kdf_type = client_kdf_type; - } - if let Some(client_kdf_iter) = data.KdfIterations { user.client_kdf_iter = client_kdf_iter; } - user.client_kdf_memory = data.KdfMemory; - user.client_kdf_parallelism = data.KdfParallelism; + if let Some(client_kdf_type) = data.Kdf { + user.client_kdf_type = client_kdf_type; + } - user.set_password(&data.MasterPasswordHash, Some(data.Key), true, None); + user.set_password(&data.MasterPasswordHash, None); + user.akey = data.Key; user.password_hint = password_hint; // Add extra fields if present @@ -239,32 +228,6 @@ async fn post_profile(data: JsonUpcase, headers: Headers, mut conn: Ok(Json(user.to_json(&mut conn).await)) } -#[derive(Deserialize)] -#[allow(non_snake_case)] -struct AvatarData { - AvatarColor: Option, -} - -#[put("/accounts/avatar", data = "")] -async fn put_avatar(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { - let data: AvatarData = data.into_inner().data; - - // It looks like it only supports the 6 hex color format. - // If you try to add the short value it will not show that color. - // Check and force 7 chars, including the #. - if let Some(color) = &data.AvatarColor { - if color.len() != 7 { - err!("The field AvatarColor must be a HTML/Hex color code with a length of 7 characters") - } - } - - let mut user = headers.user; - user.avatar_color = data.AvatarColor; - - user.save(&mut conn).await?; - Ok(Json(user.to_json(&mut conn).await)) -} - #[get("/users//public-key")] async fn get_public_keys(uuid: String, _headers: Headers, mut conn: DbConn) -> JsonResult { let user = match User::find_by_uuid(&uuid, &mut conn).await { @@ -311,6 +274,7 @@ async fn post_password( data: JsonUpcase, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let data: ChangePassData = data.into_inner().data; @@ -323,22 +287,16 @@ async fn post_password( user.password_hint = clean_password_hint(&data.MasterPasswordHint); enforce_password_hint_setting(&user.password_hint)?; - log_user_event(EventType::UserChangedPassword as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn) - .await; + log_user_event(EventType::UserChangedPassword as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; user.set_password( &data.NewMasterPasswordHash, - Some(data.Key), - true, Some(vec![String::from("post_rotatekey"), String::from("get_contacts"), String::from("get_public_keys")]), ); - + user.akey = data.Key; let save_result = user.save(&mut conn).await; - // Prevent loging out the client where the user requested this endpoint from. - // If you do logout the user it will causes issues at the client side. - // Adding the device uuid will prevent this. - nt.send_logout(&user, Some(headers.device.uuid)).await; + nt.send_user_update(UpdateType::LogOut, &user).await; save_result } @@ -348,8 +306,6 @@ async fn post_password( struct ChangeKdfData { Kdf: i32, KdfIterations: i32, - KdfMemory: Option, - KdfParallelism: Option, MasterPasswordHash: String, NewMasterPasswordHash: String, @@ -365,40 +321,13 @@ async fn post_kdf(data: JsonUpcase, headers: Headers, mut conn: D err!("Invalid password") } - if data.Kdf == UserKdfType::Pbkdf2 as i32 && data.KdfIterations < 100_000 { - err!("PBKDF2 KDF iterations must be at least 100000.") - } - - if data.Kdf == UserKdfType::Argon2id as i32 { - if data.KdfIterations < 1 { - err!("Argon2 KDF iterations must be at least 1.") - } - if let Some(m) = data.KdfMemory { - if !(15..=1024).contains(&m) { - err!("Argon2 memory must be between 15 MB and 1024 MB.") - } - user.client_kdf_memory = data.KdfMemory; - } else { - err!("Argon2 memory parameter is required.") - } - if let Some(p) = data.KdfParallelism { - if !(1..=16).contains(&p) { - err!("Argon2 parallelism must be between 1 and 16.") - } - user.client_kdf_parallelism = data.KdfParallelism; - } else { - err!("Argon2 parallelism parameter is required.") - } - } else { - user.client_kdf_memory = None; - user.client_kdf_parallelism = None; - } user.client_kdf_iter = data.KdfIterations; user.client_kdf_type = data.Kdf; - user.set_password(&data.NewMasterPasswordHash, Some(data.Key), true, None); + user.set_password(&data.NewMasterPasswordHash, None); + user.akey = data.Key; let save_result = user.save(&mut conn).await; - nt.send_logout(&user, Some(headers.device.uuid)).await; + nt.send_user_update(UpdateType::LogOut, &user).await; save_result } @@ -423,19 +352,19 @@ struct KeyData { } #[post("/accounts/key", data = "")] -async fn post_rotatekey(data: JsonUpcase, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { +async fn post_rotatekey( + data: JsonUpcase, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, + nt: Notify<'_>, +) -> EmptyResult { let data: KeyData = data.into_inner().data; if !headers.user.check_valid_password(&data.MasterPasswordHash) { err!("Invalid password") } - // Validate the import before continuing - // Bitwarden does not process the import if there is one item invalid. - // Since we check for the size of the encrypted note length, we need to do that here to pre-validate it. - // TODO: See if we can optimize the whole cipher adding/importing and prevent duplicate code and checks. - Cipher::validate_notes(&data.Ciphers)?; - let user_uuid = &headers.user.uuid; // Update folder data @@ -469,7 +398,7 @@ async fn post_rotatekey(data: JsonUpcase, headers: Headers, mut conn: D // Prevent triggering cipher updates via WebSockets by settings UpdateType::None // The user sessions are invalidated because all the ciphers were re-encrypted and thus triggering an update could cause issues. // We force the users to logout after the user has been saved to try and prevent these issues. - update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None) + update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &mut conn, &ip, &nt, UpdateType::None) .await? } @@ -482,10 +411,7 @@ async fn post_rotatekey(data: JsonUpcase, headers: Headers, mut conn: D let save_result = user.save(&mut conn).await; - // Prevent loging out the client where the user requested this endpoint from. - // If you do logout the user it will causes issues at the client side. - // Adding the device uuid will prevent this. - nt.send_logout(&user, Some(headers.device.uuid)).await; + nt.send_user_update(UpdateType::LogOut, &user).await; save_result } @@ -508,7 +434,7 @@ async fn post_sstamp( user.reset_security_stamp(); let save_result = user.save(&mut conn).await; - nt.send_logout(&user, None).await; + nt.send_user_update(UpdateType::LogOut, &user).await; save_result } @@ -607,11 +533,11 @@ async fn post_email( user.email_new = None; user.email_new_token = None; - user.set_password(&data.NewMasterPasswordHash, Some(data.Key), true, None); - + user.set_password(&data.NewMasterPasswordHash, None); + user.akey = data.Key; let save_result = user.save(&mut conn).await; - nt.send_logout(&user, None).await; + nt.send_user_update(UpdateType::LogOut, &user).await; save_result } @@ -801,19 +727,15 @@ async fn prelogin(data: JsonUpcase, conn: DbConn) -> Json { pub async fn _prelogin(data: JsonUpcase, mut conn: DbConn) -> Json { let data: PreloginData = data.into_inner().data; - let (kdf_type, kdf_iter, kdf_mem, kdf_para) = match User::find_by_mail(&data.Email, &mut conn).await { - Some(user) => (user.client_kdf_type, user.client_kdf_iter, user.client_kdf_memory, user.client_kdf_parallelism), - None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT, None, None), + let (kdf_type, kdf_iter) = match User::find_by_mail(&data.Email, &mut conn).await { + Some(user) => (user.client_kdf_type, user.client_kdf_iter), + None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT), }; - let result = json!({ + Json(json!({ "Kdf": kdf_type, - "KdfIterations": kdf_iter, - "KdfMemory": kdf_mem, - "KdfParallelism": kdf_para, - }); - - Json(result) + "KdfIterations": kdf_iter + })) } // https://github.com/bitwarden/server/blob/master/src/Api/Models/Request/Accounts/SecretVerificationRequestModel.cs @@ -841,8 +763,6 @@ async fn _api_key( headers: Headers, mut conn: DbConn, ) -> JsonResult { - use crate::util::format_date; - let data: SecretVerificationRequest = data.into_inner().data; let mut user = headers.user; @@ -857,7 +777,6 @@ async fn _api_key( Ok(Json(json!({ "ApiKey": user.api_key, - "RevisionDate": format_date(&user.updated_at), "Object": "apiKey", }))) } @@ -872,9 +791,8 @@ async fn rotate_api_key(data: JsonUpcase, headers: He _api_key(data, true, headers, conn).await } -// This variant is deprecated: https://github.com/bitwarden/server/pull/2682 #[get("/devices/knowndevice//")] -async fn get_known_device_from_path(email: String, uuid: String, mut conn: DbConn) -> JsonResult { +async fn get_known_device(email: String, uuid: String, mut conn: DbConn) -> JsonResult { // This endpoint doesn't have auth header let mut result = false; if let Some(user) = User::find_by_mail(&email, &mut conn).await { @@ -882,51 +800,3 @@ async fn get_known_device_from_path(email: String, uuid: String, mut conn: DbCon } Ok(Json(json!(result))) } - -#[get("/devices/knowndevice")] -async fn get_known_device(device: KnownDevice, conn: DbConn) -> JsonResult { - get_known_device_from_path(device.email, device.uuid, conn).await -} - -struct KnownDevice { - email: String, - uuid: String, -} - -#[rocket::async_trait] -impl<'r> FromRequest<'r> for KnownDevice { - type Error = &'static str; - - async fn from_request(req: &'r Request<'_>) -> Outcome { - let email = if let Some(email_b64) = req.headers().get_one("X-Request-Email") { - let email_bytes = match data_encoding::BASE64URL_NOPAD.decode(email_b64.as_bytes()) { - Ok(bytes) => bytes, - Err(_) => { - return Outcome::Failure(( - Status::BadRequest, - "X-Request-Email value failed to decode as base64url", - )); - } - }; - match String::from_utf8(email_bytes) { - Ok(email) => email, - Err(_) => { - return Outcome::Failure((Status::BadRequest, "X-Request-Email value failed to decode as UTF-8")); - } - } - } else { - return Outcome::Failure((Status::BadRequest, "X-Request-Email value is required")); - }; - - let uuid = if let Some(uuid) = req.headers().get_one("X-Device-Identifier") { - uuid.to_string() - } else { - return Outcome::Failure((Status::BadRequest, "X-Device-Identifier value is required")); - }; - - Outcome::Success(KnownDevice { - email, - uuid, - }) - } -} diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index 67e2ae39f4f..f97403a0eb4 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -11,7 +11,7 @@ use serde_json::Value; use crate::{ api::{self, core::log_event, EmptyResult, JsonResult, JsonUpcase, Notify, PasswordData, UpdateType}, - auth::Headers, + auth::{ClientIp, Headers}, crypto, db::{models::*, DbConn, DbPool}, CONFIG, @@ -56,9 +56,7 @@ pub fn routes() -> Vec { put_cipher_share, put_cipher_share_selected, post_cipher, - post_cipher_partial, put_cipher, - put_cipher_partial, delete_cipher_post, delete_cipher_post_admin, delete_cipher_put, @@ -106,20 +104,16 @@ async fn sync(data: SyncData, headers: Headers, mut conn: DbConn) -> Json // Get all ciphers which are visible by the user let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &mut conn).await; - let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, CipherSyncType::User, &mut conn).await; + let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, CipherSyncType::User, &mut conn).await; // Lets generate the ciphers_json using all the gathered info - let mut ciphers_json = Vec::with_capacity(ciphers.len()); + let mut ciphers_json = Vec::new(); for c in ciphers { - ciphers_json.push( - c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), CipherSyncType::User, &mut conn) - .await, - ); + ciphers_json.push(c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &mut conn).await); } - let collections = Collection::find_by_user_uuid(headers.user.uuid.clone(), &mut conn).await; - let mut collections_json = Vec::with_capacity(collections.len()); - for c in collections { + let mut collections_json = Vec::new(); + for c in Collection::find_by_user_uuid(headers.user.uuid.clone(), &mut conn).await { collections_json.push(c.to_json_details(&headers.user.uuid, Some(&cipher_sync_data), &mut conn).await); } @@ -154,14 +148,11 @@ async fn sync(data: SyncData, headers: Headers, mut conn: DbConn) -> Json #[get("/ciphers")] async fn get_ciphers(headers: Headers, mut conn: DbConn) -> Json { let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &mut conn).await; - let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, CipherSyncType::User, &mut conn).await; + let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, CipherSyncType::User, &mut conn).await; - let mut ciphers_json = Vec::with_capacity(ciphers.len()); + let mut ciphers_json = Vec::new(); for c in ciphers { - ciphers_json.push( - c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), CipherSyncType::User, &mut conn) - .await, - ); + ciphers_json.push(c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &mut conn).await); } Json(json!({ @@ -182,7 +173,7 @@ async fn get_cipher(uuid: String, headers: Headers, mut conn: DbConn) -> JsonRes err!("Cipher is not owned by user") } - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await)) } #[get("/ciphers//admin")] @@ -243,13 +234,6 @@ pub struct CipherData { LastKnownRevisionDate: Option, } -#[derive(Deserialize, Debug)] -#[allow(non_snake_case)] -pub struct PartialCipherData { - FolderId: Option, - Favorite: bool, -} - #[derive(Deserialize, Debug)] #[allow(non_snake_case)] pub struct Attachments2Data { @@ -263,9 +247,10 @@ async fn post_ciphers_admin( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - post_ciphers_create(data, headers, conn, nt).await + post_ciphers_create(data, headers, conn, ip, nt).await } /// Called when creating a new org-owned cipher, or cloning a cipher (whether @@ -276,6 +261,7 @@ async fn post_ciphers_create( data: JsonUpcase, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { let mut data: ShareCipherData = data.into_inner().data; @@ -303,12 +289,18 @@ async fn post_ciphers_create( // or otherwise), we can just ignore this field entirely. data.Cipher.LastKnownRevisionDate = None; - share_cipher_by_uuid(&cipher.uuid, data, &headers, &mut conn, &nt).await + share_cipher_by_uuid(&cipher.uuid, data, &headers, &mut conn, &ip, &nt).await } /// Called when creating a new user-owned cipher. #[post("/ciphers", data = "")] -async fn post_ciphers(data: JsonUpcase, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { +async fn post_ciphers( + data: JsonUpcase, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, + nt: Notify<'_>, +) -> JsonResult { let mut data: CipherData = data.into_inner().data; // The web/browser clients set this field to null as expected, but the @@ -318,9 +310,10 @@ async fn post_ciphers(data: JsonUpcase, headers: Headers, mut conn: data.LastKnownRevisionDate = None; let mut cipher = Cipher::new(data.Type, data.Name.clone()); - update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &nt, UpdateType::SyncCipherCreate).await?; + update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherCreate) + .await?; - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await)) } /// Enforces the personal ownership policy on user-owned ciphers, if applicable. @@ -345,12 +338,14 @@ async fn enforce_personal_ownership_policy( Ok(()) } +#[allow(clippy::too_many_arguments)] pub async fn update_cipher_from_data( cipher: &mut Cipher, data: CipherData, headers: &Headers, shared_to_collection: bool, conn: &mut DbConn, + ip: &ClientIp, nt: &Notify<'_>, ut: UpdateType, ) -> EmptyResult { @@ -506,7 +501,7 @@ pub async fn update_cipher_from_data( String::from(org_uuid), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, conn, ) .await; @@ -540,6 +535,7 @@ async fn post_ciphers_import( data: JsonUpcase, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { enforce_personal_ownership_policy(None, &headers, &mut conn).await?; @@ -574,7 +570,8 @@ async fn post_ciphers_import( cipher_data.FolderId = folder_uuid; let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone()); - update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None).await?; + update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &ip, &nt, UpdateType::None) + .await?; } let mut user = headers.user; @@ -590,9 +587,10 @@ async fn put_cipher_admin( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - put_cipher(uuid, data, headers, conn, nt).await + put_cipher(uuid, data, headers, conn, ip, nt).await } #[post("/ciphers//admin", data = "")] @@ -601,9 +599,10 @@ async fn post_cipher_admin( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - post_cipher(uuid, data, headers, conn, nt).await + post_cipher(uuid, data, headers, conn, ip, nt).await } #[post("/ciphers/", data = "")] @@ -612,9 +611,10 @@ async fn post_cipher( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - put_cipher(uuid, data, headers, conn, nt).await + put_cipher(uuid, data, headers, conn, ip, nt).await } #[put("/ciphers/", data = "")] @@ -623,6 +623,7 @@ async fn put_cipher( data: JsonUpcase, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { let data: CipherData = data.into_inner().data; @@ -641,53 +642,10 @@ async fn put_cipher( err!("Cipher is not write accessible") } - update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &nt, UpdateType::SyncCipherUpdate).await?; - - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) -} - -#[post("/ciphers//partial", data = "")] -async fn post_cipher_partial( - uuid: String, - data: JsonUpcase, - headers: Headers, - conn: DbConn, -) -> JsonResult { - put_cipher_partial(uuid, data, headers, conn).await -} - -// Only update the folder and favorite for the user, since this cipher is read-only -#[put("/ciphers//partial", data = "")] -async fn put_cipher_partial( - uuid: String, - data: JsonUpcase, - headers: Headers, - mut conn: DbConn, -) -> JsonResult { - let data: PartialCipherData = data.into_inner().data; - - let cipher = match Cipher::find_by_uuid(&uuid, &mut conn).await { - Some(cipher) => cipher, - None => err!("Cipher doesn't exist"), - }; - - if let Some(ref folder_id) = data.FolderId { - match Folder::find_by_uuid(folder_id, &mut conn).await { - Some(folder) => { - if folder.user_uuid != headers.user.uuid { - err!("Folder is not owned by user") - } - } - None => err!("Folder doesn't exist"), - } - } - - // Move cipher - cipher.move_to_folder(data.FolderId.clone(), &headers.user.uuid, &mut conn).await?; - // Update favorite - cipher.set_favorite(Some(data.Favorite), &headers.user.uuid, &mut conn).await?; + update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherUpdate) + .await?; - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await)) } #[derive(Deserialize)] @@ -702,8 +660,9 @@ async fn put_collections_update( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - post_collections_admin(uuid, data, headers, conn).await + post_collections_admin(uuid, data, headers, conn, ip).await } #[post("/ciphers//collections", data = "")] @@ -712,8 +671,9 @@ async fn post_collections_update( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - post_collections_admin(uuid, data, headers, conn).await + post_collections_admin(uuid, data, headers, conn, ip).await } #[put("/ciphers//collections-admin", data = "")] @@ -722,8 +682,9 @@ async fn put_collections_admin( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - post_collections_admin(uuid, data, headers, conn).await + post_collections_admin(uuid, data, headers, conn, ip).await } #[post("/ciphers//collections-admin", data = "")] @@ -732,6 +693,7 @@ async fn post_collections_admin( data: JsonUpcase, headers: Headers, mut conn: DbConn, + ip: ClientIp, ) -> EmptyResult { let data: CollectionsAdminData = data.into_inner().data; @@ -773,7 +735,7 @@ async fn post_collections_admin( cipher.organization_uuid.unwrap(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -794,11 +756,12 @@ async fn post_cipher_share( data: JsonUpcase, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { let data: ShareCipherData = data.into_inner().data; - share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await + share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &ip, &nt).await } #[put("/ciphers//share", data = "")] @@ -807,11 +770,12 @@ async fn put_cipher_share( data: JsonUpcase, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { let data: ShareCipherData = data.into_inner().data; - share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await + share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &ip, &nt).await } #[derive(Deserialize)] @@ -826,6 +790,7 @@ async fn put_cipher_share_selected( data: JsonUpcase, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let mut data: ShareSelectedCipherData = data.into_inner().data; @@ -853,7 +818,7 @@ async fn put_cipher_share_selected( }; match shared_cipher_data.Cipher.Id.take() { - Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &mut conn, &nt).await?, + Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &mut conn, &ip, &nt).await?, None => err!("Request missing ids field"), }; } @@ -866,6 +831,7 @@ async fn share_cipher_by_uuid( data: ShareCipherData, headers: &Headers, conn: &mut DbConn, + ip: &ClientIp, nt: &Notify<'_>, ) -> JsonResult { let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { @@ -904,9 +870,9 @@ async fn share_cipher_by_uuid( UpdateType::SyncCipherCreate }; - update_cipher_from_data(&mut cipher, data.Cipher, headers, shared_to_collection, conn, nt, ut).await?; + update_cipher_from_data(&mut cipher, data.Cipher, headers, shared_to_collection, conn, ip, nt, ut).await?; - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, conn).await)) } /// v2 API for downloading an attachment. This just redirects the client to @@ -975,7 +941,7 @@ async fn post_attachment_v2( "AttachmentId": attachment_id, "Url": url, "FileUploadType": FileUploadType::Direct as i32, - response_key: cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await, + response_key: cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await, }))) } @@ -999,6 +965,7 @@ async fn save_attachment( data: Form>, headers: &Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> Result<(Cipher, DbConn), crate::error::Error> { let cipher = match Cipher::find_by_uuid(&cipher_uuid, &mut conn).await { @@ -1117,7 +1084,7 @@ async fn save_attachment( String::from(org_uuid), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -1137,6 +1104,7 @@ async fn post_attachment_v2_data( data: Form>, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let attachment = match Attachment::find_by_id(&attachment_id, &mut conn).await { @@ -1145,7 +1113,7 @@ async fn post_attachment_v2_data( None => err!("Attachment doesn't exist"), }; - save_attachment(attachment, uuid, data, &headers, conn, nt).await?; + save_attachment(attachment, uuid, data, &headers, conn, ip, nt).await?; Ok(()) } @@ -1157,15 +1125,16 @@ async fn post_attachment( data: Form>, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { // Setting this as None signifies to save_attachment() that it should create // the attachment database record as well as saving the data to disk. let attachment = None; - let (cipher, mut conn) = save_attachment(attachment, uuid, data, &headers, conn, nt).await?; + let (cipher, mut conn) = save_attachment(attachment, uuid, data, &headers, conn, ip, nt).await?; - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await)) } #[post("/ciphers//attachment-admin", format = "multipart/form-data", data = "")] @@ -1174,9 +1143,10 @@ async fn post_attachment_admin( data: Form>, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - post_attachment(uuid, data, headers, conn, nt).await + post_attachment(uuid, data, headers, conn, ip, nt).await } #[post("/ciphers//attachment//share", format = "multipart/form-data", data = "")] @@ -1186,10 +1156,11 @@ async fn post_attachment_share( data: Form>, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await?; - post_attachment(uuid, data, headers, conn, nt).await + _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &ip, &nt).await?; + post_attachment(uuid, data, headers, conn, ip, nt).await } #[post("/ciphers//attachment//delete-admin")] @@ -1198,9 +1169,10 @@ async fn delete_attachment_post_admin( attachment_id: String, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - delete_attachment(uuid, attachment_id, headers, conn, nt).await + delete_attachment(uuid, attachment_id, headers, conn, ip, nt).await } #[post("/ciphers//attachment//delete")] @@ -1209,9 +1181,10 @@ async fn delete_attachment_post( attachment_id: String, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - delete_attachment(uuid, attachment_id, headers, conn, nt).await + delete_attachment(uuid, attachment_id, headers, conn, ip, nt).await } #[delete("/ciphers//attachment/")] @@ -1220,9 +1193,10 @@ async fn delete_attachment( attachment_id: String, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await + _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &ip, &nt).await } #[delete("/ciphers//attachment//admin")] @@ -1231,44 +1205,70 @@ async fn delete_attachment_admin( attachment_id: String, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await + _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &ip, &nt).await } #[post("/ciphers//delete")] -async fn delete_cipher_post(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await - // permanent delete +async fn delete_cipher_post( + uuid: String, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, + nt: Notify<'_>, +) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &ip, &nt).await // permanent delete } #[post("/ciphers//delete-admin")] -async fn delete_cipher_post_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await - // permanent delete +async fn delete_cipher_post_admin( + uuid: String, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, + nt: Notify<'_>, +) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &ip, &nt).await // permanent delete } #[put("/ciphers//delete")] -async fn delete_cipher_put(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &nt).await - // soft delete +async fn delete_cipher_put( + uuid: String, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, + nt: Notify<'_>, +) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &ip, &nt).await // soft delete } #[put("/ciphers//delete-admin")] -async fn delete_cipher_put_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &nt).await +async fn delete_cipher_put_admin( + uuid: String, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, + nt: Notify<'_>, +) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &ip, &nt).await } #[delete("/ciphers/")] -async fn delete_cipher(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await - // permanent delete +async fn delete_cipher(uuid: String, headers: Headers, mut conn: DbConn, ip: ClientIp, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &ip, &nt).await // permanent delete } #[delete("/ciphers//admin")] -async fn delete_cipher_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await - // permanent delete +async fn delete_cipher_admin( + uuid: String, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, + nt: Notify<'_>, +) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &ip, &nt).await // permanent delete } #[delete("/ciphers", data = "")] @@ -1276,9 +1276,10 @@ async fn delete_cipher_selected( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete + _delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete } #[post("/ciphers/delete", data = "")] @@ -1286,9 +1287,10 @@ async fn delete_cipher_selected_post( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete + _delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete } #[put("/ciphers/delete", data = "")] @@ -1296,9 +1298,10 @@ async fn delete_cipher_selected_put( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, true, nt).await // soft delete + _delete_multiple_ciphers(data, headers, conn, true, ip, nt).await // soft delete } #[delete("/ciphers/admin", data = "")] @@ -1306,9 +1309,10 @@ async fn delete_cipher_selected_admin( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete + _delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete } #[post("/ciphers/delete-admin", data = "")] @@ -1316,9 +1320,10 @@ async fn delete_cipher_selected_post_admin( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete + _delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete } #[put("/ciphers/delete-admin", data = "")] @@ -1326,19 +1331,32 @@ async fn delete_cipher_selected_put_admin( data: JsonUpcase, headers: Headers, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, true, nt).await // soft delete + _delete_multiple_ciphers(data, headers, conn, true, ip, nt).await // soft delete } #[put("/ciphers//restore")] -async fn restore_cipher_put(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { - _restore_cipher_by_uuid(&uuid, &headers, &mut conn, &nt).await +async fn restore_cipher_put( + uuid: String, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, + nt: Notify<'_>, +) -> JsonResult { + _restore_cipher_by_uuid(&uuid, &headers, &mut conn, &ip, &nt).await } #[put("/ciphers//restore-admin")] -async fn restore_cipher_put_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { - _restore_cipher_by_uuid(&uuid, &headers, &mut conn, &nt).await +async fn restore_cipher_put_admin( + uuid: String, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, + nt: Notify<'_>, +) -> JsonResult { + _restore_cipher_by_uuid(&uuid, &headers, &mut conn, &ip, &nt).await } #[put("/ciphers/restore", data = "")] @@ -1346,9 +1364,10 @@ async fn restore_cipher_selected( data: JsonUpcase, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - _restore_multiple_ciphers(data, &headers, &mut conn, &nt).await + _restore_multiple_ciphers(data, &headers, &mut conn, ip, &nt).await } #[derive(Deserialize)] @@ -1420,6 +1439,7 @@ async fn delete_all( data: JsonUpcase, headers: Headers, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let data: PasswordData = data.into_inner().data; @@ -1447,7 +1467,7 @@ async fn delete_all( org_data.org_id.clone(), user.uuid, headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -1483,6 +1503,7 @@ async fn _delete_cipher_by_uuid( headers: &Headers, conn: &mut DbConn, soft_delete: bool, + ip: &ClientIp, nt: &Notify<'_>, ) -> EmptyResult { let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { @@ -1521,16 +1542,8 @@ async fn _delete_cipher_by_uuid( false => EventType::CipherDeleted as i32, }; - log_event( - event_type, - &cipher.uuid, - org_uuid, - headers.user.uuid.clone(), - headers.device.atype, - &headers.ip.ip, - conn, - ) - .await; + log_event(event_type, &cipher.uuid, org_uuid, headers.user.uuid.clone(), headers.device.atype, &ip.ip, conn) + .await; } Ok(()) @@ -1541,6 +1554,7 @@ async fn _delete_multiple_ciphers( headers: Headers, mut conn: DbConn, soft_delete: bool, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let data: Value = data.into_inner().data; @@ -1554,7 +1568,7 @@ async fn _delete_multiple_ciphers( }; for uuid in uuids { - if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &mut conn, soft_delete, &nt).await { + if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &mut conn, soft_delete, &ip, &nt).await { return error; }; } @@ -1562,7 +1576,13 @@ async fn _delete_multiple_ciphers( Ok(()) } -async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &mut DbConn, nt: &Notify<'_>) -> JsonResult { +async fn _restore_cipher_by_uuid( + uuid: &str, + headers: &Headers, + conn: &mut DbConn, + ip: &ClientIp, + nt: &Notify<'_>, +) -> JsonResult { let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), @@ -1589,19 +1609,20 @@ async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &mut DbCon String::from(org_uuid), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, conn, ) .await; } - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, conn).await)) } async fn _restore_multiple_ciphers( data: JsonUpcase, headers: &Headers, conn: &mut DbConn, + ip: ClientIp, nt: &Notify<'_>, ) -> JsonResult { let data: Value = data.into_inner().data; @@ -1616,7 +1637,7 @@ async fn _restore_multiple_ciphers( let mut ciphers: Vec = Vec::new(); for uuid in uuids { - match _restore_cipher_by_uuid(uuid, headers, conn, nt).await { + match _restore_cipher_by_uuid(uuid, headers, conn, &ip, nt).await { Ok(json) => ciphers.push(json.into_inner()), err => return err, } @@ -1634,6 +1655,7 @@ async fn _delete_cipher_attachment_by_id( attachment_id: &str, headers: &Headers, conn: &mut DbConn, + ip: &ClientIp, nt: &Notify<'_>, ) -> EmptyResult { let attachment = match Attachment::find_by_id(attachment_id, conn).await { @@ -1670,7 +1692,7 @@ async fn _delete_cipher_attachment_by_id( org_uuid, headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, conn, ) .await; @@ -1693,16 +1715,18 @@ pub struct CipherSyncData { pub user_group_full_access_for_organizations: HashSet, } -#[derive(Eq, PartialEq)] pub enum CipherSyncType { User, Organization, } impl CipherSyncData { - pub async fn new(user_uuid: &str, sync_type: CipherSyncType, conn: &mut DbConn) -> Self { - let cipher_folders: HashMap; - let cipher_favorites: HashSet; + pub async fn new(user_uuid: &str, ciphers: &[Cipher], sync_type: CipherSyncType, conn: &mut DbConn) -> Self { + // Generate a list of Cipher UUID's to be used during a query filter with an eq_any. + let cipher_uuids = ciphers.iter().map(|c| c.uuid.clone()).collect(); + + let mut cipher_folders: HashMap = HashMap::new(); + let mut cipher_favorites: HashSet = HashSet::new(); match sync_type { // User Sync supports Folders and Favorits CipherSyncType::User => { @@ -1714,25 +1738,18 @@ impl CipherSyncData { } // Organization Sync does not support Folders and Favorits. // If these are set, it will cause issues in the web-vault. - CipherSyncType::Organization => { - cipher_folders = HashMap::with_capacity(0); - cipher_favorites = HashSet::with_capacity(0); - } + CipherSyncType::Organization => {} } // Generate a list of Cipher UUID's containing a Vec with one or more Attachment records - let user_org_uuids = UserOrganization::get_org_uuid_by_user(user_uuid, conn).await; - let attachments = Attachment::find_all_by_user_and_orgs(user_uuid, &user_org_uuids, conn).await; - let mut cipher_attachments: HashMap> = HashMap::with_capacity(attachments.len()); - for attachment in attachments { + let mut cipher_attachments: HashMap> = HashMap::new(); + for attachment in Attachment::find_all_by_ciphers(&cipher_uuids, conn).await { cipher_attachments.entry(attachment.cipher_uuid.clone()).or_default().push(attachment); } // Generate a HashMap with the Cipher UUID as key and one or more Collection UUID's - let user_cipher_collections = Cipher::get_collections_with_cipher_by_user(user_uuid.to_string(), conn).await; - let mut cipher_collections: HashMap> = - HashMap::with_capacity(user_cipher_collections.len()); - for (cipher, collection) in user_cipher_collections { + let mut cipher_collections: HashMap> = HashMap::new(); + for (cipher, collection) in Cipher::get_collections_with_cipher_by_user(user_uuid.to_string(), conn).await { cipher_collections.entry(cipher).or_default().push(collection); } @@ -1751,14 +1768,14 @@ impl CipherSyncData { .collect(); // Generate a HashMap with the collections_uuid as key and the CollectionGroup record - let user_collections_groups: HashMap = CollectionGroup::find_by_user(user_uuid, conn) + let user_collections_groups = CollectionGroup::find_by_user(user_uuid, conn) .await .into_iter() .map(|collection_group| (collection_group.collections_uuid.clone(), collection_group)) .collect(); // Get all organizations that the user has full access to via group assignement - let user_group_full_access_for_organizations: HashSet = + let user_group_full_access_for_organizations = Group::gather_user_organizations_full_access(user_uuid, conn).await.into_iter().collect(); Self { diff --git a/src/api/core/emergency_access.rs b/src/api/core/emergency_access.rs index 68309714d53..7a683ea4b52 100644 --- a/src/api/core/emergency_access.rs +++ b/src/api/core/emergency_access.rs @@ -123,9 +123,7 @@ async fn post_emergency_access( emergency_access.atype = new_type; emergency_access.wait_time_days = data.WaitTimeDays; - if data.KeyEncrypted.is_some() { - emergency_access.key_encrypted = data.KeyEncrypted; - } + emergency_access.key_encrypted = data.KeyEncrypted; emergency_access.save(&mut conn).await?; Ok(Json(emergency_access.to_json())) @@ -586,20 +584,13 @@ async fn view_emergency_access(emer_id: String, headers: Headers, mut conn: DbCo } let ciphers = Cipher::find_owned_by_user(&emergency_access.grantor_uuid, &mut conn).await; - let cipher_sync_data = CipherSyncData::new(&emergency_access.grantor_uuid, CipherSyncType::User, &mut conn).await; + let cipher_sync_data = + CipherSyncData::new(&emergency_access.grantor_uuid, &ciphers, CipherSyncType::User, &mut conn).await; - let mut ciphers_json = Vec::with_capacity(ciphers.len()); + let mut ciphers_json = Vec::new(); for c in ciphers { - ciphers_json.push( - c.to_json( - &headers.host, - &emergency_access.grantor_uuid, - Some(&cipher_sync_data), - CipherSyncType::User, - &mut conn, - ) - .await, - ); + ciphers_json + .push(c.to_json(&headers.host, &emergency_access.grantor_uuid, Some(&cipher_sync_data), &mut conn).await); } Ok(Json(json!({ @@ -628,16 +619,12 @@ async fn takeover_emergency_access(emer_id: String, headers: Headers, mut conn: None => err!("Grantor user not found."), }; - let result = json!({ - "Kdf": grantor_user.client_kdf_type, - "KdfIterations": grantor_user.client_kdf_iter, - "KdfMemory": grantor_user.client_kdf_memory, - "KdfParallelism": grantor_user.client_kdf_parallelism, - "KeyEncrypted": &emergency_access.key_encrypted, - "Object": "emergencyAccessTakeover", - }); - - Ok(Json(result)) + Ok(Json(json!({ + "Kdf": grantor_user.client_kdf_type, + "KdfIterations": grantor_user.client_kdf_iter, + "KeyEncrypted": &emergency_access.key_encrypted, + "Object": "emergencyAccessTakeover", + }))) } #[derive(Deserialize)] @@ -658,7 +645,7 @@ async fn password_emergency_access( let data: EmergencyAccessPasswordData = data.into_inner().data; let new_master_password_hash = &data.NewMasterPasswordHash; - //let key = &data.Key; + let key = data.Key; let requesting_user = headers.user; let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { @@ -676,7 +663,8 @@ async fn password_emergency_access( }; // change grantor_user password - grantor_user.set_password(new_master_password_hash, Some(data.Key), true, None); + grantor_user.set_password(new_master_password_hash, None); + grantor_user.akey = key; grantor_user.save(&mut conn).await?; // Disable TwoFactor providers since they will otherwise block logins diff --git a/src/api/core/events.rs b/src/api/core/events.rs index fab13e40e1e..4310271210f 100644 --- a/src/api/core/events.rs +++ b/src/api/core/events.rs @@ -6,7 +6,7 @@ use serde_json::Value; use crate::{ api::{EmptyResult, JsonResult, JsonUpcaseVec}, - auth::{AdminHeaders, Headers}, + auth::{AdminHeaders, ClientIp, Headers}, db::{ models::{Cipher, Event, UserOrganization}, DbConn, DbPool, @@ -161,7 +161,12 @@ struct EventCollection { // https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Events/Controllers/CollectController.cs // https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Core/Services/Implementations/EventService.cs #[post("/collect", format = "application/json", data = "")] -async fn post_events_collect(data: JsonUpcaseVec, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn post_events_collect( + data: JsonUpcaseVec, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, +) -> EmptyResult { if !CONFIG.org_events_enabled() { return Ok(()); } @@ -175,7 +180,7 @@ async fn post_events_collect(data: JsonUpcaseVec, headers: Head &headers.user.uuid, headers.device.atype, Some(event_date), - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -189,7 +194,7 @@ async fn post_events_collect(data: JsonUpcaseVec, headers: Head &headers.user.uuid, headers.device.atype, Some(event_date), - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -206,7 +211,7 @@ async fn post_events_collect(data: JsonUpcaseVec, headers: Head &headers.user.uuid, headers.device.atype, Some(event_date), - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 6a483842a04..d029cb60fb3 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -237,7 +237,6 @@ fn config() -> Json { "notifications": format!("{domain}/notifications"), "sso": "", }, - "object": "config", })) } diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 2f3681b9ca3..3fb83ae22d2 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -9,7 +9,7 @@ use crate::{ ApiResult, EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, JsonVec, Notify, NumberOrString, PasswordData, UpdateType, }, - auth::{decode_invite, AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, + auth::{decode_invite, AdminHeaders, ClientIp, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, db::{models::*, DbConn}, error::Error, mail, @@ -26,7 +26,6 @@ pub fn routes() -> Vec { leave_organization, get_user_collections, get_org_collections, - get_org_collections_details, get_org_collection_detail, get_collection_users, put_collection_users, @@ -39,7 +38,6 @@ pub fn routes() -> Vec { put_organization_collection_update, delete_organization_collection, post_organization_collection_delete, - bulk_delete_organization_collections, get_org_details, get_org_users, send_invite, @@ -64,7 +62,6 @@ pub fn routes() -> Vec { get_plans_tax_rates, import, post_org_keys, - get_organization_keys, bulk_public_keys, deactivate_organization_user, bulk_deactivate_organization_user, @@ -82,7 +79,6 @@ pub fn routes() -> Vec { get_group_details, delete_group, post_delete_group, - bulk_delete_groups, get_group_users, put_group_users, get_user_groups, @@ -90,9 +86,6 @@ pub fn routes() -> Vec { put_user_groups, delete_group_user, post_delete_group_user, - put_reset_password_enrollment, - get_reset_password_details, - put_reset_password, get_org_export ] } @@ -120,13 +113,12 @@ struct OrganizationUpdateData { #[allow(non_snake_case)] struct NewCollectionData { Name: String, - Groups: Vec, - Users: Vec, + Groups: Vec, } #[derive(Deserialize)] #[allow(non_snake_case)] -struct NewCollectionObjectData { +struct NewCollectionGroupData { HidePasswords: bool, Id: String, ReadOnly: bool, @@ -211,7 +203,7 @@ async fn post_delete_organization( } #[post("/organizations//leave")] -async fn leave_organization(org_id: String, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn leave_organization(org_id: String, headers: Headers, mut conn: DbConn, ip: ClientIp) -> EmptyResult { match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await { None => err!("User not part of organization"), Some(user_org) => { @@ -227,7 +219,7 @@ async fn leave_organization(org_id: String, headers: Headers, mut conn: DbConn) org_id, headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -251,8 +243,9 @@ async fn put_organization( headers: OwnerHeaders, data: JsonUpcase, conn: DbConn, + ip: ClientIp, ) -> JsonResult { - post_organization(org_id, headers, data, conn).await + post_organization(org_id, headers, data, conn, ip).await } #[post("/organizations/", data = "")] @@ -261,6 +254,7 @@ async fn post_organization( headers: OwnerHeaders, data: JsonUpcase, mut conn: DbConn, + ip: ClientIp, ) -> JsonResult { let data: OrganizationUpdateData = data.into_inner().data; @@ -280,7 +274,7 @@ async fn post_organization( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -311,65 +305,6 @@ async fn get_org_collections(org_id: String, _headers: ManagerHeadersLoose, mut })) } -#[get("/organizations//collections/details")] -async fn get_org_collections_details(org_id: String, headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult { - let mut data = Vec::new(); - - let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await { - Some(u) => u, - None => err!("User is not part of organization"), - }; - - let coll_users = CollectionUser::find_by_organization(&org_id, &mut conn).await; - - for col in Collection::find_by_organization(&org_id, &mut conn).await { - let groups: Vec = if CONFIG.org_groups_enabled() { - CollectionGroup::find_by_collection(&col.uuid, &mut conn) - .await - .iter() - .map(|collection_group| { - SelectionReadOnly::to_collection_group_details_read_only(collection_group).to_json() - }) - .collect() - } else { - // The Bitwarden clients seem to call this API regardless of whether groups are enabled, - // so just act as if there are no groups. - Vec::with_capacity(0) - }; - - let mut assigned = false; - let users: Vec = coll_users - .iter() - .filter(|collection_user| collection_user.collection_uuid == col.uuid) - .map(|collection_user| { - // Remember `user_uuid` is swapped here with the `user_org.uuid` with a join during the `CollectionUser::find_by_organization` call. - // We check here if the current user is assigned to this collection or not. - if collection_user.user_uuid == user_org.uuid { - assigned = true; - } - SelectionReadOnly::to_collection_user_details_read_only(collection_user).to_json() - }) - .collect(); - - if user_org.access_all { - assigned = true; - } - - let mut json_object = col.to_json(); - json_object["Assigned"] = json!(assigned); - json_object["Users"] = json!(users); - json_object["Groups"] = json!(groups); - json_object["Object"] = json!("collectionAccessDetails"); - data.push(json_object) - } - - Ok(Json(json!({ - "Data": data, - "Object": "list", - "ContinuationToken": null, - }))) -} - async fn _get_org_collections(org_id: &str, conn: &mut DbConn) -> Value { Collection::find_by_organization(org_id, conn).await.iter().map(Collection::to_json).collect::() } @@ -380,6 +315,7 @@ async fn post_organization_collections( headers: ManagerHeadersLoose, data: JsonUpcase, mut conn: DbConn, + ip: ClientIp, ) -> JsonResult { let data: NewCollectionData = data.into_inner().data; @@ -388,6 +324,12 @@ async fn post_organization_collections( None => err!("Can't find organization details"), }; + // Get the user_organization record so that we can check if the user has access to all collections. + let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await { + Some(u) => u, + None => err!("User is not part of organization"), + }; + let collection = Collection::new(org.uuid, data.Name); collection.save(&mut conn).await?; @@ -397,7 +339,7 @@ async fn post_organization_collections( org_id, headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -408,18 +350,11 @@ async fn post_organization_collections( .await?; } - for user in data.Users { - let org_user = match UserOrganization::find_by_uuid(&user.Id, &mut conn).await { - Some(u) => u, - None => err!("User is not part of organization"), - }; - - if org_user.access_all { - continue; - } - - CollectionUser::save(&org_user.user_uuid, &collection.uuid, user.ReadOnly, user.HidePasswords, &mut conn) - .await?; + // If the user doesn't have access to all collections, only in case of a Manger, + // then we need to save the creating user uuid (Manager) to the users_collection table. + // Else the user will not have access to his own created collection. + if !user_org.access_all { + CollectionUser::save(&headers.user.uuid, &collection.uuid, false, false, &mut conn).await?; } Ok(Json(collection.to_json())) @@ -432,8 +367,9 @@ async fn put_organization_collection_update( headers: ManagerHeaders, data: JsonUpcase, conn: DbConn, + ip: ClientIp, ) -> JsonResult { - post_organization_collection_update(org_id, col_id, headers, data, conn).await + post_organization_collection_update(org_id, col_id, headers, data, conn, ip).await } #[post("/organizations//collections/", data = "")] @@ -443,6 +379,7 @@ async fn post_organization_collection_update( headers: ManagerHeaders, data: JsonUpcase, mut conn: DbConn, + ip: ClientIp, ) -> JsonResult { let data: NewCollectionData = data.into_inner().data; @@ -469,7 +406,7 @@ async fn post_organization_collection_update( org_id, headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -480,21 +417,6 @@ async fn post_organization_collection_update( CollectionGroup::new(col_id.clone(), group.Id, group.ReadOnly, group.HidePasswords).save(&mut conn).await?; } - CollectionUser::delete_all_by_collection(&col_id, &mut conn).await?; - - for user in data.Users { - let org_user = match UserOrganization::find_by_uuid(&user.Id, &mut conn).await { - Some(u) => u, - None => err!("User is not part of organization"), - }; - - if org_user.access_all { - continue; - } - - CollectionUser::save(&org_user.user_uuid, &col_id, user.ReadOnly, user.HidePasswords, &mut conn).await?; - } - Ok(Json(collection.to_json())) } @@ -539,27 +461,29 @@ async fn post_organization_collection_delete_user( delete_organization_collection_user(org_id, col_id, org_user_id, headers, conn).await } -async fn _delete_organization_collection( - org_id: &str, - col_id: &str, - headers: &ManagerHeaders, - conn: &mut DbConn, +#[delete("/organizations//collections/")] +async fn delete_organization_collection( + org_id: String, + col_id: String, + headers: ManagerHeaders, + mut conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - match Collection::find_by_uuid(col_id, conn).await { + match Collection::find_by_uuid(&col_id, &mut conn).await { None => err!("Collection not found"), Some(collection) => { if collection.org_uuid == org_id { log_event( EventType::CollectionDeleted as i32, &collection.uuid, - org_id.to_string(), + org_id, headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, - conn, + &ip.ip, + &mut conn, ) .await; - collection.delete(conn).await + collection.delete(&mut conn).await } else { err!("Collection and Organization id do not match") } @@ -567,16 +491,6 @@ async fn _delete_organization_collection( } } -#[delete("/organizations//collections/")] -async fn delete_organization_collection( - org_id: String, - col_id: String, - headers: ManagerHeaders, - mut conn: DbConn, -) -> EmptyResult { - _delete_organization_collection(&org_id, &col_id, &headers, &mut conn).await -} - #[derive(Deserialize, Debug)] #[allow(non_snake_case, dead_code)] struct DeleteCollectionData { @@ -590,38 +504,10 @@ async fn post_organization_collection_delete( col_id: String, headers: ManagerHeaders, _data: JsonUpcase, - mut conn: DbConn, -) -> EmptyResult { - _delete_organization_collection(&org_id, &col_id, &headers, &mut conn).await -} - -#[derive(Deserialize, Debug)] -#[allow(non_snake_case)] -struct BulkCollectionIds { - Ids: Vec, - OrganizationId: String, -} - -#[delete("/organizations//collections", data = "")] -async fn bulk_delete_organization_collections( - org_id: &str, - headers: ManagerHeadersLoose, - data: JsonUpcase, - mut conn: DbConn, + conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - let data: BulkCollectionIds = data.into_inner().data; - if org_id != data.OrganizationId { - err!("OrganizationId mismatch"); - } - - let collections = data.Ids; - - let headers = ManagerHeaders::from_loose(headers, &collections, &mut conn).await?; - - for col_id in collections { - _delete_organization_collection(org_id, &col_id, &headers, &mut conn).await? - } - Ok(()) + delete_organization_collection(org_id, col_id, headers, conn, ip).await } #[get("/organizations//collections//details")] @@ -638,49 +524,17 @@ async fn get_org_collection_detail( err!("Collection is not owned by organization") } - let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await { - Some(u) => u, - None => err!("User is not part of organization"), - }; - - let groups: Vec = if CONFIG.org_groups_enabled() { - CollectionGroup::find_by_collection(&collection.uuid, &mut conn) - .await - .iter() - .map(|collection_group| { - SelectionReadOnly::to_collection_group_details_read_only(collection_group).to_json() - }) - .collect() - } else { - // The Bitwarden clients seem to call this API regardless of whether groups are enabled, - // so just act as if there are no groups. - Vec::with_capacity(0) - }; - - let mut assigned = false; - let users: Vec = - CollectionUser::find_by_collection_swap_user_uuid_with_org_user_uuid(&collection.uuid, &mut conn) - .await - .iter() - .map(|collection_user| { - // Remember `user_uuid` is swapped here with the `user_org.uuid` with a join during the `find_by_collection_swap_user_uuid_with_org_user_uuid` call. - // We check here if the current user is assigned to this collection or not. - if collection_user.user_uuid == user_org.uuid { - assigned = true; - } - SelectionReadOnly::to_collection_user_details_read_only(collection_user).to_json() - }) - .collect(); - - if user_org.access_all { - assigned = true; - } + let groups: Vec = CollectionGroup::find_by_collection(&collection.uuid, &mut conn) + .await + .iter() + .map(|collection_group| { + SelectionReadOnly::to_collection_group_details_read_only(collection_group).to_json() + }) + .collect(); let mut json_object = collection.to_json(); - json_object["Assigned"] = json!(assigned); - json_object["Users"] = json!(users); json_object["Groups"] = json!(groups); - json_object["Object"] = json!("collectionAccessDetails"); + json_object["Object"] = json!("collectionGroupDetails"); Ok(Json(json_object)) } @@ -763,41 +617,20 @@ async fn get_org_details(data: OrgIdData, headers: Headers, mut conn: DbConn) -> async fn _get_org_details(org_id: &str, host: &str, user_uuid: &str, conn: &mut DbConn) -> Value { let ciphers = Cipher::find_by_org(org_id, conn).await; - let cipher_sync_data = CipherSyncData::new(user_uuid, CipherSyncType::Organization, conn).await; + let cipher_sync_data = CipherSyncData::new(user_uuid, &ciphers, CipherSyncType::Organization, conn).await; - let mut ciphers_json = Vec::with_capacity(ciphers.len()); + let mut ciphers_json = Vec::new(); for c in ciphers { - ciphers_json - .push(c.to_json(host, user_uuid, Some(&cipher_sync_data), CipherSyncType::Organization, conn).await); + ciphers_json.push(c.to_json(host, user_uuid, Some(&cipher_sync_data), conn).await); } json!(ciphers_json) } -#[derive(FromForm)] -struct GetOrgUserData { - #[field(name = "includeCollections")] - include_collections: Option, - #[field(name = "includeGroups")] - include_groups: Option, -} - -#[get("/organizations//users?")] -async fn get_org_users( - data: GetOrgUserData, - org_id: String, - _headers: ManagerHeadersLoose, - mut conn: DbConn, -) -> Json { +#[get("/organizations//users")] +async fn get_org_users(org_id: String, _headers: ManagerHeadersLoose, mut conn: DbConn) -> Json { let mut users_json = Vec::new(); for u in UserOrganization::find_by_org(&org_id, &mut conn).await { - users_json.push( - u.to_json_user_details( - data.include_collections.unwrap_or(false), - data.include_groups.unwrap_or(false), - &mut conn, - ) - .await, - ); + users_json.push(u.to_json_user_details(&mut conn).await); } Json(json!({ @@ -861,6 +694,7 @@ async fn send_invite( data: JsonUpcase, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> EmptyResult { let data: InviteData = data.into_inner().data; @@ -935,7 +769,7 @@ async fn send_invite( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -1048,7 +882,6 @@ async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, co #[allow(non_snake_case)] struct AcceptData { Token: String, - ResetPasswordKey: Option, } #[post("/organizations//users/<_org_user_id>/accept", data = "")] @@ -1076,11 +909,6 @@ async fn accept_invite( err!("User already accepted the invitation") } - let master_password_required = OrgPolicy::org_is_reset_password_auto_enroll(org, &mut conn).await; - if data.ResetPasswordKey.is_none() && master_password_required { - err!("Reset password key is required, but not provided."); - } - // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type // It returns different error messages per function. if user_org.atype < UserOrgType::Admin { @@ -1096,11 +924,6 @@ async fn accept_invite( } user_org.status = UserOrgStatus::Accepted as i32; - - if master_password_required { - user_org.reset_password_key = data.ResetPasswordKey; - } - user_org.save(&mut conn).await?; } } @@ -1133,6 +956,7 @@ async fn bulk_confirm_invite( data: JsonUpcase, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> Json { let data = data.into_inner().data; @@ -1143,7 +967,8 @@ async fn bulk_confirm_invite( for invite in keys { let org_user_id = invite["Id"].as_str().unwrap_or_default(); let user_key = invite["Key"].as_str().unwrap_or_default(); - let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &mut conn, &nt).await { + let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &mut conn, &ip, &nt).await + { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1174,11 +999,12 @@ async fn confirm_invite( data: JsonUpcase, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let data = data.into_inner().data; let user_key = data["Key"].as_str().unwrap_or_default(); - _confirm_invite(&org_id, &org_user_id, user_key, &headers, &mut conn, &nt).await + _confirm_invite(&org_id, &org_user_id, user_key, &headers, &mut conn, &ip, &nt).await } async fn _confirm_invite( @@ -1187,6 +1013,7 @@ async fn _confirm_invite( key: &str, headers: &AdminHeaders, conn: &mut DbConn, + ip: &ClientIp, nt: &Notify<'_>, ) -> EmptyResult { if key.is_empty() || org_user_id.is_empty() { @@ -1229,7 +1056,7 @@ async fn _confirm_invite( String::from(org_id), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, conn, ) .await; @@ -1255,25 +1082,14 @@ async fn _confirm_invite( save_result } -#[get("/organizations//users/?")] -async fn get_user( - org_id: String, - org_user_id: String, - data: GetOrgUserData, - _headers: AdminHeaders, - mut conn: DbConn, -) -> JsonResult { +#[get("/organizations//users/")] +async fn get_user(org_id: String, org_user_id: String, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { let user = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &mut conn).await { Some(user) => user, None => err!("The specified user isn't a member of the organization"), }; - // In this case, when groups are requested we also need to include collections. - // Else these will not be shown in the interface, and could lead to missing collections when saved. - let include_groups = data.include_groups.unwrap_or(false); - Ok(Json( - user.to_json_user_details(data.include_collections.unwrap_or(include_groups), include_groups, &mut conn).await, - )) + Ok(Json(user.to_json_details(&mut conn).await)) } #[derive(Deserialize)] @@ -1281,7 +1097,6 @@ async fn get_user( struct EditUserData { Type: NumberOrString, Collections: Option>, - Groups: Option>, AccessAll: bool, } @@ -1292,8 +1107,9 @@ async fn put_organization_user( data: JsonUpcase, headers: AdminHeaders, conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - edit_user(org_id, org_user_id, data, headers, conn).await + edit_user(org_id, org_user_id, data, headers, conn, ip).await } #[post("/organizations//users/", data = "", rank = 1)] @@ -1303,6 +1119,7 @@ async fn edit_user( data: JsonUpcase, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> EmptyResult { let data: EditUserData = data.into_inner().data; @@ -1378,20 +1195,13 @@ async fn edit_user( } } - GroupUser::delete_all_by_user(&user_to_edit.uuid, &mut conn).await?; - - for group in data.Groups.iter().flatten() { - let mut group_entry = GroupUser::new(String::from(group), user_to_edit.uuid.clone()); - group_entry.save(&mut conn).await?; - } - log_event( EventType::OrganizationUserUpdated as i32, &user_to_edit.uuid, org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -1405,13 +1215,14 @@ async fn bulk_delete_user( data: JsonUpcase, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> Json { let data: OrgBulkIds = data.into_inner().data; let mut bulk_response = Vec::new(); for org_user_id in data.Ids { - let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &mut conn, &nt).await { + let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1438,9 +1249,10 @@ async fn delete_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_user(&org_id, &org_user_id, &headers, &mut conn, &nt).await + _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await } #[post("/organizations//users//delete")] @@ -1449,9 +1261,10 @@ async fn post_delete_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_user(&org_id, &org_user_id, &headers, &mut conn, &nt).await + _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await } async fn _delete_user( @@ -1459,6 +1272,7 @@ async fn _delete_user( org_user_id: &str, headers: &AdminHeaders, conn: &mut DbConn, + ip: &ClientIp, nt: &Notify<'_>, ) -> EmptyResult { let user_to_delete = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { @@ -1483,7 +1297,7 @@ async fn _delete_user( String::from(org_id), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, conn, ) .await; @@ -1558,6 +1372,7 @@ async fn post_org_import( data: JsonUpcase, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let data: ImportData = data.into_inner().data; @@ -1590,7 +1405,9 @@ async fn post_org_import( let mut ciphers = Vec::new(); for cipher_data in data.Ciphers { let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone()); - update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None).await.ok(); + update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &ip, &nt, UpdateType::None) + .await + .ok(); ciphers.push(cipher); } @@ -1676,6 +1493,7 @@ async fn put_policy( data: Json, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> JsonResult { let data: PolicyData = data.into_inner(); @@ -1708,7 +1526,7 @@ async fn put_policy( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -1742,7 +1560,7 @@ async fn put_policy( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -1767,7 +1585,7 @@ async fn put_policy( org_id, headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -1843,7 +1661,13 @@ struct OrgImportData { } #[post("/organizations//import", data = "")] -async fn import(org_id: String, data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn import( + org_id: String, + data: JsonUpcase, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, +) -> EmptyResult { let data = data.into_inner().data; // TODO: Currently we aren't storing the externalId's anywhere, so we also don't have a way @@ -1869,7 +1693,7 @@ async fn import(org_id: String, data: JsonUpcase, headers: Header org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -1899,7 +1723,7 @@ async fn import(org_id: String, data: JsonUpcase, headers: Header org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -1935,7 +1759,7 @@ async fn import(org_id: String, data: JsonUpcase, headers: Header org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -1956,8 +1780,9 @@ async fn deactivate_organization_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - _revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn).await + _revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await } // Pre web-vault v2022.9.x endpoint @@ -1967,8 +1792,9 @@ async fn bulk_deactivate_organization_user( data: JsonUpcase, headers: AdminHeaders, conn: DbConn, + ip: ClientIp, ) -> Json { - bulk_revoke_organization_user(org_id, data, headers, conn).await + bulk_revoke_organization_user(org_id, data, headers, conn, ip).await } #[put("/organizations//users//revoke")] @@ -1977,8 +1803,9 @@ async fn revoke_organization_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - _revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn).await + _revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await } #[put("/organizations//users/revoke", data = "")] @@ -1987,6 +1814,7 @@ async fn bulk_revoke_organization_user( data: JsonUpcase, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> Json { let data = data.into_inner().data; @@ -1995,7 +1823,7 @@ async fn bulk_revoke_organization_user( Some(org_users) => { for org_user_id in org_users { let org_user_id = org_user_id.as_str().unwrap_or_default(); - let err_msg = match _revoke_organization_user(&org_id, org_user_id, &headers, &mut conn).await { + let err_msg = match _revoke_organization_user(&org_id, org_user_id, &headers, &mut conn, &ip).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -2024,6 +1852,7 @@ async fn _revoke_organization_user( org_user_id: &str, headers: &AdminHeaders, conn: &mut DbConn, + ip: &ClientIp, ) -> EmptyResult { match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { Some(mut user_org) if user_org.status > UserOrgStatus::Revoked as i32 => { @@ -2048,7 +1877,7 @@ async fn _revoke_organization_user( org_id.to_string(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, conn, ) .await; @@ -2066,8 +1895,9 @@ async fn activate_organization_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - _restore_organization_user(&org_id, &org_user_id, &headers, &mut conn).await + _restore_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await } // Pre web-vault v2022.9.x endpoint @@ -2077,8 +1907,9 @@ async fn bulk_activate_organization_user( data: JsonUpcase, headers: AdminHeaders, conn: DbConn, + ip: ClientIp, ) -> Json { - bulk_restore_organization_user(org_id, data, headers, conn).await + bulk_restore_organization_user(org_id, data, headers, conn, ip).await } #[put("/organizations//users//restore")] @@ -2087,8 +1918,9 @@ async fn restore_organization_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - _restore_organization_user(&org_id, &org_user_id, &headers, &mut conn).await + _restore_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await } #[put("/organizations//users/restore", data = "")] @@ -2097,6 +1929,7 @@ async fn bulk_restore_organization_user( data: JsonUpcase, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> Json { let data = data.into_inner().data; @@ -2105,7 +1938,7 @@ async fn bulk_restore_organization_user( Some(org_users) => { for org_user_id in org_users { let org_user_id = org_user_id.as_str().unwrap_or_default(); - let err_msg = match _restore_organization_user(&org_id, org_user_id, &headers, &mut conn).await { + let err_msg = match _restore_organization_user(&org_id, org_user_id, &headers, &mut conn, &ip).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -2134,6 +1967,7 @@ async fn _restore_organization_user( org_user_id: &str, headers: &AdminHeaders, conn: &mut DbConn, + ip: &ClientIp, ) -> EmptyResult { match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { Some(mut user_org) if user_org.status < UserOrgStatus::Accepted as i32 => { @@ -2167,7 +2001,7 @@ async fn _restore_organization_user( org_id.to_string(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, conn, ) .await; @@ -2180,19 +2014,11 @@ async fn _restore_organization_user( #[get("/organizations//groups")] async fn get_groups(org_id: String, _headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult { - let groups: Vec = if CONFIG.org_groups_enabled() { - // Group::find_by_organization(&org_id, &mut conn).await.iter().map(Group::to_json).collect::() - let groups = Group::find_by_organization(&org_id, &mut conn).await; - let mut groups_json = Vec::with_capacity(groups.len()); - for g in groups { - groups_json.push(g.to_json_details(&mut conn).await) - } - groups_json - } else { - // The Bitwarden clients seem to call this API regardless of whether groups are enabled, - // so just act as if there are no groups. - Vec::with_capacity(0) - }; + if !CONFIG.org_groups_enabled() { + err!("Group support is disabled"); + } + + let groups = Group::find_by_organization(&org_id, &mut conn).await.iter().map(Group::to_json).collect::(); Ok(Json(json!({ "Data": groups, @@ -2208,7 +2034,6 @@ struct GroupRequest { AccessAll: Option, ExternalId: Option, Collections: Vec, - Users: Vec, } impl GroupRequest { @@ -2251,19 +2076,19 @@ impl SelectionReadOnly { CollectionGroup::new(self.Id.clone(), groups_uuid, self.ReadOnly, self.HidePasswords) } - pub fn to_collection_group_details_read_only(collection_group: &CollectionGroup) -> SelectionReadOnly { + pub fn to_group_details_read_only(collection_group: &CollectionGroup) -> SelectionReadOnly { SelectionReadOnly { - Id: collection_group.groups_uuid.clone(), + Id: collection_group.collections_uuid.clone(), ReadOnly: collection_group.read_only, HidePasswords: collection_group.hide_passwords, } } - pub fn to_collection_user_details_read_only(collection_user: &CollectionUser) -> SelectionReadOnly { + pub fn to_collection_group_details_read_only(collection_group: &CollectionGroup) -> SelectionReadOnly { SelectionReadOnly { - Id: collection_user.user_uuid.clone(), - ReadOnly: collection_user.read_only, - HidePasswords: collection_user.hide_passwords, + Id: collection_group.groups_uuid.clone(), + ReadOnly: collection_group.read_only, + HidePasswords: collection_group.hide_passwords, } } @@ -2279,8 +2104,9 @@ async fn post_group( data: JsonUpcase, headers: AdminHeaders, conn: DbConn, + ip: ClientIp, ) -> JsonResult { - put_group(org_id, group_id, data, headers, conn).await + put_group(org_id, group_id, data, headers, conn, ip).await } #[post("/organizations//groups", data = "")] @@ -2289,6 +2115,7 @@ async fn post_groups( headers: AdminHeaders, data: JsonUpcase, mut conn: DbConn, + ip: ClientIp, ) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); @@ -2300,15 +2127,15 @@ async fn post_groups( log_event( EventType::GroupCreated as i32, &group.uuid, - org_id.clone(), + org_id, headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; - add_update_group(group, group_request.Collections, group_request.Users, &org_id, &headers, &mut conn).await + add_update_group(group, group_request.Collections, &mut conn).await } #[put("/organizations//groups/", data = "")] @@ -2318,6 +2145,7 @@ async fn put_group( data: JsonUpcase, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); @@ -2332,51 +2160,28 @@ async fn put_group( let updated_group = group_request.update_group(group)?; CollectionGroup::delete_all_by_group(&group_id, &mut conn).await?; - GroupUser::delete_all_by_group(&group_id, &mut conn).await?; log_event( EventType::GroupUpdated as i32, &updated_group.uuid, - org_id.clone(), + org_id, headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; - add_update_group(updated_group, group_request.Collections, group_request.Users, &org_id, &headers, &mut conn).await + add_update_group(updated_group, group_request.Collections, &mut conn).await } -async fn add_update_group( - mut group: Group, - collections: Vec, - users: Vec, - org_id: &str, - headers: &AdminHeaders, - conn: &mut DbConn, -) -> JsonResult { +async fn add_update_group(mut group: Group, collections: Vec, conn: &mut DbConn) -> JsonResult { group.save(conn).await?; for selection_read_only_request in collections { let mut collection_group = selection_read_only_request.to_collection_group(group.uuid.clone()); - collection_group.save(conn).await?; - } - - for assigned_user_id in users { - let mut user_entry = GroupUser::new(group.uuid.clone(), assigned_user_id.clone()); - user_entry.save(conn).await?; - log_event( - EventType::OrganizationUserUpdatedGroups as i32, - &assigned_user_id, - String::from(org_id), - headers.user.uuid.clone(), - headers.device.atype, - &headers.ip.ip, - conn, - ) - .await; + collection_group.save(conn).await?; } Ok(Json(json!({ @@ -2399,25 +2204,46 @@ async fn get_group_details(_org_id: String, group_id: String, _headers: AdminHea _ => err!("Group could not be found!"), }; - Ok(Json(group.to_json_details(&mut conn).await)) + let collections_groups = CollectionGroup::find_by_group(&group_id, &mut conn) + .await + .iter() + .map(|entry| SelectionReadOnly::to_group_details_read_only(entry).to_json()) + .collect::(); + + Ok(Json(json!({ + "Id": group.uuid, + "OrganizationId": group.organizations_uuid, + "Name": group.name, + "AccessAll": group.access_all, + "ExternalId": group.get_external_id(), + "Collections": collections_groups + }))) } #[post("/organizations//groups//delete")] -async fn post_delete_group(org_id: String, group_id: String, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { - _delete_group(org_id, group_id, &headers, &mut conn).await +async fn post_delete_group( + org_id: String, + group_id: String, + headers: AdminHeaders, + conn: DbConn, + ip: ClientIp, +) -> EmptyResult { + delete_group(org_id, group_id, headers, conn, ip).await } #[delete("/organizations//groups/")] -async fn delete_group(org_id: String, group_id: String, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { - _delete_group(org_id, group_id, &headers, &mut conn).await -} - -async fn _delete_group(org_id: String, group_id: String, headers: &AdminHeaders, conn: &mut DbConn) -> EmptyResult { +async fn delete_group( + org_id: String, + group_id: String, + headers: AdminHeaders, + mut conn: DbConn, + ip: ClientIp, +) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } - let group = match Group::find_by_uuid(&group_id, conn).await { + let group = match Group::find_by_uuid(&group_id, &mut conn).await { Some(group) => group, _ => err!("Group not found"), }; @@ -2428,31 +2254,12 @@ async fn _delete_group(org_id: String, group_id: String, headers: &AdminHeaders, org_id, headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, - conn, + &ip.ip, + &mut conn, ) .await; - group.delete(conn).await -} - -#[delete("/organizations//groups", data = "")] -async fn bulk_delete_groups( - org_id: String, - data: JsonUpcase, - headers: AdminHeaders, - mut conn: DbConn, -) -> EmptyResult { - if !CONFIG.org_groups_enabled() { - err!("Group support is disabled"); - } - - let data: OrgBulkIds = data.into_inner().data; - - for group_id in data.Ids { - _delete_group(org_id.clone(), group_id, &headers, &mut conn).await? - } - Ok(()) + group.delete(&mut conn).await } #[get("/organizations/<_org_id>/groups/")] @@ -2496,6 +2303,7 @@ async fn put_group_users( headers: AdminHeaders, data: JsonVec, mut conn: DbConn, + ip: ClientIp, ) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); @@ -2519,7 +2327,7 @@ async fn put_group_users( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -2558,8 +2366,9 @@ async fn post_user_groups( data: JsonUpcase, headers: AdminHeaders, conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - put_user_groups(org_id, org_user_id, data, headers, conn).await + put_user_groups(org_id, org_user_id, data, headers, conn, ip).await } #[put("/organizations//users//groups", data = "")] @@ -2569,6 +2378,7 @@ async fn put_user_groups( data: JsonUpcase, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); @@ -2593,7 +2403,7 @@ async fn put_user_groups( org_id, headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -2608,8 +2418,9 @@ async fn post_delete_group_user( org_user_id: String, headers: AdminHeaders, conn: DbConn, + ip: ClientIp, ) -> EmptyResult { - delete_group_user(org_id, group_id, org_user_id, headers, conn).await + delete_group_user(org_id, group_id, org_user_id, headers, conn, ip).await } #[delete("/organizations//groups//users/")] @@ -2619,6 +2430,7 @@ async fn delete_group_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, + ip: ClientIp, ) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); @@ -2640,7 +2452,7 @@ async fn delete_group_user( org_id, headers.user.uuid.clone(), headers.device.atype, - &headers.ip.ip, + &ip.ip, &mut conn, ) .await; @@ -2648,207 +2460,6 @@ async fn delete_group_user( GroupUser::delete_by_group_id_and_user_id(&group_id, &org_user_id, &mut conn).await } -#[derive(Deserialize)] -#[allow(non_snake_case)] -struct OrganizationUserResetPasswordEnrollmentRequest { - ResetPasswordKey: Option, -} - -#[derive(Deserialize)] -#[allow(non_snake_case)] -struct OrganizationUserResetPasswordRequest { - NewMasterPasswordHash: String, - Key: String, -} - -#[get("/organizations//keys")] -async fn get_organization_keys(org_id: String, mut conn: DbConn) -> JsonResult { - let org = match Organization::find_by_uuid(&org_id, &mut conn).await { - Some(organization) => organization, - None => err!("Organization not found"), - }; - - Ok(Json(json!({ - "Object": "organizationKeys", - "PublicKey": org.public_key, - "PrivateKey": org.private_key, - }))) -} - -#[put("/organizations//users//reset-password", data = "")] -async fn put_reset_password( - org_id: String, - org_user_id: String, - headers: AdminHeaders, - data: JsonUpcase, - mut conn: DbConn, - nt: Notify<'_>, -) -> EmptyResult { - let org = match Organization::find_by_uuid(&org_id, &mut conn).await { - Some(org) => org, - None => err!("Required organization not found"), - }; - - let org_user = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org.uuid, &mut conn).await { - Some(user) => user, - None => err!("User to reset isn't member of required organization"), - }; - - let user = match User::find_by_uuid(&org_user.user_uuid, &mut conn).await { - Some(user) => user, - None => err!("User not found"), - }; - - check_reset_password_applicable_and_permissions(&org_id, &org_user_id, &headers, &mut conn).await?; - - if org_user.reset_password_key.is_none() { - err!("Password reset not or not correctly enrolled"); - } - if org_user.status != (UserOrgStatus::Confirmed as i32) { - err!("Organization user must be confirmed for password reset functionality"); - } - - // Sending email before resetting password to ensure working email configuration and the resulting - // user notification. Also this might add some protection against security flaws and misuse - if let Err(e) = mail::send_admin_reset_password(&user.email, &user.name, &org.name).await { - err!(format!("Error sending user reset password email: {e:#?}")); - } - - let reset_request = data.into_inner().data; - - let mut user = user; - user.set_password(reset_request.NewMasterPasswordHash.as_str(), Some(reset_request.Key), true, None); - user.save(&mut conn).await?; - - nt.send_logout(&user, None).await; - - log_event( - EventType::OrganizationUserAdminResetPassword as i32, - &org_user_id, - org.uuid.clone(), - headers.user.uuid.clone(), - headers.device.atype, - &headers.ip.ip, - &mut conn, - ) - .await; - - Ok(()) -} - -#[get("/organizations//users//reset-password-details")] -async fn get_reset_password_details( - org_id: String, - org_user_id: String, - headers: AdminHeaders, - mut conn: DbConn, -) -> JsonResult { - let org = match Organization::find_by_uuid(&org_id, &mut conn).await { - Some(org) => org, - None => err!("Required organization not found"), - }; - - let org_user = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &mut conn).await { - Some(user) => user, - None => err!("User to reset isn't member of required organization"), - }; - - let user = match User::find_by_uuid(&org_user.user_uuid, &mut conn).await { - Some(user) => user, - None => err!("User not found"), - }; - - check_reset_password_applicable_and_permissions(&org_id, &org_user_id, &headers, &mut conn).await?; - - // https://github.com/bitwarden/server/blob/3b50ccb9f804efaacdc46bed5b60e5b28eddefcf/src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs#L111 - Ok(Json(json!({ - "Object": "organizationUserResetPasswordDetails", - "Kdf":user.client_kdf_type, - "KdfIterations":user.client_kdf_iter, - "KdfMemory":user.client_kdf_memory, - "KdfParallelism":user.client_kdf_parallelism, - "ResetPasswordKey":org_user.reset_password_key, - "EncryptedPrivateKey":org.private_key, - - }))) -} - -async fn check_reset_password_applicable_and_permissions( - org_id: &str, - org_user_id: &str, - headers: &AdminHeaders, - conn: &mut DbConn, -) -> EmptyResult { - check_reset_password_applicable(org_id, conn).await?; - - let target_user = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { - Some(user) => user, - None => err!("Reset target user not found"), - }; - - // Resetting user must be higher/equal to user to reset - match headers.org_user_type { - UserOrgType::Owner => Ok(()), - UserOrgType::Admin if target_user.atype <= UserOrgType::Admin => Ok(()), - _ => err!("No permission to reset this user's password"), - } -} - -async fn check_reset_password_applicable(org_id: &str, conn: &mut DbConn) -> EmptyResult { - if !CONFIG.mail_enabled() { - err!("Password reset is not supported on an email-disabled instance."); - } - - let policy = match OrgPolicy::find_by_org_and_type(org_id, OrgPolicyType::ResetPassword, conn).await { - Some(p) => p, - None => err!("Policy not found"), - }; - - if !policy.enabled { - err!("Reset password policy not enabled"); - } - - Ok(()) -} - -#[put("/organizations//users//reset-password-enrollment", data = "")] -async fn put_reset_password_enrollment( - org_id: String, - org_user_id: String, - headers: Headers, - data: JsonUpcase, - mut conn: DbConn, -) -> EmptyResult { - let mut org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await { - Some(u) => u, - None => err!("User to enroll isn't member of required organization"), - }; - - check_reset_password_applicable(&org_id, &mut conn).await?; - - let reset_request = data.into_inner().data; - - if reset_request.ResetPasswordKey.is_none() - && OrgPolicy::org_is_reset_password_auto_enroll(&org_id, &mut conn).await - { - err!("Reset password can't be withdrawed due to an enterprise policy"); - } - - org_user.reset_password_key = reset_request.ResetPasswordKey; - org_user.save(&mut conn).await?; - - let log_id = if org_user.reset_password_key.is_some() { - EventType::OrganizationUserResetPasswordEnroll as i32 - } else { - EventType::OrganizationUserResetPasswordWithdraw as i32 - }; - - log_event(log_id, &org_user_id, org_id, headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, &mut conn) - .await; - - Ok(()) -} - // This is a new function active since the v2022.9.x clients. // It combines the previous two calls done before. // We call those two functions here and combine them our selfs. diff --git a/src/api/core/two_factor/authenticator.rs b/src/api/core/two_factor/authenticator.rs index 37221df104c..fa1792f250a 100644 --- a/src/api/core/two_factor/authenticator.rs +++ b/src/api/core/two_factor/authenticator.rs @@ -57,6 +57,7 @@ struct EnableAuthenticatorData { async fn activate_authenticator( data: JsonUpcase, headers: Headers, + ip: ClientIp, mut conn: DbConn, ) -> JsonResult { let data: EnableAuthenticatorData = data.into_inner().data; @@ -81,11 +82,11 @@ async fn activate_authenticator( } // Validate the token provided with the key, and save new twofactor - validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &headers.ip, &mut conn).await?; + validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &ip, &mut conn).await?; _generate_recover_code(&mut user, &mut conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; Ok(Json(json!({ "Enabled": true, @@ -98,9 +99,10 @@ async fn activate_authenticator( async fn activate_authenticator_put( data: JsonUpcase, headers: Headers, + ip: ClientIp, conn: DbConn, ) -> JsonResult { - activate_authenticator(data, headers, conn).await + activate_authenticator(data, headers, ip, conn).await } pub async fn validate_totp_code_str( diff --git a/src/api/core/two_factor/duo.rs b/src/api/core/two_factor/duo.rs index c4ca0ba8434..267d2db9d38 100644 --- a/src/api/core/two_factor/duo.rs +++ b/src/api/core/two_factor/duo.rs @@ -8,7 +8,7 @@ use crate::{ core::log_user_event, core::two_factor::_generate_recover_code, ApiResult, EmptyResult, JsonResult, JsonUpcase, PasswordData, }, - auth::Headers, + auth::{ClientIp, Headers}, crypto, db::{ models::{EventType, TwoFactor, TwoFactorType, User}, @@ -155,7 +155,7 @@ fn check_duo_fields_custom(data: &EnableDuoData) -> bool { } #[post("/two-factor/duo", data = "")] -async fn activate_duo(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn activate_duo(data: JsonUpcase, headers: Headers, mut conn: DbConn, ip: ClientIp) -> JsonResult { let data: EnableDuoData = data.into_inner().data; let mut user = headers.user; @@ -178,7 +178,7 @@ async fn activate_duo(data: JsonUpcase, headers: Headers, mut con _generate_recover_code(&mut user, &mut conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; Ok(Json(json!({ "Enabled": true, @@ -190,8 +190,8 @@ async fn activate_duo(data: JsonUpcase, headers: Headers, mut con } #[put("/two-factor/duo", data = "")] -async fn activate_duo_put(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { - activate_duo(data, headers, conn).await +async fn activate_duo_put(data: JsonUpcase, headers: Headers, conn: DbConn, ip: ClientIp) -> JsonResult { + activate_duo(data, headers, conn, ip).await } async fn duo_api_request(method: &str, path: &str, params: &str, data: &DuoData) -> EmptyResult { diff --git a/src/api/core/two_factor/email.rs b/src/api/core/two_factor/email.rs index 1ca5152b6c9..f9a0303bc9d 100644 --- a/src/api/core/two_factor/email.rs +++ b/src/api/core/two_factor/email.rs @@ -7,7 +7,7 @@ use crate::{ core::{log_user_event, two_factor::_generate_recover_code}, EmptyResult, JsonResult, JsonUpcase, PasswordData, }, - auth::Headers, + auth::{ClientIp, Headers}, crypto, db::{ models::{EventType, TwoFactor, TwoFactorType}, @@ -90,7 +90,7 @@ async fn get_email(data: JsonUpcase, headers: Headers, mut conn: D let twofactor_data = EmailTokenData::from_json(&x.data)?; (true, json!(twofactor_data.email)) } - _ => (false, serde_json::value::Value::Null), + _ => (false, json!(null)), }; Ok(Json(json!({ @@ -150,7 +150,7 @@ struct EmailData { /// Verify email belongs to user and can be used for 2FA email codes. #[put("/two-factor/email", data = "")] -async fn email(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn email(data: JsonUpcase, headers: Headers, mut conn: DbConn, ip: ClientIp) -> JsonResult { let data: EmailData = data.into_inner().data; let mut user = headers.user; @@ -180,7 +180,7 @@ async fn email(data: JsonUpcase, headers: Headers, mut conn: DbConn) _generate_recover_code(&mut user, &mut conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; Ok(Json(json!({ "Email": email_data.email, diff --git a/src/api/core/two_factor/mod.rs b/src/api/core/two_factor/mod.rs index 35c1867f264..a2bbc806ee2 100644 --- a/src/api/core/two_factor/mod.rs +++ b/src/api/core/two_factor/mod.rs @@ -6,7 +6,7 @@ use serde_json::Value; use crate::{ api::{core::log_user_event, JsonResult, JsonUpcase, NumberOrString, PasswordData}, - auth::{ClientHeaders, Headers}, + auth::{ClientHeaders, ClientIp, Headers}, crypto, db::{models::*, DbConn, DbPool}, mail, CONFIG, @@ -73,7 +73,12 @@ struct RecoverTwoFactor { } #[post("/two-factor/recover", data = "")] -async fn recover(data: JsonUpcase, client_headers: ClientHeaders, mut conn: DbConn) -> JsonResult { +async fn recover( + data: JsonUpcase, + client_headers: ClientHeaders, + mut conn: DbConn, + ip: ClientIp, +) -> JsonResult { let data: RecoverTwoFactor = data.into_inner().data; use crate::db::models::User; @@ -97,19 +102,12 @@ async fn recover(data: JsonUpcase, client_headers: ClientHeade // Remove all twofactors from the user TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?; - log_user_event( - EventType::UserRecovered2fa as i32, - &user.uuid, - client_headers.device_type, - &client_headers.ip.ip, - &mut conn, - ) - .await; + log_user_event(EventType::UserRecovered2fa as i32, &user.uuid, client_headers.device_type, &ip.ip, &mut conn).await; // Remove the recovery code, not needed without twofactors user.totp_recover = None; user.save(&mut conn).await?; - Ok(Json(Value::Object(serde_json::Map::new()))) + Ok(Json(json!({}))) } async fn _generate_recover_code(user: &mut User, conn: &mut DbConn) { @@ -128,7 +126,12 @@ struct DisableTwoFactorData { } #[post("/two-factor/disable", data = "")] -async fn disable_twofactor(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn disable_twofactor( + data: JsonUpcase, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, +) -> JsonResult { let data: DisableTwoFactorData = data.into_inner().data; let password_hash = data.MasterPasswordHash; let user = headers.user; @@ -141,8 +144,7 @@ async fn disable_twofactor(data: JsonUpcase, headers: Head if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await { twofactor.delete(&mut conn).await?; - log_user_event(EventType::UserDisabled2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn) - .await; + log_user_event(EventType::UserDisabled2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; } let twofactor_disabled = TwoFactor::find_by_user(&user.uuid, &mut conn).await.is_empty(); @@ -171,8 +173,13 @@ async fn disable_twofactor(data: JsonUpcase, headers: Head } #[put("/two-factor/disable", data = "")] -async fn disable_twofactor_put(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { - disable_twofactor(data, headers, conn).await +async fn disable_twofactor_put( + data: JsonUpcase, + headers: Headers, + conn: DbConn, + ip: ClientIp, +) -> JsonResult { + disable_twofactor(data, headers, conn, ip).await } pub async fn send_incomplete_2fa_notifications(pool: DbPool) { diff --git a/src/api/core/two_factor/webauthn.rs b/src/api/core/two_factor/webauthn.rs index 3c62754af6d..97711c7598f 100644 --- a/src/api/core/two_factor/webauthn.rs +++ b/src/api/core/two_factor/webauthn.rs @@ -9,7 +9,7 @@ use crate::{ core::{log_user_event, two_factor::_generate_recover_code}, EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData, }, - auth::Headers, + auth::{ClientIp, Headers}, db::{ models::{EventType, TwoFactor, TwoFactorType}, DbConn, @@ -242,7 +242,12 @@ impl From for PublicKeyCredential { } #[post("/two-factor/webauthn", data = "")] -async fn activate_webauthn(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn activate_webauthn( + data: JsonUpcase, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, +) -> JsonResult { let data: EnableWebauthnData = data.into_inner().data; let mut user = headers.user; @@ -281,7 +286,7 @@ async fn activate_webauthn(data: JsonUpcase, headers: Header .await?; _generate_recover_code(&mut user, &mut conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; let keys_json: Vec = registrations.iter().map(WebauthnRegistration::to_json).collect(); Ok(Json(json!({ @@ -292,8 +297,13 @@ async fn activate_webauthn(data: JsonUpcase, headers: Header } #[put("/two-factor/webauthn", data = "")] -async fn activate_webauthn_put(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { - activate_webauthn(data, headers, conn).await +async fn activate_webauthn_put( + data: JsonUpcase, + headers: Headers, + conn: DbConn, + ip: ClientIp, +) -> JsonResult { + activate_webauthn(data, headers, conn, ip).await } #[derive(Deserialize, Debug)] diff --git a/src/api/core/two_factor/yubikey.rs b/src/api/core/two_factor/yubikey.rs index 7681ab01e65..b076dd00d48 100644 --- a/src/api/core/two_factor/yubikey.rs +++ b/src/api/core/two_factor/yubikey.rs @@ -8,7 +8,7 @@ use crate::{ core::{log_user_event, two_factor::_generate_recover_code}, EmptyResult, JsonResult, JsonUpcase, PasswordData, }, - auth::Headers, + auth::{ClientIp, Headers}, db::{ models::{EventType, TwoFactor, TwoFactorType}, DbConn, @@ -47,7 +47,7 @@ fn parse_yubikeys(data: &EnableYubikeyData) -> Vec { } fn jsonify_yubikeys(yubikeys: Vec) -> serde_json::Value { - let mut result = Value::Object(serde_json::Map::new()); + let mut result = json!({}); for (i, key) in yubikeys.into_iter().enumerate() { result[format!("Key{}", i + 1)] = Value::String(key); @@ -118,7 +118,12 @@ async fn generate_yubikey(data: JsonUpcase, headers: Headers, mut } #[post("/two-factor/yubikey", data = "")] -async fn activate_yubikey(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn activate_yubikey( + data: JsonUpcase, + headers: Headers, + mut conn: DbConn, + ip: ClientIp, +) -> JsonResult { let data: EnableYubikeyData = data.into_inner().data; let mut user = headers.user; @@ -164,7 +169,7 @@ async fn activate_yubikey(data: JsonUpcase, headers: Headers, _generate_recover_code(&mut user, &mut conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; let mut result = jsonify_yubikeys(yubikey_metadata.Keys); @@ -176,8 +181,13 @@ async fn activate_yubikey(data: JsonUpcase, headers: Headers, } #[put("/two-factor/yubikey", data = "")] -async fn activate_yubikey_put(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { - activate_yubikey(data, headers, conn).await +async fn activate_yubikey_put( + data: JsonUpcase, + headers: Headers, + conn: DbConn, + ip: ClientIp, +) -> JsonResult { + activate_yubikey(data, headers, conn, ip).await } pub async fn validate_yubikey_login(response: &str, twofactor_data: &str) -> EmptyResult { diff --git a/src/api/icons.rs b/src/api/icons.rs index 9bff01622b2..23d122f126c 100644 --- a/src/api/icons.rs +++ b/src/api/icons.rs @@ -79,7 +79,7 @@ async fn icon_redirect(domain: &str, template: &str) -> Option { return None; } - if check_domain_blacklist_reason(domain).await.is_some() { + if is_domain_blacklisted(domain).await { return None; } @@ -258,15 +258,9 @@ mod tests { } } -#[derive(Debug, Clone)] -enum DomainBlacklistReason { - Regex, - IP, -} - use cached::proc_macro::cached; #[cached(key = "String", convert = r#"{ domain.to_string() }"#, size = 16, time = 60)] -async fn check_domain_blacklist_reason(domain: &str) -> Option { +async fn is_domain_blacklisted(domain: &str) -> bool { // First check the blacklist regex if there is a match. // This prevents the blocked domain(s) from being leaked via a DNS lookup. if let Some(blacklist) = CONFIG.icon_blacklist_regex() { @@ -290,7 +284,7 @@ async fn check_domain_blacklist_reason(domain: &str) -> Option Option Option<(Vec, String)> { @@ -570,10 +564,8 @@ async fn get_page(url: &str) -> Result { } async fn get_page_with_referer(url: &str, referer: &str) -> Result { - match check_domain_blacklist_reason(url::Url::parse(url).unwrap().host_str().unwrap_or_default()).await { - Some(DomainBlacklistReason::Regex) => warn!("Favicon '{}' is from a blacklisted domain!", url), - Some(DomainBlacklistReason::IP) => warn!("Favicon '{}' is hosted on a non-global IP!", url), - None => (), + if is_domain_blacklisted(url::Url::parse(url).unwrap().host_str().unwrap_or_default()).await { + warn!("Favicon '{}' resolves to a blacklisted domain or IP!", url); } let mut client = CLIENT.get(url); @@ -667,10 +659,8 @@ fn parse_sizes(sizes: &str) -> (u16, u16) { } async fn download_icon(domain: &str) -> Result<(Bytes, Option<&str>), Error> { - match check_domain_blacklist_reason(domain).await { - Some(DomainBlacklistReason::Regex) => err_silent!("Domain is blacklisted", domain), - Some(DomainBlacklistReason::IP) => err_silent!("Host resolves to a non-global IP", domain), - None => (), + if is_domain_blacklisted(domain).await { + err_silent!("Domain is blacklisted", domain) } let icon_result = get_icon_url(domain).await?; diff --git a/src/api/identity.rs b/src/api/identity.rs index cefee692859..0cb1c03a9e9 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -25,7 +25,7 @@ pub fn routes() -> Vec { } #[post("/connect/token", data = "")] -async fn login(data: Form, client_header: ClientHeaders, mut conn: DbConn) -> JsonResult { +async fn login(data: Form, client_header: ClientHeaders, mut conn: DbConn, ip: ClientIp) -> JsonResult { let data: ConnectData = data.into_inner(); let mut user_uuid: Option = None; @@ -45,18 +45,14 @@ async fn login(data: Form, client_header: ClientHeaders, mut conn: _check_is_some(&data.device_name, "device_name cannot be blank")?; _check_is_some(&data.device_type, "device_type cannot be blank")?; - _password_login(data, &mut user_uuid, &mut conn, &client_header.ip).await + _password_login(data, &mut user_uuid, &mut conn, &ip).await } "client_credentials" => { _check_is_some(&data.client_id, "client_id cannot be blank")?; _check_is_some(&data.client_secret, "client_secret cannot be blank")?; _check_is_some(&data.scope, "scope cannot be blank")?; - _check_is_some(&data.device_identifier, "device_identifier cannot be blank")?; - _check_is_some(&data.device_name, "device_name cannot be blank")?; - _check_is_some(&data.device_type, "device_type cannot be blank")?; - - _api_key_login(data, &mut user_uuid, &mut conn, &client_header.ip).await + _api_key_login(data, &mut user_uuid, &mut conn, &ip).await } t => err!("Invalid type", t), }; @@ -68,21 +64,14 @@ async fn login(data: Form, client_header: ClientHeaders, mut conn: EventType::UserLoggedIn as i32, &user_uuid, client_header.device_type, - &client_header.ip.ip, + &ip.ip, &mut conn, ) .await; } Err(e) => { if let Some(ev) = e.get_event() { - log_user_event( - ev.event as i32, - &user_uuid, - client_header.device_type, - &client_header.ip.ip, - &mut conn, - ) - .await + log_user_event(ev.event as i32, &user_uuid, client_header.device_type, &ip.ip, &mut conn).await } } } @@ -107,7 +96,7 @@ async fn _refresh_login(data: ConnectData, conn: &mut DbConn) -> JsonResult { let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec); device.save(conn).await?; - let result = json!({ + Ok(Json(json!({ "access_token": access_token, "expires_in": expires_in, "token_type": "Bearer", @@ -117,14 +106,10 @@ async fn _refresh_login(data: ConnectData, conn: &mut DbConn) -> JsonResult { "Kdf": user.client_kdf_type, "KdfIterations": user.client_kdf_iter, - "KdfMemory": user.client_kdf_memory, - "KdfParallelism": user.client_kdf_parallelism, "ResetMasterPassword": false, // TODO: according to official server seems something like: user.password_hash.is_empty(), but would need testing "scope": scope, "unofficialServer": true, - }); - - Ok(Json(result)) + }))) } async fn _password_login( @@ -145,7 +130,7 @@ async fn _password_login( // Get the user let username = data.username.as_ref().unwrap().trim(); - let mut user = match User::find_by_mail(username, conn).await { + let user = match User::find_by_mail(username, conn).await { Some(user) => user, None => err!("Username or password is incorrect. Try again", format!("IP: {}. Username: {}.", ip.ip, username)), }; @@ -165,16 +150,6 @@ async fn _password_login( ) } - // Change the KDF Iterations - if user.password_iterations != CONFIG.password_iterations() { - user.password_iterations = CONFIG.password_iterations(); - user.set_password(password, None, false, None); - - if let Err(e) = user.save(conn).await { - error!("Error updating user: {:#?}", e); - } - } - // Check if the user is disabled if !user.enabled { err!( @@ -197,6 +172,7 @@ async fn _password_login( if resend_limit == 0 || user.login_verify_count < resend_limit { // We want to send another email verification if we require signups to verify // their email address, and we haven't sent them a reminder in a while... + let mut user = user; user.last_verifying_at = Some(now); user.login_verify_count += 1; @@ -255,8 +231,6 @@ async fn _password_login( "Kdf": user.client_kdf_type, "KdfIterations": user.client_kdf_iter, - "KdfMemory": user.client_kdf_memory, - "KdfParallelism": user.client_kdf_parallelism, "ResetMasterPassword": false,// TODO: Same as above "scope": scope, "unofficialServer": true, @@ -350,7 +324,7 @@ async fn _api_key_login( // Note: No refresh_token is returned. The CLI just repeats the // client_credentials login flow when the existing token expires. - let result = json!({ + Ok(Json(json!({ "access_token": access_token, "expires_in": expires_in, "token_type": "Bearer", @@ -359,14 +333,10 @@ async fn _api_key_login( "Kdf": user.client_kdf_type, "KdfIterations": user.client_kdf_iter, - "KdfMemory": user.client_kdf_memory, - "KdfParallelism": user.client_kdf_parallelism, "ResetMasterPassword": false, // TODO: Same as above "scope": scope, "unofficialServer": true, - }); - - Ok(Json(result)) + }))) } /// Retrieves an existing device or creates a new device from ConnectData and the User diff --git a/src/api/notifications.rs b/src/api/notifications.rs index 2c96f0e4f29..b51e13800ad 100644 --- a/src/api/notifications.rs +++ b/src/api/notifications.rs @@ -10,7 +10,8 @@ use std::{ use chrono::NaiveDateTime; use futures::{SinkExt, StreamExt}; use rmpv::Value; -use rocket::Route; +use rocket::{serde::json::Json, Route}; +use serde_json::Value as JsonValue; use tokio::{ net::{TcpListener, TcpStream}, sync::mpsc::Sender, @@ -22,12 +23,13 @@ use tokio_tungstenite::{ use crate::{ api::EmptyResult, + auth::Headers, db::models::{Cipher, Folder, Send, User}, Error, CONFIG, }; pub fn routes() -> Vec { - routes![websockets_err] + routes![negotiate, websockets_err] } #[get("/hub")] @@ -49,6 +51,29 @@ fn websockets_err() -> EmptyResult { } } +#[post("/hub/negotiate")] +fn negotiate(_headers: Headers) -> Json { + use crate::crypto; + use data_encoding::BASE64URL; + + let conn_id = crypto::encode_random_bytes::<16>(BASE64URL); + let mut available_transports: Vec = Vec::new(); + + if CONFIG.websocket_enabled() { + available_transports.push(json!({"transport":"WebSockets", "transferFormats":["Text","Binary"]})); + } + + // TODO: Implement transports + // Rocket WS support: https://github.com/SergioBenitez/Rocket/issues/90 + // Rocket SSE support: https://github.com/SergioBenitez/Rocket/issues/33 + // {"transport":"ServerSentEvents", "transferFormats":["Text"]}, + // {"transport":"LongPolling", "transferFormats":["Text","Binary"]} + Json(json!({ + "connectionId": conn_id, + "availableTransports": available_transports + })) +} + // // Websockets server // @@ -145,16 +170,6 @@ impl WebSocketUsers { self.send_update(&user.uuid, &data).await; } - pub async fn send_logout(&self, user: &User, acting_device_uuid: Option) { - let data = create_update( - vec![("UserId".into(), user.uuid.clone().into()), ("Date".into(), serialize_date(user.updated_at))], - UpdateType::LogOut, - acting_device_uuid, - ); - - self.send_update(&user.uuid, &data).await; - } - pub async fn send_folder_update(&self, ut: UpdateType, folder: &Folder, acting_device_uuid: &String) { let data = create_update( vec![ diff --git a/src/api/web.rs b/src/api/web.rs index f7fbeec4a73..6e3921ed51d 100644 --- a/src/api/web.rs +++ b/src/api/web.rs @@ -4,7 +4,7 @@ use rocket::{fs::NamedFile, http::ContentType, response::content::RawHtml as Htm use serde_json::Value; use crate::{ - api::{core::now, ApiResult, EmptyResult}, + api::{core::now, ApiResult}, error::Error, util::{Cached, SafeString}, CONFIG, @@ -14,9 +14,9 @@ pub fn routes() -> Vec { // If addding more routes here, consider also adding them to // crate::utils::LOGGED_ROUTES to make sure they appear in the log if CONFIG.web_vault_enabled() { - routes![web_index, web_index_head, app_id, web_files, attachments, alive, alive_head, static_files] + routes![web_index, app_id, web_files, attachments, alive, static_files] } else { - routes![attachments, alive, alive_head, static_files] + routes![attachments, alive, static_files] } } @@ -43,17 +43,6 @@ async fn web_index() -> Cached> { Cached::short(NamedFile::open(Path::new(&CONFIG.web_vault_folder()).join("index.html")).await.ok(), false) } -#[head("/")] -fn web_index_head() -> EmptyResult { - // Add an explicit HEAD route to prevent uptime monitoring services from - // generating "No matching routes for HEAD /" error messages. - // - // Rocket automatically implements a HEAD route when there's a matching GET - // route, but relying on this behavior also means a spurious error gets - // logged due to . - Ok(()) -} - #[get("/app-id.json")] fn app_id() -> Cached<(ContentType, Json)> { let content_type = ContentType::new("application", "fido.trusted-apps+json"); @@ -103,13 +92,6 @@ fn alive(_conn: DbConn) -> Json { now() } -#[head("/alive")] -fn alive_head(_conn: DbConn) -> EmptyResult { - // Avoid logging spurious "No matching routes for HEAD /alive" errors - // due to . - Ok(()) -} - #[get("/vw_static/")] pub fn static_files(filename: String) -> Result<(ContentType, &'static [u8]), Error> { match filename.as_ref() { @@ -136,8 +118,8 @@ pub fn static_files(filename: String) -> Result<(ContentType, &'static [u8]), Er "jdenticon.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jdenticon.js"))), "datatables.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/datatables.js"))), "datatables.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/datatables.css"))), - "jquery-3.6.3.slim.js" => { - Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.6.3.slim.js"))) + "jquery-3.6.2.slim.js" => { + Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.6.2.slim.js"))) } _ => err!(format!("Static file not found: {filename}")), } diff --git a/src/auth.rs b/src/auth.rs index b1b68af5036..03f14cb8895 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -241,7 +241,7 @@ pub fn generate_admin_claims() -> BasicJwtClaims { let time_now = Utc::now().naive_utc(); BasicJwtClaims { nbf: time_now.timestamp(), - exp: (time_now + Duration::minutes(CONFIG.admin_session_lifetime())).timestamp(), + exp: (time_now + Duration::minutes(20)).timestamp(), iss: JWT_ADMIN_ISSUER.to_string(), sub: "admin_panel".to_string(), } @@ -318,7 +318,6 @@ impl<'r> FromRequest<'r> for Host { pub struct ClientHeaders { pub host: String, pub device_type: i32, - pub ip: ClientIp, } #[rocket::async_trait] @@ -327,10 +326,6 @@ impl<'r> FromRequest<'r> for ClientHeaders { async fn from_request(request: &'r Request<'_>) -> Outcome { let host = try_outcome!(Host::from_request(request).await).host; - let ip = match ClientIp::from_request(request).await { - Outcome::Success(ip) => ip, - _ => err_handler!("Error getting Client IP"), - }; // When unknown or unable to parse, return 14, which is 'Unknown Browser' let device_type: i32 = request.headers().get_one("device-type").map(|d| d.parse().unwrap_or(14)).unwrap_or_else(|| 14); @@ -338,7 +333,6 @@ impl<'r> FromRequest<'r> for ClientHeaders { Outcome::Success(ClientHeaders { host, device_type, - ip, }) } } @@ -347,7 +341,6 @@ pub struct Headers { pub host: String, pub device: Device, pub user: User, - pub ip: ClientIp, } #[rocket::async_trait] @@ -358,10 +351,6 @@ impl<'r> FromRequest<'r> for Headers { let headers = request.headers(); let host = try_outcome!(Host::from_request(request).await).host; - let ip = match ClientIp::from_request(request).await { - Outcome::Success(ip) => ip, - _ => err_handler!("Error getting Client IP"), - }; // Get access_token let access_token: &str = match headers.get_one("Authorization") { @@ -431,7 +420,6 @@ impl<'r> FromRequest<'r> for Headers { host, device, user, - ip, }) } } @@ -443,7 +431,6 @@ pub struct OrgHeaders { pub org_user_type: UserOrgType, pub org_user: UserOrganization, pub org_id: String, - pub ip: ClientIp, } // org_id is usually the second path param ("/organizations/"), @@ -504,7 +491,6 @@ impl<'r> FromRequest<'r> for OrgHeaders { }, org_user, org_id, - ip: headers.ip, }) } _ => err_handler!("Error getting the organization id"), @@ -518,7 +504,6 @@ pub struct AdminHeaders { pub user: User, pub org_user_type: UserOrgType, pub client_version: Option, - pub ip: ClientIp, } #[rocket::async_trait] @@ -535,7 +520,6 @@ impl<'r> FromRequest<'r> for AdminHeaders { user: headers.user, org_user_type: headers.org_user_type, client_version, - ip: headers.ip, }) } else { err_handler!("You need to be Admin or Owner to call this endpoint") @@ -549,7 +533,6 @@ impl From for Headers { host: h.host, device: h.device, user: h.user, - ip: h.ip, } } } @@ -581,7 +564,6 @@ pub struct ManagerHeaders { pub device: Device, pub user: User, pub org_user_type: UserOrgType, - pub ip: ClientIp, } #[rocket::async_trait] @@ -598,7 +580,14 @@ impl<'r> FromRequest<'r> for ManagerHeaders { _ => err_handler!("Error getting DB"), }; - if !can_access_collection(&headers.org_user, &col_id, &mut conn).await { + if !headers.org_user.has_full_access() + && !Collection::has_access_by_collection_and_user_uuid( + &col_id, + &headers.org_user.user_uuid, + &mut conn, + ) + .await + { err_handler!("The current user isn't a manager for this collection") } } @@ -610,7 +599,6 @@ impl<'r> FromRequest<'r> for ManagerHeaders { device: headers.device, user: headers.user, org_user_type: headers.org_user_type, - ip: headers.ip, }) } else { err_handler!("You need to be a Manager, Admin or Owner to call this endpoint") @@ -624,7 +612,6 @@ impl From for Headers { host: h.host, device: h.device, user: h.user, - ip: h.ip, } } } @@ -635,9 +622,7 @@ pub struct ManagerHeadersLoose { pub host: String, pub device: Device, pub user: User, - pub org_user: UserOrganization, pub org_user_type: UserOrgType, - pub ip: ClientIp, } #[rocket::async_trait] @@ -651,9 +636,7 @@ impl<'r> FromRequest<'r> for ManagerHeadersLoose { host: headers.host, device: headers.device, user: headers.user, - org_user: headers.org_user, org_user_type: headers.org_user_type, - ip: headers.ip, }) } else { err_handler!("You need to be a Manager, Admin or Owner to call this endpoint") @@ -667,45 +650,14 @@ impl From for Headers { host: h.host, device: h.device, user: h.user, - ip: h.ip, } } } -async fn can_access_collection(org_user: &UserOrganization, col_id: &str, conn: &mut DbConn) -> bool { - org_user.has_full_access() - || Collection::has_access_by_collection_and_user_uuid(col_id, &org_user.user_uuid, conn).await -} - -impl ManagerHeaders { - pub async fn from_loose( - h: ManagerHeadersLoose, - collections: &Vec, - conn: &mut DbConn, - ) -> Result { - for col_id in collections { - if uuid::Uuid::parse_str(col_id).is_err() { - err!("Collection Id is malformed!"); - } - if !can_access_collection(&h.org_user, col_id, conn).await { - err!("You don't have access to all collections!"); - } - } - - Ok(ManagerHeaders { - host: h.host, - device: h.device, - user: h.user, - org_user_type: h.org_user_type, - ip: h.ip, - }) - } -} pub struct OwnerHeaders { pub host: String, pub device: Device, pub user: User, - pub ip: ClientIp, } #[rocket::async_trait] @@ -719,7 +671,6 @@ impl<'r> FromRequest<'r> for OwnerHeaders { host: headers.host, device: headers.device, user: headers.user, - ip: headers.ip, }) } else { err_handler!("You need to be Owner to call this endpoint") diff --git a/src/config.rs b/src/config.rs index 6ed19a79819..fa8bea66df9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,3 @@ -use std::env::consts::EXE_SUFFIX; use std::process::exit; use std::sync::RwLock; @@ -19,7 +18,7 @@ static CONFIG_FILE: Lazy = Lazy::new(|| { pub static CONFIG: Lazy = Lazy::new(|| { Config::load().unwrap_or_else(|e| { - println!("Error loading config:\n {e:?}\n"); + println!("Error loading config:\n\t{e:?}\n"); exit(12) }) }); @@ -142,8 +141,6 @@ macro_rules! make_config { )+)+ config.domain_set = _domain_set; - config.domain = config.domain.trim_end_matches('/').to_string(); - config.signups_domains_whitelist = config.signups_domains_whitelist.trim().to_lowercase(); config.org_creation_users = config.org_creation_users.trim().to_lowercase(); @@ -466,9 +463,9 @@ make_config! { invitation_expiration_hours: u32, false, def, 120; /// Allow emergency access |> Controls whether users can enable emergency access to their accounts. This setting applies globally to all users. emergency_access_allowed: bool, true, def, true; - /// Password iterations |> Number of server-side passwords hashing iterations for the password hash. - /// The default for new users. If changed, it will be updated during login for existing users. - password_iterations: i32, true, def, 600_000; + /// Password iterations |> Number of server-side passwords hashing iterations. + /// The changes only apply when a user changes their password. Not recommended to lower the value + password_iterations: i32, true, def, 100_000; /// Allow password hints |> Controls whether users can set password hints. This setting applies globally to all users. password_hints_allowed: bool, true, def, true; /// Show password hint |> Controls whether a password hint should be shown directly in the web page @@ -581,9 +578,6 @@ make_config! { /// Max burst size for admin login requests |> Allow a burst of requests of up to this size, while maintaining the average indicated by `admin_ratelimit_seconds` admin_ratelimit_max_burst: u32, false, def, 3; - /// Admin session lifetime |> Set the lifetime of admin sessions to this value (in minutes). - admin_session_lifetime: i64, true, def, 20; - /// Enable groups (BETA!) (Know the risks!) |> Enables groups support for organizations (Currently contains known issues!). org_groups_enabled: bool, false, def, false; }, @@ -618,10 +612,6 @@ make_config! { smtp: _enable_smtp { /// Enabled _enable_smtp: bool, true, def, true; - /// Use Sendmail |> Whether to send mail via the `sendmail` command - use_sendmail: bool, true, def, false; - /// Sendmail Command |> Which sendmail command to use. The one found in the $PATH is used if not specified. - sendmail_command: String, true, option; /// Host smtp_host: String, true, option; /// DEPRECATED smtp_ssl |> DEPRECATED - Please use SMTP_SECURITY @@ -661,7 +651,7 @@ make_config! { /// Email 2FA Settings email_2fa: _enable_email_2fa { /// Enabled |> Disabling will prevent users from setting up new email 2FA and using existing email 2FA configured - _enable_email_2fa: bool, true, auto, |c| c._enable_smtp && (c.smtp_host.is_some() || c.use_sendmail); + _enable_email_2fa: bool, true, auto, |c| c._enable_smtp && c.smtp_host.is_some(); /// Email token size |> Number of digits in an email 2FA token (min: 6, max: 255). Note that the Bitwarden clients are hardcoded to mention 6 digit codes regardless of this setting. email_token_size: u8, true, def, 6; /// Token expiration time |> Maximum time in seconds a token is valid. The time the user has to open email client and copy token. @@ -683,10 +673,6 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { } } - if cfg.password_iterations < 100_000 { - err!("PASSWORD_ITERATIONS should be at least 100000 or higher. The default is 600000!"); - } - let limit = 256; if cfg.database_max_conns < 1 || cfg.database_max_conns > limit { err!(format!("`DATABASE_MAX_CONNS` contains an invalid value. Ensure it is between 1 and {limit}.",)); @@ -752,51 +738,20 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { ), } - if cfg.use_sendmail { - let command = cfg.sendmail_command.clone().unwrap_or_else(|| format!("sendmail{EXE_SUFFIX}")); - - let mut path = std::path::PathBuf::from(&command); - - if !path.is_absolute() { - match which::which(&command) { - Ok(result) => path = result, - Err(_) => err!(format!("sendmail command {command:?} not found in $PATH")), - } - } - - match path.metadata() { - Err(err) if err.kind() == std::io::ErrorKind::NotFound => { - err!(format!("sendmail command not found at `{path:?}`")) - } - Err(err) => { - err!(format!("failed to access sendmail command at `{path:?}`: {err}")) - } - Ok(metadata) => { - if metadata.is_dir() { - err!(format!("sendmail command at `{path:?}` isn't a directory")); - } + if cfg.smtp_host.is_some() == cfg.smtp_from.is_empty() { + err!("Both `SMTP_HOST` and `SMTP_FROM` need to be set for email support") + } - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - if !metadata.permissions().mode() & 0o111 != 0 { - err!(format!("sendmail command at `{path:?}` isn't executable")); - } - } - } - } - } else { - if cfg.smtp_host.is_some() == cfg.smtp_from.is_empty() { - err!("Both `SMTP_HOST` and `SMTP_FROM` need to be set for email support without `USE_SENDMAIL`") - } + if cfg.smtp_host.is_some() && !cfg.smtp_from.contains('@') { + err!("SMTP_FROM does not contain a mandatory @ sign") + } - if cfg.smtp_username.is_some() != cfg.smtp_password.is_some() { - err!("Both `SMTP_USERNAME` and `SMTP_PASSWORD` need to be set to enable email authentication without `USE_SENDMAIL`") - } + if cfg.smtp_username.is_some() != cfg.smtp_password.is_some() { + err!("Both `SMTP_USERNAME` and `SMTP_PASSWORD` need to be set to enable email authentication") } - if (cfg.smtp_host.is_some() || cfg.use_sendmail) && !cfg.smtp_from.contains('@') { - err!("SMTP_FROM does not contain a mandatory @ sign") + if cfg._enable_email_2fa && (!cfg._enable_smtp || cfg.smtp_host.is_none()) { + err!("To enable email 2FA, SMTP must be configured") } if cfg._enable_email_2fa && cfg.email_token_size < 6 { @@ -804,10 +759,6 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { } } - if cfg._enable_email_2fa && !(cfg.smtp_host.is_some() || cfg.use_sendmail) { - err!("To enable email 2FA, a mail transport must be configured") - } - // Check if the icon blacklist regex is valid if let Some(ref r) = cfg.icon_blacklist_regex { let validate_regex = regex::Regex::new(r); @@ -872,23 +823,6 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { err!("`EVENT_CLEANUP_SCHEDULE` is not a valid cron expression") } - if !cfg.disable_admin_token { - match cfg.admin_token.as_ref() { - Some(t) if t.starts_with("$argon2") => { - if let Err(e) = argon2::password_hash::PasswordHash::new(t) { - err!(format!("The configured Argon2 PHC in `ADMIN_TOKEN` is invalid: '{e}'")) - } - } - Some(_) => { - println!( - "[NOTICE] You are using a plain text `ADMIN_TOKEN` which is insecure.\n\ - Please generate a secure Argon2 PHC string by using `vaultwarden hash` or `argon2`.\n\ - See: https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page#secure-the-admin_token\n" - ); - } - _ => {} - } - } Ok(()) } @@ -1105,7 +1039,7 @@ impl Config { } pub fn mail_enabled(&self) -> bool { let inner = &self.inner.read().unwrap().config; - inner._enable_smtp && (inner.smtp_host.is_some() || inner.use_sendmail) + inner._enable_smtp && inner.smtp_host.is_some() } pub fn get_duo_akey(&self) -> String { @@ -1198,7 +1132,6 @@ where reg!("email/email_footer"); reg!("email/email_footer_text"); - reg!("email/admin_reset_password", ".html"); reg!("email/change_email", ".html"); reg!("email/delete_account", ".html"); reg!("email/emergency_access_invite_accepted", ".html"); @@ -1289,7 +1222,7 @@ fn to_json<'reg, 'rc>( ) -> HelperResult { let param = h.param(0).ok_or_else(|| RenderError::new("Expected 1 parameter for \"to_json\""))?.value(); let json = serde_json::to_string(param) - .map_err(|e| RenderError::new(format!("Can't serialize parameter to JSON: {e}")))?; + .map_err(|e| RenderError::new(format!("Can't serialize parameter to JSON: {}", e)))?; out.write(&json)?; Ok(()) } diff --git a/src/db/models/attachment.rs b/src/db/models/attachment.rs index e850537da87..325b9a2773f 100644 --- a/src/db/models/attachment.rs +++ b/src/db/models/attachment.rs @@ -187,15 +187,10 @@ impl Attachment { }} } - // This will return all attachments linked to the user or org - // There is no filtering done here if the user actually has access! - // It is used to speed up the sync process, and the matching is done in a different part. - pub async fn find_all_by_user_and_orgs(user_uuid: &str, org_uuids: &Vec, conn: &mut DbConn) -> Vec { + pub async fn find_all_by_ciphers(cipher_uuids: &Vec, conn: &mut DbConn) -> Vec { db_run! { conn: { attachments::table - .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) - .filter(ciphers::user_uuid.eq(user_uuid)) - .or_filter(ciphers::organization_uuid.eq_any(org_uuids)) + .filter(attachments::cipher_uuid.eq_any(cipher_uuids)) .select(attachments::all_columns) .load::(conn) .expect("Error loading attachments") diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index 9e324b7ba5b..b7d26bd3d7c 100644 --- a/src/db/models/cipher.rs +++ b/src/db/models/cipher.rs @@ -6,7 +6,7 @@ use super::{ Attachment, CollectionCipher, Favorite, FolderCipher, Group, User, UserOrgStatus, UserOrgType, UserOrganization, }; -use crate::api::core::{CipherData, CipherSyncData, CipherSyncType}; +use crate::api::core::{CipherData, CipherSyncData}; use std::borrow::Cow; @@ -114,7 +114,6 @@ impl Cipher { host: &str, user_uuid: &str, cipher_sync_data: Option<&CipherSyncData>, - sync_type: CipherSyncType, conn: &mut DbConn, ) -> Value { use crate::util::format_date; @@ -135,24 +134,17 @@ impl Cipher { let password_history_json = self.password_history.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or(Value::Null); - // We don't need these values at all for Organizational syncs - // Skip any other database calls if this is the case and just return false. - let (read_only, hide_passwords) = if sync_type == CipherSyncType::User { - match self.get_access_restrictions(user_uuid, cipher_sync_data, conn).await { - Some((ro, hp)) => (ro, hp), - None => { - error!("Cipher ownership assertion failure"); - (true, true) - } + let (read_only, hide_passwords) = match self.get_access_restrictions(user_uuid, cipher_sync_data, conn).await { + Some((ro, hp)) => (ro, hp), + None => { + error!("Cipher ownership assertion failure"); + (true, true) } - } else { - (false, false) }; // Get the type_data or a default to an empty json object '{}'. // If not passing an empty object, mobile clients will crash. - let mut type_data_json: Value = - serde_json::from_str(&self.data).unwrap_or_else(|_| Value::Object(serde_json::Map::new())); + let mut type_data_json: Value = serde_json::from_str(&self.data).unwrap_or_else(|_| json!({})); // NOTE: This was marked as *Backwards Compatibility Code*, but as of January 2021 this is still being used by upstream // Set the first element of the Uris array as Uri, this is needed several (mobile) clients. @@ -171,10 +163,10 @@ impl Cipher { // NOTE: This was marked as *Backwards Compatibility Code*, but as of January 2021 this is still being used by upstream // data_json should always contain the following keys with every atype - data_json["Fields"] = fields_json.clone(); + data_json["Fields"] = json!(fields_json); data_json["Name"] = json!(self.name); data_json["Notes"] = json!(self.notes); - data_json["PasswordHistory"] = password_history_json.clone(); + data_json["PasswordHistory"] = json!(password_history_json); let collection_ids = if let Some(cipher_sync_data) = cipher_sync_data { if let Some(cipher_collections) = cipher_sync_data.cipher_collections.get(&self.uuid) { @@ -200,6 +192,8 @@ impl Cipher { "CreationDate": format_date(&self.created_at), "RevisionDate": format_date(&self.updated_at), "DeletedDate": self.deleted_at.map_or(Value::Null, |d| Value::String(format_date(&d))), + "FolderId": if let Some(cipher_sync_data) = cipher_sync_data { cipher_sync_data.cipher_folders.get(&self.uuid).map(|c| c.to_string() ) } else { self.get_folder_uuid(user_uuid, conn).await }, + "Favorite": if let Some(cipher_sync_data) = cipher_sync_data { cipher_sync_data.cipher_favorites.contains(&self.uuid) } else { self.is_favorite(user_uuid, conn).await }, "Reprompt": self.reprompt.unwrap_or(RepromptType::None as i32), "OrganizationId": self.organization_uuid, "Attachments": attachments_json, @@ -216,6 +210,12 @@ impl Cipher { "Data": data_json, + // These values are true by default, but can be false if the + // cipher belongs to a collection where the org owner has enabled + // the "Read Only" or "Hide Passwords" restrictions for the user. + "Edit": !read_only, + "ViewPassword": !hide_passwords, + "PasswordHistory": password_history_json, // All Cipher types are included by default as null, but only the matching one will be populated @@ -225,27 +225,6 @@ impl Cipher { "Identity": null, }); - // These values are only needed for user/default syncs - // Not during an organizational sync like `get_org_details` - // Skip adding these fields in that case - if sync_type == CipherSyncType::User { - json_object["FolderId"] = json!(if let Some(cipher_sync_data) = cipher_sync_data { - cipher_sync_data.cipher_folders.get(&self.uuid).map(|c| c.to_string()) - } else { - self.get_folder_uuid(user_uuid, conn).await - }); - json_object["Favorite"] = json!(if let Some(cipher_sync_data) = cipher_sync_data { - cipher_sync_data.cipher_favorites.contains(&self.uuid) - } else { - self.is_favorite(user_uuid, conn).await - }); - // These values are true by default, but can be false if the - // cipher belongs to a collection or group where the org owner has enabled - // the "Read Only" or "Hide Passwords" restrictions for the user. - json_object["Edit"] = json!(!read_only); - json_object["ViewPassword"] = json!(!hide_passwords); - } - let key = match self.atype { 1 => "Login", 2 => "SecureNote", @@ -761,7 +740,6 @@ impl Cipher { .or_filter(groups::access_all.eq(true)) //Access via group .or_filter(collections_groups::collections_uuid.is_not_null()) //Access via group .select(ciphers_collections::all_columns) - .distinct() .load::<(String, String)>(conn).unwrap_or_default() }} } diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index 6a9acab84b3..eba0ffee319 100644 --- a/src/db/models/collection.rs +++ b/src/db/models/collection.rs @@ -64,8 +64,6 @@ impl Collection { Some(_) => { if let Some(uc) = cipher_sync_data.user_collections.get(&self.uuid) { (uc.read_only, uc.hide_passwords) - } else if let Some(cg) = cipher_sync_data.user_collections_groups.get(&self.uuid) { - (cg.read_only, cg.hide_passwords) } else { (false, false) } @@ -234,17 +232,6 @@ impl Collection { }} } - pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { - db_run! { conn: { - collections::table - .filter(collections::org_uuid.eq(org_uuid)) - .count() - .first::(conn) - .ok() - .unwrap_or(0) - }} - } - pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { collections::table @@ -300,95 +287,47 @@ impl Collection { } pub async fn is_writable_by_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { - let user_uuid = user_uuid.to_string(); - db_run! { conn: { - collections::table - .left_join(users_collections::table.on( - users_collections::collection_uuid.eq(collections::uuid).and( - users_collections::user_uuid.eq(user_uuid.clone()) - ) - )) - .left_join(users_organizations::table.on( - collections::org_uuid.eq(users_organizations::org_uuid).and( - users_organizations::user_uuid.eq(user_uuid) - ) - )) - .left_join(groups_users::table.on( - groups_users::users_organizations_uuid.eq(users_organizations::uuid) - )) - .left_join(groups::table.on( - groups::uuid.eq(groups_users::groups_uuid) - )) - .left_join(collections_groups::table.on( - collections_groups::groups_uuid.eq(groups_users::groups_uuid).and( - collections_groups::collections_uuid.eq(collections::uuid) - ) - )) - .filter(collections::uuid.eq(&self.uuid)) - .filter( - users_collections::collection_uuid.eq(&self.uuid).and(users_collections::read_only.eq(false)).or(// Directly accessed collection - users_organizations::access_all.eq(true).or( // access_all in Organization - users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner - )).or( - groups::access_all.eq(true) // access_all in groups - ).or( // access via groups - groups_users::users_organizations_uuid.eq(users_organizations::uuid).and( - collections_groups::collections_uuid.is_not_null().and( - collections_groups::read_only.eq(false)) - ) - ) - ) - .count() - .first::(conn) - .ok() - .unwrap_or(0) != 0 - }} + match UserOrganization::find_by_user_and_org(user_uuid, &self.org_uuid, conn).await { + None => false, // Not in Org + Some(user_org) => { + if user_org.has_full_access() { + return true; + } + + db_run! { conn: { + users_collections::table + .filter(users_collections::collection_uuid.eq(&self.uuid)) + .filter(users_collections::user_uuid.eq(user_uuid)) + .filter(users_collections::read_only.eq(false)) + .count() + .first::(conn) + .ok() + .unwrap_or(0) != 0 + }} + } + } } pub async fn hide_passwords_for_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { - let user_uuid = user_uuid.to_string(); - db_run! { conn: { - collections::table - .left_join(users_collections::table.on( - users_collections::collection_uuid.eq(collections::uuid).and( - users_collections::user_uuid.eq(user_uuid.clone()) - ) - )) - .left_join(users_organizations::table.on( - collections::org_uuid.eq(users_organizations::org_uuid).and( - users_organizations::user_uuid.eq(user_uuid) - ) - )) - .left_join(groups_users::table.on( - groups_users::users_organizations_uuid.eq(users_organizations::uuid) - )) - .left_join(groups::table.on( - groups::uuid.eq(groups_users::groups_uuid) - )) - .left_join(collections_groups::table.on( - collections_groups::groups_uuid.eq(groups_users::groups_uuid).and( - collections_groups::collections_uuid.eq(collections::uuid) - ) - )) - .filter(collections::uuid.eq(&self.uuid)) - .filter( - users_collections::collection_uuid.eq(&self.uuid).and(users_collections::hide_passwords.eq(true)).or(// Directly accessed collection - users_organizations::access_all.eq(true).or( // access_all in Organization - users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner - )).or( - groups::access_all.eq(true) // access_all in groups - ).or( // access via groups - groups_users::users_organizations_uuid.eq(users_organizations::uuid).and( - collections_groups::collections_uuid.is_not_null().and( - collections_groups::hide_passwords.eq(true)) - ) - ) - ) - .count() - .first::(conn) - .ok() - .unwrap_or(0) != 0 - }} + match UserOrganization::find_by_user_and_org(user_uuid, &self.org_uuid, conn).await { + None => true, // Not in Org + Some(user_org) => { + if user_org.has_full_access() { + return false; + } + + db_run! { conn: { + users_collections::table + .filter(users_collections::collection_uuid.eq(&self.uuid)) + .filter(users_collections::user_uuid.eq(user_uuid)) + .filter(users_collections::hide_passwords.eq(true)) + .count() + .first::(conn) + .ok() + .unwrap_or(0) != 0 + }} + } + } } } @@ -407,19 +346,6 @@ impl CollectionUser { }} } - pub async fn find_by_organization(org_uuid: &str, conn: &mut DbConn) -> Vec { - db_run! { conn: { - users_collections::table - .inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid))) - .filter(collections::org_uuid.eq(org_uuid)) - .inner_join(users_organizations::table.on(users_organizations::user_uuid.eq(users_collections::user_uuid))) - .select((users_organizations::uuid, users_collections::collection_uuid, users_collections::read_only, users_collections::hide_passwords)) - .load::(conn) - .expect("Error loading users_collections") - .from_db() - }} - } - pub async fn save( user_uuid: &str, collection_uuid: &str, @@ -503,21 +429,6 @@ impl CollectionUser { }} } - pub async fn find_by_collection_swap_user_uuid_with_org_user_uuid( - collection_uuid: &str, - conn: &mut DbConn, - ) -> Vec { - db_run! { conn: { - users_collections::table - .filter(users_collections::collection_uuid.eq(collection_uuid)) - .inner_join(users_organizations::table.on(users_organizations::user_uuid.eq(users_collections::user_uuid))) - .select((users_organizations::uuid, users_collections::collection_uuid, users_collections::read_only, users_collections::hide_passwords)) - .load::(conn) - .expect("Error loading users_collections") - .from_db() - }} - } - pub async fn find_by_collection_and_user( collection_uuid: &str, user_uuid: &str, diff --git a/src/db/models/event.rs b/src/db/models/event.rs index af2f6c66ba2..9196b8a81aa 100644 --- a/src/db/models/event.rs +++ b/src/db/models/event.rs @@ -87,9 +87,9 @@ pub enum EventType { OrganizationUserRemoved = 1503, OrganizationUserUpdatedGroups = 1504, // OrganizationUserUnlinkedSso = 1505, // Not supported - OrganizationUserResetPasswordEnroll = 1506, - OrganizationUserResetPasswordWithdraw = 1507, - OrganizationUserAdminResetPassword = 1508, + // OrganizationUserResetPasswordEnroll = 1506, // Not supported + // OrganizationUserResetPasswordWithdraw = 1507, // Not supported + // OrganizationUserAdminResetPassword = 1508, // Not supported // OrganizationUserResetSsoLink = 1509, // Not supported // OrganizationUserFirstSsoLogin = 1510, // Not supported OrganizationUserRevoked = 1511, @@ -263,17 +263,6 @@ impl Event { }} } - pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { - db_run! { conn: { - event::table - .filter(event::org_uuid.eq(org_uuid)) - .count() - .first::(conn) - .ok() - .unwrap_or(0) - }} - } - pub async fn find_by_org_and_user_org( org_uuid: &str, user_org_uuid: &str, diff --git a/src/db/models/group.rs b/src/db/models/group.rs index 258b9e42a15..1d2e6062aaf 100644 --- a/src/db/models/group.rs +++ b/src/db/models/group.rs @@ -64,32 +64,7 @@ impl Group { "AccessAll": self.access_all, "ExternalId": self.external_id, "CreationDate": format_date(&self.creation_date), - "RevisionDate": format_date(&self.revision_date), - "Object": "group" - }) - } - - pub async fn to_json_details(&self, conn: &mut DbConn) -> Value { - let collections_groups: Vec = CollectionGroup::find_by_group(&self.uuid, conn) - .await - .iter() - .map(|entry| { - json!({ - "Id": entry.collections_uuid, - "ReadOnly": entry.read_only, - "HidePasswords": entry.hide_passwords - }) - }) - .collect(); - - json!({ - "Id": self.uuid, - "OrganizationId": self.organizations_uuid, - "Name": self.name, - "AccessAll": self.access_all, - "ExternalId": self.external_id, - "Collections": collections_groups, - "Object": "groupDetails" + "RevisionDate": format_date(&self.revision_date) }) } @@ -176,13 +151,6 @@ impl Group { } } - pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult { - for group in Self::find_by_organization(org_uuid, conn).await { - group.delete(conn).await?; - } - Ok(()) - } - pub async fn find_by_organization(organizations_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { groups::table @@ -193,17 +161,6 @@ impl Group { }} } - pub async fn count_by_org(organizations_uuid: &str, conn: &mut DbConn) -> i64 { - db_run! { conn: { - groups::table - .filter(groups::organizations_uuid.eq(organizations_uuid)) - .count() - .first::(conn) - .ok() - .unwrap_or(0) - }} - } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { groups::table diff --git a/src/db/models/mod.rs b/src/db/models/mod.rs index 96dc27ce597..274d48e8876 100644 --- a/src/db/models/mod.rs +++ b/src/db/models/mod.rs @@ -28,4 +28,4 @@ pub use self::organization::{Organization, UserOrgStatus, UserOrgType, UserOrgan pub use self::send::{Send, SendType}; pub use self::two_factor::{TwoFactor, TwoFactorType}; pub use self::two_factor_incomplete::TwoFactorIncomplete; -pub use self::user::{Invitation, User, UserKdfType, UserStampException}; +pub use self::user::{Invitation, User, UserStampException}; diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs index c9fd5c34851..caa3335f5c0 100644 --- a/src/db/models/org_policy.rs +++ b/src/db/models/org_policy.rs @@ -32,7 +32,7 @@ pub enum OrgPolicyType { PersonalOwnership = 5, DisableSend = 6, SendOptions = 7, - ResetPassword = 8, + // ResetPassword = 8, // Not supported // MaximumVaultTimeout = 9, // Not supported (Not AGPLv3 Licensed) // DisablePersonalVaultExport = 10, // Not supported (Not AGPLv3 Licensed) } @@ -44,13 +44,6 @@ pub struct SendOptionsPolicyData { pub DisableHideEmail: bool, } -// https://github.com/bitwarden/server/blob/5cbdee137921a19b1f722920f0fa3cd45af2ef0f/src/Core/Models/Data/Organizations/Policies/ResetPasswordDataModel.cs -#[derive(Deserialize)] -#[allow(non_snake_case)] -pub struct ResetPasswordDataModel { - pub AutoEnrollEnabled: bool, -} - pub type OrgPolicyResult = Result<(), OrgPolicyErr>; #[derive(Debug)] @@ -305,20 +298,6 @@ impl OrgPolicy { Ok(()) } - pub async fn org_is_reset_password_auto_enroll(org_uuid: &str, conn: &mut DbConn) -> bool { - match OrgPolicy::find_by_org_and_type(org_uuid, OrgPolicyType::ResetPassword, conn).await { - Some(policy) => match serde_json::from_str::>(&policy.data) { - Ok(opts) => { - return opts.data.AutoEnrollEnabled; - } - _ => error!("Failed to deserialize ResetPasswordDataModel: {}", policy.data), - }, - None => return false, - } - - false - } - /// Returns true if the user belongs to an org that has enabled the `DisableHideEmail` /// option of the `Send Options` policy, and the user is not an owner or admin of that org. pub async fn is_hide_email_disabled(user_uuid: &str, conn: &mut DbConn) -> bool { diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs index e0b92239b92..a2ab75a7956 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -2,7 +2,7 @@ use num_traits::FromPrimitive; use serde_json::Value; use std::cmp::Ordering; -use super::{CollectionUser, Group, GroupUser, OrgPolicy, OrgPolicyType, TwoFactor, User}; +use super::{CollectionUser, GroupUser, OrgPolicy, OrgPolicyType, User}; use crate::CONFIG; db_object! { @@ -29,7 +29,6 @@ db_object! { pub akey: String, pub status: i32, pub atype: i32, - pub reset_password_key: Option, } } @@ -159,7 +158,7 @@ impl Organization { "SelfHost": true, "UseApi": false, // Not supported "HasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(), - "UseResetPassword": CONFIG.mail_enabled(), + "UseResetPassword": false, // Not supported "BusinessName": null, "BusinessAddress1": null, @@ -195,7 +194,6 @@ impl UserOrganization { akey: String::new(), status: UserOrgStatus::Accepted as i32, atype: UserOrgType::User as i32, - reset_password_key: None, } } @@ -267,7 +265,6 @@ impl Organization { Collection::delete_all_by_organization(&self.uuid, conn).await?; UserOrganization::delete_all_by_organization(&self.uuid, conn).await?; OrgPolicy::delete_all_by_organization(&self.uuid, conn).await?; - Group::delete_all_by_organization(&self.uuid, conn).await?; db_run! { conn: { diesel::delete(organizations::table.filter(organizations::uuid.eq(self.uuid))) @@ -314,8 +311,7 @@ impl UserOrganization { "UseApi": false, // Not supported "SelfHost": true, "HasPublicAndPrivateKeys": org.private_key.is_some() && org.public_key.is_some(), - "ResetPasswordEnrolled": self.reset_password_key.is_some(), - "UseResetPassword": CONFIG.mail_enabled(), + "ResetPasswordEnrolled": false, // Not supported "SsoBound": false, // Not supported "UseSso": false, // Not supported "ProviderId": null, @@ -326,7 +322,7 @@ impl UserOrganization { // TODO: Add support for Custom User Roles // See: https://bitwarden.com/help/article/user-types-access-control/#custom-role // "Permissions": { - // "AccessEventLogs": false, + // "AccessEventLogs": false, // Not supported // "AccessImportExport": false, // "AccessReports": false, // "ManageAllCollections": false, @@ -337,9 +333,9 @@ impl UserOrganization { // "editAssignedCollections": false, // "deleteAssignedCollections": false, // "ManageCiphers": false, - // "ManageGroups": false, + // "ManageGroups": false, // Not supported // "ManagePolicies": false, - // "ManageResetPassword": false, + // "ManageResetPassword": false, // Not supported // "ManageSso": false, // Not supported // "ManageUsers": false, // "ManageScim": false, // Not supported (Not AGPLv3 Licensed) @@ -358,12 +354,7 @@ impl UserOrganization { }) } - pub async fn to_json_user_details( - &self, - include_collections: bool, - include_groups: bool, - conn: &mut DbConn, - ) -> Value { + pub async fn to_json_user_details(&self, conn: &mut DbConn) -> Value { let user = User::find_by_uuid(&self.user_uuid, conn).await.unwrap(); // Because BitWarden want the status to be -1 for revoked users we need to catch that here. @@ -374,45 +365,15 @@ impl UserOrganization { self.status }; - let twofactor_enabled = !TwoFactor::find_by_user(&user.uuid, conn).await.is_empty(); - - let groups: Vec = if include_groups && CONFIG.org_groups_enabled() { - GroupUser::find_by_user(&self.uuid, conn).await.iter().map(|gu| gu.groups_uuid.clone()).collect() - } else { - // The Bitwarden clients seem to call this API regardless of whether groups are enabled, - // so just act as if there are no groups. - Vec::with_capacity(0) - }; - - let collections: Vec = if include_collections { - CollectionUser::find_by_organization_and_user_uuid(&self.org_uuid, &self.user_uuid, conn) - .await - .iter() - .map(|cu| { - json!({ - "Id": cu.collection_uuid, - "ReadOnly": cu.read_only, - "HidePasswords": cu.hide_passwords, - }) - }) - .collect() - } else { - Vec::with_capacity(0) - }; - json!({ "Id": self.uuid, "UserId": self.user_uuid, "Name": user.name, "Email": user.email, - "Groups": groups, - "Collections": collections, "Status": status, "Type": self.atype, "AccessAll": self.access_all, - "TwoFactorEnabled": twofactor_enabled, - "ResetPasswordEnrolled":self.reset_password_key.is_some(), "Object": "organizationUserUserDetails", }) @@ -715,7 +676,6 @@ impl UserOrganization { ) ) .select(users_organizations::all_columns) - .distinct() .load::(conn).expect("Error loading user organizations").from_db() }} } diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 83a595246c9..b59f10b8cc4 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -44,12 +44,8 @@ db_object! { pub client_kdf_type: i32, pub client_kdf_iter: i32, - pub client_kdf_memory: Option, - pub client_kdf_parallelism: Option, pub api_key: Option, - - pub avatar_color: Option, } #[derive(Identifiable, Queryable, Insertable)] @@ -60,11 +56,6 @@ db_object! { } } -pub enum UserKdfType { - Pbkdf2 = 0, - Argon2id = 1, -} - enum UserStatus { Enabled = 0, Invited = 1, @@ -80,8 +71,8 @@ pub struct UserStampException { /// Local methods impl User { - pub const CLIENT_KDF_TYPE_DEFAULT: i32 = UserKdfType::Pbkdf2 as i32; - pub const CLIENT_KDF_ITER_DEFAULT: i32 = 600_000; + pub const CLIENT_KDF_TYPE_DEFAULT: i32 = 0; // PBKDF2: 0 + pub const CLIENT_KDF_ITER_DEFAULT: i32 = 100_000; pub fn new(email: String) -> Self { let now = Utc::now().naive_utc(); @@ -120,12 +111,8 @@ impl User { client_kdf_type: Self::CLIENT_KDF_TYPE_DEFAULT, client_kdf_iter: Self::CLIENT_KDF_ITER_DEFAULT, - client_kdf_memory: None, - client_kdf_parallelism: None, api_key: None, - - avatar_color: None, } } @@ -156,31 +143,18 @@ impl User { /// # Arguments /// /// * `password` - A str which contains a hashed version of the users master password. - /// * `new_key` - A String which contains the new aKey value of the users master password. /// * `allow_next_route` - A Option> with the function names of the next allowed (rocket) routes. /// These routes are able to use the previous stamp id for the next 2 minutes. /// After these 2 minutes this stamp will expire. /// - pub fn set_password( - &mut self, - password: &str, - new_key: Option, - reset_security_stamp: bool, - allow_next_route: Option>, - ) { + pub fn set_password(&mut self, password: &str, allow_next_route: Option>) { self.password_hash = crypto::hash_password(password.as_bytes(), &self.salt, self.password_iterations as u32); if let Some(route) = allow_next_route { self.set_stamp_exception(route); } - if let Some(new_key) = new_key { - self.akey = new_key; - } - - if reset_security_stamp { - self.reset_security_stamp() - } + self.reset_security_stamp() } pub fn reset_security_stamp(&mut self) { @@ -252,7 +226,6 @@ impl User { "Providers": [], "ProviderOrganizations": [], "ForcePasswordReset": false, - "AvatarColor": self.avatar_color, "Object": "profile", }) } diff --git a/src/db/schemas/mysql/schema.rs b/src/db/schemas/mysql/schema.rs index da51799a294..0073a9d5de8 100644 --- a/src/db/schemas/mysql/schema.rs +++ b/src/db/schemas/mysql/schema.rs @@ -199,10 +199,7 @@ table! { excluded_globals -> Text, client_kdf_type -> Integer, client_kdf_iter -> Integer, - client_kdf_memory -> Nullable, - client_kdf_parallelism -> Nullable, api_key -> Nullable, - avatar_color -> Nullable, } } @@ -224,7 +221,6 @@ table! { akey -> Text, status -> Integer, atype -> Integer, - reset_password_key -> Nullable, } } diff --git a/src/db/schemas/postgresql/schema.rs b/src/db/schemas/postgresql/schema.rs index aef644921cc..1421513cf33 100644 --- a/src/db/schemas/postgresql/schema.rs +++ b/src/db/schemas/postgresql/schema.rs @@ -199,10 +199,7 @@ table! { excluded_globals -> Text, client_kdf_type -> Integer, client_kdf_iter -> Integer, - client_kdf_memory -> Nullable, - client_kdf_parallelism -> Nullable, api_key -> Nullable, - avatar_color -> Nullable, } } @@ -224,7 +221,6 @@ table! { akey -> Text, status -> Integer, atype -> Integer, - reset_password_key -> Nullable, } } diff --git a/src/db/schemas/sqlite/schema.rs b/src/db/schemas/sqlite/schema.rs index a30b74338f0..0fedcf1dc60 100644 --- a/src/db/schemas/sqlite/schema.rs +++ b/src/db/schemas/sqlite/schema.rs @@ -199,10 +199,7 @@ table! { excluded_globals -> Text, client_kdf_type -> Integer, client_kdf_iter -> Integer, - client_kdf_memory -> Nullable, - client_kdf_parallelism -> Nullable, api_key -> Nullable, - avatar_color -> Nullable, } } @@ -224,7 +221,6 @@ table! { akey -> Text, status -> Integer, atype -> Integer, - reset_password_key -> Nullable, } } diff --git a/src/mail.rs b/src/mail.rs index 565a1f2cd47..8ecb11c61fc 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -8,7 +8,7 @@ use lettre::{ transport::smtp::authentication::{Credentials, Mechanism as SmtpAuthMechanism}, transport::smtp::client::{Tls, TlsParameters}, transport::smtp::extension::ClientId, - Address, AsyncSendmailTransport, AsyncSmtpTransport, AsyncTransport, Tokio1Executor, + Address, AsyncSmtpTransport, AsyncTransport, Tokio1Executor, }; use crate::{ @@ -21,15 +21,7 @@ use crate::{ CONFIG, }; -fn sendmail_transport() -> AsyncSendmailTransport { - if let Some(command) = CONFIG.sendmail_command() { - AsyncSendmailTransport::new_with_command(command) - } else { - AsyncSendmailTransport::new() - } -} - -fn smtp_transport() -> AsyncSmtpTransport { +fn mailer() -> AsyncSmtpTransport { use std::time::Duration; let host = CONFIG.smtp_host().unwrap(); @@ -504,71 +496,6 @@ pub async fn send_test(address: &str) -> EmptyResult { send_email(address, &subject, body_html, body_text).await } -pub async fn send_admin_reset_password(address: &str, user_name: &str, org_name: &str) -> EmptyResult { - let (subject, body_html, body_text) = get_text( - "email/admin_reset_password", - json!({ - "url": CONFIG.domain(), - "img_src": CONFIG._smtp_img_src(), - "user_name": user_name, - "org_name": org_name, - }), - )?; - send_email(address, &subject, body_html, body_text).await -} - -async fn send_with_selected_transport(email: Message) -> EmptyResult { - if CONFIG.use_sendmail() { - match sendmail_transport().send(email).await { - Ok(_) => Ok(()), - // Match some common errors and make them more user friendly - Err(e) => { - if e.is_client() { - debug!("Sendmail client error: {:#?}", e); - err!(format!("Sendmail client error: {e}")); - } else if e.is_response() { - debug!("Sendmail response error: {:#?}", e); - err!(format!("Sendmail response error: {e}")); - } else { - debug!("Sendmail error: {:#?}", e); - err!(format!("Sendmail error: {e}")); - } - } - } - } else { - match smtp_transport().send(email).await { - Ok(_) => Ok(()), - // Match some common errors and make them more user friendly - Err(e) => { - if e.is_client() { - debug!("SMTP client error: {:#?}", e); - err!(format!("SMTP client error: {e}")); - } else if e.is_transient() { - debug!("SMTP 4xx error: {:#?}", e); - err!(format!("SMTP 4xx error: {e}")); - } else if e.is_permanent() { - debug!("SMTP 5xx error: {:#?}", e); - let mut msg = e.to_string(); - // Add a special check for 535 to add a more descriptive message - if msg.contains("(535)") { - msg = format!("{msg} - Authentication credentials invalid"); - } - err!(format!("SMTP 5xx error: {msg}")); - } else if e.is_timeout() { - debug!("SMTP timeout error: {:#?}", e); - err!(format!("SMTP timeout error: {e}")); - } else if e.is_tls() { - debug!("SMTP encryption error: {:#?}", e); - err!(format!("SMTP encryption error: {e}")); - } else { - debug!("SMTP error: {:#?}", e); - err!(format!("SMTP error: {e}")); - } - } - } - } -} - async fn send_email(address: &str, subject: &str, body_html: String, body_text: String) -> EmptyResult { let smtp_from = &CONFIG.smtp_from(); @@ -598,5 +525,34 @@ async fn send_email(address: &str, subject: &str, body_html: String, body_text: .subject(subject) .multipart(body)?; - send_with_selected_transport(email).await + match mailer().send(email).await { + Ok(_) => Ok(()), + // Match some common errors and make them more user friendly + Err(e) => { + if e.is_client() { + debug!("SMTP Client error: {:#?}", e); + err!(format!("SMTP Client error: {e}")); + } else if e.is_transient() { + debug!("SMTP 4xx error: {:#?}", e); + err!(format!("SMTP 4xx error: {e}")); + } else if e.is_permanent() { + debug!("SMTP 5xx error: {:#?}", e); + let mut msg = e.to_string(); + // Add a special check for 535 to add a more descriptive message + if msg.contains("(535)") { + msg = format!("{msg} - Authentication credentials invalid"); + } + err!(format!("SMTP 5xx error: {msg}")); + } else if e.is_timeout() { + debug!("SMTP timeout error: {:#?}", e); + err!(format!("SMTP timeout error: {e}")); + } else if e.is_tls() { + debug!("SMTP Encryption error: {:#?}", e); + err!(format!("SMTP Encryption error: {e}")); + } else { + debug!("SMTP {:#?}", e); + err!(format!("SMTP {e}")); + } + } + } } diff --git a/src/main.rs b/src/main.rs index 8c9b63a77fc..8a5f53dac70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ // The more key/value pairs there are the more recursion occurs. // We want to keep this as low as possible, but not higher then 128. // If you go above 128 it will cause rust-analyzer to fail, -#![recursion_limit = "103"] +#![recursion_limit = "94"] // When enabled use MiMalloc as malloc instead of the default malloc #[cfg(feature = "enable_mimalloc")] @@ -111,29 +111,21 @@ async fn main() -> Result<(), Error> { create_dir(&CONFIG.attachments_folder(), "attachments folder"); let pool = create_db_pool().await; - schedule_jobs(pool.clone()); + schedule_jobs(pool.clone()).await; crate::db::models::TwoFactor::migrate_u2f_to_webauthn(&mut pool.get().await.unwrap()).await.unwrap(); launch_rocket(pool, extra_debug).await // Blocks until program termination. } const HELP: &str = "\ -Alternative implementation of the Bitwarden server API written in Rust + Alternative implementation of the Bitwarden server API written in Rust -USAGE: - vaultwarden [FLAGS|COMMAND] - -FLAGS: - -h, --help Prints help information - -v, --version Prints the app version - -COMMAND: - hash [--preset {bitwarden|owasp}] Generate an Argon2id PHC ADMIN_TOKEN - -PRESETS: m= t= p= - bitwarden (default) 64MiB, 3 Iterations, 4 Threads - owasp 19MiB, 2 Iterations, 1 Thread + USAGE: + vaultwarden + FLAGS: + -h, --help Prints help information + -v, --version Prints the app version "; pub const VERSION: Option<&str> = option_env!("VW_VERSION"); @@ -150,88 +142,24 @@ fn parse_args() { println!("vaultwarden {version}"); exit(0); } - - if let Some(command) = pargs.subcommand().unwrap_or_default() { - if command == "hash" { - use argon2::{ - password_hash::SaltString, Algorithm::Argon2id, Argon2, ParamsBuilder, PasswordHasher, Version::V0x13, - }; - - let mut argon2_params = ParamsBuilder::new(); - let preset: Option = pargs.opt_value_from_str(["-p", "--preset"]).unwrap_or_default(); - let selected_preset; - match preset.as_deref() { - Some("owasp") => { - selected_preset = "owasp"; - argon2_params.m_cost(19456); - argon2_params.t_cost(2); - argon2_params.p_cost(1); - } - _ => { - // Bitwarden preset is the default - selected_preset = "bitwarden"; - argon2_params.m_cost(65540); - argon2_params.t_cost(3); - argon2_params.p_cost(4); - } - } - - println!("Generate an Argon2id PHC string using the '{selected_preset}' preset:\n"); - - let password = rpassword::prompt_password("Password: ").unwrap(); - if password.len() < 8 { - println!("\nPassword must contain at least 8 characters"); - exit(1); - } - - let password_verify = rpassword::prompt_password("Confirm Password: ").unwrap(); - if password != password_verify { - println!("\nPasswords do not match"); - exit(1); - } - - let argon2 = Argon2::new(Argon2id, V0x13, argon2_params.build().unwrap()); - let salt = SaltString::encode_b64(&crate::crypto::get_random_bytes::<32>()).unwrap(); - - let argon2_timer = tokio::time::Instant::now(); - if let Ok(password_hash) = argon2.hash_password(password.as_bytes(), &salt) { - println!( - "\n\ - ADMIN_TOKEN='{password_hash}'\n\n\ - Generation of the Argon2id PHC string took: {:?}", - argon2_timer.elapsed() - ); - } else { - error!("Unable to generate Argon2id PHC hash."); - exit(1); - } - } - exit(0); - } } + fn launch_info() { - println!( - "\ - /--------------------------------------------------------------------\\\n\ - | Starting Vaultwarden |" - ); + println!("/--------------------------------------------------------------------\\"); + println!("| Starting Vaultwarden |"); if let Some(version) = VERSION { println!("|{:^68}|", format!("Version {version}")); } - println!( - "\ - |--------------------------------------------------------------------|\n\ - | This is an *unofficial* Bitwarden implementation, DO NOT use the |\n\ - | official channels to report bugs/features, regardless of client. |\n\ - | Send usage/configuration questions or feature requests to: |\n\ - | https://github.com/dani-garcia/vaultwarden/discussions or |\n\ - | https://vaultwarden.discourse.group/ |\n\ - | Report suspected bugs/issues in the software itself at: |\n\ - | https://github.com/dani-garcia/vaultwarden/issues/new |\n\ - \\--------------------------------------------------------------------/\n" - ); + println!("|--------------------------------------------------------------------|"); + println!("| This is an *unofficial* Bitwarden implementation, DO NOT use the |"); + println!("| official channels to report bugs/features, regardless of client. |"); + println!("| Send usage/configuration questions or feature requests to: |"); + println!("| https://vaultwarden.discourse.group/ |"); + println!("| Report suspected bugs/issues in the software itself at: |"); + println!("| https://github.com/dani-garcia/vaultwarden/issues/new |"); + println!("\\--------------------------------------------------------------------/\n"); } fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> { @@ -250,14 +178,6 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> { log::LevelFilter::Off }; - // Only show rocket underscore `_` logs when the level is Debug or higher - // Else this will bloat the log output with useless messages. - let rocket_underscore_level = if level >= log::LevelFilter::Debug { - log::LevelFilter::Warn - } else { - log::LevelFilter::Off - }; - let mut logger = fern::Dispatch::new() .level(level) // Hide unknown certificate errors if using self-signed @@ -265,7 +185,7 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> { // Hide failed to close stream messages .level_for("hyper::server", log::LevelFilter::Warn) // Silence rocket logs - .level_for("_", rocket_underscore_level) + .level_for("_", log::LevelFilter::Warn) .level_for("rocket::launch", log::LevelFilter::Error) .level_for("rocket::launch_", log::LevelFilter::Error) .level_for("rocket::rocket", log::LevelFilter::Warn) @@ -277,8 +197,7 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> { // Prevent cookie_store logs .level_for("cookie_store", log::LevelFilter::Off) // Variable level for trust-dns used by reqwest - .level_for("trust_dns_resolver::name_server::name_server", trust_dns_level) - .level_for("trust_dns_proto::xfer", trust_dns_level) + .level_for("trust_dns_proto", trust_dns_level) .level_for("diesel_logger", diesel_logger_level) .chain(std::io::stdout()); @@ -286,9 +205,9 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> { // This can contain sensitive information we do not want in the default debug/trace logging. if CONFIG.smtp_debug() { println!( - "[WARNING] SMTP Debugging is enabled (SMTP_DEBUG=true). Sensitive information could be disclosed via logs!\n\ - [WARNING] Only enable SMTP_DEBUG during troubleshooting!\n" + "[WARNING] SMTP Debugging is enabled (SMTP_DEBUG=true). Sensitive information could be disclosed via logs!" ); + println!("[WARNING] Only enable SMTP_DEBUG during troubleshooting!\n"); logger = logger.level_for("lettre::transport::smtp", log::LevelFilter::Debug) } else { logger = logger.level_for("lettre::transport::smtp", log::LevelFilter::Off) @@ -334,12 +253,12 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> { }, }; - let backtrace = std::backtrace::Backtrace::force_capture(); + let backtrace = backtrace::Backtrace::new(); match info.location() { Some(location) => { error!( - target: "panic", "thread '{}' panicked at '{}': {}:{}\n{:}", + target: "panic", "thread '{}' panicked at '{}': {}:{}\n{:?}", thread, msg, location.file(), @@ -349,7 +268,7 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> { } None => error!( target: "panic", - "thread '{}' panicked at '{}'\n{:}", + "thread '{}' panicked at '{}'\n{:?}", thread, msg, backtrace @@ -548,7 +467,7 @@ async fn launch_rocket(pool: db::DbPool, extra_debug: bool) -> Result<(), Error> Ok(()) } -fn schedule_jobs(pool: db::DbPool) { +async fn schedule_jobs(pool: db::DbPool) { if CONFIG.job_poll_interval_ms() == 0 { info!("Job scheduler disabled."); return; @@ -624,7 +543,9 @@ fn schedule_jobs(pool: db::DbPool) { // tick, the one that was added earlier will run first. loop { sched.tick(); - runtime.block_on(tokio::time::sleep(tokio::time::Duration::from_millis(CONFIG.job_poll_interval_ms()))); + runtime.block_on(async move { + tokio::time::sleep(tokio::time::Duration::from_millis(CONFIG.job_poll_interval_ms())).await + }); } }) .expect("Error spawning job scheduler thread"); diff --git a/src/static/scripts/admin.css b/src/static/scripts/admin.css index d700af3cf6e..d77b5372c52 100644 --- a/src/static/scripts/admin.css +++ b/src/static/scripts/admin.css @@ -18,35 +18,24 @@ img { border: var(--bs-alert-border); } -#users-table .vw-account-details { - min-width: 250px; -} #users-table .vw-created-at, #users-table .vw-last-active { - min-width: 85px; - max-width: 85px; + width: 85px; + min-width: 70px; } -#users-table .vw-ciphers, #orgs-table .vw-users, #orgs-table .vw-ciphers { +#users-table .vw-items { + width: 35px; min-width: 35px; - max-width: 40px; -} -#orgs-table .vw-misc { - min-width: 65px; - max-width: 80px; } -#users-table .vw-attachments, #orgs-table .vw-attachments { - min-width: 100px; - max-width: 130px; +#users-table .vw-organizations { + min-width: 120px; } #users-table .vw-actions, #orgs-table .vw-actions { + width: 130px; min-width: 130px; - max-width: 130px; } #users-table .vw-org-cell { max-height: 120px; } -#orgs-table .vw-org-details { - min-width: 285px; -} #support-string { height: 16rem; diff --git a/src/static/scripts/admin.js b/src/static/scripts/admin.js index 7408c95540d..7849ac193f5 100644 --- a/src/static/scripts/admin.js +++ b/src/static/scripts/admin.js @@ -1,6 +1,4 @@ "use strict"; -/* eslint-env es2017, browser */ -/* exported BASE_URL, _post */ function getBaseUrl() { // If the base URL is `https://vaultwarden.example.com/base/path/`, @@ -28,8 +26,6 @@ function msg(text, reload_page = true) { } function _post(url, successMsg, errMsg, body, reload_page = true) { - let respStatus; - let respStatusText; fetch(url, { method: "POST", body: body, @@ -37,30 +33,22 @@ function _post(url, successMsg, errMsg, body, reload_page = true) { credentials: "same-origin", headers: { "Content-Type": "application/json" } }).then( resp => { - if (resp.ok) { - msg(successMsg, reload_page); - // Abuse the catch handler by setting error to false and continue - return Promise.reject({error: false}); - } - respStatus = resp.status; - respStatusText = resp.statusText; + if (resp.ok) { msg(successMsg, reload_page); return Promise.reject({error: false}); } + const respStatus = resp.status; + const respStatusText = resp.statusText; return resp.text(); }).then( respText => { try { const respJson = JSON.parse(respText); - if (respJson.ErrorModel && respJson.ErrorModel.Message) { - return respJson.ErrorModel.Message; - } else { - return Promise.reject({body:`${respStatus} - ${respStatusText}\n\nUnknown error`, error: true}); - } + return respJson ? respJson.ErrorModel.Message : "Unknown error"; } catch (e) { - return Promise.reject({body:`${respStatus} - ${respStatusText}\n\n[Catch] ${e}`, error: true}); + return Promise.reject({body:respStatus + " - " + respStatusText, error: true}); } }).then( apiMsg => { - msg(`${errMsg}\n${apiMsg}`, reload_page); + msg(errMsg + "\n" + apiMsg, reload_page); }).catch( e => { if (e.error === false) { return true; } - else { msg(`${errMsg}\n${e.body}`, reload_page); } + else { msg(errMsg + "\n" + e.body, reload_page); } }); } diff --git a/src/static/scripts/admin_diagnostics.js b/src/static/scripts/admin_diagnostics.js index 5fbed2dad53..84a7ecc5b64 100644 --- a/src/static/scripts/admin_diagnostics.js +++ b/src/static/scripts/admin_diagnostics.js @@ -1,10 +1,7 @@ "use strict"; -/* eslint-env es2017, browser */ -/* global BASE_URL:readable, BSN:readable */ var dnsCheck = false; var timeCheck = false; -var ntpTimeCheck = false; var domainCheck = false; var httpsCheck = false; @@ -68,7 +65,7 @@ function checkVersions(platform, installed, latest, commit=null) { // ================================ // Generate support string to be pasted on github or the forum -async function generateSupportString(event, dj) { +async function generateSupportString(dj) { event.preventDefault(); event.stopPropagation(); @@ -91,8 +88,7 @@ async function generateSupportString(event, dj) { supportString += `* Internet access: ${dj.has_http_access}\n`; supportString += `* Internet access via a proxy: ${dj.uses_proxy}\n`; supportString += `* DNS Check: ${dnsCheck}\n`; - supportString += `* Browser/Server Time Check: ${timeCheck}\n`; - supportString += `* Server/NTP Time Check: ${ntpTimeCheck}\n`; + supportString += `* Time Check: ${timeCheck}\n`; supportString += `* Domain Configuration Check: ${domainCheck}\n`; supportString += `* HTTPS Check: ${httpsCheck}\n`; supportString += `* Database type: ${dj.db_type}\n`; @@ -118,7 +114,7 @@ async function generateSupportString(event, dj) { document.getElementById("copy-support").classList.remove("d-none"); } -function copyToClipboard(event) { +function copyToClipboard() { event.preventDefault(); event.stopPropagation(); @@ -138,17 +134,16 @@ function copyToClipboard(event) { new BSN.Toast("#toastClipboardCopy").show(); } -function checkTimeDrift(utcTimeA, utcTimeB, statusPrefix) { +function checkTimeDrift(browserUTC, serverUTC) { const timeDrift = ( - Date.parse(utcTimeA.replace(" ", "T").replace(" UTC", "")) - - Date.parse(utcTimeB.replace(" ", "T").replace(" UTC", "")) + Date.parse(serverUTC.replace(" ", "T").replace(" UTC", "")) - + Date.parse(browserUTC.replace(" ", "T").replace(" UTC", "")) ) / 1000; - if (timeDrift > 15 || timeDrift < -15) { - document.getElementById(`${statusPrefix}-warning`).classList.remove("d-none"); - return false; + if (timeDrift > 20 || timeDrift < -20) { + document.getElementById("time-warning").classList.remove("d-none"); } else { - document.getElementById(`${statusPrefix}-success`).classList.remove("d-none"); - return true; + document.getElementById("time-success").classList.remove("d-none"); + timeCheck = true; } } @@ -198,18 +193,7 @@ function checkDns(dns_resolved) { function init(dj) { // Time check document.getElementById("time-browser-string").innerText = browserUTC; - - // Check if we were able to fetch a valid NTP Time - // If so, compare both browser and server with NTP - // Else, compare browser and server. - if (dj.ntp_time.indexOf("UTC") !== -1) { - timeCheck = checkTimeDrift(dj.server_time, browserUTC, "time"); - checkTimeDrift(dj.ntp_time, browserUTC, "ntp-browser"); - ntpTimeCheck = checkTimeDrift(dj.ntp_time, dj.server_time, "ntp-server"); - } else { - timeCheck = checkTimeDrift(dj.server_time, browserUTC, "time"); - ntpTimeCheck = "n/a"; - } + checkTimeDrift(browserUTC, dj.server_time); // Domain check const browserURL = location.href.toLowerCase(); @@ -224,18 +208,12 @@ function init(dj) { } // onLoad events -document.addEventListener("DOMContentLoaded", (event) => { +document.addEventListener("DOMContentLoaded", (/*event*/) => { const diag_json = JSON.parse(document.getElementById("diagnostics_json").innerText); init(diag_json); - const btnGenSupport = document.getElementById("gen-support"); - if (btnGenSupport) { - btnGenSupport.addEventListener("click", () => { - generateSupportString(event, diag_json); - }); - } - const btnCopySupport = document.getElementById("copy-support"); - if (btnCopySupport) { - btnCopySupport.addEventListener("click", copyToClipboard); - } + document.getElementById("gen-support").addEventListener("click", () => { + generateSupportString(diag_json); + }); + document.getElementById("copy-support").addEventListener("click", copyToClipboard); }); \ No newline at end of file diff --git a/src/static/scripts/admin_organizations.js b/src/static/scripts/admin_organizations.js index c885344eb05..ae15e2fd5ba 100644 --- a/src/static/scripts/admin_organizations.js +++ b/src/static/scripts/admin_organizations.js @@ -1,8 +1,6 @@ "use strict"; -/* eslint-env es2017, browser, jquery */ -/* global _post:readable, BASE_URL:readable, reload:readable, jdenticon:readable */ -function deleteOrganization(event) { +function deleteOrganization() { event.preventDefault(); event.stopPropagation(); const org_uuid = event.target.dataset.vwOrgUuid; @@ -30,22 +28,9 @@ function deleteOrganization(event) { } } -function initActions() { - document.querySelectorAll("button[vw-delete-organization]").forEach(btn => { - btn.addEventListener("click", deleteOrganization); - }); - - if (jdenticon) { - jdenticon(); - } -} - // onLoad events document.addEventListener("DOMContentLoaded", (/*event*/) => { jQuery("#orgs-table").DataTable({ - "drawCallback": function() { - initActions(); - }, "stateSave": true, "responsive": true, "lengthMenu": [ @@ -54,17 +39,16 @@ document.addEventListener("DOMContentLoaded", (/*event*/) => { ], "pageLength": -1, // Default show all "columnDefs": [{ - "targets": [4,5], + "targets": 4, "searchable": false, "orderable": false }] }); // Add click events for organization actions - initActions(); + document.querySelectorAll("button[vw-delete-organization]").forEach(btn => { + btn.addEventListener("click", deleteOrganization); + }); - const btnReload = document.getElementById("reload"); - if (btnReload) { - btnReload.addEventListener("click", reload); - } + document.getElementById("reload").addEventListener("click", reload); }); \ No newline at end of file diff --git a/src/static/scripts/admin_settings.js b/src/static/scripts/admin_settings.js index 06f15e0a9c8..4f248cbd6b3 100644 --- a/src/static/scripts/admin_settings.js +++ b/src/static/scripts/admin_settings.js @@ -1,8 +1,6 @@ "use strict"; -/* eslint-env es2017, browser */ -/* global _post:readable, BASE_URL:readable */ -function smtpTest(event) { +function smtpTest() { event.preventDefault(); event.stopPropagation(); if (formHasChanges(config_form)) { @@ -43,7 +41,7 @@ function getFormData() { return data; } -function saveConfig(event) { +function saveConfig() { const data = JSON.stringify(getFormData()); _post(`${BASE_URL}/admin/config/`, "Config saved correctly", @@ -53,7 +51,7 @@ function saveConfig(event) { event.preventDefault(); } -function deleteConf(event) { +function deleteConf() { event.preventDefault(); event.stopPropagation(); const input = prompt( @@ -70,7 +68,7 @@ function deleteConf(event) { } } -function backupDatabase(event) { +function backupDatabase() { event.preventDefault(); event.stopPropagation(); _post(`${BASE_URL}/admin/config/backup_db`, @@ -96,26 +94,24 @@ function formHasChanges(form) { // This function will prevent submitting a from when someone presses enter. function preventFormSubmitOnEnter(form) { - if (form) { - form.addEventListener("keypress", (event) => { - if (event.key == "Enter") { - event.preventDefault(); - } - }); - } + form.onkeypress = function(e) { + const key = e.charCode || e.keyCode || 0; + if (key == 13) { + e.preventDefault(); + } + }; } // This function will hook into the smtp-test-email input field and will call the smtpTest() function when enter is pressed. function submitTestEmailOnEnter() { const smtp_test_email_input = document.getElementById("smtp-test-email"); - if (smtp_test_email_input) { - smtp_test_email_input.addEventListener("keypress", (event) => { - if (event.key == "Enter") { - event.preventDefault(); - smtpTest(event); - } - }); - } + smtp_test_email_input.onkeypress = function(e) { + const key = e.charCode || e.keyCode || 0; + if (key == 13) { + e.preventDefault(); + smtpTest(); + } + }; } // Colorize some settings which are high risk @@ -128,11 +124,11 @@ function colorRiskSettings() { }); } -function toggleVis(event) { +function toggleVis(evt) { event.preventDefault(); event.stopPropagation(); - const elem = document.getElementById(event.target.dataset.vwPwToggle); + const elem = document.getElementById(evt.target.dataset.vwPwToggle); const type = elem.getAttribute("type"); if (type === "text") { elem.setAttribute("type", "password"); @@ -150,46 +146,9 @@ function masterCheck(check_id, inputs_query) { } const checkbox = document.getElementById(check_id); - if (checkbox) { - const onChange = onChanged(checkbox, inputs_query); - onChange(); // Trigger the event initially - checkbox.addEventListener("change", onChange); - } -} - -// This will check if the ADMIN_TOKEN is not a Argon2 hashed value. -// Else it will show a warning, unless someone has closed it. -// Then it will not show this warning for 30 days. -function checkAdminToken() { - const admin_token = document.getElementById("input_admin_token"); - const disable_admin_token = document.getElementById("input_disable_admin_token"); - if (!disable_admin_token.checked && !admin_token.value.startsWith("$argon2")) { - // Check if the warning has been closed before and 30 days have passed - const admin_token_warning_closed = localStorage.getItem("admin_token_warning_closed"); - if (admin_token_warning_closed !== null) { - const closed_date = new Date(parseInt(admin_token_warning_closed)); - const current_date = new Date(); - const thirtyDays = 1000*60*60*24*30; - if (current_date - closed_date < thirtyDays) { - return; - } - } - - // When closing the alert, store the current date/time in the browser - const admin_token_warning = document.getElementById("admin_token_warning"); - admin_token_warning.addEventListener("closed.bs.alert", function() { - const d = new Date(); - localStorage.setItem("admin_token_warning_closed", d.getTime()); - }); - - // Display the warning - admin_token_warning.classList.remove("d-none"); - } -} - -// This will check for specific configured values, and when needed will show a warning div -function showWarnings() { - checkAdminToken(); + const onChange = onChanged(checkbox, inputs_query); + onChange(); // Trigger the event initially + checkbox.addEventListener("change", onChange); } const config_form = document.getElementById("config-form"); @@ -213,20 +172,9 @@ document.addEventListener("DOMContentLoaded", (/*event*/) => { password_toggle_btn.addEventListener("click", toggleVis); }); - const btnBackupDatabase = document.getElementById("backupDatabase"); - if (btnBackupDatabase) { - btnBackupDatabase.addEventListener("click", backupDatabase); - } - const btnDeleteConf = document.getElementById("deleteConf"); - if (btnDeleteConf) { - btnDeleteConf.addEventListener("click", deleteConf); - } - const btnSmtpTest = document.getElementById("smtpTest"); - if (btnSmtpTest) { - btnSmtpTest.addEventListener("click", smtpTest); - } + document.getElementById("backupDatabase").addEventListener("click", backupDatabase); + document.getElementById("deleteConf").addEventListener("click", deleteConf); + document.getElementById("smtpTest").addEventListener("click", smtpTest); config_form.addEventListener("submit", saveConfig); - - showWarnings(); }); \ No newline at end of file diff --git a/src/static/scripts/admin_users.js b/src/static/scripts/admin_users.js index 65815dd340e..8f7ddf200bd 100644 --- a/src/static/scripts/admin_users.js +++ b/src/static/scripts/admin_users.js @@ -1,8 +1,6 @@ "use strict"; -/* eslint-env es2017, browser, jquery */ -/* global _post:readable, BASE_URL:readable, reload:readable, jdenticon:readable */ -function deleteUser(event) { +function deleteUser() { event.preventDefault(); event.stopPropagation(); const id = event.target.parentNode.dataset.vwUserUuid; @@ -24,43 +22,35 @@ function deleteUser(event) { } } -function remove2fa(event) { +function remove2fa() { event.preventDefault(); event.stopPropagation(); const id = event.target.parentNode.dataset.vwUserUuid; - const email = event.target.parentNode.dataset.vwUserEmail; - if (!id || !email) { + if (!id) { alert("Required parameters not found!"); return false; } - const confirmed = confirm(`Are you sure you want to remove 2FA for "${email}"?`); - if (confirmed) { - _post(`${BASE_URL}/admin/users/${id}/remove-2fa`, - "2FA removed correctly", - "Error removing 2FA" - ); - } + _post(`${BASE_URL}/admin/users/${id}/remove-2fa`, + "2FA removed correctly", + "Error removing 2FA" + ); } -function deauthUser(event) { +function deauthUser() { event.preventDefault(); event.stopPropagation(); const id = event.target.parentNode.dataset.vwUserUuid; - const email = event.target.parentNode.dataset.vwUserEmail; - if (!id || !email) { + if (!id) { alert("Required parameters not found!"); return false; } - const confirmed = confirm(`Are you sure you want to deauthorize sessions for "${email}"?`); - if (confirmed) { - _post(`${BASE_URL}/admin/users/${id}/deauth`, - "Sessions deauthorized correctly", - "Error deauthorizing sessions" - ); - } + _post(`${BASE_URL}/admin/users/${id}/deauth`, + "Sessions deauthorized correctly", + "Error deauthorizing sessions" + ); } -function disableUser(event) { +function disableUser() { event.preventDefault(); event.stopPropagation(); const id = event.target.parentNode.dataset.vwUserUuid; @@ -78,7 +68,7 @@ function disableUser(event) { } } -function enableUser(event) { +function enableUser() { event.preventDefault(); event.stopPropagation(); const id = event.target.parentNode.dataset.vwUserUuid; @@ -96,7 +86,7 @@ function enableUser(event) { } } -function updateRevisions(event) { +function updateRevisions() { event.preventDefault(); event.stopPropagation(); _post(`${BASE_URL}/admin/users/update_revision`, @@ -105,7 +95,7 @@ function updateRevisions(event) { ); } -function inviteUser(event) { +function inviteUser() { event.preventDefault(); event.stopPropagation(); const email = document.getElementById("inviteEmail"); @@ -120,24 +110,6 @@ function inviteUser(event) { ); } -function resendUserInvite (event) { - event.preventDefault(); - event.stopPropagation(); - const id = event.target.parentNode.dataset.vwUserUuid; - const email = event.target.parentNode.dataset.vwUserEmail; - if (!id || !email) { - alert("Required parameters not found!"); - return false; - } - const confirmed = confirm(`Are you sure you want to resend invitation for "${email}"?`); - if (confirmed) { - _post(`${BASE_URL}/admin/users/${id}/invite/resend`, - "Invite sent successfully", - "Error resend invite" - ); - } -} - const ORG_TYPES = { "0": { "name": "Owner", @@ -210,7 +182,7 @@ userOrgTypeDialog.addEventListener("hide.bs.modal", function() { document.getElementById("userOrgTypeOrgUuid").value = ""; }, false); -function updateUserOrgType(event) { +function updateUserOrgType() { event.preventDefault(); event.stopPropagation(); @@ -223,7 +195,26 @@ function updateUserOrgType(event) { ); } -function initUserTable() { +// onLoad events +document.addEventListener("DOMContentLoaded", (/*event*/) => { + jQuery("#users-table").DataTable({ + "stateSave": true, + "responsive": true, + "lengthMenu": [ + [-1, 5, 10, 25, 50], + ["All", 5, 10, 25, 50] + ], + "pageLength": -1, // Default show all + "columnDefs": [{ + "targets": [1, 2], + "type": "date-iso" + }, { + "targets": 6, + "searchable": false, + "orderable": false + }] + }); + // Color all the org buttons per type document.querySelectorAll("button[data-vw-org-type]").forEach(function(e) { const orgType = ORG_TYPES[e.dataset.vwOrgType]; @@ -231,6 +222,7 @@ function initUserTable() { e.title = orgType.name; }); + // Add click events for user actions document.querySelectorAll("button[vw-remove2fa]").forEach(btn => { btn.addEventListener("click", remove2fa); }); @@ -246,55 +238,9 @@ function initUserTable() { document.querySelectorAll("button[vw-enable-user]").forEach(btn => { btn.addEventListener("click", enableUser); }); - document.querySelectorAll("button[vw-resend-user-invite]").forEach(btn => { - btn.addEventListener("click", resendUserInvite); - }); - - if (jdenticon) { - jdenticon(); - } -} - -// onLoad events -document.addEventListener("DOMContentLoaded", (/*event*/) => { - jQuery("#users-table").DataTable({ - "drawCallback": function() { - initUserTable(); - }, - "stateSave": true, - "responsive": true, - "lengthMenu": [ - [-1, 2, 5, 10, 25, 50], - ["All", 2, 5, 10, 25, 50] - ], - "pageLength": -1, // Default show all - "columnDefs": [{ - "targets": [1, 2], - "type": "date-iso" - }, { - "targets": 6, - "searchable": false, - "orderable": false - }] - }); - - // Add click events for user actions - initUserTable(); - const btnUpdateRevisions = document.getElementById("updateRevisions"); - if (btnUpdateRevisions) { - btnUpdateRevisions.addEventListener("click", updateRevisions); - } - const btnReload = document.getElementById("reload"); - if (btnReload) { - btnReload.addEventListener("click", reload); - } - const btnUserOrgTypeForm = document.getElementById("userOrgTypeForm"); - if (btnUserOrgTypeForm) { - btnUserOrgTypeForm.addEventListener("submit", updateUserOrgType); - } - const btnInviteUserForm = document.getElementById("inviteUserForm"); - if (btnInviteUserForm) { - btnInviteUserForm.addEventListener("submit", inviteUser); - } + document.getElementById("updateRevisions").addEventListener("click", updateRevisions); + document.getElementById("reload").addEventListener("click", reload); + document.getElementById("userOrgTypeForm").addEventListener("submit", updateUserOrgType); + document.getElementById("inviteUserForm").addEventListener("submit", inviteUser); }); \ No newline at end of file diff --git a/src/static/scripts/datatables.css b/src/static/scripts/datatables.css index d22b2250e2d..a19cb110906 100644 --- a/src/static/scripts/datatables.css +++ b/src/static/scripts/datatables.css @@ -4,19 +4,13 @@ * * To rebuild or modify this file with the latest versions of the included * software please visit: - * https://datatables.net/download/#bs5/dt-1.13.2 + * https://datatables.net/download/#bs5/dt-1.13.1 * * Included libraries: - * DataTables 1.13.2 + * DataTables 1.13.1 */ @charset "UTF-8"; -:root { - --dt-row-selected: 13, 110, 253; - --dt-row-selected-text: 255, 255, 255; - --dt-row-selected-link: 9, 10, 11; -} - table.dataTable td.dt-control { text-align: center; cursor: pointer; @@ -132,7 +126,7 @@ div.dataTables_processing > div:last-child > div { width: 13px; height: 13px; border-radius: 50%; - background: 13 110 253; + background: rgba(13, 110, 253, 0.9); animation-timing-function: cubic-bezier(0, 1, 1, 0); } div.dataTables_processing > div:last-child > div:nth-child(1) { @@ -290,28 +284,23 @@ table.dataTable > tbody > tr { background-color: transparent; } table.dataTable > tbody > tr.selected > * { - box-shadow: inset 0 0 0 9999px rgb(13, 110, 253); - box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected)); - color: rgb(255, 255, 255); - color: rgb(var(--dt-row-selected-text)); + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.9); + color: white; } table.dataTable > tbody > tr.selected a { - color: rgb(9, 10, 11); - color: rgb(var(--dt-row-selected-link)); + color: #090a0b; } table.dataTable.table-striped > tbody > tr.odd > * { box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.05); } table.dataTable.table-striped > tbody > tr.odd.selected > * { box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95); - box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95); } table.dataTable.table-hover > tbody > tr:hover > * { box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.075); } table.dataTable.table-hover > tbody > tr.selected:hover > * { box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975); - box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975); } div.dataTables_wrapper div.dataTables_length label { @@ -385,9 +374,9 @@ div.dataTables_scrollFoot > .dataTables_scrollFootInner > table { @media screen and (max-width: 767px) { div.dataTables_wrapper div.dataTables_length, - div.dataTables_wrapper div.dataTables_filter, - div.dataTables_wrapper div.dataTables_info, - div.dataTables_wrapper div.dataTables_paginate { +div.dataTables_wrapper div.dataTables_filter, +div.dataTables_wrapper div.dataTables_info, +div.dataTables_wrapper div.dataTables_paginate { text-align: center; } div.dataTables_wrapper div.dataTables_paginate ul.pagination { diff --git a/src/static/scripts/datatables.js b/src/static/scripts/datatables.js index 9854358e35b..1aeda98209f 100644 --- a/src/static/scripts/datatables.js +++ b/src/static/scripts/datatables.js @@ -4,20 +4,20 @@ * * To rebuild or modify this file with the latest versions of the included * software please visit: - * https://datatables.net/download/#bs5/dt-1.13.2 + * https://datatables.net/download/#bs5/dt-1.13.1 * * Included libraries: - * DataTables 1.13.2 + * DataTables 1.13.1 */ -/*! DataTables 1.13.2 - * ©2008-2023 SpryMedia Ltd - datatables.net/license +/*! DataTables 1.13.1 + * ©2008-2022 SpryMedia Ltd - datatables.net/license */ /** * @summary DataTables * @description Paginate, search and order HTML tables - * @version 1.13.2 + * @version 1.13.1 * @author SpryMedia Ltd * @contact www.datatables.net * @copyright SpryMedia Ltd. @@ -1382,12 +1382,7 @@ var _isNumber = function ( d, decimalPoint, formatted ) { - let type = typeof d; - var strType = type === 'string'; - - if ( type === 'number' || type === 'bigint') { - return true; - } + var strType = typeof d === 'string'; // If empty return immediately so there must be a number if it is a // formatted string (this stops the string "k", or "kr", etc being detected @@ -6794,15 +6789,8 @@ if ( eventName !== null ) { var e = $.Event( eventName+'.dt' ); - var table = $(settings.nTable); - table.trigger( e, args ); - - // If not yet attached to the document, trigger the event - // on the body directly to sort of simulate the bubble - if (table.parents('body').length === 0) { - $('body').trigger( e, args ); - } + $(settings.nTable).trigger( e, args ); ret.push( e.result ); } @@ -7268,7 +7256,7 @@ pluck: function ( prop ) { - var fn = DataTable.util.get(prop); + let fn = DataTable.util.get(prop); return this.map( function ( el ) { return fn(el); @@ -8365,9 +8353,10 @@ $(document).on('plugin-init.dt', function (e, context) { var api = new _Api( context ); - var namespace = 'on-plugin-init'; - var stateSaveParamsEvent = 'stateSaveParams.' + namespace; - var destroyEvent = 'destroy. ' + namespace; + + const namespace = 'on-plugin-init'; + const stateSaveParamsEvent = `stateSaveParams.${namespace}`; + const destroyEvent = `destroy.${namespace}`; api.on( stateSaveParamsEvent, function ( e, settings, d ) { // This could be more compact with the API, but it is a lot faster as a simple @@ -8386,7 +8375,7 @@ }); api.on( destroyEvent, function () { - api.off(stateSaveParamsEvent + ' ' + destroyEvent); + api.off(`${stateSaveParamsEvent} ${destroyEvent}`); }); var loaded = api.state.loaded(); @@ -9708,7 +9697,7 @@ * @type string * @default Version number */ - DataTable.version = "1.13.2"; + DataTable.version = "1.13.1"; /** * Private data store, containing all of the settings objects that are @@ -14132,7 +14121,7 @@ * * @type string */ - build:"bs5/dt-1.13.2", + build:"bs5/dt-1.13.1", /** @@ -14841,17 +14830,10 @@ } if ( btnDisplay !== null ) { - var tag = settings.oInit.pagingTag || 'a'; - var disabled = btnClass.indexOf(disabledClass) !== -1; - - - node = $('<'+tag+'>', { + node = $('', { 'class': classes.sPageButton+' '+btnClass, 'aria-controls': settings.sTableId, - 'aria-disabled': disabled ? 'true' : null, 'aria-label': aria[ button ], - 'aria-role': 'link', - 'aria-current': btnClass === classes.sPageButtonActive ? 'page' : null, 'data-dt-idx': button, 'tabindex': tabIndex, 'id': idx === 0 && typeof button === 'string' ? @@ -14983,12 +14965,6 @@ if ( d !== 0 && (!d || d === '-') ) { return -Infinity; } - - let type = typeof d; - - if (type === 'number' || type === 'bigint') { - return d; - } // If a decimal place other than `.` is used, it needs to be given to the // function so we can detect it and replace with a `.` which is the only @@ -15671,6 +15647,7 @@ require('datatables.net')(root, $); } + return factory( $, root, root.document ); }; } @@ -15778,8 +15755,6 @@ DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, bu } if ( btnDisplay ) { - var disabled = btnClass.indexOf('disabled') !== -1; - node = $('
  • ', { 'class': classes.sPageButton+' '+btnClass, 'id': idx === 0 && typeof button === 'string' ? @@ -15787,12 +15762,9 @@ DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, bu null } ) .append( $('', { - 'href': disabled ? null : '#', + 'href': '#', 'aria-controls': settings.sTableId, - 'aria-disabled': disabled ? 'true' : null, 'aria-label': aria[ button ], - 'aria-role': 'link', - 'aria-current': btnClass === 'active' ? 'page' : null, 'data-dt-idx': button, 'tabindex': settings.iTabIndex, 'class': 'page-link' diff --git a/src/static/scripts/jquery-3.6.3.slim.js b/src/static/scripts/jquery-3.6.2.slim.js similarity index 99% rename from src/static/scripts/jquery-3.6.3.slim.js rename to src/static/scripts/jquery-3.6.2.slim.js index d7e1a94c615..4c41f3eb6bf 100644 --- a/src/static/scripts/jquery-3.6.3.slim.js +++ b/src/static/scripts/jquery-3.6.2.slim.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v3.6.3 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector + * jQuery JavaScript Library v3.6.2 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector * https://jquery.com/ * * Includes Sizzle.js @@ -9,7 +9,7 @@ * Released under the MIT license * https://jquery.org/license * - * Date: 2022-12-20T21:28Z + * Date: 2022-12-13T14:56Z */ ( function( global, factory ) { @@ -151,7 +151,7 @@ function toType( obj ) { var - version = "3.6.3 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector", + version = "3.6.2 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector", // Define a local copy of jQuery jQuery = function( selector, context ) { @@ -522,14 +522,14 @@ function isArrayLike( obj ) { } var Sizzle = /*! - * Sizzle CSS Selector Engine v2.3.9 + * Sizzle CSS Selector Engine v2.3.8 * https://sizzlejs.com/ * * Copyright JS Foundation and other contributors * Released under the MIT license * https://js.foundation/ * - * Date: 2022-12-19 + * Date: 2022-11-16 */ ( function( window ) { var i, @@ -890,7 +890,7 @@ function Sizzle( selector, context, results, seed ) { if ( support.cssSupportsSelector && // eslint-disable-next-line no-undef - !CSS.supports( "selector(:is(" + newSelector + "))" ) ) { + !CSS.supports( "selector(" + newSelector + ")" ) ) { // Support: IE 11+ // Throw to get to the same code path as an error directly in qSA. @@ -1492,8 +1492,9 @@ setDocument = Sizzle.setDocument = function( node ) { // `:has()` uses a forgiving selector list as an argument so our regular // `try-catch` mechanism fails to catch `:has()` with arguments not supported // natively like `:has(:contains("Foo"))`. Where supported & spec-compliant, - // we now use `CSS.supports("selector(:is(SELECTOR_TO_BE_TESTED))")`, but - // outside that we mark `:has` as buggy. + // we now use `CSS.supports("selector(SELECTOR_TO_BE_TESTED)")` but outside + // that, let's mark `:has` as buggy to always use jQuery traversal for + // `:has()`. rbuggyQSA.push( ":has" ); } diff --git a/src/static/templates/admin/base.hbs b/src/static/templates/admin/base.hbs index 2fe1ee54492..e296b11498a 100644 --- a/src/static/templates/admin/base.hbs +++ b/src/static/templates/admin/base.hbs @@ -1,7 +1,7 @@ - + diff --git a/src/static/templates/admin/diagnostics.hbs b/src/static/templates/admin/diagnostics.hbs index a61d899232a..de83ae11f99 100644 --- a/src/static/templates/admin/diagnostics.hbs +++ b/src/static/templates/admin/diagnostics.hbs @@ -144,15 +144,10 @@ Server: {{page_data.server_time_local}}
    Date & Time (UTC) - Server/Browser Ok - Server/Browser Error - Server NTP Ok - Server NTP Error - Browser NTP Ok - Browser NTP Error + Ok + Error
    - NTP: {{page_data.ntp_time}} Server: {{page_data.server_time}} Browser:
    diff --git a/src/static/templates/admin/organizations.hbs b/src/static/templates/admin/organizations.hbs index 9dd86622f88..eef6ae1afae 100644 --- a/src/static/templates/admin/organizations.hbs +++ b/src/static/templates/admin/organizations.hbs @@ -5,11 +5,10 @@ - - - - - + + + + @@ -38,13 +37,8 @@ Size: {{attachment_size}} {{/if}} - {{/each}} @@ -59,7 +53,7 @@ - + diff --git a/src/static/templates/admin/settings.hbs b/src/static/templates/admin/settings.hbs index b8ee5f4b25c..e387433516a 100644 --- a/src/static/templates/admin/settings.hbs +++ b/src/static/templates/admin/settings.hbs @@ -1,10 +1,4 @@
    -
    - - You are using a plain text `ADMIN_TOKEN` which is insecure.
    - Please generate a secure Argon2 PHC string by using `vaultwarden hash` or `argon2`.
    - See: Enabling admin page - Secure the `ADMIN_TOKEN` -
    Configuration
    @@ -53,7 +47,7 @@
    - +
    Please provide a valid email address
    @@ -91,7 +85,7 @@ {{else}} - + {{#case type "password"}} {{/case}} diff --git a/src/static/templates/admin/users.hbs b/src/static/templates/admin/users.hbs index bdb2870f6d9..3dbee11c37d 100644 --- a/src/static/templates/admin/users.hbs +++ b/src/static/templates/admin/users.hbs @@ -5,10 +5,10 @@
    OrganizationUsersCiphersAttachmentsMiscOrganizationUsersItemsAttachments Actions
    - Collections: {{collection_count}} - Groups: {{group_count}} - Events: {{event_count}} - -
    +
    - + - + @@ -63,18 +63,15 @@ @@ -99,7 +96,7 @@ Email: - + @@ -140,7 +137,7 @@ - + diff --git a/src/static/templates/email/admin_reset_password.hbs b/src/static/templates/email/admin_reset_password.hbs deleted file mode 100644 index 8d38177254b..00000000000 --- a/src/static/templates/email/admin_reset_password.hbs +++ /dev/null @@ -1,6 +0,0 @@ -Master Password Has Been Changed - -The master password for {{user_name}} has been changed by an administrator in your {{org_name}} organization. If you did not initiate this request, please reach out to your administrator immediately. - -=== -Github: https://github.com/dani-garcia/vaultwarden diff --git a/src/static/templates/email/admin_reset_password.html.hbs b/src/static/templates/email/admin_reset_password.html.hbs deleted file mode 100644 index d9749d22a9d..00000000000 --- a/src/static/templates/email/admin_reset_password.html.hbs +++ /dev/null @@ -1,11 +0,0 @@ -Master Password Has Been Changed - -{{> email/email_header }} -
    User Created at Last ActiveCiphersItems Attachments Organizations Actions {{#if TwoFactorEnabled}} -
    + {{/if}} -
    -
    + + {{#if user_enabled}} -
    + {{else}} -
    + {{/if}} - {{#case _Status 1}} -
    - {{/case}}
    - - - -
    - The master password for {{user_name}} has been changed by an administrator in your {{org_name}} organization. If you did not initiate this request, please reach out to your administrator immediately. -
    -{{> email/email_footer }} diff --git a/src/util.rs b/src/util.rs index f8d35c7cc87..3297fad5686 100644 --- a/src/util.rs +++ b/src/util.rs @@ -50,7 +50,7 @@ impl Fairing for AppHeaders { // Have I Been Pwned and Gravator to allow those calls to work. // # Connect src: // Leaked Passwords check: api.pwnedpasswords.com - // 2FA/MFA Site check: api.2fa.directory + // 2FA/MFA Site check: 2fa.directory // # Mail Relay: https://bitwarden.com/blog/add-privacy-and-security-using-email-aliases-with-bitwarden/ // app.simplelogin.io, app.anonaddy.com, api.fastmail.com, quack.duckduckgo.com let csp = format!( @@ -58,7 +58,7 @@ impl Fairing for AppHeaders { base-uri 'self'; \ form-action 'self'; \ object-src 'self' blob:; \ - script-src 'self' 'wasm-unsafe-eval'; \ + script-src 'self'; \ style-src 'self' 'unsafe-inline'; \ child-src 'self' https://*.duosecurity.com https://*.duofederal.com; \ frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; \ @@ -73,7 +73,7 @@ impl Fairing for AppHeaders { {icon_service_csp}; \ connect-src 'self' \ https://api.pwnedpasswords.com \ - https://api.2fa.directory \ + https://2fa.directory \ https://app.simplelogin.io/api/ \ https://app.anonaddy.com/api/ \ https://api.fastmail.com/ \ @@ -231,7 +231,8 @@ impl<'r> FromParam<'r> for SafeString { // Log all the routes from the main paths list, and the attachments endpoint // Effectively ignores, any static file route, and the alive endpoint -const LOGGED_ROUTES: [&str; 5] = ["/api", "/admin", "/identity", "/icons", "/attachments"]; +const LOGGED_ROUTES: [&str; 6] = + ["/api", "/admin", "/identity", "/icons", "/notifications/hub/negotiate", "/attachments"]; // Boolean is extra debug, when true, we ignore the whitelist above and also print the mounts pub struct BetterLogging(pub bool); @@ -587,7 +588,7 @@ impl<'de> Visitor<'de> for UpCaseVisitor { fn upcase_value(value: Value) -> Value { if let Value::Object(map) = value { - let mut new_value = Value::Object(serde_json::Map::new()); + let mut new_value = json!({}); for (key, val) in map.into_iter() { let processed_key = _process_key(&key); @@ -596,7 +597,7 @@ fn upcase_value(value: Value) -> Value { new_value } else if let Value::Array(array) = value { // Initialize array with null values - let mut new_value = Value::Array(vec![Value::Null; array.len()]); + let mut new_value = json!(vec![Value::Null; array.len()]); for (index, val) in array.into_iter().enumerate() { new_value[index] = upcase_value(val); @@ -635,7 +636,7 @@ where if tries >= max_tries { return err; } - Handle::current().block_on(sleep(Duration::from_millis(500))); + Handle::current().block_on(async move { sleep(Duration::from_millis(500)).await }); } } }