Spiegel von
https://github.com/dani-garcia/vaultwarden.git
synchronisiert 2025-02-07 11:17:02 +01:00
Release v1.33.0
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEPFu8Fz2BGGz/3nKpWMgKKqbHZeEFAmeU088ACgkQWMgKKqbH ZeGBdQ//WuDOcJQKf67OGV6LJvxVhcHFq5QnG9FoBN3OH+dJoImbr9pxIMwFbjp5 15BKiOVnnIYU0n6Su2iC6eZ3ch/ygOmay2z3QOPgOPTU/mziaox031OQ3ToRil5S Vdn1WXyUd+73qzWSAOfUfsMawmtRgHz3pNq1JUJT8NGvdY0L2D6bq65j4pcGc5UE QCCd5/sMNhBPUT5sSr8l6uiLU/6ls1ktt2oV4kISeiEjLgDvbJIqX6gnhdhquJui mqgiw3n8lU++ug0cyj4/0oBRhGzHjAApxnxdl/y0Nz/vUPQNkzwfp2NTC0eDJhEX +bQFMojpsh5duOsCppOy/I6y2jvc8i9YH1JSuRh2P4HjtbRJej6O2RU3xKv7s/SP EoDf0Uc7GnGjjRzLH3VS4epJ4DVEnOSFXETMbkDMjoYctaxYmfS/67Yksw9sJ6Fb QJJDcikp3OmstqU2nLAcb033tALOfDJ6znG6SngJP5jhhHgJqnq51puGUbXdS69a /ELMljrm2q6eWu3F5G56hGxQGoNzFlkBpnAP0DyHTAMposE9GBKATrIbljBYTFEw Y/gTwhK5JiQgFanSSqwB4K1o2RK3JI1r8BMAxnAnzv4QXH32uSyBWkF0udumWpGj KZcmjQ+5MSNaCsRMRjHn3qTYhAD+Lla51SzC3YPvH4I38OHqZVQ= =VrFy -----END PGP SIGNATURE----- Merge tag '1.33.0' into sso-support Release v1.33.0
Dieser Commit ist enthalten in:
Commit
004aae9daa
15 geänderte Dateien mit 438 neuen und 242 gelöschten Zeilen
16
.github/workflows/build.yml
gevendort
16
.github/workflows/build.yml
gevendort
|
@ -120,37 +120,37 @@ jobs:
|
|||
# First test all features together, afterwards test them separately.
|
||||
- name: "test features: sqlite,mysql,postgresql,enable_mimalloc,query_logger"
|
||||
id: test_sqlite_mysql_postgresql_mimalloc_logger
|
||||
if: $${{ always() }}
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
cargo test --features sqlite,mysql,postgresql,enable_mimalloc,query_logger
|
||||
|
||||
- name: "test features: sqlite,mysql,postgresql,enable_mimalloc"
|
||||
id: test_sqlite_mysql_postgresql_mimalloc
|
||||
if: $${{ always() }}
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
cargo test --features sqlite,mysql,postgresql,enable_mimalloc
|
||||
|
||||
- name: "test features: sqlite,mysql,postgresql"
|
||||
id: test_sqlite_mysql_postgresql
|
||||
if: $${{ always() }}
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
cargo test --features sqlite,mysql,postgresql
|
||||
|
||||
- name: "test features: sqlite"
|
||||
id: test_sqlite
|
||||
if: $${{ always() }}
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
cargo test --features sqlite
|
||||
|
||||
- name: "test features: mysql"
|
||||
id: test_mysql
|
||||
if: $${{ always() }}
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
cargo test --features mysql
|
||||
|
||||
- name: "test features: postgresql"
|
||||
id: test_postgresql
|
||||
if: $${{ always() }}
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
cargo test --features postgresql
|
||||
# End Run cargo tests
|
||||
|
@ -159,7 +159,7 @@ jobs:
|
|||
# Run cargo clippy, and fail on warnings
|
||||
- name: "clippy features: sqlite,mysql,postgresql,enable_mimalloc"
|
||||
id: clippy
|
||||
if: ${{ always() && matrix.channel == 'rust-toolchain' }}
|
||||
if: ${{ !cancelled() && matrix.channel == 'rust-toolchain' }}
|
||||
run: |
|
||||
cargo clippy --features sqlite,mysql,postgresql,enable_mimalloc -- -D warnings
|
||||
# End Run cargo clippy
|
||||
|
@ -168,7 +168,7 @@ jobs:
|
|||
# Run cargo fmt (Only run on rust-toolchain defined version)
|
||||
- name: "check formatting"
|
||||
id: formatting
|
||||
if: ${{ always() && matrix.channel == 'rust-toolchain' }}
|
||||
if: ${{ !cancelled() && matrix.channel == 'rust-toolchain' }}
|
||||
run: |
|
||||
cargo fmt --all -- --check
|
||||
# End Run cargo fmt
|
||||
|
|
202
Cargo.lock
generiert
202
Cargo.lock
generiert
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
|
@ -383,9 +383,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
|
@ -431,9 +431,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "brotli-decompressor"
|
||||
version = "4.0.1"
|
||||
version = "4.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362"
|
||||
checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
|
@ -538,9 +538,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.7"
|
||||
version = "1.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
|
||||
checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
@ -568,9 +568,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd6dd8046d00723a59a2f8c5f295c515b9bb9a331ee4f8f3d4dd49e428acd3b6"
|
||||
checksum = "9c6ac4f2c0bf0f44e9161aec9675e1050aa4a530663c4a9e37e108fa948bca9f"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz-build",
|
||||
|
@ -665,9 +665,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
|||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -820,9 +820,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||
checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f"
|
||||
|
||||
[[package]]
|
||||
name = "data-url"
|
||||
|
@ -929,7 +929,7 @@ version = "0.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.8.0",
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
|
@ -943,7 +943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ccf1bedf64cdb9643204a36dd15b19a6ce8e7aa7f7b105868e9f1fad5ffa7d12"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.8.0",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"diesel_derives",
|
||||
|
@ -1373,9 +1373,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
|||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1"
|
||||
checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
|
@ -1520,7 +1520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2d9e3df7f0222ce5184154973d247c591d9aadc28ce7a73c6cd31100c9facff6"
|
||||
dependencies = [
|
||||
"codemap",
|
||||
"indexmap 2.7.0",
|
||||
"indexmap 2.7.1",
|
||||
"lasso",
|
||||
"once_cell",
|
||||
"phf",
|
||||
|
@ -1549,7 +1549,7 @@ dependencies = [
|
|||
"futures-sink",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"indexmap 2.7.0",
|
||||
"indexmap 2.7.1",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
|
@ -1568,7 +1568,7 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-sink",
|
||||
"http 1.2.0",
|
||||
"indexmap 2.7.0",
|
||||
"indexmap 2.7.1",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
|
@ -1594,7 +1594,7 @@ dependencies = [
|
|||
"pest_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.9",
|
||||
"thiserror 2.0.11",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
|
@ -1874,7 +1874,7 @@ dependencies = [
|
|||
"http 1.2.0",
|
||||
"hyper 1.5.2",
|
||||
"hyper-util",
|
||||
"rustls 0.23.20",
|
||||
"rustls 0.23.21",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.1",
|
||||
|
@ -2097,9 +2097,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.0"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.2",
|
||||
|
@ -2126,19 +2126,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.10.1"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
|
||||
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.13"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
|
||||
checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37"
|
||||
dependencies = [
|
||||
"hermit-abi 0.4.0",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2175,9 +2175,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.76"
|
||||
version = "0.3.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
|
||||
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
|
@ -2325,9 +2325,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
version = "0.4.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||
dependencies = [
|
||||
"value-bag",
|
||||
]
|
||||
|
@ -2444,9 +2444,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
|
||||
checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
@ -2493,9 +2493,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.12"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
|
||||
checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
|
@ -2717,11 +2717,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.68"
|
||||
version = "0.10.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
|
||||
checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.8.0",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
|
@ -2743,9 +2743,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
|
@ -2918,7 +2918,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.9",
|
||||
"thiserror 2.0.11",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
|
@ -3120,9 +3120,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -3171,7 +3171,7 @@ version = "0.9.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.8.0",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
@ -3255,11 +3255,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "raw-cpuid"
|
||||
version = "11.2.0"
|
||||
version = "11.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0"
|
||||
checksum = "c6928fa44c097620b706542d428957635951bade7143269085389d42c8a4927e"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3268,7 +3268,7 @@ version = "0.5.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3508,7 +3508,7 @@ dependencies = [
|
|||
"either",
|
||||
"figment",
|
||||
"futures",
|
||||
"indexmap 2.7.0",
|
||||
"indexmap 2.7.1",
|
||||
"log",
|
||||
"memchr",
|
||||
"multer",
|
||||
|
@ -3540,7 +3540,7 @@ checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46"
|
|||
dependencies = [
|
||||
"devise",
|
||||
"glob",
|
||||
"indexmap 2.7.0",
|
||||
"indexmap 2.7.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rocket_http",
|
||||
|
@ -3560,7 +3560,7 @@ dependencies = [
|
|||
"futures",
|
||||
"http 0.2.12",
|
||||
"hyper 0.14.32",
|
||||
"indexmap 2.7.0",
|
||||
"indexmap 2.7.1",
|
||||
"log",
|
||||
"memchr",
|
||||
"pear",
|
||||
|
@ -3647,11 +3647,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.43"
|
||||
version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.8.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
|
@ -3672,9 +3672,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.20"
|
||||
version = "0.23.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
|
||||
checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"rustls-pki-types",
|
||||
|
@ -3809,7 +3809,7 @@ version = "2.11.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.8.0",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
|
@ -3828,9 +3828,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.24"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
||||
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -3877,9 +3877,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.135"
|
||||
version = "1.0.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
|
||||
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
@ -3937,7 +3937,7 @@ dependencies = [
|
|||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.7.0",
|
||||
"indexmap 2.7.1",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
|
@ -4025,13 +4025,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085"
|
||||
checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.11",
|
||||
"time",
|
||||
]
|
||||
|
||||
|
@ -4157,9 +4157,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.95"
|
||||
version = "2.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -4221,7 +4221,7 @@ version = "0.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.8.0",
|
||||
"core-foundation",
|
||||
"system-configuration-sys 0.6.0",
|
||||
]
|
||||
|
@ -4277,11 +4277,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.9"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.9",
|
||||
"thiserror-impl 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4297,9 +4297,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.9"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -4438,7 +4438,7 @@ version = "0.26.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
|
||||
dependencies = [
|
||||
"rustls 0.23.20",
|
||||
"rustls 0.23.21",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
@ -4517,7 +4517,7 @@ version = "0.22.22"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||
dependencies = [
|
||||
"indexmap 2.7.0",
|
||||
"indexmap 2.7.1",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
|
@ -4695,9 +4695,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
|
@ -4743,18 +4743,18 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
|||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.11.0"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
||||
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "value-bag"
|
||||
|
@ -4869,20 +4869,21 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.99"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.99"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
|
@ -4894,9 +4895,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.49"
|
||||
version = "0.4.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
|
||||
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
|
@ -4907,9 +4908,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.99"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -4917,9 +4918,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.99"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -4930,9 +4931,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.99"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
|
@ -4949,9 +4953,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.76"
|
||||
version = "0.3.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
|
||||
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
@ -5249,9 +5253,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.22"
|
||||
version = "0.6.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980"
|
||||
checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
14
Cargo.toml
14
Cargo.toml
|
@ -44,7 +44,7 @@ syslog = "7.0.0"
|
|||
macros = { path = "./macros" }
|
||||
|
||||
# Logging
|
||||
log = "0.4.22"
|
||||
log = "0.4.25"
|
||||
fern = { version = "0.7.1", features = ["syslog-7", "reopen-1"] }
|
||||
tracing = { version = "0.1.41", features = ["log"] } # Needed to have lettre and webauthn-rs trace logging to work
|
||||
|
||||
|
@ -71,11 +71,11 @@ dashmap = "6.1.0"
|
|||
|
||||
# Async futures
|
||||
futures = "0.3.31"
|
||||
tokio = { version = "1.42.0", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time", "signal", "net"] }
|
||||
tokio = { version = "1.43.0", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time", "signal", "net"] }
|
||||
|
||||
# A generic serialization/deserialization framework
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_json = "1.0.135"
|
||||
serde_json = "1.0.137"
|
||||
|
||||
# A safe, extensible ORM and Query builder
|
||||
diesel = { version = "2.2.6", features = ["chrono", "r2d2", "numeric"] }
|
||||
|
@ -93,18 +93,18 @@ rand = { version = "0.8.5", features = ["small_rng"] }
|
|||
ring = "0.17.8"
|
||||
|
||||
# UUID generation
|
||||
uuid = { version = "1.11.0", features = ["v4"] }
|
||||
uuid = { version = "1.12.1", features = ["v4"] }
|
||||
|
||||
# Date and time libraries
|
||||
chrono = { version = "0.4.39", features = ["clock", "serde"], default-features = false }
|
||||
chrono-tz = "0.10.0"
|
||||
chrono-tz = "0.10.1"
|
||||
time = "0.3.37"
|
||||
|
||||
# Job scheduler
|
||||
job_scheduler_ng = "2.0.5"
|
||||
|
||||
# Data encoding library Hex/Base32/Base64
|
||||
data-encoding = "2.6.0"
|
||||
data-encoding = "2.7.0"
|
||||
|
||||
# JWT library
|
||||
jsonwebtoken = "9.3.0"
|
||||
|
@ -161,7 +161,7 @@ openidconnect = "3.5.0"
|
|||
mini-moka = "0.10.2"
|
||||
|
||||
# Check client versions for specific features.
|
||||
semver = "1.0.24"
|
||||
semver = "1.0.25"
|
||||
|
||||
# Allow overriding the default memory allocator
|
||||
# Mainly used for the musl builds, since the default musl malloc is very slow
|
||||
|
|
|
@ -10,4 +10,4 @@ proc-macro = true
|
|||
|
||||
[dependencies]
|
||||
quote = "1.0.38"
|
||||
syn = "2.0.94"
|
||||
syn = "2.0.96"
|
||||
|
|
|
@ -171,7 +171,7 @@ struct LoginForm {
|
|||
redirect: Option<String>,
|
||||
}
|
||||
|
||||
#[post("/", data = "<data>")]
|
||||
#[post("/", format = "application/x-www-form-urlencoded", data = "<data>")]
|
||||
fn post_admin_login(
|
||||
data: Form<LoginForm>,
|
||||
cookies: &CookieJar<'_>,
|
||||
|
@ -289,7 +289,7 @@ async fn get_user_or_404(user_id: &UserId, conn: &mut DbConn) -> ApiResult<User>
|
|||
}
|
||||
}
|
||||
|
||||
#[post("/invite", data = "<data>")]
|
||||
#[post("/invite", format = "application/json", data = "<data>")]
|
||||
async fn invite_user(data: Json<InviteData>, _token: AdminToken, mut conn: DbConn) -> JsonResult {
|
||||
let data: InviteData = data.into_inner();
|
||||
if User::find_by_mail(&data.email, &mut conn).await.is_some() {
|
||||
|
@ -315,7 +315,7 @@ async fn invite_user(data: Json<InviteData>, _token: AdminToken, mut conn: DbCon
|
|||
Ok(Json(user.to_json(&mut conn).await))
|
||||
}
|
||||
|
||||
#[post("/test/smtp", data = "<data>")]
|
||||
#[post("/test/smtp", format = "application/json", data = "<data>")]
|
||||
async fn test_smtp(data: Json<InviteData>, _token: AdminToken) -> EmptyResult {
|
||||
let data: InviteData = data.into_inner();
|
||||
|
||||
|
@ -393,7 +393,7 @@ async fn get_user_json(user_id: UserId, _token: AdminToken, mut conn: DbConn) ->
|
|||
Ok(Json(usr))
|
||||
}
|
||||
|
||||
#[post("/users/<user_id>/delete")]
|
||||
#[post("/users/<user_id>/delete", format = "application/json")]
|
||||
async fn delete_user(user_id: UserId, token: AdminToken, mut conn: DbConn) -> EmptyResult {
|
||||
let user = get_user_or_404(&user_id, &mut conn).await?;
|
||||
|
||||
|
@ -417,7 +417,7 @@ async fn delete_user(user_id: UserId, token: AdminToken, mut conn: DbConn) -> Em
|
|||
res
|
||||
}
|
||||
|
||||
#[post("/users/<user_id>/deauth")]
|
||||
#[post("/users/<user_id>/deauth", format = "application/json")]
|
||||
async fn deauth_user(user_id: UserId, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
let mut user = get_user_or_404(&user_id, &mut conn).await?;
|
||||
|
||||
|
@ -438,7 +438,7 @@ async fn deauth_user(user_id: UserId, _token: AdminToken, mut conn: DbConn, nt:
|
|||
user.save(&mut conn).await
|
||||
}
|
||||
|
||||
#[post("/users/<user_id>/disable")]
|
||||
#[post("/users/<user_id>/disable", format = "application/json")]
|
||||
async fn disable_user(user_id: UserId, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
let mut user = get_user_or_404(&user_id, &mut conn).await?;
|
||||
Device::delete_all_by_user(&user.uuid, &mut conn).await?;
|
||||
|
@ -452,7 +452,7 @@ async fn disable_user(user_id: UserId, _token: AdminToken, mut conn: DbConn, nt:
|
|||
save_result
|
||||
}
|
||||
|
||||
#[post("/users/<user_id>/enable")]
|
||||
#[post("/users/<user_id>/enable", format = "application/json")]
|
||||
async fn enable_user(user_id: UserId, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
|
||||
let mut user = get_user_or_404(&user_id, &mut conn).await?;
|
||||
user.enabled = true;
|
||||
|
@ -460,7 +460,7 @@ async fn enable_user(user_id: UserId, _token: AdminToken, mut conn: DbConn) -> E
|
|||
user.save(&mut conn).await
|
||||
}
|
||||
|
||||
#[post("/users/<user_id>/remove-2fa")]
|
||||
#[post("/users/<user_id>/remove-2fa", format = "application/json")]
|
||||
async fn remove_2fa(user_id: UserId, token: AdminToken, mut conn: DbConn) -> EmptyResult {
|
||||
let mut user = get_user_or_404(&user_id, &mut conn).await?;
|
||||
TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?;
|
||||
|
@ -469,7 +469,7 @@ async fn remove_2fa(user_id: UserId, token: AdminToken, mut conn: DbConn) -> Emp
|
|||
user.save(&mut conn).await
|
||||
}
|
||||
|
||||
#[post("/users/<user_id>/invite/resend")]
|
||||
#[post("/users/<user_id>/invite/resend", format = "application/json")]
|
||||
async fn resend_user_invite(user_id: UserId, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
|
||||
if let Some(user) = User::find_by_uuid(&user_id, &mut conn).await {
|
||||
//TODO: replace this with user.status check when it will be available (PR#3397)
|
||||
|
@ -496,7 +496,7 @@ struct MembershipTypeData {
|
|||
org_uuid: OrganizationId,
|
||||
}
|
||||
|
||||
#[post("/users/org_type", data = "<data>")]
|
||||
#[post("/users/org_type", format = "application/json", data = "<data>")]
|
||||
async fn update_membership_type(data: Json<MembershipTypeData>, token: AdminToken, mut conn: DbConn) -> EmptyResult {
|
||||
let data: MembershipTypeData = data.into_inner();
|
||||
|
||||
|
@ -550,7 +550,7 @@ async fn update_membership_type(data: Json<MembershipTypeData>, token: AdminToke
|
|||
member_to_edit.save(&mut conn).await
|
||||
}
|
||||
|
||||
#[post("/users/update_revision")]
|
||||
#[post("/users/update_revision", format = "application/json")]
|
||||
async fn update_revision_users(_token: AdminToken, mut conn: DbConn) -> EmptyResult {
|
||||
User::update_all_revisions(&mut conn).await
|
||||
}
|
||||
|
@ -575,7 +575,7 @@ async fn organizations_overview(_token: AdminToken, mut conn: DbConn) -> ApiResu
|
|||
Ok(Html(text))
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/delete")]
|
||||
#[post("/organizations/<org_id>/delete", format = "application/json")]
|
||||
async fn delete_organization(org_id: OrganizationId, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
|
||||
let org = Organization::find_by_uuid(&org_id, &mut conn).await.map_res("Organization doesn't exist")?;
|
||||
org.delete(&mut conn).await
|
||||
|
@ -733,7 +733,7 @@ async fn diagnostics(_token: AdminToken, ip_header: IpHeader, mut conn: DbConn)
|
|||
Ok(Html(text))
|
||||
}
|
||||
|
||||
#[get("/diagnostics/config")]
|
||||
#[get("/diagnostics/config", format = "application/json")]
|
||||
fn get_diagnostics_config(_token: AdminToken) -> Json<Value> {
|
||||
let support_json = CONFIG.get_support_json();
|
||||
Json(support_json)
|
||||
|
@ -744,16 +744,16 @@ fn get_diagnostics_http(code: u16, _token: AdminToken) -> EmptyResult {
|
|||
err_code!(format!("Testing error {code} response"), code);
|
||||
}
|
||||
|
||||
#[post("/config", data = "<data>")]
|
||||
#[post("/config", format = "application/json", data = "<data>")]
|
||||
fn post_config(data: Json<ConfigBuilder>, _token: AdminToken) -> EmptyResult {
|
||||
let data: ConfigBuilder = data.into_inner();
|
||||
if let Err(e) = CONFIG.update_config(data) {
|
||||
if let Err(e) = CONFIG.update_config(data, true) {
|
||||
err!(format!("Unable to save config: {e:?}"))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[post("/config/delete")]
|
||||
#[post("/config/delete", format = "application/json")]
|
||||
fn delete_config(_token: AdminToken) -> EmptyResult {
|
||||
if let Err(e) = CONFIG.delete_user_config() {
|
||||
err!(format!("Unable to delete config: {e:?}"))
|
||||
|
@ -761,7 +761,7 @@ fn delete_config(_token: AdminToken) -> EmptyResult {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[post("/config/backup_db")]
|
||||
#[post("/config/backup_db", format = "application/json")]
|
||||
async fn backup_db(_token: AdminToken, mut conn: DbConn) -> ApiResult<String> {
|
||||
if *CAN_BACKUP {
|
||||
match backup_database(&mut conn).await {
|
||||
|
|
|
@ -34,9 +34,13 @@ struct EventRange {
|
|||
async fn get_org_events(
|
||||
org_id: OrganizationId,
|
||||
data: EventRange,
|
||||
_headers: AdminHeaders,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
|
||||
// Return an empty vec when we org events are disabled.
|
||||
// This prevents client errors
|
||||
let events_json: Vec<Value> = if !CONFIG.org_events_enabled() {
|
||||
|
@ -100,9 +104,12 @@ async fn get_user_events(
|
|||
org_id: OrganizationId,
|
||||
member_id: MembershipId,
|
||||
data: EventRange,
|
||||
_headers: AdminHeaders,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
// Return an empty vec when we org events are disabled.
|
||||
// This prevents client errors
|
||||
let events_json: Vec<Value> = if !CONFIG.org_events_enabled() {
|
||||
|
|
|
@ -210,8 +210,8 @@ fn config() -> Json<Value> {
|
|||
// This means they expect a version that closely matches the Bitwarden server version
|
||||
// We should make sure that we keep this updated when we support the new server features
|
||||
// Version history:
|
||||
// - Individual cipher key encryption: 2023.9.1
|
||||
"version": "2024.2.0",
|
||||
// - Individual cipher key encryption: 2024.2.0
|
||||
"version": "2025.1.0",
|
||||
"gitHash": option_env!("GIT_REV"),
|
||||
"server": {
|
||||
"name": "Vaultwarden",
|
||||
|
|
|
@ -10,7 +10,10 @@ use crate::{
|
|||
core::{log_event, two_factor, CipherSyncData, CipherSyncType},
|
||||
EmptyResult, JsonResult, Notify, PasswordOrOtpData, UpdateType,
|
||||
},
|
||||
auth::{decode_invite, AdminHeaders, ClientVersion, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders},
|
||||
auth::{
|
||||
decode_invite, AdminHeaders, ClientVersion, Headers, ManagerHeaders, ManagerHeadersLoose, OrgMemberHeaders,
|
||||
OwnerHeaders,
|
||||
},
|
||||
db::{models::*, DbConn},
|
||||
mail,
|
||||
util::{convert_json_key_lcase_first, get_uuid, NumberOrString},
|
||||
|
@ -216,6 +219,9 @@ async fn delete_organization(
|
|||
headers: OwnerHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data: PasswordOrOtpData = data.into_inner();
|
||||
|
||||
data.validate(&headers.user, true, &mut conn).await?;
|
||||
|
@ -264,7 +270,10 @@ async fn leave_organization(org_id: OrganizationId, headers: Headers, mut conn:
|
|||
}
|
||||
|
||||
#[get("/organizations/<org_id>")]
|
||||
async fn get_organization(org_id: OrganizationId, _headers: OwnerHeaders, mut conn: DbConn) -> JsonResult {
|
||||
async fn get_organization(org_id: OrganizationId, headers: OwnerHeaders, mut conn: DbConn) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
match Organization::find_by_uuid(&org_id, &mut conn).await {
|
||||
Some(organization) => Ok(Json(organization.to_json())),
|
||||
None => err!("Can't find organization details"),
|
||||
|
@ -288,14 +297,18 @@ async fn post_organization(
|
|||
data: Json<OrganizationUpdateData>,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
|
||||
let data: OrganizationUpdateData = data.into_inner();
|
||||
|
||||
let Some(mut org) = Organization::find_by_uuid(&org_id, &mut conn).await else {
|
||||
err!("Can't find organization details")
|
||||
err!("Organization not found")
|
||||
};
|
||||
|
||||
org.name = data.name;
|
||||
org.billing_email = data.billing_email;
|
||||
org.billing_email = data.billing_email.to_lowercase();
|
||||
|
||||
org.save(&mut conn).await?;
|
||||
|
||||
|
@ -339,12 +352,15 @@ fn get_auto_enroll_status(_identifier: &str) -> JsonResult {
|
|||
}
|
||||
|
||||
#[get("/organizations/<org_id>/collections")]
|
||||
async fn get_org_collections(org_id: OrganizationId, _headers: ManagerHeadersLoose, mut conn: DbConn) -> Json<Value> {
|
||||
Json(json!({
|
||||
async fn get_org_collections(org_id: OrganizationId, headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult {
|
||||
if org_id != headers.membership.org_uuid {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
Ok(Json(json!({
|
||||
"data": _get_org_collections(&org_id, &mut conn).await,
|
||||
"object": "list",
|
||||
"continuationToken": null,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
#[get("/organizations/<org_id>/collections/details")]
|
||||
|
@ -353,6 +369,9 @@ async fn get_org_collections_details(
|
|||
headers: ManagerHeadersLoose,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.membership.org_uuid {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let mut data = Vec::new();
|
||||
|
||||
let Some(member) = Membership::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await else {
|
||||
|
@ -427,6 +446,9 @@ async fn post_organization_collections(
|
|||
data: Json<NewCollectionData>,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.membership.org_uuid {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data: NewCollectionData = data.into_inner();
|
||||
|
||||
let Some(org) = Organization::find_by_uuid(&org_id, &mut conn).await else {
|
||||
|
@ -499,6 +521,9 @@ async fn post_organization_collection_update(
|
|||
data: Json<NewCollectionData>,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data: NewCollectionData = data.into_inner();
|
||||
|
||||
if Organization::find_by_uuid(&org_id, &mut conn).await.is_none() {
|
||||
|
@ -559,9 +584,12 @@ async fn delete_organization_collection_member(
|
|||
org_id: OrganizationId,
|
||||
col_id: CollectionId,
|
||||
member_id: MembershipId,
|
||||
_headers: AdminHeaders,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let Some(collection) = Collection::find_by_uuid_and_org(&col_id, &org_id, &mut conn).await else {
|
||||
err!("Collection not found", "Collection does not exist or does not belong to this organization")
|
||||
};
|
||||
|
@ -652,6 +680,9 @@ async fn bulk_delete_organization_collections(
|
|||
data: Json<BulkCollectionIds>,
|
||||
mut conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
if org_id != headers.membership.org_uuid {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data: BulkCollectionIds = data.into_inner();
|
||||
|
||||
let collections = data.ids;
|
||||
|
@ -671,6 +702,9 @@ async fn get_org_collection_detail(
|
|||
headers: ManagerHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
match Collection::find_by_uuid_and_user(&col_id, headers.user.uuid.clone(), &mut conn).await {
|
||||
None => err!("Collection not found"),
|
||||
Some(collection) => {
|
||||
|
@ -732,9 +766,12 @@ async fn get_org_collection_detail(
|
|||
async fn get_collection_users(
|
||||
org_id: OrganizationId,
|
||||
col_id: CollectionId,
|
||||
_headers: ManagerHeaders,
|
||||
headers: ManagerHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
// Get org and collection, check that collection is from org
|
||||
let Some(collection) = Collection::find_by_uuid_and_org(&col_id, &org_id, &mut conn).await else {
|
||||
err!("Collection not found in Organization")
|
||||
|
@ -758,9 +795,12 @@ async fn put_collection_users(
|
|||
org_id: OrganizationId,
|
||||
col_id: CollectionId,
|
||||
data: Json<Vec<MembershipData>>,
|
||||
_headers: ManagerHeaders,
|
||||
headers: ManagerHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
// Get org and collection, check that collection is from org
|
||||
if Collection::find_by_uuid_and_org(&col_id, &org_id, &mut conn).await.is_none() {
|
||||
err!("Collection not found in Organization")
|
||||
|
@ -792,10 +832,9 @@ struct OrgIdData {
|
|||
}
|
||||
|
||||
#[get("/ciphers/organization-details?<data..>")]
|
||||
async fn get_org_details(data: OrgIdData, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
if Membership::find_confirmed_by_user_and_org(&headers.user.uuid, &data.organization_id, &mut conn).await.is_none()
|
||||
{
|
||||
err_code!("Resource not found.", rocket::http::Status::NotFound.code);
|
||||
async fn get_org_details(data: OrgIdData, headers: OrgMemberHeaders, mut conn: DbConn) -> JsonResult {
|
||||
if data.organization_id != headers.org_id {
|
||||
err_code!("Resource not found.", "Organization id's do not match", rocket::http::Status::NotFound.code);
|
||||
}
|
||||
|
||||
Ok(Json(json!({
|
||||
|
@ -840,9 +879,12 @@ struct GetOrgUserData {
|
|||
async fn get_members(
|
||||
data: GetOrgUserData,
|
||||
org_id: OrganizationId,
|
||||
_headers: ManagerHeadersLoose,
|
||||
headers: ManagerHeadersLoose,
|
||||
mut conn: DbConn,
|
||||
) -> Json<Value> {
|
||||
) -> JsonResult {
|
||||
if org_id != headers.membership.org_uuid {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let mut users_json = Vec::new();
|
||||
for u in Membership::find_by_org(&org_id, &mut conn).await {
|
||||
users_json.push(
|
||||
|
@ -855,20 +897,23 @@ async fn get_members(
|
|||
);
|
||||
}
|
||||
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"data": users_json,
|
||||
"object": "list",
|
||||
"continuationToken": null,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/keys", data = "<data>")]
|
||||
async fn post_org_keys(
|
||||
org_id: OrganizationId,
|
||||
data: Json<OrgKeyData>,
|
||||
_headers: AdminHeaders,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data: OrgKeyData = data.into_inner();
|
||||
|
||||
let mut org = match Organization::find_by_uuid(&org_id, &mut conn).await {
|
||||
|
@ -931,6 +976,9 @@ async fn send_invite(
|
|||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let mut data: InviteData = data.into_inner();
|
||||
|
||||
// HACK: We need the raw user-type to be sure custom role is selected to determine the access_all permission
|
||||
|
@ -1071,7 +1119,10 @@ async fn bulk_reinvite_members(
|
|||
data: Json<BulkMembershipIds>,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> Json<Value> {
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data: BulkMembershipIds = data.into_inner();
|
||||
|
||||
let mut bulk_response = Vec::new();
|
||||
|
@ -1090,11 +1141,11 @@ async fn bulk_reinvite_members(
|
|||
))
|
||||
}
|
||||
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"data": bulk_response,
|
||||
"object": "list",
|
||||
"continuationToken": null
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/users/<member_id>/reinvite")]
|
||||
|
@ -1263,7 +1314,10 @@ async fn bulk_confirm_invite(
|
|||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
nt: Notify<'_>,
|
||||
) -> Json<Value> {
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data = data.into_inner();
|
||||
|
||||
let mut bulk_response = Vec::new();
|
||||
|
@ -1289,11 +1343,11 @@ async fn bulk_confirm_invite(
|
|||
None => error!("No keys to confirm"),
|
||||
}
|
||||
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"data": bulk_response,
|
||||
"object": "list",
|
||||
"continuationToken": null
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
#[post("/organizations/<org_id>/users/<member_id>/confirm", data = "<data>")]
|
||||
|
@ -1390,19 +1444,22 @@ async fn _confirm_invite(
|
|||
#[get("/organizations/<org_id>/users/mini-details", rank = 1)]
|
||||
async fn get_org_user_mini_details(
|
||||
org_id: OrganizationId,
|
||||
_headers: ManagerHeadersLoose,
|
||||
headers: ManagerHeadersLoose,
|
||||
mut conn: DbConn,
|
||||
) -> Json<Value> {
|
||||
) -> JsonResult {
|
||||
if org_id != headers.membership.org_uuid {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let mut members_json = Vec::new();
|
||||
for m in Membership::find_by_org(&org_id, &mut conn).await {
|
||||
members_json.push(m.to_json_mini_details(&mut conn).await);
|
||||
}
|
||||
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"data": members_json,
|
||||
"object": "list",
|
||||
"continuationToken": null,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
#[get("/organizations/<org_id>/users/<member_id>?<data..>", rank = 2)]
|
||||
|
@ -1410,9 +1467,12 @@ async fn get_user(
|
|||
org_id: OrganizationId,
|
||||
member_id: MembershipId,
|
||||
data: GetOrgUserData,
|
||||
_headers: AdminHeaders,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let Some(user) = Membership::find_by_uuid_and_org(&member_id, &org_id, &mut conn).await else {
|
||||
err!("The specified user isn't a member of the organization")
|
||||
};
|
||||
|
@ -1456,6 +1516,9 @@ async fn edit_member(
|
|||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let mut data: EditUserData = data.into_inner();
|
||||
|
||||
// HACK: We need the raw user-type to be sure custom role is selected to determine the access_all permission
|
||||
|
@ -1577,7 +1640,10 @@ async fn bulk_delete_member(
|
|||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
nt: Notify<'_>,
|
||||
) -> Json<Value> {
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data: BulkMembershipIds = data.into_inner();
|
||||
|
||||
let mut bulk_response = Vec::new();
|
||||
|
@ -1596,11 +1662,11 @@ async fn bulk_delete_member(
|
|||
))
|
||||
}
|
||||
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"data": bulk_response,
|
||||
"object": "list",
|
||||
"continuationToken": null
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
#[delete("/organizations/<org_id>/users/<member_id>")]
|
||||
|
@ -1670,9 +1736,12 @@ async fn _delete_member(
|
|||
async fn bulk_public_keys(
|
||||
org_id: OrganizationId,
|
||||
data: Json<BulkMembershipIds>,
|
||||
_headers: AdminHeaders,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> Json<Value> {
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data: BulkMembershipIds = data.into_inner();
|
||||
|
||||
let mut bulk_response = Vec::new();
|
||||
|
@ -1696,11 +1765,11 @@ async fn bulk_public_keys(
|
|||
}
|
||||
}
|
||||
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"data": bulk_response,
|
||||
"object": "list",
|
||||
"continuationToken": null
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
use super::ciphers::update_cipher_from_data;
|
||||
|
@ -1845,15 +1914,18 @@ async fn post_bulk_collections(data: Json<BulkCollectionsData>, headers: Headers
|
|||
}
|
||||
|
||||
#[get("/organizations/<org_id>/policies")]
|
||||
async fn list_policies(org_id: OrganizationId, _headers: AdminHeaders, mut conn: DbConn) -> Json<Value> {
|
||||
async fn list_policies(org_id: OrganizationId, headers: AdminHeaders, mut conn: DbConn) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let policies = OrgPolicy::find_by_org(&org_id, &mut conn).await;
|
||||
let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
|
||||
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"data": policies_json,
|
||||
"object": "list",
|
||||
"continuationToken": null
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
#[get("/organizations/<org_id>/policies/token?<token>")]
|
||||
|
@ -1896,7 +1968,11 @@ fn get_master_password_policy(org_id: OrganizationId, _headers: Headers) -> Json
|
|||
}
|
||||
|
||||
#[get("/organizations/<org_id>/policies/<pol_type>", rank = 2)]
|
||||
async fn get_policy(org_id: OrganizationId, pol_type: i32, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult {
|
||||
async fn get_policy(org_id: OrganizationId, pol_type: i32, headers: AdminHeaders, mut conn: DbConn) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
|
||||
let Some(pol_type_enum) = OrgPolicyType::from_i32(pol_type) else {
|
||||
err!("Invalid or unsupported policy type")
|
||||
};
|
||||
|
@ -1925,6 +2001,9 @@ async fn put_policy(
|
|||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data: PolicyData = data.into_inner();
|
||||
|
||||
let Some(pol_type_enum) = OrgPolicyType::from_i32(pol_type) else {
|
||||
|
@ -2258,7 +2337,7 @@ async fn bulk_deactivate_members(
|
|||
data: Json<BulkRevokeMembershipIds>,
|
||||
headers: AdminHeaders,
|
||||
conn: DbConn,
|
||||
) -> Json<Value> {
|
||||
) -> JsonResult {
|
||||
bulk_revoke_members(org_id, data, headers, conn).await
|
||||
}
|
||||
|
||||
|
@ -2278,7 +2357,10 @@ async fn bulk_revoke_members(
|
|||
data: Json<BulkRevokeMembershipIds>,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> Json<Value> {
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data = data.into_inner();
|
||||
|
||||
let mut bulk_response = Vec::new();
|
||||
|
@ -2302,11 +2384,11 @@ async fn bulk_revoke_members(
|
|||
None => error!("No users to revoke"),
|
||||
}
|
||||
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"data": bulk_response,
|
||||
"object": "list",
|
||||
"continuationToken": null
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
async fn _revoke_member(
|
||||
|
@ -2367,7 +2449,7 @@ async fn bulk_activate_members(
|
|||
data: Json<BulkMembershipIds>,
|
||||
headers: AdminHeaders,
|
||||
conn: DbConn,
|
||||
) -> Json<Value> {
|
||||
) -> JsonResult {
|
||||
bulk_restore_members(org_id, data, headers, conn).await
|
||||
}
|
||||
|
||||
|
@ -2387,7 +2469,10 @@ async fn bulk_restore_members(
|
|||
data: Json<BulkMembershipIds>,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> Json<Value> {
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let data = data.into_inner();
|
||||
|
||||
let mut bulk_response = Vec::new();
|
||||
|
@ -2406,11 +2491,11 @@ async fn bulk_restore_members(
|
|||
));
|
||||
}
|
||||
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"data": bulk_response,
|
||||
"object": "list",
|
||||
"continuationToken": null
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
async fn _restore_member(
|
||||
|
@ -2467,7 +2552,10 @@ async fn _restore_member(
|
|||
}
|
||||
|
||||
#[get("/organizations/<org_id>/groups")]
|
||||
async fn get_groups(org_id: OrganizationId, _headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult {
|
||||
async fn get_groups(org_id: OrganizationId, headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult {
|
||||
if org_id != headers.membership.org_uuid {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let groups: Vec<Value> = if CONFIG.org_groups_enabled() {
|
||||
// Group::find_by_organization(&org_id, &mut conn).await.iter().map(Group::to_json).collect::<Value>()
|
||||
let groups = Group::find_by_organization(&org_id, &mut conn).await;
|
||||
|
@ -2655,9 +2743,12 @@ async fn add_update_group(
|
|||
async fn get_group_details(
|
||||
org_id: OrganizationId,
|
||||
group_id: GroupId,
|
||||
_headers: AdminHeaders,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
if !CONFIG.org_groups_enabled() {
|
||||
err!("Group support is disabled");
|
||||
}
|
||||
|
@ -2737,7 +2828,10 @@ async fn bulk_delete_groups(
|
|||
}
|
||||
|
||||
#[get("/organizations/<org_id>/groups/<group_id>", rank = 2)]
|
||||
async fn get_group(org_id: OrganizationId, group_id: GroupId, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult {
|
||||
async fn get_group(org_id: OrganizationId, group_id: GroupId, headers: AdminHeaders, mut conn: DbConn) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
if !CONFIG.org_groups_enabled() {
|
||||
err!("Group support is disabled");
|
||||
}
|
||||
|
@ -2753,9 +2847,12 @@ async fn get_group(org_id: OrganizationId, group_id: GroupId, _headers: AdminHea
|
|||
async fn get_group_members(
|
||||
org_id: OrganizationId,
|
||||
group_id: GroupId,
|
||||
_headers: AdminHeaders,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
if !CONFIG.org_groups_enabled() {
|
||||
err!("Group support is disabled");
|
||||
}
|
||||
|
@ -2815,9 +2912,12 @@ async fn put_group_members(
|
|||
async fn get_user_groups(
|
||||
org_id: OrganizationId,
|
||||
member_id: MembershipId,
|
||||
_headers: AdminHeaders,
|
||||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
if !CONFIG.org_groups_enabled() {
|
||||
err!("Group support is disabled");
|
||||
}
|
||||
|
@ -2857,6 +2957,9 @@ async fn put_user_groups(
|
|||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
if !CONFIG.org_groups_enabled() {
|
||||
err!("Group support is disabled");
|
||||
}
|
||||
|
@ -2906,6 +3009,9 @@ async fn delete_group_member(
|
|||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> EmptyResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
if !CONFIG.org_groups_enabled() {
|
||||
err!("Group support is disabled");
|
||||
}
|
||||
|
@ -2951,7 +3057,14 @@ struct OrganizationUserResetPasswordRequest {
|
|||
// But the clients do not seem to use this at all
|
||||
// Just add it here in case they will
|
||||
#[get("/organizations/<org_id>/public-key")]
|
||||
async fn get_organization_public_key(org_id: OrganizationId, _headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
async fn get_organization_public_key(
|
||||
org_id: OrganizationId,
|
||||
headers: OrgMemberHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let Some(org) = Organization::find_by_uuid(&org_id, &mut conn).await else {
|
||||
err!("Organization not found")
|
||||
};
|
||||
|
@ -2965,7 +3078,7 @@ async fn get_organization_public_key(org_id: OrganizationId, _headers: Headers,
|
|||
// Obsolete - Renamed to public-key (2023.8), left for backwards compatibility with older clients
|
||||
// https://github.com/bitwarden/server/blob/25dc0c9178e3e3584074bbef0d4be827b7c89415/src/Api/AdminConsole/Controllers/OrganizationsController.cs#L463-L468
|
||||
#[get("/organizations/<org_id>/keys")]
|
||||
async fn get_organization_keys(org_id: OrganizationId, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
async fn get_organization_keys(org_id: OrganizationId, headers: OrgMemberHeaders, conn: DbConn) -> JsonResult {
|
||||
get_organization_public_key(org_id, headers, conn).await
|
||||
}
|
||||
|
||||
|
@ -2978,6 +3091,9 @@ async fn put_reset_password(
|
|||
mut conn: DbConn,
|
||||
nt: Notify<'_>,
|
||||
) -> EmptyResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let Some(org) = Organization::find_by_uuid(&org_id, &mut conn).await else {
|
||||
err!("Required organization not found")
|
||||
};
|
||||
|
@ -3034,6 +3150,9 @@ async fn get_reset_password_details(
|
|||
headers: AdminHeaders,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
let Some(org) = Organization::find_by_uuid(&org_id, &mut conn).await else {
|
||||
err!("Required organization not found")
|
||||
};
|
||||
|
@ -3155,7 +3274,10 @@ async fn get_org_export(
|
|||
headers: AdminHeaders,
|
||||
client_version: Option<ClientVersion>,
|
||||
mut conn: DbConn,
|
||||
) -> Json<Value> {
|
||||
) -> JsonResult {
|
||||
if org_id != headers.org_id {
|
||||
err!("Organization not found", "Organization id's do not match");
|
||||
}
|
||||
// Since version v2023.1.0 the format of the export is different.
|
||||
// Also, this endpoint was created since v2022.9.0.
|
||||
// Therefore, we will check for any version smaller then v2023.1.0 and return a different response.
|
||||
|
@ -3171,7 +3293,7 @@ async fn get_org_export(
|
|||
// Also both main keys here need to be lowercase, else the export will fail.
|
||||
if use_list_response_model {
|
||||
// Backwards compatible pre v2023.1.0 response
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"collections": {
|
||||
"data": convert_json_key_lcase_first(_get_org_collections(&org_id, &mut conn).await),
|
||||
"object": "list",
|
||||
|
@ -3182,13 +3304,13 @@ async fn get_org_export(
|
|||
"object": "list",
|
||||
"continuationToken": null,
|
||||
}
|
||||
}))
|
||||
})))
|
||||
} else {
|
||||
// v2023.1.0 and newer response
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"collections": convert_json_key_lcase_first(_get_org_collections(&org_id, &mut conn).await),
|
||||
"ciphers": convert_json_key_lcase_first(_get_org_details(&org_id, &headers.host, &headers.user.uuid, &mut conn).await),
|
||||
}))
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
52
src/auth.rs
52
src/auth.rs
|
@ -635,24 +635,17 @@ impl<'r> FromRequest<'r> for OrgHeaders {
|
|||
// but there are cases where it is a query value.
|
||||
// First check the path, if this is not a valid uuid, try the query values.
|
||||
let url_org_id: Option<OrganizationId> = {
|
||||
let mut url_org_id = None;
|
||||
if let Some(Ok(org_id)) = request.param::<&str>(1) {
|
||||
if uuid::Uuid::parse_str(org_id).is_ok() {
|
||||
url_org_id = Some(org_id.to_string().into());
|
||||
}
|
||||
if let Some(Ok(org_id)) = request.param::<OrganizationId>(1) {
|
||||
Some(org_id.clone())
|
||||
} else if let Some(Ok(org_id)) = request.query_value::<OrganizationId>("organizationId") {
|
||||
Some(org_id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
if let Some(Ok(org_id)) = request.query_value::<&str>("organizationId") {
|
||||
if uuid::Uuid::parse_str(org_id).is_ok() {
|
||||
url_org_id = Some(org_id.to_string().into());
|
||||
}
|
||||
}
|
||||
|
||||
url_org_id
|
||||
};
|
||||
|
||||
match url_org_id {
|
||||
Some(org_id) => {
|
||||
Some(org_id) if uuid::Uuid::parse_str(&org_id).is_ok() => {
|
||||
let mut conn = match DbConn::from_request(request).await {
|
||||
Outcome::Success(conn) => conn,
|
||||
_ => err_handler!("Error getting DB"),
|
||||
|
@ -697,6 +690,7 @@ pub struct AdminHeaders {
|
|||
pub user: User,
|
||||
pub membership_type: MembershipType,
|
||||
pub ip: ClientIp,
|
||||
pub org_id: OrganizationId,
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
|
@ -712,6 +706,7 @@ impl<'r> FromRequest<'r> for AdminHeaders {
|
|||
user: headers.user,
|
||||
membership_type: headers.membership_type,
|
||||
ip: headers.ip,
|
||||
org_id: headers.membership.org_uuid,
|
||||
})
|
||||
} else {
|
||||
err_handler!("You need to be Admin or Owner to call this endpoint")
|
||||
|
@ -757,6 +752,7 @@ pub struct ManagerHeaders {
|
|||
pub device: Device,
|
||||
pub user: User,
|
||||
pub ip: ClientIp,
|
||||
pub org_id: OrganizationId,
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
|
@ -785,6 +781,7 @@ impl<'r> FromRequest<'r> for ManagerHeaders {
|
|||
device: headers.device,
|
||||
user: headers.user,
|
||||
ip: headers.ip,
|
||||
org_id: headers.membership.org_uuid,
|
||||
})
|
||||
} else {
|
||||
err_handler!("You need to be a Manager, Admin or Owner to call this endpoint")
|
||||
|
@ -864,6 +861,7 @@ impl ManagerHeaders {
|
|||
device: h.device,
|
||||
user: h.user,
|
||||
ip: h.ip,
|
||||
org_id: h.membership.org_uuid,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -872,6 +870,7 @@ pub struct OwnerHeaders {
|
|||
pub device: Device,
|
||||
pub user: User,
|
||||
pub ip: ClientIp,
|
||||
pub org_id: OrganizationId,
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
|
@ -885,6 +884,7 @@ impl<'r> FromRequest<'r> for OwnerHeaders {
|
|||
device: headers.device,
|
||||
user: headers.user,
|
||||
ip: headers.ip,
|
||||
org_id: headers.membership.org_uuid,
|
||||
})
|
||||
} else {
|
||||
err_handler!("You need to be Owner to call this endpoint")
|
||||
|
@ -892,6 +892,30 @@ impl<'r> FromRequest<'r> for OwnerHeaders {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct OrgMemberHeaders {
|
||||
pub host: String,
|
||||
pub user: User,
|
||||
pub org_id: OrganizationId,
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
impl<'r> FromRequest<'r> for OrgMemberHeaders {
|
||||
type Error = &'static str;
|
||||
|
||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
let headers = try_outcome!(OrgHeaders::from_request(request).await);
|
||||
if headers.membership_type >= MembershipType::User {
|
||||
Outcome::Success(Self {
|
||||
host: headers.host,
|
||||
user: headers.user,
|
||||
org_id: headers.membership.org_uuid,
|
||||
})
|
||||
} else {
|
||||
err_handler!("You need to be a Member of the Organization to call this endpoint")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Client IP address detection
|
||||
//
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use std::env::consts::EXE_SUFFIX;
|
||||
use std::process::exit;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
RwLock,
|
||||
use std::{
|
||||
env::consts::EXE_SUFFIX,
|
||||
process::exit,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
RwLock,
|
||||
},
|
||||
};
|
||||
|
||||
use job_scheduler_ng::Schedule;
|
||||
|
@ -12,7 +14,7 @@ use reqwest::Url;
|
|||
use crate::{
|
||||
db::DbConnType,
|
||||
error::Error,
|
||||
util::{get_env, get_env_bool, get_web_vault_version, parse_experimental_client_feature_flags},
|
||||
util::{get_env, get_env_bool, get_web_vault_version, is_valid_email, parse_experimental_client_feature_flags},
|
||||
};
|
||||
|
||||
static CONFIG_FILE: Lazy<String> = Lazy::new(|| {
|
||||
|
@ -114,6 +116,14 @@ macro_rules! make_config {
|
|||
serde_json::from_str(&config_str).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn clear_non_editable(&mut self) {
|
||||
$($(
|
||||
if !$editable {
|
||||
self.$name = None;
|
||||
}
|
||||
)+)+
|
||||
}
|
||||
|
||||
/// Merges the values of both builders into a new builder.
|
||||
/// If both have the same element, `other` wins.
|
||||
fn merge(&self, other: &Self, show_overrides: bool, overrides: &mut Vec<String>) -> Self {
|
||||
|
@ -716,7 +726,7 @@ make_config! {
|
|||
/// Use Sendmail |> Whether to send mail via the `sendmail` command
|
||||
use_sendmail: bool, true, def, false;
|
||||
/// Sendmail Command |> Which sendmail command to use. The one found in the $PATH is used if not specified.
|
||||
sendmail_command: String, true, option;
|
||||
sendmail_command: String, false, option;
|
||||
/// Host
|
||||
smtp_host: String, true, option;
|
||||
/// DEPRECATED smtp_ssl |> DEPRECATED - Please use SMTP_SECURITY
|
||||
|
@ -943,12 +953,12 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
|
|||
let command = cfg.sendmail_command.clone().unwrap_or_else(|| format!("sendmail{EXE_SUFFIX}"));
|
||||
|
||||
let mut path = std::path::PathBuf::from(&command);
|
||||
|
||||
// Check if we can find the sendmail command to execute when no absolute path is given
|
||||
if !path.is_absolute() {
|
||||
match which::which(&command) {
|
||||
Ok(result) => path = result,
|
||||
Err(_) => err!(format!("sendmail command {command:?} not found in $PATH")),
|
||||
}
|
||||
let Ok(which_path) = which::which(&command) else {
|
||||
err!(format!("sendmail command {command} not found in $PATH"))
|
||||
};
|
||||
path = which_path;
|
||||
}
|
||||
|
||||
match path.metadata() {
|
||||
|
@ -982,8 +992,8 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
|
|||
}
|
||||
}
|
||||
|
||||
if (cfg.smtp_host.is_some() || cfg.use_sendmail) && !cfg.smtp_from.contains('@') {
|
||||
err!("SMTP_FROM does not contain a mandatory @ sign")
|
||||
if (cfg.smtp_host.is_some() || cfg.use_sendmail) && !is_valid_email(&cfg.smtp_from) {
|
||||
err!(format!("SMTP_FROM '{}' is not a valid email address", cfg.smtp_from))
|
||||
}
|
||||
|
||||
if cfg._enable_email_2fa && cfg.email_token_size < 6 {
|
||||
|
@ -1249,12 +1259,17 @@ impl Config {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn update_config(&self, other: ConfigBuilder) -> Result<(), Error> {
|
||||
pub fn update_config(&self, other: ConfigBuilder, ignore_non_editable: bool) -> Result<(), Error> {
|
||||
// Remove default values
|
||||
//let builder = other.remove(&self.inner.read().unwrap()._env);
|
||||
|
||||
// TODO: Remove values that are defaults, above only checks those set by env and not the defaults
|
||||
let builder = other;
|
||||
let mut builder = other;
|
||||
|
||||
// Remove values that are not editable
|
||||
if ignore_non_editable {
|
||||
builder.clear_non_editable();
|
||||
}
|
||||
|
||||
// Serialize now before we consume the builder
|
||||
let config_str = serde_json::to_string_pretty(&builder)?;
|
||||
|
@ -1289,7 +1304,7 @@ impl Config {
|
|||
let mut _overrides = Vec::new();
|
||||
usr.merge(&other, false, &mut _overrides)
|
||||
};
|
||||
self.update_config(builder)
|
||||
self.update_config(builder, false)
|
||||
}
|
||||
|
||||
// The `signups_allowed` setting is overrided if:
|
||||
|
|
|
@ -152,6 +152,7 @@ impl PartialOrd<MembershipType> for i32 {
|
|||
/// Local methods
|
||||
impl Organization {
|
||||
pub fn new(name: String, billing_email: String, private_key: Option<String>, public_key: Option<String>) -> Self {
|
||||
let billing_email = billing_email.to_lowercase();
|
||||
Self {
|
||||
uuid: OrganizationId(crate::util::get_uuid()),
|
||||
name,
|
||||
|
@ -307,8 +308,8 @@ use crate::error::MapResult;
|
|||
/// Database methods
|
||||
impl Organization {
|
||||
pub async fn save(&self, conn: &mut DbConn) -> EmptyResult {
|
||||
if !email_address::EmailAddress::is_valid(self.billing_email.trim()) {
|
||||
err!(format!("BillingEmail {} is not a valid email address", self.billing_email.trim()))
|
||||
if !crate::util::is_valid_email(&self.billing_email) {
|
||||
err!(format!("BillingEmail {} is not a valid email address", self.billing_email))
|
||||
}
|
||||
|
||||
for member in Membership::find_by_org(&self.uuid, conn).await.iter() {
|
||||
|
|
|
@ -276,8 +276,8 @@ impl User {
|
|||
}
|
||||
|
||||
pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult {
|
||||
if self.email.trim().is_empty() {
|
||||
err!("User email can't be empty")
|
||||
if !crate::util::is_valid_email(&self.email) {
|
||||
err!(format!("User email {} is not a valid email address", self.email))
|
||||
}
|
||||
|
||||
self.updated_at = Utc::now().naive_utc();
|
||||
|
@ -417,8 +417,8 @@ impl Invitation {
|
|||
}
|
||||
|
||||
pub async fn save(&self, conn: &mut DbConn) -> EmptyResult {
|
||||
if self.email.trim().is_empty() {
|
||||
err!("Invitation email can't be empty")
|
||||
if !crate::util::is_valid_email(&self.email) {
|
||||
err!(format!("Invitation email {} is not a valid email address", self.email))
|
||||
}
|
||||
|
||||
db_run! {conn:
|
||||
|
|
11
src/mail.rs
11
src/mail.rs
|
@ -1,7 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use chrono::NaiveDateTime;
|
||||
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
||||
use std::{env::consts::EXE_SUFFIX, str::FromStr};
|
||||
|
||||
use lettre::{
|
||||
message::{Attachment, Body, Mailbox, Message, MultiPart, SinglePart},
|
||||
|
@ -26,7 +25,7 @@ fn sendmail_transport() -> AsyncSendmailTransport<Tokio1Executor> {
|
|||
if let Some(command) = CONFIG.sendmail_command() {
|
||||
AsyncSendmailTransport::new_with_command(command)
|
||||
} else {
|
||||
AsyncSendmailTransport::new()
|
||||
AsyncSendmailTransport::new_with_command(format!("sendmail{EXE_SUFFIX}"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,13 +622,13 @@ async fn send_with_selected_transport(email: Message) -> EmptyResult {
|
|||
// Match some common errors and make them more user friendly
|
||||
Err(e) => {
|
||||
if e.is_client() {
|
||||
debug!("Sendmail client error: {:#?}", e);
|
||||
debug!("Sendmail client error: {:?}", e);
|
||||
err!(format!("Sendmail client error: {e}"));
|
||||
} else if e.is_response() {
|
||||
debug!("Sendmail response error: {:#?}", e);
|
||||
debug!("Sendmail response error: {:?}", e);
|
||||
err!(format!("Sendmail response error: {e}"));
|
||||
} else {
|
||||
debug!("Sendmail error: {:#?}", e);
|
||||
debug!("Sendmail error: {:?}", e);
|
||||
err!(format!("Sendmail error: {e}"));
|
||||
}
|
||||
}
|
||||
|
|
5
src/static/scripts/admin_diagnostics.js
gevendort
5
src/static/scripts/admin_diagnostics.js
gevendort
|
@ -236,8 +236,11 @@ function checkSecurityHeaders(headers, omit) {
|
|||
"referrer-policy": ["same-origin"],
|
||||
"x-xss-protection": ["0"],
|
||||
"x-robots-tag": ["noindex", "nofollow"],
|
||||
"cross-origin-resource-policy": ["same-origin"],
|
||||
"content-security-policy": [
|
||||
"default-src 'self'",
|
||||
"default-src 'none'",
|
||||
"font-src 'self'",
|
||||
"manifest-src 'self'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
"object-src 'self' blob:",
|
||||
|
|
23
src/util.rs
23
src/util.rs
|
@ -55,6 +55,8 @@ impl Fairing for AppHeaders {
|
|||
res.set_raw_header("Referrer-Policy", "same-origin");
|
||||
res.set_raw_header("X-Content-Type-Options", "nosniff");
|
||||
res.set_raw_header("X-Robots-Tag", "noindex, nofollow");
|
||||
res.set_raw_header("Cross-Origin-Resource-Policy", "same-origin");
|
||||
|
||||
// Obsolete in modern browsers, unsafe (XS-Leak), and largely replaced by CSP
|
||||
res.set_raw_header("X-XSS-Protection", "0");
|
||||
|
||||
|
@ -74,7 +76,9 @@ impl Fairing for AppHeaders {
|
|||
// # Mail Relay: https://bitwarden.com/blog/add-privacy-and-security-using-email-aliases-with-bitwarden/
|
||||
// app.simplelogin.io, app.addy.io, api.fastmail.com, quack.duckduckgo.com
|
||||
let csp = format!(
|
||||
"default-src 'self'; \
|
||||
"default-src 'none'; \
|
||||
font-src 'self'; \
|
||||
manifest-src 'self'; \
|
||||
base-uri 'self'; \
|
||||
form-action 'self'; \
|
||||
object-src 'self' blob:; \
|
||||
|
@ -464,6 +468,23 @@ pub fn parse_date(date: &str) -> NaiveDateTime {
|
|||
DateTime::parse_from_rfc3339(date).unwrap().naive_utc()
|
||||
}
|
||||
|
||||
/// Returns true or false if an email address is valid or not
|
||||
///
|
||||
/// Some extra checks instead of only using email_address
|
||||
/// This prevents from weird email formats still excepted but in the end invalid
|
||||
pub fn is_valid_email(email: &str) -> bool {
|
||||
let Ok(email) = email_address::EmailAddress::from_str(email) else {
|
||||
return false;
|
||||
};
|
||||
let Ok(email_url) = url::Url::parse(&format!("https://{}", email.domain())) else {
|
||||
return false;
|
||||
};
|
||||
if email_url.path().ne("/") || email_url.domain().is_none() || email_url.query().is_some() {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
//
|
||||
// Deployment environment methods
|
||||
//
|
||||
|
|
Laden …
Tabelle hinzufügen
In neuem Issue referenzieren