Spiegel von
https://github.com/dani-garcia/vaultwarden.git
synchronisiert 2025-01-31 10:08:56 +01:00
Merge branch 'main' into register_verify_email
Dieser Commit ist enthalten in:
Commit
23b0863091
22 geänderte Dateien mit 690 neuen und 308 gelöschten Zeilen
|
@ -351,6 +351,7 @@
|
||||||
## - "browser-fileless-import": Directly import credentials from other providers without a file.
|
## - "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)
|
## - "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.
|
## - "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-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)
|
## - "ssh-agent": Enable SSH agent support on Desktop. (Needs desktop >=2024.12.0)
|
||||||
# EXPERIMENTAL_CLIENT_FEATURE_FLAGS=fido2-vault-credentials
|
# EXPERIMENTAL_CLIENT_FEATURE_FLAGS=fido2-vault-credentials
|
||||||
|
|
7
.github/workflows/build.yml
gevendort
7
.github/workflows/build.yml
gevendort
|
@ -75,7 +75,7 @@ jobs:
|
||||||
|
|
||||||
# Only install the clippy and rustfmt components on the default rust-toolchain
|
# Only install the clippy and rustfmt components on the default rust-toolchain
|
||||||
- name: "Install rust-toolchain version"
|
- 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' }}
|
if: ${{ matrix.channel == 'rust-toolchain' }}
|
||||||
with:
|
with:
|
||||||
toolchain: "${{steps.toolchain.outputs.RUST_TOOLCHAIN}}"
|
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
|
# Install the any other channel to be used for which we do not execute clippy and rustfmt
|
||||||
- name: "Install MSRV version"
|
- 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' }}
|
if: ${{ matrix.channel != 'rust-toolchain' }}
|
||||||
with:
|
with:
|
||||||
toolchain: "${{steps.toolchain.outputs.RUST_TOOLCHAIN}}"
|
toolchain: "${{steps.toolchain.outputs.RUST_TOOLCHAIN}}"
|
||||||
|
@ -107,7 +107,8 @@ jobs:
|
||||||
# End Show environment
|
# End Show environment
|
||||||
|
|
||||||
# Enable Rust Caching
|
# Enable Rust Caching
|
||||||
- uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5
|
- name: Rust Caching
|
||||||
|
uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7
|
||||||
with:
|
with:
|
||||||
# Use a custom prefix-key to force a fresh start. This is sometimes needed with bigger changes.
|
# 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.
|
# Like changing the build host from Ubuntu 20.04 to 22.04 for example.
|
||||||
|
|
2
.github/workflows/hadolint.yml
gevendort
2
.github/workflows/hadolint.yml
gevendort
|
@ -18,7 +18,7 @@ jobs:
|
||||||
|
|
||||||
# Start Docker Buildx
|
# Start Docker Buildx
|
||||||
- name: Setup 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
|
# https://github.com/moby/buildkit/issues/3969
|
||||||
# Also set max parallelism to 2, the default of 4 breaks GitHub Actions and causes OOMKills
|
# Also set max parallelism to 2, the default of 4 breaks GitHub Actions and causes OOMKills
|
||||||
with:
|
with:
|
||||||
|
|
12
.github/workflows/release.yml
gevendort
12
.github/workflows/release.yml
gevendort
|
@ -69,7 +69,7 @@ jobs:
|
||||||
|
|
||||||
# Start Docker Buildx
|
# Start Docker Buildx
|
||||||
- name: Setup 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
|
# https://github.com/moby/buildkit/issues/3969
|
||||||
# Also set max parallelism to 2, the default of 4 breaks GitHub Actions and causes OOMKills
|
# Also set max parallelism to 2, the default of 4 breaks GitHub Actions and causes OOMKills
|
||||||
with:
|
with:
|
||||||
|
@ -165,7 +165,7 @@ jobs:
|
||||||
echo "CONTAINER_REGISTRIES=${CONTAINER_REGISTRIES:+${CONTAINER_REGISTRIES},}localhost:5000/vaultwarden/server" | tee -a "${GITHUB_ENV}"
|
echo "CONTAINER_REGISTRIES=${CONTAINER_REGISTRIES:+${CONTAINER_REGISTRIES},}localhost:5000/vaultwarden/server" | tee -a "${GITHUB_ENV}"
|
||||||
|
|
||||||
- name: Bake ${{ matrix.base_image }} containers
|
- name: Bake ${{ matrix.base_image }} containers
|
||||||
uses: docker/bake-action@2e3d19baedb14545e5d41222653874f25d5b4dfb # v5.10.0
|
uses: docker/bake-action@3fc70e1131fee40a422dd8dd0ff22014ae20a1f3 # v5.11.0
|
||||||
env:
|
env:
|
||||||
BASE_TAGS: "${{ env.BASE_TAGS }}"
|
BASE_TAGS: "${{ env.BASE_TAGS }}"
|
||||||
SOURCE_COMMIT: "${{ env.SOURCE_COMMIT }}"
|
SOURCE_COMMIT: "${{ env.SOURCE_COMMIT }}"
|
||||||
|
@ -223,28 +223,28 @@ jobs:
|
||||||
|
|
||||||
# Upload artifacts to Github Actions
|
# Upload artifacts to Github Actions
|
||||||
- name: "Upload amd64 artifact"
|
- 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' }}
|
if: ${{ matrix.base_image == 'alpine' }}
|
||||||
with:
|
with:
|
||||||
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-amd64
|
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-amd64
|
||||||
path: vaultwarden-amd64
|
path: vaultwarden-amd64
|
||||||
|
|
||||||
- name: "Upload arm64 artifact"
|
- 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' }}
|
if: ${{ matrix.base_image == 'alpine' }}
|
||||||
with:
|
with:
|
||||||
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-arm64
|
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-arm64
|
||||||
path: vaultwarden-arm64
|
path: vaultwarden-arm64
|
||||||
|
|
||||||
- name: "Upload armv7 artifact"
|
- 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' }}
|
if: ${{ matrix.base_image == 'alpine' }}
|
||||||
with:
|
with:
|
||||||
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-armv7
|
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-armv7
|
||||||
path: vaultwarden-armv7
|
path: vaultwarden-armv7
|
||||||
|
|
||||||
- name: "Upload armv6 artifact"
|
- 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' }}
|
if: ${{ matrix.base_image == 'alpine' }}
|
||||||
with:
|
with:
|
||||||
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-armv6
|
name: vaultwarden-${{ env.SOURCE_VERSION }}-linux-armv6
|
||||||
|
|
242
Cargo.lock
generiert
242
Cargo.lock
generiert
|
@ -176,7 +176,7 @@ version = "3.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
|
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"event-listener 5.3.1",
|
"event-listener 5.4.0",
|
||||||
"event-listener-strategy",
|
"event-listener-strategy",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
@ -194,7 +194,7 @@ dependencies = [
|
||||||
"async-task",
|
"async-task",
|
||||||
"blocking",
|
"blocking",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"event-listener 5.3.1",
|
"event-listener 5.4.0",
|
||||||
"futures-lite",
|
"futures-lite",
|
||||||
"rustix",
|
"rustix",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -275,9 +275,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.83"
|
version = "0.1.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -435,9 +435,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.20.0"
|
version = "1.21.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
|
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
|
@ -489,9 +489,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.4"
|
version = "1.2.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf"
|
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
@ -637,9 +637,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.20"
|
version = "0.8.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
|
@ -721,6 +721,37 @@ dependencies = [
|
||||||
"powerfmt",
|
"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]]
|
[[package]]
|
||||||
name = "devise"
|
name = "devise"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
@ -918,6 +949,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_home"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -942,9 +979,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "event-listener"
|
name = "event-listener"
|
||||||
version = "5.3.1"
|
version = "5.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
|
checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
"parking",
|
"parking",
|
||||||
|
@ -957,7 +994,7 @@ version = "0.5.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
|
checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"event-listener 5.3.1",
|
"event-listener 5.4.0",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1185,9 +1222,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gloo-timers"
|
name = "gloo-timers"
|
||||||
|
@ -1262,17 +1299,18 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "handlebars"
|
name = "handlebars"
|
||||||
version = "6.2.0"
|
version = "6.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315"
|
checksum = "3d6b224b95c1e668ac0270325ad563b2eef1469fbbb8959bc7c692c844b813d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"derive_builder",
|
||||||
"log",
|
"log",
|
||||||
"num-order",
|
"num-order",
|
||||||
"pest",
|
"pest",
|
||||||
"pest_derive",
|
"pest_derive",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 1.0.69",
|
"thiserror 2.0.9",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1364,15 +1402,6 @@ dependencies = [
|
||||||
"digest",
|
"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]]
|
[[package]]
|
||||||
name = "hostname"
|
name = "hostname"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -1474,9 +1503,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.31"
|
version = "0.14.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85"
|
checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
|
@ -1497,9 +1526,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.5.1"
|
version = "1.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f"
|
checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
|
@ -1517,13 +1546,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-rustls"
|
name = "hyper-rustls"
|
||||||
version = "0.27.3"
|
version = "0.27.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
|
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.2.0",
|
"http 1.2.0",
|
||||||
"hyper 1.5.1",
|
"hyper 1.5.2",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"rustls 0.23.20",
|
"rustls 0.23.20",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
@ -1540,7 +1569,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper 1.5.1",
|
"hyper 1.5.2",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -1559,7 +1588,7 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.2.0",
|
"http 1.2.0",
|
||||||
"http-body 1.0.1",
|
"http-body 1.0.1",
|
||||||
"hyper 1.5.1",
|
"hyper 1.5.2",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -1886,9 +1915,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.168"
|
version = "0.2.169"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
|
@ -1925,9 +1954,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.14"
|
version = "0.4.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
|
@ -2049,9 +2078,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.0"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler2",
|
"adler2",
|
||||||
]
|
]
|
||||||
|
@ -2226,9 +2255,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.5"
|
version = "0.36.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
@ -2400,7 +2429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
|
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.9",
|
||||||
"ucd-trie",
|
"ucd-trie",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2440,9 +2469,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.11.2"
|
version = "0.11.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_macros",
|
"phf_macros",
|
||||||
"phf_shared",
|
"phf_shared",
|
||||||
|
@ -2450,9 +2479,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf_codegen"
|
name = "phf_codegen"
|
||||||
version = "0.11.2"
|
version = "0.11.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
|
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_generator",
|
"phf_generator",
|
||||||
"phf_shared",
|
"phf_shared",
|
||||||
|
@ -2460,9 +2489,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf_generator"
|
name = "phf_generator"
|
||||||
version = "0.11.2"
|
version = "0.11.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
|
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_shared",
|
"phf_shared",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -2470,9 +2499,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf_macros"
|
name = "phf_macros"
|
||||||
version = "0.11.2"
|
version = "0.11.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
|
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_generator",
|
"phf_generator",
|
||||||
"phf_shared",
|
"phf_shared",
|
||||||
|
@ -2483,9 +2512,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf_shared"
|
name = "phf_shared"
|
||||||
version = "0.11.2"
|
version = "0.11.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
|
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"siphasher",
|
"siphasher",
|
||||||
]
|
]
|
||||||
|
@ -2498,9 +2527,9 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.15"
|
version = "0.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "pin-utils"
|
||||||
|
@ -2619,9 +2648,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quanta"
|
name = "quanta"
|
||||||
version = "0.12.4"
|
version = "0.12.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "773ce68d0bb9bc7ef20be3536ffe94e223e1f365bd374108b2659fac0c65cfe6"
|
checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -2640,9 +2669,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.37"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -2789,9 +2818,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.9"
|
version = "0.12.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f"
|
checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-compression",
|
"async-compression",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
|
@ -2806,7 +2835,7 @@ dependencies = [
|
||||||
"http 1.2.0",
|
"http 1.2.0",
|
||||||
"http-body 1.0.1",
|
"http-body 1.0.1",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper 1.5.1",
|
"hyper 1.5.2",
|
||||||
"hyper-rustls",
|
"hyper-rustls",
|
||||||
"hyper-tls",
|
"hyper-tls",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
|
@ -2828,6 +2857,7 @@ dependencies = [
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
"tokio-socks",
|
"tokio-socks",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
"tower",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
@ -2948,7 +2978,7 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"futures",
|
"futures",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
"hyper 0.14.31",
|
"hyper 0.14.32",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -3007,9 +3037,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.42"
|
version = "0.38.43"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
|
@ -3090,9 +3120,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.18"
|
version = "1.0.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
|
@ -3164,9 +3194,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework-sys"
|
name = "security-framework-sys"
|
||||||
version = "2.12.1"
|
version = "2.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2"
|
checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -3180,9 +3210,9 @@ checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.216"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
@ -3199,9 +3229,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.216"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -3210,9 +3240,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.133"
|
version = "1.0.135"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -3311,9 +3341,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.11"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
|
@ -3406,9 +3436,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.90"
|
version = "2.0.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -3470,12 +3500,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.14.0"
|
version = "3.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
|
"getrandom",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
|
@ -3492,11 +3523,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.7"
|
version = "2.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767"
|
checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl 2.0.7",
|
"thiserror-impl 2.0.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3512,9 +3543,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "2.0.7"
|
version = "2.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36"
|
checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -3585,9 +3616,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.8.0"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec_macros",
|
"tinyvec_macros",
|
||||||
]
|
]
|
||||||
|
@ -3751,6 +3782,27 @@ dependencies = [
|
||||||
"sha2",
|
"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]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
@ -4165,12 +4217,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "which"
|
name = "which"
|
||||||
version = "7.0.0"
|
version = "7.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9cad3279ade7346b96e38731a641d7343dd6a53d55083dd54eadfa5a1b38c6b"
|
checksum = "fb4a9e33648339dc1642b0e36e21b3385e6148e289226f657c809dee59df5028"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"home",
|
"env_home",
|
||||||
"rustix",
|
"rustix",
|
||||||
"winsafe",
|
"winsafe",
|
||||||
]
|
]
|
||||||
|
@ -4420,9 +4472,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.20"
|
version = "0.6.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"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"] }
|
tokio = { version = "1.42.0", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time", "signal", "net"] }
|
||||||
|
|
||||||
# A generic serialization/deserialization framework
|
# A generic serialization/deserialization framework
|
||||||
serde = { version = "1.0.216", features = ["derive"] }
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
serde_json = "1.0.133"
|
serde_json = "1.0.135"
|
||||||
|
|
||||||
# A safe, extensible ORM and Query builder
|
# A safe, extensible ORM and Query builder
|
||||||
diesel = { version = "2.2.6", features = ["chrono", "r2d2", "numeric"] }
|
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"
|
email_address = "0.2.9"
|
||||||
|
|
||||||
# HTML Template library
|
# 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)
|
# 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"
|
hickory-resolver = "0.24.2"
|
||||||
|
|
||||||
# Favicon extraction libraries
|
# Favicon extraction libraries
|
||||||
|
@ -155,7 +155,7 @@ semver = "1.0.24"
|
||||||
# Allow overriding the default memory allocator
|
# Allow overriding the default memory allocator
|
||||||
# Mainly used for the musl builds, since the default musl malloc is very slow
|
# 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 }
|
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 library with support for the PHC format
|
||||||
argon2 = "0.5.3"
|
argon2 = "0.5.3"
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
---
|
---
|
||||||
vault_version: "v2024.6.2c"
|
vault_version: "v2025.1.0"
|
||||||
vault_image_digest: "sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b"
|
vault_image_digest: "sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8"
|
||||||
# Cross Compile Docker Helper Scripts v1.5.0
|
# 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
|
# 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
|
# 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
|
rust_version: 1.83.0 # Rust version to be used
|
||||||
debian_version: bookworm # Debian release name to be used
|
debian_version: bookworm # Debian release name to be used
|
||||||
alpine_version: "3.21" # Alpine version 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,
|
# - 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.
|
# click the tag name to view the digest of the image it currently points to.
|
||||||
# - From the command line:
|
# - From the command line:
|
||||||
# $ docker pull docker.io/vaultwarden/web-vault:v2024.6.2c
|
# $ docker pull docker.io/vaultwarden/web-vault:v2025.1.0
|
||||||
# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2024.6.2c
|
# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2025.1.0
|
||||||
# [docker.io/vaultwarden/web-vault@sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b]
|
# [docker.io/vaultwarden/web-vault@sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8]
|
||||||
#
|
#
|
||||||
# - Conversely, to get the tag name from the digest:
|
# - Conversely, to get the tag name from the digest:
|
||||||
# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b
|
# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8
|
||||||
# [docker.io/vaultwarden/web-vault:v2024.6.2c]
|
# [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 ##########################
|
########################## ALPINE BUILD IMAGES ##########################
|
||||||
## NOTE: The Alpine Base Images do not support other platforms then linux/amd64
|
## 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,
|
# - 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.
|
# click the tag name to view the digest of the image it currently points to.
|
||||||
# - From the command line:
|
# - From the command line:
|
||||||
# $ docker pull docker.io/vaultwarden/web-vault:v2024.6.2c
|
# $ docker pull docker.io/vaultwarden/web-vault:v2025.1.0
|
||||||
# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2024.6.2c
|
# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2025.1.0
|
||||||
# [docker.io/vaultwarden/web-vault@sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b]
|
# [docker.io/vaultwarden/web-vault@sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8]
|
||||||
#
|
#
|
||||||
# - Conversely, to get the tag name from the digest:
|
# - Conversely, to get the tag name from the digest:
|
||||||
# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf71620c23e34fc2b
|
# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:72d636334b4ad6fe9ba1d12e0cda562cd31772cf28772f6b2fe4121a537b72a8
|
||||||
# [docker.io/vaultwarden/web-vault:v2024.6.2c]
|
# [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 ##########################
|
########################## Cross Compile Docker Helper Scripts ##########################
|
||||||
## We use the linux/amd64 no matter which Build Platform, since these are all bash 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
|
## 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 ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# hadolint ignore=DL3006
|
# hadolint ignore=DL3006
|
||||||
|
|
|
@ -399,7 +399,12 @@ async fn post_password(data: Json<ChangePassData>, headers: Headers, mut conn: D
|
||||||
&data.new_master_password_hash,
|
&data.new_master_password_hash,
|
||||||
Some(data.key),
|
Some(data.key),
|
||||||
true,
|
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;
|
let save_result = user.save(&mut conn).await;
|
||||||
|
|
|
@ -18,7 +18,7 @@ pub use sends::purge_sends;
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
let mut eq_domains_routes = routes![get_eq_domains, post_eq_domains, put_eq_domains];
|
let mut eq_domains_routes = routes![get_eq_domains, post_eq_domains, put_eq_domains];
|
||||||
let mut hibp_routes = routes![hibp_breach];
|
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();
|
let mut routes = Vec::new();
|
||||||
routes.append(&mut accounts::routes());
|
routes.append(&mut accounts::routes());
|
||||||
|
@ -184,6 +184,18 @@ fn version() -> Json<&'static str> {
|
||||||
Json(crate::VERSION.unwrap_or_default())
|
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")]
|
#[get("/config")]
|
||||||
fn config() -> Json<Value> {
|
fn config() -> Json<Value> {
|
||||||
let domain = crate::CONFIG.domain();
|
let domain = crate::CONFIG.domain();
|
||||||
|
|
|
@ -48,6 +48,7 @@ pub fn routes() -> Vec<Route> {
|
||||||
confirm_invite,
|
confirm_invite,
|
||||||
bulk_confirm_invite,
|
bulk_confirm_invite,
|
||||||
accept_invite,
|
accept_invite,
|
||||||
|
get_org_user_mini_details,
|
||||||
get_user,
|
get_user,
|
||||||
edit_user,
|
edit_user,
|
||||||
put_organization_user,
|
put_organization_user,
|
||||||
|
@ -77,6 +78,7 @@ pub fn routes() -> Vec<Route> {
|
||||||
restore_organization_user,
|
restore_organization_user,
|
||||||
bulk_restore_organization_user,
|
bulk_restore_organization_user,
|
||||||
get_groups,
|
get_groups,
|
||||||
|
get_groups_details,
|
||||||
post_groups,
|
post_groups,
|
||||||
get_group,
|
get_group,
|
||||||
put_group,
|
put_group,
|
||||||
|
@ -98,6 +100,7 @@ pub fn routes() -> Vec<Route> {
|
||||||
get_org_export,
|
get_org_export,
|
||||||
api_key,
|
api_key,
|
||||||
rotate_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
|
// 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)
|
// 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
|
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()
|
|| (CONFIG.org_groups_enabled()
|
||||||
&& GroupUser::has_access_to_collection_by_member(&col.uuid, &user_org.uuid, &mut conn).await);
|
&& 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
|
// get the users assigned directly to the given collection
|
||||||
let users: Vec<Value> = coll_users
|
let users: Vec<Value> = coll_users
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|collection_user| collection_user.collection_uuid == col.uuid)
|
.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();
|
.collect();
|
||||||
|
|
||||||
// get the group details for the given collection
|
// get the group details for the given collection
|
||||||
|
@ -645,12 +666,24 @@ async fn get_org_collection_detail(
|
||||||
Vec::with_capacity(0)
|
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> =
|
let users: Vec<Value> =
|
||||||
CollectionUser::find_by_collection_swap_user_uuid_with_org_user_uuid(&collection.uuid, &mut conn)
|
CollectionUser::find_by_collection_swap_user_uuid_with_org_user_uuid(&collection.uuid, &mut conn)
|
||||||
.await
|
.await
|
||||||
.iter()
|
.iter()
|
||||||
.map(|collection_user| {
|
.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();
|
.collect();
|
||||||
|
|
||||||
|
@ -830,13 +863,19 @@ struct InviteData {
|
||||||
collections: Option<Vec<CollectionData>>,
|
collections: Option<Vec<CollectionData>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
access_all: bool,
|
access_all: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
permissions: HashMap<String, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/organizations/<org_id>/users/invite", data = "<data>")]
|
#[post("/organizations/<org_id>/users/invite", data = "<data>")]
|
||||||
async fn send_invite(org_id: &str, data: Json<InviteData>, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult {
|
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,
|
Some(new_type) => new_type as i32,
|
||||||
None => err!("Invalid type"),
|
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")
|
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() {
|
for email in data.emails.iter() {
|
||||||
let mut user_org_status = UserOrgStatus::Invited as i32;
|
let mut user_org_status = UserOrgStatus::Invited as i32;
|
||||||
let user = match User::find_by_mail(email, &mut conn).await {
|
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() {
|
if !CONFIG.mail_enabled() {
|
||||||
let invitation = Invitation::new(email);
|
Invitation::new(email).save(&mut conn).await?;
|
||||||
invitation.save(&mut conn).await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut user = User::new(email.clone());
|
let mut new_user = User::new(email.clone());
|
||||||
user.save(&mut conn).await?;
|
new_user.save(&mut conn).await?;
|
||||||
user
|
user_created = true;
|
||||||
|
new_user
|
||||||
}
|
}
|
||||||
Some(user) => {
|
Some(user) => {
|
||||||
if UserOrganization::find_by_user_and_org(&user.uuid, org_id, &mut conn).await.is_some() {
|
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;
|
let access_all = data.access_all;
|
||||||
new_user.access_all = access_all;
|
new_member.access_all = access_all;
|
||||||
new_user.atype = new_type;
|
new_member.atype = new_type;
|
||||||
new_user.status = user_org_status;
|
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 no accessAll, add the collections received
|
||||||
if !access_all {
|
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() {
|
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?;
|
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(())
|
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);
|
let invitation = Invitation::new(&user.email);
|
||||||
invitation.save(conn).await?;
|
invitation.save(conn).await?;
|
||||||
} else {
|
} else {
|
||||||
let _ = Invitation::take(&user.email, conn).await;
|
Invitation::take(&user.email, conn).await;
|
||||||
let mut user_org = user_org;
|
let mut user_org = user_org;
|
||||||
user_org.status = UserOrgStatus::Accepted as i32;
|
user_org.status = UserOrgStatus::Accepted as i32;
|
||||||
user_org.save(conn).await?;
|
user_org.save(conn).await?;
|
||||||
|
@ -1254,7 +1314,21 @@ async fn _confirm_invite(
|
||||||
save_result
|
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(
|
async fn get_user(
|
||||||
org_id: &str,
|
org_id: &str,
|
||||||
org_user_id: &str,
|
org_user_id: &str,
|
||||||
|
@ -1282,6 +1356,8 @@ struct EditUserData {
|
||||||
groups: Option<Vec<String>>,
|
groups: Option<Vec<String>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
access_all: bool,
|
access_all: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
permissions: HashMap<String, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[put("/organizations/<org_id>/users/<org_user_id>", data = "<data>", rank = 1)]
|
#[put("/organizations/<org_id>/users/<org_user_id>", data = "<data>", rank = 1)]
|
||||||
|
@ -1303,14 +1379,30 @@ async fn edit_user(
|
||||||
headers: AdminHeaders,
|
headers: AdminHeaders,
|
||||||
mut conn: DbConn,
|
mut conn: DbConn,
|
||||||
) -> EmptyResult {
|
) -> 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")
|
err!("Invalid type")
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(mut user_to_edit) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await else {
|
// HACK: This converts the Custom role which has the `Manage all collections` box checked into an access_all flag
|
||||||
err!("The specified user isn't member of the organization")
|
// 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
|
if new_type != user_to_edit.atype
|
||||||
|
@ -1901,6 +1993,12 @@ fn get_plans_tax_rates(_headers: Headers) -> Json<Value> {
|
||||||
Json(_empty_data_json())
|
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 {
|
fn _empty_data_json() -> Value {
|
||||||
json!({
|
json!({
|
||||||
"object": "list",
|
"object": "list",
|
||||||
|
@ -1938,6 +2036,9 @@ struct OrgImportData {
|
||||||
users: Vec<OrgImportUserData>,
|
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>")]
|
#[post("/organizations/<org_id>/import", data = "<data>")]
|
||||||
async fn import(org_id: &str, data: Json<OrgImportData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
|
async fn import(org_id: &str, data: Json<OrgImportData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
|
||||||
let data = data.into_inner();
|
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
|
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));
|
let mut new_member = UserOrganization::new(user.uuid.clone(), String::from(org_id));
|
||||||
new_org_user.access_all = false;
|
new_member.access_all = false;
|
||||||
new_org_user.atype = UserOrgType::User as i32;
|
new_member.atype = UserOrgType::User as i32;
|
||||||
new_org_user.status = user_org_status;
|
new_member.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;
|
|
||||||
|
|
||||||
if CONFIG.mail_enabled() {
|
if CONFIG.mail_enabled() {
|
||||||
let org_name = match Organization::find_by_uuid(org_id, &mut conn).await {
|
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(
|
mail::send_invite(
|
||||||
&user,
|
&user,
|
||||||
Some(String::from(org_id)),
|
Some(String::from(org_id)),
|
||||||
Some(new_org_user.uuid),
|
Some(new_member.uuid.clone()),
|
||||||
&org_name,
|
&org_name,
|
||||||
Some(headers.user.email.clone()),
|
Some(headers.user.email.clone()),
|
||||||
)
|
)
|
||||||
.await?;
|
.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)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct GroupRequest {
|
struct GroupRequest {
|
||||||
|
@ -2331,6 +2439,7 @@ struct SelectionReadOnly {
|
||||||
id: String,
|
id: String,
|
||||||
read_only: bool,
|
read_only: bool,
|
||||||
hide_passwords: bool,
|
hide_passwords: bool,
|
||||||
|
manage: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectionReadOnly {
|
impl SelectionReadOnly {
|
||||||
|
@ -2339,18 +2448,31 @@ impl SelectionReadOnly {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_collection_group_details_read_only(collection_group: &CollectionGroup) -> 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 {
|
SelectionReadOnly {
|
||||||
id: collection_group.groups_uuid.clone(),
|
id: collection_group.groups_uuid.clone(),
|
||||||
read_only: collection_group.read_only,
|
read_only: collection_group.read_only,
|
||||||
hide_passwords: collection_group.hide_passwords,
|
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 {
|
SelectionReadOnly {
|
||||||
id: collection_user.user_uuid.clone(),
|
id: collection_user.user_uuid.clone(),
|
||||||
read_only: collection_user.read_only,
|
read_only: collection_user.read_only,
|
||||||
hide_passwords: collection_user.hide_passwords,
|
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(())
|
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 {
|
async fn get_group(org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult {
|
||||||
if !CONFIG.org_groups_enabled() {
|
if !CONFIG.org_groups_enabled() {
|
||||||
err!("Group support is disabled");
|
err!("Group support is disabled");
|
||||||
|
@ -2904,7 +3026,7 @@ async fn put_reset_password_enrollment(
|
||||||
if reset_request.reset_password_key.is_none()
|
if reset_request.reset_password_key.is_none()
|
||||||
&& OrgPolicy::org_is_reset_password_auto_enroll(org_id, &mut conn).await
|
&& 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() {
|
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();
|
let data = data.into_inner();
|
||||||
|
|
||||||
for user_data in &data.members {
|
for user_data in &data.members {
|
||||||
|
let mut user_created: bool = false;
|
||||||
if user_data.deleted {
|
if user_data.deleted {
|
||||||
// If user is marked for deletion and it exists, revoke it
|
// If user is marked for deletion and it exists, revoke it
|
||||||
if let Some(mut user_org) =
|
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?;
|
new_user.save(&mut conn).await?;
|
||||||
|
|
||||||
if !CONFIG.mail_enabled() {
|
if !CONFIG.mail_enabled() {
|
||||||
let invitation = Invitation::new(&new_user.email);
|
Invitation::new(&new_user.email).save(&mut conn).await?;
|
||||||
invitation.save(&mut conn).await?;
|
|
||||||
}
|
}
|
||||||
|
user_created = true;
|
||||||
new_user
|
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
|
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());
|
let mut new_member = UserOrganization::new(user.uuid.clone(), org_id.clone());
|
||||||
new_org_user.set_external_id(Some(user_data.external_id.clone()));
|
new_member.set_external_id(Some(user_data.external_id.clone()));
|
||||||
new_org_user.access_all = false;
|
new_member.access_all = false;
|
||||||
new_org_user.atype = UserOrgType::User as i32;
|
new_member.atype = UserOrgType::User as i32;
|
||||||
new_org_user.status = user_org_status;
|
new_member.status = user_org_status;
|
||||||
|
|
||||||
new_org_user.save(&mut conn).await?;
|
new_member.save(&mut conn).await?;
|
||||||
|
|
||||||
if CONFIG.mail_enabled() {
|
if CONFIG.mail_enabled() {
|
||||||
let (org_name, org_email) = match Organization::find_by_uuid(&org_id, &mut conn).await {
|
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"),
|
None => err!("Error looking up organization"),
|
||||||
};
|
};
|
||||||
|
|
||||||
mail::send_invite(&user, Some(org_id.clone()), Some(new_org_user.uuid), &org_name, Some(org_email))
|
if let Err(e) = mail::send_invite(
|
||||||
.await?;
|
&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 use crate::config::CONFIG;
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
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>")]
|
#[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 std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use rocket::{
|
use rocket::{
|
||||||
|
@ -14,7 +13,7 @@ use crate::{
|
||||||
api::{core::now, ApiResult, EmptyResult},
|
api::{core::now, ApiResult, EmptyResult},
|
||||||
auth::decode_file_download,
|
auth::decode_file_download,
|
||||||
error::Error,
|
error::Error,
|
||||||
util::{get_web_vault_version, Cached, SafeString},
|
util::{Cached, SafeString},
|
||||||
CONFIG,
|
CONFIG,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,43 +53,7 @@ fn not_found() -> ApiResult<Html<String>> {
|
||||||
|
|
||||||
#[get("/css/vaultwarden.css")]
|
#[get("/css/vaultwarden.css")]
|
||||||
fn vaultwarden_css() -> Cached<Css<String>> {
|
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!({
|
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(),
|
"signup_disabled": !CONFIG.signups_allowed() && CONFIG.signups_domains_whitelist().is_empty(),
|
||||||
"mail_enabled": CONFIG.mail_enabled(),
|
"mail_enabled": CONFIG.mail_enabled(),
|
||||||
"yubico_enabled": CONFIG._enable_yubico() && (CONFIG.yubico_client_id().is_some() == CONFIG.yubico_secret_key().is_some()),
|
"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::{
|
use crate::{
|
||||||
db::DbConnType,
|
db::DbConnType,
|
||||||
error::Error,
|
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(|| {
|
static CONFIG_FILE: Lazy<String> = Lazy::new(|| {
|
||||||
|
@ -830,6 +830,7 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
|
||||||
"browser-fileless-import",
|
"browser-fileless-import",
|
||||||
"extension-refresh",
|
"extension-refresh",
|
||||||
"fido2-vault-credentials",
|
"fido2-vault-credentials",
|
||||||
|
"inline-menu-positioning-improvements",
|
||||||
"ssh-key-vault-item",
|
"ssh-key-vault-item",
|
||||||
"ssh-agent",
|
"ssh-agent",
|
||||||
];
|
];
|
||||||
|
@ -1327,6 +1328,8 @@ where
|
||||||
// Register helpers
|
// Register helpers
|
||||||
hb.register_helper("case", Box::new(case_helper));
|
hb.register_helper("case", Box::new(case_helper));
|
||||||
hb.register_helper("to_json", Box::new(to_json));
|
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 {
|
macro_rules! reg {
|
||||||
($name:expr) => {{
|
($name:expr) => {{
|
||||||
|
@ -1431,3 +1434,42 @@ fn to_json<'reg, 'rc>(
|
||||||
out.write(&json)?;
|
out.write(&json)?;
|
||||||
Ok(())
|
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
|
// 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.
|
// Set the first element of the Uris array as Uri, this is needed several (mobile) clients.
|
||||||
if self.atype == 1 {
|
if self.atype == 1 {
|
||||||
if type_data_json["uris"].is_array() {
|
// Upstream always has an `uri` key/value
|
||||||
let uri = type_data_json["uris"][0]["uri"].clone();
|
type_data_json["uri"] = Value::Null;
|
||||||
type_data_json["uri"] = uri;
|
if let Some(uris) = type_data_json["uris"].as_array_mut() {
|
||||||
} else {
|
if !uris.is_empty() {
|
||||||
// Upstream always has an Uri key/value
|
// Fix uri match values first, they are only allowed to be a number or null
|
||||||
type_data_json["uri"] = Value::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.
|
// Clone the type_data and add some default value.
|
||||||
let mut data_json = type_data_json.clone();
|
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: {
|
db_run! { conn: {
|
||||||
users_collections::table
|
users_collections::table
|
||||||
.inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid)))
|
.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 {
|
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)
|
let collections_groups: Vec<Value> = CollectionGroup::find_by_group(&self.uuid, conn)
|
||||||
.await
|
.await
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -82,7 +85,7 @@ impl Group {
|
||||||
"id": entry.collections_uuid,
|
"id": entry.collections_uuid,
|
||||||
"readOnly": entry.read_only,
|
"readOnly": entry.read_only,
|
||||||
"hidePasswords": entry.hide_passwords,
|
"hidePasswords": entry.hide_passwords,
|
||||||
"manage": false
|
"manage": !entry.read_only && !entry.hide_passwords,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -73,6 +73,8 @@ impl UserOrgType {
|
||||||
"1" | "Admin" => Some(UserOrgType::Admin),
|
"1" | "Admin" => Some(UserOrgType::Admin),
|
||||||
"2" | "User" => Some(UserOrgType::User),
|
"2" | "User" => Some(UserOrgType::User),
|
||||||
"3" | "Manager" => Some(UserOrgType::Manager),
|
"3" | "Manager" => Some(UserOrgType::Manager),
|
||||||
|
// HACK: We convert the custom role to a manager role
|
||||||
|
"4" | "Custom" => Some(UserOrgType::Manager),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +87,7 @@ impl Ord for UserOrgType {
|
||||||
3, // Owner
|
3, // Owner
|
||||||
2, // Admin
|
2, // Admin
|
||||||
0, // User
|
0, // User
|
||||||
1, // Manager
|
1, // Manager && Custom
|
||||||
];
|
];
|
||||||
ACCESS_LEVEL[*self as usize].cmp(&ACCESS_LEVEL[*other as usize])
|
ACCESS_LEVEL[*self as usize].cmp(&ACCESS_LEVEL[*other as usize])
|
||||||
}
|
}
|
||||||
|
@ -158,33 +160,46 @@ impl Organization {
|
||||||
pub fn to_json(&self) -> Value {
|
pub fn to_json(&self) -> Value {
|
||||||
json!({
|
json!({
|
||||||
"id": self.uuid,
|
"id": self.uuid,
|
||||||
"identifier": null, // not supported by us
|
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"seats": null,
|
"seats": null,
|
||||||
"maxCollections": null,
|
"maxCollections": null,
|
||||||
"maxStorageGb": i16::MAX, // The value doesn't matter, we don't check server-side
|
"maxStorageGb": i16::MAX, // The value doesn't matter, we don't check server-side
|
||||||
"use2fa": true,
|
"use2fa": true,
|
||||||
"useCustomPermissions": false,
|
"useCustomPermissions": true,
|
||||||
"useDirectory": false, // Is supported, but this value isn't checked anywhere (yet)
|
"useDirectory": false, // Is supported, but this value isn't checked anywhere (yet)
|
||||||
"useEvents": CONFIG.org_events_enabled(),
|
"useEvents": CONFIG.org_events_enabled(),
|
||||||
"useGroups": CONFIG.org_groups_enabled(),
|
"useGroups": CONFIG.org_groups_enabled(),
|
||||||
"useTotp": true,
|
"useTotp": true,
|
||||||
"usePolicies": true,
|
"usePolicies": true,
|
||||||
// "useScim": false, // Not supported (Not AGPLv3 Licensed)
|
"useScim": false, // Not supported (Not AGPLv3 Licensed)
|
||||||
"useSso": false, // Not supported
|
"useSso": false, // Not supported
|
||||||
// "useKeyConnector": false, // Not supported
|
"useKeyConnector": false, // Not supported
|
||||||
|
"usePasswordManager": true,
|
||||||
|
"useSecretsManager": false, // Not supported (Not AGPLv3 Licensed)
|
||||||
"selfHost": true,
|
"selfHost": true,
|
||||||
"useApi": true,
|
"useApi": true,
|
||||||
"hasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(),
|
"hasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(),
|
||||||
"useResetPassword": CONFIG.mail_enabled(),
|
"useResetPassword": CONFIG.mail_enabled(),
|
||||||
|
"allowAdminAccessToAllCollectionItems": true,
|
||||||
|
"limitCollectionCreation": true,
|
||||||
|
"limitCollectionCreationDeletion": true,
|
||||||
|
"limitCollectionDeletion": true,
|
||||||
|
|
||||||
"businessName": null,
|
"businessName": self.name,
|
||||||
"businessAddress1": null,
|
"businessAddress1": null,
|
||||||
"businessAddress2": null,
|
"businessAddress2": null,
|
||||||
"businessAddress3": null,
|
"businessAddress3": null,
|
||||||
"businessCountry": null,
|
"businessCountry": null,
|
||||||
"businessTaxNumber": null,
|
"businessTaxNumber": null,
|
||||||
|
|
||||||
|
"maxAutoscaleSeats": null,
|
||||||
|
"maxAutoscaleSmSeats": null,
|
||||||
|
"maxAutoscaleSmServiceAccounts": null,
|
||||||
|
|
||||||
|
"secretsManagerPlan": null,
|
||||||
|
"smSeats": null,
|
||||||
|
"smServiceAccounts": null,
|
||||||
|
|
||||||
"billingEmail": self.billing_email,
|
"billingEmail": self.billing_email,
|
||||||
"planType": 6, // Custom plan
|
"planType": 6, // Custom plan
|
||||||
"usersGetPremium": true,
|
"usersGetPremium": true,
|
||||||
|
@ -252,6 +267,15 @@ impl UserOrganization {
|
||||||
}
|
}
|
||||||
false
|
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 {
|
impl OrganizationApiKey {
|
||||||
|
@ -356,17 +380,21 @@ impl UserOrganization {
|
||||||
pub async fn to_json(&self, conn: &mut DbConn) -> Value {
|
pub async fn to_json(&self, conn: &mut DbConn) -> Value {
|
||||||
let org = Organization::find_by_uuid(&self.org_uuid, conn).await.unwrap();
|
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!({
|
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
|
// 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,
|
"accessEventLogs": false,
|
||||||
"accessImportExport": false,
|
"accessImportExport": false,
|
||||||
"accessReports": false,
|
"accessReports": false,
|
||||||
"createNewCollections": false,
|
// If the following 3 Collection roles are set to true a custom user has access all permission
|
||||||
"editAnyCollection": false,
|
"createNewCollections": user_org_type == 4 && self.access_all,
|
||||||
"deleteAnyCollection": false,
|
"editAnyCollection": user_org_type == 4 && self.access_all,
|
||||||
"editAssignedCollections": false,
|
"deleteAnyCollection": user_org_type == 4 && self.access_all,
|
||||||
"deleteAssignedCollections": false,
|
|
||||||
"manageGroups": false,
|
"manageGroups": false,
|
||||||
"managePolicies": false,
|
"managePolicies": false,
|
||||||
"manageSso": false, // Not supported
|
"manageSso": false, // Not supported
|
||||||
|
@ -398,9 +426,9 @@ impl UserOrganization {
|
||||||
"ssoBound": false, // Not supported
|
"ssoBound": false, // Not supported
|
||||||
"useSso": false, // Not supported
|
"useSso": false, // Not supported
|
||||||
"useKeyConnector": false,
|
"useKeyConnector": false,
|
||||||
"useSecretsManager": false,
|
"useSecretsManager": false, // Not supported (Not AGPLv3 Licensed)
|
||||||
"usePasswordManager": true,
|
"usePasswordManager": true,
|
||||||
"useCustomPermissions": false,
|
"useCustomPermissions": true,
|
||||||
"useActivateAutofillPolicy": false,
|
"useActivateAutofillPolicy": false,
|
||||||
|
|
||||||
"organizationUserId": self.uuid,
|
"organizationUserId": self.uuid,
|
||||||
|
@ -417,9 +445,11 @@ impl UserOrganization {
|
||||||
"familySponsorshipValidUntil": null,
|
"familySponsorshipValidUntil": null,
|
||||||
"familySponsorshipToDelete": null,
|
"familySponsorshipToDelete": null,
|
||||||
"accessSecretsManager": false,
|
"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,
|
"allowAdminAccessToAllCollectionItems": true,
|
||||||
"flexibleCollections": false,
|
"userIsManagedByOrganization": false, // Means not managed via the Members UI, like SSO
|
||||||
|
|
||||||
"permissions": permissions,
|
"permissions": permissions,
|
||||||
|
|
||||||
|
@ -429,7 +459,7 @@ impl UserOrganization {
|
||||||
"userId": self.user_uuid,
|
"userId": self.user_uuid,
|
||||||
"key": self.akey,
|
"key": self.akey,
|
||||||
"status": self.status,
|
"status": self.status,
|
||||||
"type": self.atype,
|
"type": user_org_type,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|
||||||
"object": "profileOrganization",
|
"object": "profileOrganization",
|
||||||
|
@ -516,24 +546,34 @@ impl UserOrganization {
|
||||||
Vec::with_capacity(0)
|
Vec::with_capacity(0)
|
||||||
};
|
};
|
||||||
|
|
||||||
let permissions = json!({
|
// HACK: Convert the manager type to a custom type
|
||||||
// TODO: Add support for Custom User Roles
|
// It will be converted back on other locations
|
||||||
// See: https://bitwarden.com/help/article/user-types-access-control/#custom-role
|
let user_org_type = self.type_manager_as_custom();
|
||||||
"accessEventLogs": false,
|
|
||||||
"accessImportExport": false,
|
// HACK: Only return permissions if the user is of type custom and has access_all
|
||||||
"accessReports": false,
|
// Else Bitwarden will assume the defaults of all false
|
||||||
"createNewCollections": false,
|
let permissions = if user_org_type == 4 && self.access_all {
|
||||||
"editAnyCollection": false,
|
json!({
|
||||||
"deleteAnyCollection": false,
|
// TODO: Add full support for Custom User Roles
|
||||||
"editAssignedCollections": false,
|
// See: https://bitwarden.com/help/article/user-types-access-control/#custom-role
|
||||||
"deleteAssignedCollections": false,
|
// Currently we use the custom role as a manager role and link the 3 Collection roles to mimic the access_all permission
|
||||||
"manageGroups": false,
|
"accessEventLogs": false,
|
||||||
"managePolicies": false,
|
"accessImportExport": false,
|
||||||
"manageSso": false, // Not supported
|
"accessReports": false,
|
||||||
"manageUsers": false,
|
// If the following 3 Collection roles are set to true a custom user has access all permission
|
||||||
"manageResetPassword": false,
|
"createNewCollections": true,
|
||||||
"manageScim": false // Not supported (Not AGPLv3 Licensed)
|
"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!({
|
json!({
|
||||||
"id": self.uuid,
|
"id": self.uuid,
|
||||||
|
@ -546,7 +586,7 @@ impl UserOrganization {
|
||||||
"collections": collections,
|
"collections": collections,
|
||||||
|
|
||||||
"status": status,
|
"status": status,
|
||||||
"type": self.atype,
|
"type": user_org_type,
|
||||||
"accessAll": self.access_all,
|
"accessAll": self.access_all,
|
||||||
"twoFactorEnabled": twofactor_enabled,
|
"twoFactorEnabled": twofactor_enabled,
|
||||||
"resetPasswordEnrolled": self.reset_password_key.is_some(),
|
"resetPasswordEnrolled": self.reset_password_key.is_some(),
|
||||||
|
@ -608,6 +648,29 @@ impl UserOrganization {
|
||||||
"object": "organizationUserDetails",
|
"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 {
|
pub async fn save(&self, conn: &mut DbConn) -> EmptyResult {
|
||||||
User::update_uuid_revision(&self.user_uuid, conn).await;
|
User::update_uuid_revision(&self.user_uuid, conn).await;
|
||||||
|
|
||||||
|
@ -1015,5 +1078,6 @@ mod tests {
|
||||||
assert!(UserOrgType::Owner > UserOrgType::Admin);
|
assert!(UserOrgType::Owner > UserOrgType::Admin);
|
||||||
assert!(UserOrgType::Admin > UserOrgType::Manager);
|
assert!(UserOrgType::Admin > UserOrgType::Manager);
|
||||||
assert!(UserOrgType::Manager > UserOrgType::User);
|
assert!(UserOrgType::Manager > UserOrgType::User);
|
||||||
|
assert!(UserOrgType::Manager == UserOrgType::from_str("4").unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
src/static/scripts/admin_users.js
gevendort
2
src/static/scripts/admin_users.js
gevendort
|
@ -152,7 +152,7 @@ const ORG_TYPES = {
|
||||||
"name": "User",
|
"name": "User",
|
||||||
"bg": "blue"
|
"bg": "blue"
|
||||||
},
|
},
|
||||||
"3": {
|
"4": {
|
||||||
"name": "Manager",
|
"name": "Manager",
|
||||||
"bg": "green"
|
"bg": "green"
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,12 +42,6 @@ label[for^="ownedBusiness"] {
|
||||||
@extend %vw-hide;
|
@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 */
|
/* Hide Business Name */
|
||||||
app-org-account form div bit-form-field.tw-block:nth-child(3) {
|
app-org-account form div bit-form-field.tw-block:nth-child(3) {
|
||||||
@extend %vw-hide;
|
@extend %vw-hide;
|
||||||
|
@ -58,42 +52,77 @@ app-organization-plans > form > bit-section:nth-child(2) {
|
||||||
@extend %vw-hide;
|
@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 */
|
/* Hide Device Verification form at the Two Step Login screen */
|
||||||
app-security > app-two-factor-setup > form {
|
app-security > app-two-factor-setup > form {
|
||||||
@extend %vw-hide;
|
@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 ****/
|
/**** END Static Vaultwarden Changes ****/
|
||||||
/**** START Dynamic Vaultwarden Changes ****/
|
/**** START Dynamic Vaultwarden Changes ****/
|
||||||
{{#if signup_disabled}}
|
{{#if signup_disabled}}
|
||||||
/* Hide the register link on the login screen */
|
/* 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;
|
@extend %vw-hide;
|
||||||
}
|
}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
/* Hide `Email` 2FA if mail is not enabled */
|
|
||||||
{{#unless mail_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;
|
@extend %vw-hide;
|
||||||
}
|
}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
/* Hide `YubiKey OTP security key` 2FA if it is not enabled */
|
|
||||||
{{#unless yubico_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;
|
@extend %vw-hide;
|
||||||
}
|
}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
/* Hide Emergency Access if not allowed */
|
|
||||||
{{#unless emergency_access_allowed}}
|
{{#unless emergency_access_allowed}}
|
||||||
|
/* Hide Emergency Access if not allowed */
|
||||||
bit-nav-item[route="settings/emergency-access"] {
|
bit-nav-item[route="settings/emergency-access"] {
|
||||||
@extend %vw-hide;
|
@extend %vw-hide;
|
||||||
}
|
}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
/* Hide Sends if not allowed */
|
|
||||||
{{#unless sends_allowed}}
|
{{#unless sends_allowed}}
|
||||||
|
/* Hide Sends if not allowed */
|
||||||
bit-nav-item[route="sends"] {
|
bit-nav-item[route="sends"] {
|
||||||
@extend %vw-hide;
|
@extend %vw-hide;
|
||||||
}
|
}
|
||||||
|
|
Laden …
Tabelle hinzufügen
In neuem Issue referenzieren