mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-01-26 22:23:19 -05:00
Merge branch 'main' into register_verify_email
This commit is contained in:
commit
23b0863091
@ -351,6 +351,7 @@
|
||||
## - "browser-fileless-import": Directly import credentials from other providers without a file.
|
||||
## - "extension-refresh": Temporarily enable the new extension design until general availability (should be used with the beta Chrome extension)
|
||||
## - "fido2-vault-credentials": Enable the use of FIDO2 security keys as second factor.
|
||||
## - "inline-menu-positioning-improvements": Enable the use of inline menu password generator and identity suggestions in the browser extension.
|
||||
## - "ssh-key-vault-item": Enable the creation and use of SSH key vault items. (Needs clients >=2024.12.0)
|
||||
## - "ssh-agent": Enable SSH agent support on Desktop. (Needs desktop >=2024.12.0)
|
||||
# EXPERIMENTAL_CLIENT_FEATURE_FLAGS=fido2-vault-credentials
|
||||
|
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@ -75,7 +75,7 @@ jobs:
|
||||
|
||||
# Only install the clippy and rustfmt components on the default rust-toolchain
|
||||
- name: "Install rust-toolchain version"
|
||||
uses: dtolnay/rust-toolchain@315e265cd78dad1e1dcf3a5074f6d6c47029d5aa # master @ Nov 18, 2024, 5:36 AM GMT+1
|
||||
uses: dtolnay/rust-toolchain@a54c7afa936fefeb4456b2dd8068152669aa8203 # master @ Dec 14, 2024, 5:49 AM GMT+1
|
||||
if: ${{ matrix.channel == 'rust-toolchain' }}
|
||||
with:
|
||||
toolchain: "${{steps.toolchain.outputs.RUST_TOOLCHAIN}}"
|
||||
@ -85,7 +85,7 @@ jobs:
|
||||
|
||||
# Install the any other channel to be used for which we do not execute clippy and rustfmt
|
||||
- name: "Install MSRV version"
|
||||
uses: dtolnay/rust-toolchain@315e265cd78dad1e1dcf3a5074f6d6c47029d5aa # master @ Nov 18, 2024, 5:36 AM GMT+1
|
||||
uses: dtolnay/rust-toolchain@a54c7afa936fefeb4456b2dd8068152669aa8203 # master @ Dec 14, 2024, 5:49 AM GMT+1
|
||||
if: ${{ matrix.channel != 'rust-toolchain' }}
|
||||
with:
|
||||
toolchain: "${{steps.toolchain.outputs.RUST_TOOLCHAIN}}"
|
||||
@ -107,7 +107,8 @@ jobs:
|
||||
# End Show environment
|
||||
|
||||
# Enable Rust Caching
|
||||
- uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5
|
||||
- name: Rust Caching
|
||||
uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7
|
||||
with:
|
||||
# Use a custom prefix-key to force a fresh start. This is sometimes needed with bigger changes.
|
||||
# Like changing the build host from Ubuntu 20.04 to 22.04 for example.
|
||||
|
2
.github/workflows/hadolint.yml
vendored
2
.github/workflows/hadolint.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
|
||||
# Start Docker Buildx
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1
|
||||
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
|
||||
# https://github.com/moby/buildkit/issues/3969
|
||||
# Also set max parallelism to 2, the default of 4 breaks GitHub Actions and causes OOMKills
|
||||
with:
|
||||
|
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@ -69,7 +69,7 @@ jobs:
|
||||
|
||||
# Start Docker Buildx
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1
|
||||
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
|
||||
# https://github.com/moby/buildkit/issues/3969
|
||||
# Also set max parallelism to 2, the default of 4 breaks GitHub Actions and causes OOMKills
|
||||
with:
|
||||
@ -165,7 +165,7 @@ jobs:
|
||||
echo "CONTAINER_REGISTRIES=${CONTAINER_REGISTRIES:+${CONTAINER_REGISTRIES},}localhost:5000/vaultwarden/server" | tee -a "${GITHUB_ENV}"
|
||||
|
||||
- name: Bake ${{ matrix.base_image }} containers
|
||||
uses: docker/bake-action@2e3d19baedb14545e5d41222653874f25d5b4dfb # v5.10.0
|
||||
uses: docker/bake-action@3fc70e1131fee40a422dd8dd0ff22014ae20a1f3 # v5.11.0
|
||||
env:
|
||||
BASE_TAGS: "${{ env.BASE_TAGS }}"
|
||||
SOURCE_COMMIT: "${{ env.SOURCE_COMMIT }}"
|
||||
@ -223,28 +223,28 @@ jobs:
|
||||
|
||||
# Upload artifacts to Github Actions
|
||||
- name: "Upload amd64 artifact"
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b #v4.5.0
|
||||
if: ${{ matrix.base_image == 'alpine' }}
|
||||
with:
|
||||
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-amd64
|
||||
path: vaultwarden-amd64
|
||||
|
||||
- name: "Upload arm64 artifact"
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b #v4.5.0
|
||||
if: ${{ matrix.base_image == 'alpine' }}
|
||||
with:
|
||||
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-arm64
|
||||
path: vaultwarden-arm64
|
||||
|
||||
- name: "Upload armv7 artifact"
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b #v4.5.0
|
||||
if: ${{ matrix.base_image == 'alpine' }}
|
||||
with:
|
||||
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-armv7
|
||||
path: vaultwarden-armv7
|
||||
|
||||
- name: "Upload armv6 artifact"
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b #v4.5.0
|
||||
if: ${{ matrix.base_image == 'alpine' }}
|
||||
with:
|
||||
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-armv6
|
||||
|
242
Cargo.lock
generated
242
Cargo.lock
generated
@ -176,7 +176,7 @@ version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
|
||||
dependencies = [
|
||||
"event-listener 5.3.1",
|
||||
"event-listener 5.4.0",
|
||||
"event-listener-strategy",
|
||||
"pin-project-lite",
|
||||
]
|
||||
@ -194,7 +194,7 @@ dependencies = [
|
||||
"async-task",
|
||||
"blocking",
|
||||
"cfg-if",
|
||||
"event-listener 5.3.1",
|
||||
"event-listener 5.4.0",
|
||||
"futures-lite",
|
||||
"rustix",
|
||||
"tracing",
|
||||
@ -275,9 +275,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.83"
|
||||
version = "0.1.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -435,9 +435,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.20.0"
|
||||
version = "1.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
|
||||
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@ -489,9 +489,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.4"
|
||||
version = "1.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf"
|
||||
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@ -637,9 +637,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.20"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
@ -721,6 +721,37 @@ dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "devise"
|
||||
version = "0.4.2"
|
||||
@ -918,6 +949,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_home"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
@ -942,9 +979,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.3.1"
|
||||
version = "5.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
|
||||
checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"parking",
|
||||
@ -957,7 +994,7 @@ version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
|
||||
dependencies = [
|
||||
"event-listener 5.3.1",
|
||||
"event-listener 5.4.0",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
@ -1185,9 +1222,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
@ -1262,17 +1299,18 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403"
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "6.2.0"
|
||||
version = "6.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315"
|
||||
checksum = "3d6b224b95c1e668ac0270325ad563b2eef1469fbbb8959bc7c692c844b813d9"
|
||||
dependencies = [
|
||||
"derive_builder",
|
||||
"log",
|
||||
"num-order",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.9",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@ -1364,15 +1402,6 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hostname"
|
||||
version = "0.3.1"
|
||||
@ -1474,9 +1503,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.31"
|
||||
version = "0.14.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85"
|
||||
checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@ -1497,9 +1526,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.5.1"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f"
|
||||
checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@ -1517,13 +1546,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.3"
|
||||
version = "0.27.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
|
||||
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 1.2.0",
|
||||
"hyper 1.5.1",
|
||||
"hyper 1.5.2",
|
||||
"hyper-util",
|
||||
"rustls 0.23.20",
|
||||
"rustls-pki-types",
|
||||
@ -1540,7 +1569,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"hyper 1.5.1",
|
||||
"hyper 1.5.2",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
@ -1559,7 +1588,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"http 1.2.0",
|
||||
"http-body 1.0.1",
|
||||
"hyper 1.5.1",
|
||||
"hyper 1.5.2",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
@ -1886,9 +1915,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.168"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@ -1925,9 +1954,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
@ -2049,9 +2078,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
@ -2226,9 +2255,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.5"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -2400,7 +2429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.9",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
@ -2440,9 +2469,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.2"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared",
|
||||
@ -2450,9 +2479,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.2"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
|
||||
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
@ -2460,9 +2489,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.2"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
@ -2470,9 +2499,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.2"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
|
||||
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
@ -2483,9 +2512,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.2"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
@ -2498,9 +2527,9 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@ -2619,9 +2648,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quanta"
|
||||
version = "0.12.4"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773ce68d0bb9bc7ef20be3536ffe94e223e1f365bd374108b2659fac0c65cfe6"
|
||||
checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"libc",
|
||||
@ -2640,9 +2669,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -2789,9 +2818,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.9"
|
||||
version = "0.12.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f"
|
||||
checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"base64 0.22.1",
|
||||
@ -2806,7 +2835,7 @@ dependencies = [
|
||||
"http 1.2.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.5.1",
|
||||
"hyper 1.5.2",
|
||||
"hyper-rustls",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
@ -2828,6 +2857,7 @@ dependencies = [
|
||||
"tokio-native-tls",
|
||||
"tokio-socks",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
@ -2948,7 +2978,7 @@ dependencies = [
|
||||
"either",
|
||||
"futures",
|
||||
"http 0.2.12",
|
||||
"hyper 0.14.31",
|
||||
"hyper 0.14.32",
|
||||
"indexmap",
|
||||
"log",
|
||||
"memchr",
|
||||
@ -3007,9 +3037,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.42"
|
||||
version = "0.38.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
||||
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@ -3090,9 +3120,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.18"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
@ -3164,9 +3194,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.12.1"
|
||||
version = "2.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2"
|
||||
checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@ -3180,9 +3210,9 @@ checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.216"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@ -3199,9 +3229,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.216"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3210,9 +3240,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.133"
|
||||
version = "1.0.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||
checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@ -3311,9 +3341,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.11"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
@ -3406,9 +3436,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
version = "2.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3470,12 +3500,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.14.0"
|
||||
version = "3.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
@ -3492,11 +3523,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.7"
|
||||
version = "2.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767"
|
||||
checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.7",
|
||||
"thiserror-impl 2.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3512,9 +3543,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.7"
|
||||
version = "2.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36"
|
||||
checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3585,9 +3616,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||
checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
@ -3751,6 +3782,27 @@ dependencies = [
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.3"
|
||||
@ -4165,12 +4217,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "7.0.0"
|
||||
version = "7.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9cad3279ade7346b96e38731a641d7343dd6a53d55083dd54eadfa5a1b38c6b"
|
||||
checksum = "fb4a9e33648339dc1642b0e36e21b3385e6148e289226f657c809dee59df5028"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"env_home",
|
||||
"rustix",
|
||||
"winsafe",
|
||||
]
|
||||
@ -4420,9 +4472,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.20"
|
||||
version = "0.6.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||
checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
10
Cargo.toml
10
Cargo.toml
@ -70,8 +70,8 @@ futures = "0.3.31"
|
||||
tokio = { version = "1.42.0", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time", "signal", "net"] }
|
||||
|
||||
# A generic serialization/deserialization framework
|
||||
serde = { version = "1.0.216", features = ["derive"] }
|
||||
serde_json = "1.0.133"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_json = "1.0.135"
|
||||
|
||||
# A safe, extensible ORM and Query builder
|
||||
diesel = { version = "2.2.6", features = ["chrono", "r2d2", "numeric"] }
|
||||
@ -120,10 +120,10 @@ percent-encoding = "2.3.1" # URL encoding library used for URL's in the emails
|
||||
email_address = "0.2.9"
|
||||
|
||||
# HTML Template library
|
||||
handlebars = { version = "6.2.0", features = ["dir_source"] }
|
||||
handlebars = { version = "6.3.0", features = ["dir_source"] }
|
||||
|
||||
# HTTP client (Used for favicons, version check, DUO and HIBP API)
|
||||
reqwest = { version = "0.12.9", features = ["native-tls-alpn", "stream", "json", "gzip", "brotli", "socks", "cookies"] }
|
||||
reqwest = { version = "0.12.12", features = ["native-tls-alpn", "stream", "json", "gzip", "brotli", "socks", "cookies"] }
|
||||
hickory-resolver = "0.24.2"
|
||||
|
||||
# Favicon extraction libraries
|
||||
@ -155,7 +155,7 @@ semver = "1.0.24"
|
||||
# Allow overriding the default memory allocator
|
||||
# Mainly used for the musl builds, since the default musl malloc is very slow
|
||||
mimalloc = { version = "0.1.43", features = ["secure"], default-features = false, optional = true }
|
||||
which = "7.0.0"
|
||||
which = "7.0.1"
|
||||
|
||||
# Argon2 library with support for the PHC format
|
||||
argon2 = "0.5.3"
|
||||
|
@ -1,10 +1,10 @@
|
||||
---
|
||||
vault_version: "v2024.6.2c"
|
||||
vault_image_digest: "sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b"
|
||||
# Cross Compile Docker Helper Scripts v1.5.0
|
||||
vault_version: "v2025.1.0"
|
||||
vault_image_digest: "sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8"
|
||||
# Cross Compile Docker Helper Scripts v1.6.1
|
||||
# We use the linux/amd64 platform shell scripts since there is no difference between the different platform scripts
|
||||
# https://github.com/tonistiigi/xx | https://hub.docker.com/r/tonistiigi/xx/tags
|
||||
xx_image_digest: "sha256:1978e7a58a1777cb0ef0dde76bad60b7914b21da57cfa88047875e4f364297aa"
|
||||
xx_image_digest: "sha256:9c207bead753dda9430bdd15425c6518fc7a03d866103c516a2c6889188f5894"
|
||||
rust_version: 1.83.0 # Rust version to be used
|
||||
debian_version: bookworm # Debian release name to be used
|
||||
alpine_version: "3.21" # Alpine version to be used
|
||||
|
@ -19,15 +19,15 @@
|
||||
# - 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 docker.io/vaultwarden/web-vault:v2024.6.2c
|
||||
# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2024.6.2c
|
||||
# [docker.io/vaultwarden/web-vault@sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b]
|
||||
# $ docker pull docker.io/vaultwarden/web-vault:v2025.1.0
|
||||
# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2025.1.0
|
||||
# [docker.io/vaultwarden/web-vault@sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8]
|
||||
#
|
||||
# - Conversely, to get the tag name from the digest:
|
||||
# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b
|
||||
# [docker.io/vaultwarden/web-vault:v2024.6.2c]
|
||||
# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8
|
||||
# [docker.io/vaultwarden/web-vault:v2025.1.0]
|
||||
#
|
||||
FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b AS vault
|
||||
FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8 AS vault
|
||||
|
||||
########################## ALPINE BUILD IMAGES ##########################
|
||||
## NOTE: The Alpine Base Images do not support other platforms then linux/amd64
|
||||
|
@ -19,20 +19,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 docker.io/vaultwarden/web-vault:v2024.6.2c
|
||||
# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2024.6.2c
|
||||
# [docker.io/vaultwarden/web-vault@sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b]
|
||||
# $ docker pull docker.io/vaultwarden/web-vault:v2025.1.0
|
||||
# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2025.1.0
|
||||
# [docker.io/vaultwarden/web-vault@sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8]
|
||||
#
|
||||
# - Conversely, to get the tag name from the digest:
|
||||
# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b
|
||||
# [docker.io/vaultwarden/web-vault:v2024.6.2c]
|
||||
# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8
|
||||
# [docker.io/vaultwarden/web-vault:v2025.1.0]
|
||||
#
|
||||
FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b AS vault
|
||||
FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8 AS vault
|
||||
|
||||
########################## Cross Compile Docker Helper Scripts ##########################
|
||||
## We use the linux/amd64 no matter which Build Platform, since these are all bash scripts
|
||||
## And these bash scripts do not have any significant difference if at all
|
||||
FROM --platform=linux/amd64 docker.io/tonistiigi/xx@sha256:1978e7a58a1777cb0ef0dde76bad60b7914b21da57cfa88047875e4f364297aa AS xx
|
||||
FROM --platform=linux/amd64 docker.io/tonistiigi/xx@sha256:9c207bead753dda9430bdd15425c6518fc7a03d866103c516a2c6889188f5894 AS xx
|
||||
|
||||
########################## BUILD IMAGE ##########################
|
||||
# hadolint ignore=DL3006
|
||||
|
@ -399,7 +399,12 @@ async fn post_password(data: Json<ChangePassData>, headers: Headers, mut conn: D
|
||||
&data.new_master_password_hash,
|
||||
Some(data.key),
|
||||
true,
|
||||
Some(vec![String::from("post_rotatekey"), String::from("get_contacts"), String::from("get_public_keys")]),
|
||||
Some(vec![
|
||||
String::from("post_rotatekey"),
|
||||
String::from("get_contacts"),
|
||||
String::from("get_public_keys"),
|
||||
String::from("get_api_webauthn"),
|
||||
]),
|
||||
);
|
||||
|
||||
let save_result = user.save(&mut conn).await;
|
||||
|
@ -18,7 +18,7 @@ pub use sends::purge_sends;
|
||||
pub fn routes() -> Vec<Route> {
|
||||
let mut eq_domains_routes = routes![get_eq_domains, post_eq_domains, put_eq_domains];
|
||||
let mut hibp_routes = routes![hibp_breach];
|
||||
let mut meta_routes = routes![alive, now, version, config];
|
||||
let mut meta_routes = routes![alive, now, version, config, get_api_webauthn];
|
||||
|
||||
let mut routes = Vec::new();
|
||||
routes.append(&mut accounts::routes());
|
||||
@ -184,6 +184,18 @@ fn version() -> Json<&'static str> {
|
||||
Json(crate::VERSION.unwrap_or_default())
|
||||
}
|
||||
|
||||
#[get("/webauthn")]
|
||||
fn get_api_webauthn(_headers: Headers) -> Json<Value> {
|
||||
// Prevent a 404 error, which also causes key-rotation issues
|
||||
// It looks like this is used when login with passkeys is enabled, which Vaultwarden does not (yet) support
|
||||
// An empty list/data also works fine
|
||||
Json(json!({
|
||||
"object": "list",
|
||||
"data": [],
|
||||
"continuationToken": null
|
||||
}))
|
||||
}
|
||||
|
||||
#[get("/config")]
|
||||
fn config() -> Json<Value> {
|
||||
let domain = crate::CONFIG.domain();
|
||||
|
@ -48,6 +48,7 @@ pub fn routes() -> Vec<Route> {
|
||||
confirm_invite,
|
||||
bulk_confirm_invite,
|
||||
accept_invite,
|
||||
get_org_user_mini_details,
|
||||
get_user,
|
||||
edit_user,
|
||||
put_organization_user,
|
||||
@ -77,6 +78,7 @@ pub fn routes() -> Vec<Route> {
|
||||
restore_organization_user,
|
||||
bulk_restore_organization_user,
|
||||
get_groups,
|
||||
get_groups_details,
|
||||
post_groups,
|
||||
get_group,
|
||||
put_group,
|
||||
@ -98,6 +100,7 @@ pub fn routes() -> Vec<Route> {
|
||||
get_org_export,
|
||||
api_key,
|
||||
rotate_api_key,
|
||||
get_billing_metadata,
|
||||
]
|
||||
}
|
||||
|
||||
@ -322,7 +325,14 @@ async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose,
|
||||
};
|
||||
|
||||
// get all collection memberships for the current organization
|
||||
let coll_users = CollectionUser::find_by_organization(org_id, &mut conn).await;
|
||||
let coll_users = CollectionUser::find_by_organization_swap_user_uuid_with_org_user_uuid(org_id, &mut conn).await;
|
||||
// Generate a HashMap to get the correct UserOrgType per user to determine the manage permission
|
||||
// We use the uuid instead of the user_uuid here, since that is what is used in CollectionUser
|
||||
let users_org_type: HashMap<String, i32> = UserOrganization::find_confirmed_by_org(org_id, &mut conn)
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|uo| (uo.uuid, uo.atype))
|
||||
.collect();
|
||||
|
||||
// check if current user has full access to the organization (either directly or via any group)
|
||||
let has_full_access_to_org = user_org.access_all
|
||||
@ -336,11 +346,22 @@ async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose,
|
||||
|| (CONFIG.org_groups_enabled()
|
||||
&& GroupUser::has_access_to_collection_by_member(&col.uuid, &user_org.uuid, &mut conn).await);
|
||||
|
||||
// Not assigned collections should not be returned
|
||||
if !assigned {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get the users assigned directly to the given collection
|
||||
let users: Vec<Value> = coll_users
|
||||
.iter()
|
||||
.filter(|collection_user| collection_user.collection_uuid == col.uuid)
|
||||
.map(|collection_user| SelectionReadOnly::to_collection_user_details_read_only(collection_user).to_json())
|
||||
.map(|collection_user| {
|
||||
SelectionReadOnly::to_collection_user_details_read_only(
|
||||
collection_user,
|
||||
*users_org_type.get(&collection_user.user_uuid).unwrap_or(&(UserOrgType::User as i32)),
|
||||
)
|
||||
.to_json()
|
||||
})
|
||||
.collect();
|
||||
|
||||
// get the group details for the given collection
|
||||
@ -645,12 +666,24 @@ async fn get_org_collection_detail(
|
||||
Vec::with_capacity(0)
|
||||
};
|
||||
|
||||
// Generate a HashMap to get the correct UserOrgType per user to determine the manage permission
|
||||
// We use the uuid instead of the user_uuid here, since that is what is used in CollectionUser
|
||||
let users_org_type: HashMap<String, i32> = UserOrganization::find_confirmed_by_org(org_id, &mut conn)
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|uo| (uo.uuid, uo.atype))
|
||||
.collect();
|
||||
|
||||
let users: Vec<Value> =
|
||||
CollectionUser::find_by_collection_swap_user_uuid_with_org_user_uuid(&collection.uuid, &mut conn)
|
||||
.await
|
||||
.iter()
|
||||
.map(|collection_user| {
|
||||
SelectionReadOnly::to_collection_user_details_read_only(collection_user).to_json()
|
||||
SelectionReadOnly::to_collection_user_details_read_only(
|
||||
collection_user,
|
||||
*users_org_type.get(&collection_user.user_uuid).unwrap_or(&(UserOrgType::User as i32)),
|
||||
)
|
||||
.to_json()
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -830,13 +863,19 @@ struct InviteData {
|
||||
collections: Option<Vec<CollectionData>>,
|
||||
#[serde(default)]
|
||||
access_all: bool,
|
||||
#[serde(default)]
|
||||
permissions: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/users/invite", data = "<data>")]
|
||||
async fn send_invite(org_id: &str, data: Json<InviteData>, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult {
|
||||
let data: InviteData = data.into_inner();
|
||||
let mut data: InviteData = data.into_inner();
|
||||
|
||||
let new_type = match UserOrgType::from_str(&data.r#type.into_string()) {
|
||||
// HACK: We need the raw user-type to be sure custom role is selected to determine the access_all permission
|
||||
// The from_str() will convert the custom role type into a manager role type
|
||||
let raw_type = &data.r#type.into_string();
|
||||
// UserOrgType::from_str will convert custom (4) to manager (3)
|
||||
let new_type = match UserOrgType::from_str(raw_type) {
|
||||
Some(new_type) => new_type as i32,
|
||||
None => err!("Invalid type"),
|
||||
};
|
||||
@ -845,6 +884,18 @@ async fn send_invite(org_id: &str, data: Json<InviteData>, headers: AdminHeaders
|
||||
err!("Only Owners can invite Managers, Admins or Owners")
|
||||
}
|
||||
|
||||
// HACK: This converts the Custom role which has the `Manage all collections` box checked into an access_all flag
|
||||
// Since the parent checkbox is not sent to the server we need to check and verify the child checkboxes
|
||||
// If the box is not checked, the user will still be a manager, but not with the access_all permission
|
||||
if raw_type.eq("4")
|
||||
&& data.permissions.get("editAnyCollection") == Some(&json!(true))
|
||||
&& data.permissions.get("deleteAnyCollection") == Some(&json!(true))
|
||||
&& data.permissions.get("createNewCollections") == Some(&json!(true))
|
||||
{
|
||||
data.access_all = true;
|
||||
}
|
||||
|
||||
let mut user_created: bool = false;
|
||||
for email in data.emails.iter() {
|
||||
let mut user_org_status = UserOrgStatus::Invited as i32;
|
||||
let user = match User::find_by_mail(email, &mut conn).await {
|
||||
@ -858,13 +909,13 @@ async fn send_invite(org_id: &str, data: Json<InviteData>, headers: AdminHeaders
|
||||
}
|
||||
|
||||
if !CONFIG.mail_enabled() {
|
||||
let invitation = Invitation::new(email);
|
||||
invitation.save(&mut conn).await?;
|
||||
Invitation::new(email).save(&mut conn).await?;
|
||||
}
|
||||
|
||||
let mut user = User::new(email.clone());
|
||||
user.save(&mut conn).await?;
|
||||
user
|
||||
let mut new_user = User::new(email.clone());
|
||||
new_user.save(&mut conn).await?;
|
||||
user_created = true;
|
||||
new_user
|
||||
}
|
||||
Some(user) => {
|
||||
if UserOrganization::find_by_user_and_org(&user.uuid, org_id, &mut conn).await.is_some() {
|
||||
@ -879,11 +930,49 @@ async fn send_invite(org_id: &str, data: Json<InviteData>, headers: AdminHeaders
|
||||
}
|
||||
};
|
||||
|
||||
let mut new_user = UserOrganization::new(user.uuid.clone(), String::from(org_id));
|
||||
let mut new_member = UserOrganization::new(user.uuid.clone(), String::from(org_id));
|
||||
let access_all = data.access_all;
|
||||
new_user.access_all = access_all;
|
||||
new_user.atype = new_type;
|
||||
new_user.status = user_org_status;
|
||||
new_member.access_all = access_all;
|
||||
new_member.atype = new_type;
|
||||
new_member.status = user_org_status;
|
||||
new_member.save(&mut conn).await?;
|
||||
|
||||
if CONFIG.mail_enabled() {
|
||||
let org_name = match Organization::find_by_uuid(org_id, &mut conn).await {
|
||||
Some(org) => org.name,
|
||||
None => err!("Error looking up organization"),
|
||||
};
|
||||
|
||||
if let Err(e) = mail::send_invite(
|
||||
&user,
|
||||
Some(String::from(org_id)),
|
||||
Some(new_member.uuid.clone()),
|
||||
&org_name,
|
||||
Some(headers.user.email.clone()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
// Upon error delete the user, invite and org member records when needed
|
||||
if user_created {
|
||||
user.delete(&mut conn).await?;
|
||||
} else {
|
||||
new_member.delete(&mut conn).await?;
|
||||
}
|
||||
|
||||
err!(format!("Error sending invite: {e:?} "));
|
||||
};
|
||||
}
|
||||
|
||||
log_event(
|
||||
EventType::OrganizationUserInvited as i32,
|
||||
&new_member.uuid.clone(),
|
||||
org_id,
|
||||
&headers.user.uuid,
|
||||
headers.device.atype,
|
||||
&headers.ip.ip,
|
||||
&mut conn,
|
||||
)
|
||||
.await;
|
||||
|
||||
// If no accessAll, add the collections received
|
||||
if !access_all {
|
||||
@ -904,39 +993,10 @@ async fn send_invite(org_id: &str, data: Json<InviteData>, headers: AdminHeaders
|
||||
}
|
||||
}
|
||||
|
||||
new_user.save(&mut conn).await?;
|
||||
|
||||
for group in data.groups.iter() {
|
||||
let mut group_entry = GroupUser::new(String::from(group), user.uuid.clone());
|
||||
let mut group_entry = GroupUser::new(String::from(group), new_member.uuid.clone());
|
||||
group_entry.save(&mut conn).await?;
|
||||
}
|
||||
|
||||
log_event(
|
||||
EventType::OrganizationUserInvited as i32,
|
||||
&new_user.uuid,
|
||||
org_id,
|
||||
&headers.user.uuid,
|
||||
headers.device.atype,
|
||||
&headers.ip.ip,
|
||||
&mut conn,
|
||||
)
|
||||
.await;
|
||||
|
||||
if CONFIG.mail_enabled() {
|
||||
let org_name = match Organization::find_by_uuid(org_id, &mut conn).await {
|
||||
Some(org) => org.name,
|
||||
None => err!("Error looking up organization"),
|
||||
};
|
||||
|
||||
mail::send_invite(
|
||||
&user,
|
||||
Some(String::from(org_id)),
|
||||
Some(new_user.uuid),
|
||||
&org_name,
|
||||
Some(headers.user.email.clone()),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -1014,7 +1074,7 @@ async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, co
|
||||
let invitation = Invitation::new(&user.email);
|
||||
invitation.save(conn).await?;
|
||||
} else {
|
||||
let _ = Invitation::take(&user.email, conn).await;
|
||||
Invitation::take(&user.email, conn).await;
|
||||
let mut user_org = user_org;
|
||||
user_org.status = UserOrgStatus::Accepted as i32;
|
||||
user_org.save(conn).await?;
|
||||
@ -1254,7 +1314,21 @@ async fn _confirm_invite(
|
||||
save_result
|
||||
}
|
||||
|
||||
#[get("/organizations/<org_id>/users/<org_user_id>?<data..>")]
|
||||
#[get("/organizations/<org_id>/users/mini-details", rank = 1)]
|
||||
async fn get_org_user_mini_details(org_id: &str, _headers: ManagerHeadersLoose, mut conn: DbConn) -> Json<Value> {
|
||||
let mut users_json = Vec::new();
|
||||
for u in UserOrganization::find_by_org(org_id, &mut conn).await {
|
||||
users_json.push(u.to_json_mini_details(&mut conn).await);
|
||||
}
|
||||
|
||||
Json(json!({
|
||||
"data": users_json,
|
||||
"object": "list",
|
||||
"continuationToken": null,
|
||||
}))
|
||||
}
|
||||
|
||||
#[get("/organizations/<org_id>/users/<org_user_id>?<data..>", rank = 2)]
|
||||
async fn get_user(
|
||||
org_id: &str,
|
||||
org_user_id: &str,
|
||||
@ -1282,6 +1356,8 @@ struct EditUserData {
|
||||
groups: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
access_all: bool,
|
||||
#[serde(default)]
|
||||
permissions: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
#[put("/organizations/<org_id>/users/<org_user_id>", data = "<data>", rank = 1)]
|
||||
@ -1303,14 +1379,30 @@ async fn edit_user(
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
let data: EditUserData = data.into_inner();
|
||||
let mut data: EditUserData = data.into_inner();
|
||||
|
||||
let Some(new_type) = UserOrgType::from_str(&data.r#type.into_string()) else {
|
||||
// HACK: We need the raw user-type to be sure custom role is selected to determine the access_all permission
|
||||
// The from_str() will convert the custom role type into a manager role type
|
||||
let raw_type = &data.r#type.into_string();
|
||||
// UserOrgType::from_str will convert custom (4) to manager (3)
|
||||
let Some(new_type) = UserOrgType::from_str(raw_type) else {
|
||||
err!("Invalid type")
|
||||
};
|
||||
|
||||
let Some(mut user_to_edit) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await else {
|
||||
err!("The specified user isn't member of the organization")
|
||||
// HACK: This converts the Custom role which has the `Manage all collections` box checked into an access_all flag
|
||||
// Since the parent checkbox is not sent to the server we need to check and verify the child checkboxes
|
||||
// If the box is not checked, the user will still be a manager, but not with the access_all permission
|
||||
if raw_type.eq("4")
|
||||
&& data.permissions.get("editAnyCollection") == Some(&json!(true))
|
||||
&& data.permissions.get("deleteAnyCollection") == Some(&json!(true))
|
||||
&& data.permissions.get("createNewCollections") == Some(&json!(true))
|
||||
{
|
||||
data.access_all = true;
|
||||
}
|
||||
|
||||
let mut user_to_edit = 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 member of the organization"),
|
||||
};
|
||||
|
||||
if new_type != user_to_edit.atype
|
||||
@ -1901,6 +1993,12 @@ fn get_plans_tax_rates(_headers: Headers) -> Json<Value> {
|
||||
Json(_empty_data_json())
|
||||
}
|
||||
|
||||
#[get("/organizations/<_org_id>/billing/metadata")]
|
||||
fn get_billing_metadata(_org_id: &str, _headers: Headers) -> Json<Value> {
|
||||
// Prevent a 404 error, which also causes Javascript errors.
|
||||
Json(_empty_data_json())
|
||||
}
|
||||
|
||||
fn _empty_data_json() -> Value {
|
||||
json!({
|
||||
"object": "list",
|
||||
@ -1938,6 +2036,9 @@ struct OrgImportData {
|
||||
users: Vec<OrgImportUserData>,
|
||||
}
|
||||
|
||||
/// This function seems to be deprected
|
||||
/// It is only used with older directory connectors
|
||||
/// TODO: Cleanup Tech debt
|
||||
#[post("/organizations/<org_id>/import", data = "<data>")]
|
||||
async fn import(org_id: &str, data: Json<OrgImportData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
|
||||
let data = data.into_inner();
|
||||
@ -1981,23 +2082,10 @@ async fn import(org_id: &str, data: Json<OrgImportData>, headers: Headers, mut c
|
||||
UserOrgStatus::Accepted as i32 // Automatically mark user as accepted if no email invites
|
||||
};
|
||||
|
||||
let mut new_org_user = UserOrganization::new(user.uuid.clone(), String::from(org_id));
|
||||
new_org_user.access_all = false;
|
||||
new_org_user.atype = UserOrgType::User as i32;
|
||||
new_org_user.status = user_org_status;
|
||||
|
||||
new_org_user.save(&mut conn).await?;
|
||||
|
||||
log_event(
|
||||
EventType::OrganizationUserInvited as i32,
|
||||
&new_org_user.uuid,
|
||||
org_id,
|
||||
&headers.user.uuid,
|
||||
headers.device.atype,
|
||||
&headers.ip.ip,
|
||||
&mut conn,
|
||||
)
|
||||
.await;
|
||||
let mut new_member = UserOrganization::new(user.uuid.clone(), String::from(org_id));
|
||||
new_member.access_all = false;
|
||||
new_member.atype = UserOrgType::User as i32;
|
||||
new_member.status = user_org_status;
|
||||
|
||||
if CONFIG.mail_enabled() {
|
||||
let org_name = match Organization::find_by_uuid(org_id, &mut conn).await {
|
||||
@ -2008,12 +2096,27 @@ async fn import(org_id: &str, data: Json<OrgImportData>, headers: Headers, mut c
|
||||
mail::send_invite(
|
||||
&user,
|
||||
Some(String::from(org_id)),
|
||||
Some(new_org_user.uuid),
|
||||
Some(new_member.uuid.clone()),
|
||||
&org_name,
|
||||
Some(headers.user.email.clone()),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// Save the member after sending an email
|
||||
// If sending fails the member will not be saved to the database, and will not result in the admin needing to reinvite the users manually
|
||||
new_member.save(&mut conn).await?;
|
||||
|
||||
log_event(
|
||||
EventType::OrganizationUserInvited as i32,
|
||||
&new_member.uuid,
|
||||
org_id,
|
||||
&headers.user.uuid,
|
||||
headers.device.atype,
|
||||
&headers.ip.ip,
|
||||
&mut conn,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2299,6 +2402,11 @@ async fn get_groups(org_id: &str, _headers: ManagerHeadersLoose, mut conn: DbCon
|
||||
})))
|
||||
}
|
||||
|
||||
#[get("/organizations/<org_id>/groups/details", rank = 1)]
|
||||
async fn get_groups_details(org_id: &str, headers: ManagerHeadersLoose, conn: DbConn) -> JsonResult {
|
||||
get_groups(org_id, headers, conn).await
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GroupRequest {
|
||||
@ -2331,6 +2439,7 @@ struct SelectionReadOnly {
|
||||
id: String,
|
||||
read_only: bool,
|
||||
hide_passwords: bool,
|
||||
manage: bool,
|
||||
}
|
||||
|
||||
impl SelectionReadOnly {
|
||||
@ -2339,18 +2448,31 @@ impl SelectionReadOnly {
|
||||
}
|
||||
|
||||
pub fn to_collection_group_details_read_only(collection_group: &CollectionGroup) -> SelectionReadOnly {
|
||||
// If both read_only and hide_passwords are false, then manage should be true
|
||||
// You can't have an entry with read_only and manage, or hide_passwords and manage
|
||||
// Or an entry with everything to false
|
||||
SelectionReadOnly {
|
||||
id: collection_group.groups_uuid.clone(),
|
||||
read_only: collection_group.read_only,
|
||||
hide_passwords: collection_group.hide_passwords,
|
||||
manage: !collection_group.read_only && !collection_group.hide_passwords,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_collection_user_details_read_only(collection_user: &CollectionUser) -> SelectionReadOnly {
|
||||
pub fn to_collection_user_details_read_only(
|
||||
collection_user: &CollectionUser,
|
||||
user_org_type: i32,
|
||||
) -> SelectionReadOnly {
|
||||
// Vaultwarden allows manage access for Admins and Owners by default
|
||||
// For managers (Or custom role) it depends if they have read_ony or hide_passwords set to true or not
|
||||
SelectionReadOnly {
|
||||
id: collection_user.user_uuid.clone(),
|
||||
read_only: collection_user.read_only,
|
||||
hide_passwords: collection_user.hide_passwords,
|
||||
manage: user_org_type >= UserOrgType::Admin
|
||||
|| (user_org_type == UserOrgType::Manager
|
||||
&& !collection_user.read_only
|
||||
&& !collection_user.hide_passwords),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2534,7 +2656,7 @@ async fn bulk_delete_groups(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[get("/organizations/<org_id>/groups/<group_id>")]
|
||||
#[get("/organizations/<org_id>/groups/<group_id>", rank = 2)]
|
||||
async fn get_group(org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult {
|
||||
if !CONFIG.org_groups_enabled() {
|
||||
err!("Group support is disabled");
|
||||
@ -2904,7 +3026,7 @@ async fn put_reset_password_enrollment(
|
||||
if reset_request.reset_password_key.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");
|
||||
err!("Reset password can't be withdrawn due to an enterprise policy");
|
||||
}
|
||||
|
||||
if reset_request.reset_password_key.is_some() {
|
||||
|
@ -52,6 +52,7 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
|
||||
let data = data.into_inner();
|
||||
|
||||
for user_data in &data.members {
|
||||
let mut user_created: bool = false;
|
||||
if user_data.deleted {
|
||||
// If user is marked for deletion and it exists, revoke it
|
||||
if let Some(mut user_org) =
|
||||
@ -97,9 +98,9 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
|
||||
new_user.save(&mut conn).await?;
|
||||
|
||||
if !CONFIG.mail_enabled() {
|
||||
let invitation = Invitation::new(&new_user.email);
|
||||
invitation.save(&mut conn).await?;
|
||||
Invitation::new(&new_user.email).save(&mut conn).await?;
|
||||
}
|
||||
user_created = true;
|
||||
new_user
|
||||
}
|
||||
};
|
||||
@ -109,13 +110,13 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
|
||||
UserOrgStatus::Accepted as i32 // Automatically mark user as accepted if no email invites
|
||||
};
|
||||
|
||||
let mut new_org_user = UserOrganization::new(user.uuid.clone(), org_id.clone());
|
||||
new_org_user.set_external_id(Some(user_data.external_id.clone()));
|
||||
new_org_user.access_all = false;
|
||||
new_org_user.atype = UserOrgType::User as i32;
|
||||
new_org_user.status = user_org_status;
|
||||
let mut new_member = UserOrganization::new(user.uuid.clone(), org_id.clone());
|
||||
new_member.set_external_id(Some(user_data.external_id.clone()));
|
||||
new_member.access_all = false;
|
||||
new_member.atype = UserOrgType::User as i32;
|
||||
new_member.status = user_org_status;
|
||||
|
||||
new_org_user.save(&mut conn).await?;
|
||||
new_member.save(&mut conn).await?;
|
||||
|
||||
if CONFIG.mail_enabled() {
|
||||
let (org_name, org_email) = match Organization::find_by_uuid(&org_id, &mut conn).await {
|
||||
@ -123,8 +124,24 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
|
||||
None => err!("Error looking up organization"),
|
||||
};
|
||||
|
||||
mail::send_invite(&user, Some(org_id.clone()), Some(new_org_user.uuid), &org_name, Some(org_email))
|
||||
.await?;
|
||||
if let Err(e) = mail::send_invite(
|
||||
&user,
|
||||
Some(org_id.clone()),
|
||||
Some(new_member.uuid.clone()),
|
||||
&org_name,
|
||||
Some(org_email),
|
||||
)
|
||||
.await
|
||||
{
|
||||
// Upon error delete the user, invite and org member records when needed
|
||||
if user_created {
|
||||
user.delete(&mut conn).await?;
|
||||
} else {
|
||||
new_member.delete(&mut conn).await?;
|
||||
}
|
||||
|
||||
err!(format!("Error sending invite: {e:?} "));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ use crate::{
|
||||
pub use crate::config::CONFIG;
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![generate_authenticator, activate_authenticator, activate_authenticator_put,]
|
||||
routes![generate_authenticator, activate_authenticator, activate_authenticator_put, disable_authenticator]
|
||||
}
|
||||
|
||||
#[post("/two-factor/get-authenticator", data = "<data>")]
|
||||
@ -175,3 +175,47 @@ pub async fn validate_totp_code(
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct DisableAuthenticatorData {
|
||||
key: String,
|
||||
master_password_hash: String,
|
||||
r#type: NumberOrString,
|
||||
}
|
||||
|
||||
#[delete("/two-factor/authenticator", data = "<data>")]
|
||||
async fn disable_authenticator(data: Json<DisableAuthenticatorData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let user = headers.user;
|
||||
let type_ = data.r#type.into_i32()?;
|
||||
|
||||
if !user.check_valid_password(&data.master_password_hash) {
|
||||
err!("Invalid password");
|
||||
}
|
||||
|
||||
if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await {
|
||||
if twofactor.data == data.key {
|
||||
twofactor.delete(&mut conn).await?;
|
||||
log_user_event(
|
||||
EventType::UserDisabled2fa as i32,
|
||||
&user.uuid,
|
||||
headers.device.atype,
|
||||
&headers.ip.ip,
|
||||
&mut conn,
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
err!(format!("TOTP key for user {} does not match recorded value, cannot deactivate", &user.email));
|
||||
}
|
||||
}
|
||||
|
||||
if TwoFactor::find_by_user(&user.uuid, &mut conn).await.is_empty() {
|
||||
super::enforce_2fa_policy(&user, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await?;
|
||||
}
|
||||
|
||||
Ok(Json(json!({
|
||||
"enabled": false,
|
||||
"keys": type_,
|
||||
"object": "twoFactorProvider"
|
||||
})))
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use rocket::{
|
||||
@ -14,7 +13,7 @@ use crate::{
|
||||
api::{core::now, ApiResult, EmptyResult},
|
||||
auth::decode_file_download,
|
||||
error::Error,
|
||||
util::{get_web_vault_version, Cached, SafeString},
|
||||
util::{Cached, SafeString},
|
||||
CONFIG,
|
||||
};
|
||||
|
||||
@ -54,43 +53,7 @@ fn not_found() -> ApiResult<Html<String>> {
|
||||
|
||||
#[get("/css/vaultwarden.css")]
|
||||
fn vaultwarden_css() -> Cached<Css<String>> {
|
||||
// Configure the web-vault version as an integer so it can be used as a comparison smaller or greater then.
|
||||
// The default is based upon the version since this feature is added.
|
||||
static WEB_VAULT_VERSION: Lazy<u32> = Lazy::new(|| {
|
||||
let re = regex::Regex::new(r"(\d{4})\.(\d{1,2})\.(\d{1,2})").unwrap();
|
||||
let vault_version = get_web_vault_version();
|
||||
|
||||
let (major, minor, patch) = match re.captures(&vault_version) {
|
||||
Some(c) if c.len() == 4 => (
|
||||
c.get(1).unwrap().as_str().parse().unwrap(),
|
||||
c.get(2).unwrap().as_str().parse().unwrap(),
|
||||
c.get(3).unwrap().as_str().parse().unwrap(),
|
||||
),
|
||||
_ => (2024, 6, 2),
|
||||
};
|
||||
format!("{major}{minor:02}{patch:02}").parse::<u32>().unwrap()
|
||||
});
|
||||
|
||||
// Configure the Vaultwarden version as an integer so it can be used as a comparison smaller or greater then.
|
||||
// The default is based upon the version since this feature is added.
|
||||
static VW_VERSION: Lazy<u32> = Lazy::new(|| {
|
||||
let re = regex::Regex::new(r"(\d{1})\.(\d{1,2})\.(\d{1,2})").unwrap();
|
||||
let vw_version = crate::VERSION.unwrap_or("1.32.1");
|
||||
|
||||
let (major, minor, patch) = match re.captures(vw_version) {
|
||||
Some(c) if c.len() == 4 => (
|
||||
c.get(1).unwrap().as_str().parse().unwrap(),
|
||||
c.get(2).unwrap().as_str().parse().unwrap(),
|
||||
c.get(3).unwrap().as_str().parse().unwrap(),
|
||||
),
|
||||
_ => (1, 32, 1),
|
||||
};
|
||||
format!("{major}{minor:02}{patch:02}").parse::<u32>().unwrap()
|
||||
});
|
||||
|
||||
let css_options = json!({
|
||||
"web_vault_version": *WEB_VAULT_VERSION,
|
||||
"vw_version": *VW_VERSION,
|
||||
"signup_disabled": !CONFIG.signups_allowed() && CONFIG.signups_domains_whitelist().is_empty(),
|
||||
"mail_enabled": CONFIG.mail_enabled(),
|
||||
"yubico_enabled": CONFIG._enable_yubico() && (CONFIG.yubico_client_id().is_some() == CONFIG.yubico_secret_key().is_some()),
|
||||
|
@ -12,7 +12,7 @@ use reqwest::Url;
|
||||
use crate::{
|
||||
db::DbConnType,
|
||||
error::Error,
|
||||
util::{get_env, get_env_bool, parse_experimental_client_feature_flags},
|
||||
util::{get_env, get_env_bool, get_web_vault_version, parse_experimental_client_feature_flags},
|
||||
};
|
||||
|
||||
static CONFIG_FILE: Lazy<String> = Lazy::new(|| {
|
||||
@ -830,6 +830,7 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
|
||||
"browser-fileless-import",
|
||||
"extension-refresh",
|
||||
"fido2-vault-credentials",
|
||||
"inline-menu-positioning-improvements",
|
||||
"ssh-key-vault-item",
|
||||
"ssh-agent",
|
||||
];
|
||||
@ -1327,6 +1328,8 @@ where
|
||||
// Register helpers
|
||||
hb.register_helper("case", Box::new(case_helper));
|
||||
hb.register_helper("to_json", Box::new(to_json));
|
||||
hb.register_helper("webver", Box::new(webver));
|
||||
hb.register_helper("vwver", Box::new(vwver));
|
||||
|
||||
macro_rules! reg {
|
||||
($name:expr) => {{
|
||||
@ -1431,3 +1434,42 @@ fn to_json<'reg, 'rc>(
|
||||
out.write(&json)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Configure the web-vault version as an integer so it can be used as a comparison smaller or greater then.
|
||||
// The default is based upon the version since this feature is added.
|
||||
static WEB_VAULT_VERSION: Lazy<semver::Version> = Lazy::new(|| {
|
||||
let vault_version = get_web_vault_version();
|
||||
// Use a single regex capture to extract version components
|
||||
let re = regex::Regex::new(r"(\d{4})\.(\d{1,2})\.(\d{1,2})").unwrap();
|
||||
re.captures(&vault_version)
|
||||
.and_then(|c| {
|
||||
(c.len() == 4).then(|| {
|
||||
format!("{}.{}.{}", c.get(1).unwrap().as_str(), c.get(2).unwrap().as_str(), c.get(3).unwrap().as_str())
|
||||
})
|
||||
})
|
||||
.and_then(|v| semver::Version::parse(&v).ok())
|
||||
.unwrap_or_else(|| semver::Version::parse("2024.6.2").unwrap())
|
||||
});
|
||||
|
||||
// Configure the Vaultwarden version as an integer so it can be used as a comparison smaller or greater then.
|
||||
// The default is based upon the version since this feature is added.
|
||||
static VW_VERSION: Lazy<semver::Version> = Lazy::new(|| {
|
||||
let vw_version = crate::VERSION.unwrap_or("1.32.5");
|
||||
// Use a single regex capture to extract version components
|
||||
let re = regex::Regex::new(r"(\d{1})\.(\d{1,2})\.(\d{1,2})").unwrap();
|
||||
re.captures(vw_version)
|
||||
.and_then(|c| {
|
||||
(c.len() == 4).then(|| {
|
||||
format!("{}.{}.{}", c.get(1).unwrap().as_str(), c.get(2).unwrap().as_str(), c.get(3).unwrap().as_str())
|
||||
})
|
||||
})
|
||||
.and_then(|v| semver::Version::parse(&v).ok())
|
||||
.unwrap_or_else(|| semver::Version::parse("1.32.5").unwrap())
|
||||
});
|
||||
|
||||
handlebars::handlebars_helper!(webver: | web_vault_version: String |
|
||||
semver::VersionReq::parse(&web_vault_version).expect("Invalid web-vault version compare string").matches(&WEB_VAULT_VERSION)
|
||||
);
|
||||
handlebars::handlebars_helper!(vwver: | vw_version: String |
|
||||
semver::VersionReq::parse(&vw_version).expect("Invalid Vaultwarden version compare string").matches(&VW_VERSION)
|
||||
);
|
||||
|
@ -241,12 +241,23 @@ impl Cipher {
|
||||
// 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.
|
||||
if self.atype == 1 {
|
||||
if type_data_json["uris"].is_array() {
|
||||
let uri = type_data_json["uris"][0]["uri"].clone();
|
||||
type_data_json["uri"] = uri;
|
||||
} else {
|
||||
// Upstream always has an Uri key/value
|
||||
type_data_json["uri"] = Value::Null;
|
||||
// Upstream always has an `uri` key/value
|
||||
type_data_json["uri"] = Value::Null;
|
||||
if let Some(uris) = type_data_json["uris"].as_array_mut() {
|
||||
if !uris.is_empty() {
|
||||
// Fix uri match values first, they are only allowed to be a number or null
|
||||
// If it is a string, convert it to an int or null if that fails
|
||||
for uri in &mut *uris {
|
||||
if uri["match"].is_string() {
|
||||
let match_value = match uri["match"].as_str().unwrap_or_default().parse::<u8>() {
|
||||
Ok(n) => json!(n),
|
||||
_ => Value::Null,
|
||||
};
|
||||
uri["match"] = match_value;
|
||||
}
|
||||
}
|
||||
type_data_json["uri"] = uris[0]["uri"].clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,6 +272,19 @@ impl Cipher {
|
||||
}
|
||||
}
|
||||
|
||||
// Fix invalid SSH Entries
|
||||
// This breaks at least the native mobile client if invalid
|
||||
// The only way to fix this is by setting type_data_json to `null`
|
||||
// Opening this ssh-key in the mobile client will probably crash the client, but you can edit, save and afterwards delete it
|
||||
if self.atype == 5
|
||||
&& (type_data_json["keyFingerprint"].as_str().is_none_or(|v| v.is_empty())
|
||||
|| type_data_json["privateKey"].as_str().is_none_or(|v| v.is_empty())
|
||||
|| type_data_json["publicKey"].as_str().is_none_or(|v| v.is_empty()))
|
||||
{
|
||||
warn!("Error parsing ssh-key, mandatory fields are invalid for {}", self.uuid);
|
||||
type_data_json = Value::Null;
|
||||
}
|
||||
|
||||
// Clone the type_data and add some default value.
|
||||
let mut data_json = type_data_json.clone();
|
||||
|
||||
|
@ -511,7 +511,10 @@ impl CollectionUser {
|
||||
}}
|
||||
}
|
||||
|
||||
pub async fn find_by_organization(org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
|
||||
pub async fn find_by_organization_swap_user_uuid_with_org_user_uuid(
|
||||
org_uuid: &str,
|
||||
conn: &mut DbConn,
|
||||
) -> Vec<Self> {
|
||||
db_run! { conn: {
|
||||
users_collections::table
|
||||
.inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid)))
|
||||
|
@ -74,6 +74,9 @@ impl Group {
|
||||
}
|
||||
|
||||
pub async fn to_json_details(&self, conn: &mut DbConn) -> Value {
|
||||
// If both read_only and hide_passwords are false, then manage should be true
|
||||
// You can't have an entry with read_only and manage, or hide_passwords and manage
|
||||
// Or an entry with everything to false
|
||||
let collections_groups: Vec<Value> = CollectionGroup::find_by_group(&self.uuid, conn)
|
||||
.await
|
||||
.iter()
|
||||
@ -82,7 +85,7 @@ impl Group {
|
||||
"id": entry.collections_uuid,
|
||||
"readOnly": entry.read_only,
|
||||
"hidePasswords": entry.hide_passwords,
|
||||
"manage": false
|
||||
"manage": !entry.read_only && !entry.hide_passwords,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
@ -73,6 +73,8 @@ impl UserOrgType {
|
||||
"1" | "Admin" => Some(UserOrgType::Admin),
|
||||
"2" | "User" => Some(UserOrgType::User),
|
||||
"3" | "Manager" => Some(UserOrgType::Manager),
|
||||
// HACK: We convert the custom role to a manager role
|
||||
"4" | "Custom" => Some(UserOrgType::Manager),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -85,7 +87,7 @@ impl Ord for UserOrgType {
|
||||
3, // Owner
|
||||
2, // Admin
|
||||
0, // User
|
||||
1, // Manager
|
||||
1, // Manager && Custom
|
||||
];
|
||||
ACCESS_LEVEL[*self as usize].cmp(&ACCESS_LEVEL[*other as usize])
|
||||
}
|
||||
@ -158,33 +160,46 @@ impl Organization {
|
||||
pub fn to_json(&self) -> Value {
|
||||
json!({
|
||||
"id": self.uuid,
|
||||
"identifier": null, // not supported by us
|
||||
"name": self.name,
|
||||
"seats": null,
|
||||
"maxCollections": null,
|
||||
"maxStorageGb": i16::MAX, // The value doesn't matter, we don't check server-side
|
||||
"use2fa": true,
|
||||
"useCustomPermissions": false,
|
||||
"useCustomPermissions": true,
|
||||
"useDirectory": false, // Is supported, but this value isn't checked anywhere (yet)
|
||||
"useEvents": CONFIG.org_events_enabled(),
|
||||
"useGroups": CONFIG.org_groups_enabled(),
|
||||
"useTotp": true,
|
||||
"usePolicies": true,
|
||||
// "useScim": false, // Not supported (Not AGPLv3 Licensed)
|
||||
"useScim": false, // Not supported (Not AGPLv3 Licensed)
|
||||
"useSso": false, // Not supported
|
||||
// "useKeyConnector": false, // Not supported
|
||||
"useKeyConnector": false, // Not supported
|
||||
"usePasswordManager": true,
|
||||
"useSecretsManager": false, // Not supported (Not AGPLv3 Licensed)
|
||||
"selfHost": true,
|
||||
"useApi": true,
|
||||
"hasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(),
|
||||
"useResetPassword": CONFIG.mail_enabled(),
|
||||
"allowAdminAccessToAllCollectionItems": true,
|
||||
"limitCollectionCreation": true,
|
||||
"limitCollectionCreationDeletion": true,
|
||||
"limitCollectionDeletion": true,
|
||||
|
||||
"businessName": null,
|
||||
"businessName": self.name,
|
||||
"businessAddress1": null,
|
||||
"businessAddress2": null,
|
||||
"businessAddress3": null,
|
||||
"businessCountry": null,
|
||||
"businessTaxNumber": null,
|
||||
|
||||
"maxAutoscaleSeats": null,
|
||||
"maxAutoscaleSmSeats": null,
|
||||
"maxAutoscaleSmServiceAccounts": null,
|
||||
|
||||
"secretsManagerPlan": null,
|
||||
"smSeats": null,
|
||||
"smServiceAccounts": null,
|
||||
|
||||
"billingEmail": self.billing_email,
|
||||
"planType": 6, // Custom plan
|
||||
"usersGetPremium": true,
|
||||
@ -252,6 +267,15 @@ impl UserOrganization {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// HACK: Convert the manager type to a custom type
|
||||
/// It will be converted back on other locations
|
||||
pub fn type_manager_as_custom(&self) -> i32 {
|
||||
match self.atype {
|
||||
3 => 4,
|
||||
_ => self.atype,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OrganizationApiKey {
|
||||
@ -356,17 +380,21 @@ impl UserOrganization {
|
||||
pub async fn to_json(&self, conn: &mut DbConn) -> Value {
|
||||
let org = Organization::find_by_uuid(&self.org_uuid, conn).await.unwrap();
|
||||
|
||||
// HACK: Convert the manager type to a custom type
|
||||
// It will be converted back on other locations
|
||||
let user_org_type = self.type_manager_as_custom();
|
||||
|
||||
let permissions = json!({
|
||||
// TODO: Add support for Custom User Roles
|
||||
// TODO: Add full support for Custom User Roles
|
||||
// See: https://bitwarden.com/help/article/user-types-access-control/#custom-role
|
||||
// Currently we use the custom role as a manager role and link the 3 Collection roles to mimic the access_all permission
|
||||
"accessEventLogs": false,
|
||||
"accessImportExport": false,
|
||||
"accessReports": false,
|
||||
"createNewCollections": false,
|
||||
"editAnyCollection": false,
|
||||
"deleteAnyCollection": false,
|
||||
"editAssignedCollections": false,
|
||||
"deleteAssignedCollections": false,
|
||||
// If the following 3 Collection roles are set to true a custom user has access all permission
|
||||
"createNewCollections": user_org_type == 4 && self.access_all,
|
||||
"editAnyCollection": user_org_type == 4 && self.access_all,
|
||||
"deleteAnyCollection": user_org_type == 4 && self.access_all,
|
||||
"manageGroups": false,
|
||||
"managePolicies": false,
|
||||
"manageSso": false, // Not supported
|
||||
@ -398,9 +426,9 @@ impl UserOrganization {
|
||||
"ssoBound": false, // Not supported
|
||||
"useSso": false, // Not supported
|
||||
"useKeyConnector": false,
|
||||
"useSecretsManager": false,
|
||||
"useSecretsManager": false, // Not supported (Not AGPLv3 Licensed)
|
||||
"usePasswordManager": true,
|
||||
"useCustomPermissions": false,
|
||||
"useCustomPermissions": true,
|
||||
"useActivateAutofillPolicy": false,
|
||||
|
||||
"organizationUserId": self.uuid,
|
||||
@ -417,9 +445,11 @@ impl UserOrganization {
|
||||
"familySponsorshipValidUntil": null,
|
||||
"familySponsorshipToDelete": null,
|
||||
"accessSecretsManager": false,
|
||||
"limitCollectionCreationDeletion": false, // This should be set to true only when we can handle roles like createNewCollections
|
||||
"limitCollectionCreation": true,
|
||||
"limitCollectionCreationDeletion": true,
|
||||
"limitCollectionDeletion": true,
|
||||
"allowAdminAccessToAllCollectionItems": true,
|
||||
"flexibleCollections": false,
|
||||
"userIsManagedByOrganization": false, // Means not managed via the Members UI, like SSO
|
||||
|
||||
"permissions": permissions,
|
||||
|
||||
@ -429,7 +459,7 @@ impl UserOrganization {
|
||||
"userId": self.user_uuid,
|
||||
"key": self.akey,
|
||||
"status": self.status,
|
||||
"type": self.atype,
|
||||
"type": user_org_type,
|
||||
"enabled": true,
|
||||
|
||||
"object": "profileOrganization",
|
||||
@ -516,24 +546,34 @@ impl UserOrganization {
|
||||
Vec::with_capacity(0)
|
||||
};
|
||||
|
||||
let permissions = json!({
|
||||
// TODO: Add support for Custom User Roles
|
||||
// See: https://bitwarden.com/help/article/user-types-access-control/#custom-role
|
||||
"accessEventLogs": false,
|
||||
"accessImportExport": false,
|
||||
"accessReports": false,
|
||||
"createNewCollections": false,
|
||||
"editAnyCollection": false,
|
||||
"deleteAnyCollection": false,
|
||||
"editAssignedCollections": false,
|
||||
"deleteAssignedCollections": false,
|
||||
"manageGroups": false,
|
||||
"managePolicies": false,
|
||||
"manageSso": false, // Not supported
|
||||
"manageUsers": false,
|
||||
"manageResetPassword": false,
|
||||
"manageScim": false // Not supported (Not AGPLv3 Licensed)
|
||||
});
|
||||
// HACK: Convert the manager type to a custom type
|
||||
// It will be converted back on other locations
|
||||
let user_org_type = self.type_manager_as_custom();
|
||||
|
||||
// HACK: Only return permissions if the user is of type custom and has access_all
|
||||
// Else Bitwarden will assume the defaults of all false
|
||||
let permissions = if user_org_type == 4 && self.access_all {
|
||||
json!({
|
||||
// TODO: Add full support for Custom User Roles
|
||||
// See: https://bitwarden.com/help/article/user-types-access-control/#custom-role
|
||||
// Currently we use the custom role as a manager role and link the 3 Collection roles to mimic the access_all permission
|
||||
"accessEventLogs": false,
|
||||
"accessImportExport": false,
|
||||
"accessReports": false,
|
||||
// If the following 3 Collection roles are set to true a custom user has access all permission
|
||||
"createNewCollections": true,
|
||||
"editAnyCollection": true,
|
||||
"deleteAnyCollection": true,
|
||||
"manageGroups": false,
|
||||
"managePolicies": false,
|
||||
"manageSso": false, // Not supported
|
||||
"manageUsers": false,
|
||||
"manageResetPassword": false,
|
||||
"manageScim": false // Not supported (Not AGPLv3 Licensed)
|
||||
})
|
||||
} else {
|
||||
json!(null)
|
||||
};
|
||||
|
||||
json!({
|
||||
"id": self.uuid,
|
||||
@ -546,7 +586,7 @@ impl UserOrganization {
|
||||
"collections": collections,
|
||||
|
||||
"status": status,
|
||||
"type": self.atype,
|
||||
"type": user_org_type,
|
||||
"accessAll": self.access_all,
|
||||
"twoFactorEnabled": twofactor_enabled,
|
||||
"resetPasswordEnrolled": self.reset_password_key.is_some(),
|
||||
@ -608,6 +648,29 @@ impl UserOrganization {
|
||||
"object": "organizationUserDetails",
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn to_json_mini_details(&self, conn: &mut DbConn) -> Value {
|
||||
let user = User::find_by_uuid(&self.user_uuid, conn).await.unwrap();
|
||||
|
||||
// Because Bitwarden wants the status to be -1 for revoked users we need to catch that here.
|
||||
// We subtract/add a number so we can restore/activate the user to it's previous state again.
|
||||
let status = if self.status < UserOrgStatus::Revoked as i32 {
|
||||
UserOrgStatus::Revoked as i32
|
||||
} else {
|
||||
self.status
|
||||
};
|
||||
|
||||
json!({
|
||||
"id": self.uuid,
|
||||
"userId": self.user_uuid,
|
||||
"type": self.type_manager_as_custom(), // HACK: Convert the manager type to a custom type
|
||||
"status": status,
|
||||
"name": user.name,
|
||||
"email": user.email,
|
||||
"object": "organizationUserUserMiniDetails",
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn save(&self, conn: &mut DbConn) -> EmptyResult {
|
||||
User::update_uuid_revision(&self.user_uuid, conn).await;
|
||||
|
||||
@ -1015,5 +1078,6 @@ mod tests {
|
||||
assert!(UserOrgType::Owner > UserOrgType::Admin);
|
||||
assert!(UserOrgType::Admin > UserOrgType::Manager);
|
||||
assert!(UserOrgType::Manager > UserOrgType::User);
|
||||
assert!(UserOrgType::Manager == UserOrgType::from_str("4").unwrap());
|
||||
}
|
||||
}
|
||||
|
2
src/static/scripts/admin_users.js
vendored
2
src/static/scripts/admin_users.js
vendored
@ -152,7 +152,7 @@ const ORG_TYPES = {
|
||||
"name": "User",
|
||||
"bg": "blue"
|
||||
},
|
||||
"3": {
|
||||
"4": {
|
||||
"name": "Manager",
|
||||
"bg": "green"
|
||||
},
|
||||
|
@ -42,12 +42,6 @@ label[for^="ownedBusiness"] {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
|
||||
/* Hide the radio button and label for the `Custom` org user type */
|
||||
#userTypeCustom,
|
||||
label[for^="userTypeCustom"] {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
|
||||
/* Hide Business Name */
|
||||
app-org-account form div bit-form-field.tw-block:nth-child(3) {
|
||||
@extend %vw-hide;
|
||||
@ -58,42 +52,77 @@ app-organization-plans > form > bit-section:nth-child(2) {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
|
||||
/* Hide Collection Management Form */
|
||||
app-org-account form.ng-untouched:nth-child(6) {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
|
||||
/* Hide 'Member Access' Report Card from Org Reports */
|
||||
app-org-reports-home > app-report-list > div.tw-inline-grid > div:nth-child(6) {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
|
||||
/* Hide Device Verification form at the Two Step Login screen */
|
||||
app-security > app-two-factor-setup > form {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
|
||||
/* Hide unsupported Custom Role options */
|
||||
bit-dialog div.tw-ml-4:has(bit-form-control input),
|
||||
bit-dialog div.tw-col-span-4:has(input[formcontrolname*="access"], input[formcontrolname*="manage"]) {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
|
||||
/* Hide Log in with passkey */
|
||||
app-login div.tw-flex:nth-child(4) {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
|
||||
/* Change collapsed menu icon to Vaultwarden */
|
||||
bit-nav-logo bit-nav-item a:before {
|
||||
content: "";
|
||||
background-image: url("../images/icon-white.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
height: 32px;
|
||||
display: block;
|
||||
}
|
||||
bit-nav-logo bit-nav-item .bwi-shield {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
/**** END Static Vaultwarden Changes ****/
|
||||
/**** START Dynamic Vaultwarden Changes ****/
|
||||
{{#if signup_disabled}}
|
||||
/* Hide the register link on the login screen */
|
||||
app-frontend-layout > app-login > form > div > div > div > p {
|
||||
app-login form div + div + div + div + hr,
|
||||
app-login form div + div + div + div + hr + p {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
{{/if}}
|
||||
|
||||
/* Hide `Email` 2FA if mail is not enabled */
|
||||
{{#unless mail_enabled}}
|
||||
app-two-factor-setup ul.list-group.list-group-2fa li.list-group-item:nth-child(5) {
|
||||
/* Hide `Email` 2FA if mail is not enabled */
|
||||
app-two-factor-setup ul.list-group.list-group-2fa li.list-group-item:nth-child(1) {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
{{/unless}}
|
||||
|
||||
/* Hide `YubiKey OTP security key` 2FA if it is not enabled */
|
||||
{{#unless yubico_enabled}}
|
||||
app-two-factor-setup ul.list-group.list-group-2fa li.list-group-item:nth-child(2) {
|
||||
/* Hide `YubiKey OTP security key` 2FA if it is not enabled */
|
||||
app-two-factor-setup ul.list-group.list-group-2fa li.list-group-item:nth-child(4) {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
{{/unless}}
|
||||
|
||||
/* Hide Emergency Access if not allowed */
|
||||
{{#unless emergency_access_allowed}}
|
||||
/* Hide Emergency Access if not allowed */
|
||||
bit-nav-item[route="settings/emergency-access"] {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
{{/unless}}
|
||||
|
||||
/* Hide Sends if not allowed */
|
||||
{{#unless sends_allowed}}
|
||||
/* Hide Sends if not allowed */
|
||||
bit-nav-item[route="sends"] {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user