mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2026-06-10 20:49:13 +03:00
Compare commits
6 Commits
1.36.0
..
70f9dfbe8b
| Author | SHA1 | Date | |
|---|---|---|---|
| 70f9dfbe8b | |||
| 54895ad4be | |||
| a057c7deae | |||
| 2f85b62d2f | |||
| 9bc14e6e77 | |||
| cdf711bb30 |
+5
-4
@@ -50,10 +50,11 @@
|
|||||||
#########################
|
#########################
|
||||||
|
|
||||||
## Database URL
|
## Database URL
|
||||||
## When using SQLite, this is the path to the DB file, and it defaults to
|
## When using SQLite, this should use the sqlite:// scheme followed by the path
|
||||||
## %DATA_FOLDER%/db.sqlite3. If DATA_FOLDER is set to an external location, this
|
## to the DB file. It defaults to sqlite://%DATA_FOLDER%/db.sqlite3.
|
||||||
## must be set to a local sqlite3 file path.
|
## Bare paths without the sqlite:// scheme are supported for backwards compatibility,
|
||||||
# DATABASE_URL=data/db.sqlite3
|
## but only if the database file already exists.
|
||||||
|
# DATABASE_URL=sqlite://data/db.sqlite3
|
||||||
## When using MySQL, specify an appropriate connection URI.
|
## When using MySQL, specify an appropriate connection URI.
|
||||||
## Details: https://docs.diesel.rs/2.1.x/diesel/mysql/struct.MysqlConnection.html
|
## Details: https://docs.diesel.rs/2.1.x/diesel/mysql/struct.MysqlConnection.html
|
||||||
# DATABASE_URL=mysql://user:password@host[:port]/database_name
|
# DATABASE_URL=mysql://user:password@host[:port]/database_name
|
||||||
|
|||||||
Generated
+156
-326
@@ -8,17 +8,6 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aes"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cipher",
|
|
||||||
"cpufeatures 0.2.17",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.8.12"
|
version = "0.8.12"
|
||||||
@@ -670,17 +659,6 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "backon"
|
|
||||||
version = "1.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef"
|
|
||||||
dependencies = [
|
|
||||||
"fastrand",
|
|
||||||
"gloo-timers",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base16ct"
|
name = "base16ct"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -778,15 +756,6 @@ dependencies = [
|
|||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-padding"
|
|
||||||
version = "0.3.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blocking"
|
name = "blocking"
|
||||||
version = "1.6.2"
|
version = "1.6.2"
|
||||||
@@ -892,15 +861,6 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
|
checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cbc"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
|
|
||||||
dependencies = [
|
|
||||||
"cipher",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.61"
|
version = "1.2.61"
|
||||||
@@ -919,12 +879,6 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg_aliases"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chacha20"
|
name = "chacha20"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@@ -960,16 +914,6 @@ dependencies = [
|
|||||||
"phf 0.12.1",
|
"phf 0.12.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cipher"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
|
||||||
dependencies = [
|
|
||||||
"crypto-common 0.1.6",
|
|
||||||
"inout",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cmov"
|
name = "cmov"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -2357,15 +2301,6 @@ dependencies = [
|
|||||||
"digest 0.11.2",
|
"digest 0.11.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "home"
|
|
||||||
version = "0.5.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys 0.61.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hostname"
|
name = "hostname"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
@@ -2516,11 +2451,9 @@ dependencies = [
|
|||||||
"hyper 1.9.0",
|
"hyper 1.9.0",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"rustls 0.23.40",
|
"rustls 0.23.40",
|
||||||
"rustls-native-certs",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.26.4",
|
"tokio-rustls 0.26.4",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"webpki-roots",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2716,16 +2649,6 @@ version = "0.1.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
|
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "inout"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
|
||||||
dependencies = [
|
|
||||||
"block-padding",
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipconfig"
|
name = "ipconfig"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@@ -2798,10 +2721,12 @@ checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"jiff-static",
|
"jiff-static",
|
||||||
"jiff-tzdb-platform",
|
"jiff-tzdb-platform",
|
||||||
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"portable-atomic-util",
|
"portable-atomic-util",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
|
"wasm-bindgen",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2913,21 +2838,6 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jsonwebtoken"
|
|
||||||
version = "9.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.22.1",
|
|
||||||
"js-sys",
|
|
||||||
"pem",
|
|
||||||
"ring",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"simple_asn1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonwebtoken"
|
name = "jsonwebtoken"
|
||||||
version = "10.3.0"
|
version = "10.3.0"
|
||||||
@@ -3098,12 +3008,6 @@ dependencies = [
|
|||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lru-slab"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "macros"
|
name = "macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -3131,6 +3035,15 @@ dependencies = [
|
|||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mea"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6747f54621d156e1b47eb6b25f39a941b9fc347f98f67d25d8881ff99e8ed832"
|
||||||
|
dependencies = [
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.8.0"
|
version = "2.8.0"
|
||||||
@@ -3408,7 +3321,6 @@ dependencies = [
|
|||||||
"getrandom 0.2.17",
|
"getrandom 0.2.17",
|
||||||
"http 1.4.0",
|
"http 1.4.0",
|
||||||
"rand 0.8.6",
|
"rand 0.8.6",
|
||||||
"reqwest",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_path_to_error",
|
"serde_path_to_error",
|
||||||
@@ -3438,31 +3350,76 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opendal"
|
name = "opendal"
|
||||||
version = "0.55.0"
|
version = "0.56.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d075ab8a203a6ab4bc1bce0a4b9fe486a72bf8b939037f4b78d95386384bc80a"
|
checksum = "97b31d3d8e99a85d83b73ec26647f5607b80578ed9375810b6e44ffa3590a236"
|
||||||
|
dependencies = [
|
||||||
|
"opendal-core",
|
||||||
|
"opendal-service-fs",
|
||||||
|
"opendal-service-s3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opendal-core"
|
||||||
|
version = "0.56.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1849dd2687e173e776d3af5fce1ba3ae47b9dd37a09d1c4deba850ef45fe00ca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"backon",
|
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"crc32c",
|
|
||||||
"futures",
|
"futures",
|
||||||
"getrandom 0.2.17",
|
|
||||||
"http 1.4.0",
|
"http 1.4.0",
|
||||||
"http-body 1.0.1",
|
"http-body 1.0.1",
|
||||||
"jiff",
|
"jiff",
|
||||||
"log",
|
"log",
|
||||||
"md-5",
|
"md-5",
|
||||||
|
"mea",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"quick-xml 0.38.4",
|
"quick-xml 0.38.4",
|
||||||
"reqsign",
|
"reqsign-core",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
"web-time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opendal-service-fs"
|
||||||
|
version = "0.56.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf0be0417abeeb0053376d816b90fceb9ca98f20dfb54ebf1f2a282729f83663"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"log",
|
||||||
|
"opendal-core",
|
||||||
|
"serde",
|
||||||
|
"tokio",
|
||||||
|
"xattr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opendal-service-s3"
|
||||||
|
version = "0.56.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dadddeb9bb50b0d30927dd914c298c4ddca47e4c1cfa7674d311f0cf9b051c8"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"bytes",
|
||||||
|
"crc32c",
|
||||||
|
"http 1.4.0",
|
||||||
|
"log",
|
||||||
|
"md-5",
|
||||||
|
"opendal-core",
|
||||||
|
"quick-xml 0.38.4",
|
||||||
|
"reqsign-aws-v4",
|
||||||
|
"reqsign-core",
|
||||||
|
"reqsign-file-read-tokio",
|
||||||
|
"serde",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3651,16 +3608,6 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5a797f0e07bdf071d15742978fc3128ec6c22891c31a3a931513263904c982a"
|
checksum = "c5a797f0e07bdf071d15742978fc3128ec6c22891c31a3a931513263904c982a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pbkdf2"
|
|
||||||
version = "0.12.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
|
||||||
dependencies = [
|
|
||||||
"digest 0.10.7",
|
|
||||||
"hmac 0.12.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pear"
|
name = "pear"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
@@ -3852,21 +3799,6 @@ dependencies = [
|
|||||||
"spki",
|
"spki",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pkcs5"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6"
|
|
||||||
dependencies = [
|
|
||||||
"aes",
|
|
||||||
"cbc",
|
|
||||||
"der",
|
|
||||||
"pbkdf2",
|
|
||||||
"scrypt",
|
|
||||||
"sha2 0.10.9",
|
|
||||||
"spki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkcs8"
|
name = "pkcs8"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
@@ -3874,8 +3806,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"der",
|
"der",
|
||||||
"pkcs5",
|
|
||||||
"rand_core 0.6.4",
|
|
||||||
"spki",
|
"spki",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4038,16 +3968,6 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quick-xml"
|
|
||||||
version = "0.37.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.38.4"
|
version = "0.38.4"
|
||||||
@@ -4059,58 +3979,13 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quinn"
|
name = "quick-xml"
|
||||||
version = "0.11.9"
|
version = "0.39.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
|
checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"memchr",
|
||||||
"cfg_aliases",
|
"serde",
|
||||||
"pin-project-lite",
|
|
||||||
"quinn-proto",
|
|
||||||
"quinn-udp",
|
|
||||||
"rustc-hash",
|
|
||||||
"rustls 0.23.40",
|
|
||||||
"socket2 0.6.3",
|
|
||||||
"thiserror 2.0.18",
|
|
||||||
"tokio",
|
|
||||||
"tracing",
|
|
||||||
"web-time",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quinn-proto"
|
|
||||||
version = "0.11.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"getrandom 0.3.4",
|
|
||||||
"lru-slab",
|
|
||||||
"rand 0.9.4",
|
|
||||||
"ring",
|
|
||||||
"rustc-hash",
|
|
||||||
"rustls 0.23.40",
|
|
||||||
"rustls-pki-types",
|
|
||||||
"slab",
|
|
||||||
"thiserror 2.0.18",
|
|
||||||
"tinyvec",
|
|
||||||
"tracing",
|
|
||||||
"web-time",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quinn-udp"
|
|
||||||
version = "0.5.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
|
|
||||||
dependencies = [
|
|
||||||
"cfg_aliases",
|
|
||||||
"libc",
|
|
||||||
"once_cell",
|
|
||||||
"socket2 0.6.3",
|
|
||||||
"tracing",
|
|
||||||
"windows-sys 0.60.2",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4312,43 +4187,64 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqsign"
|
name = "reqsign-aws-v4"
|
||||||
version = "0.16.5"
|
version = "3.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "43451dbf3590a7590684c25fb8d12ecdcc90ed3ac123433e500447c7d77ed701"
|
checksum = "44eaca382e94505a49f1a4849658d153aebf79d9c1a58e5dd3b10361511e9f43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"bytes",
|
||||||
"base64 0.22.1",
|
|
||||||
"chrono",
|
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"getrandom 0.2.17",
|
|
||||||
"hex",
|
|
||||||
"hmac 0.12.1",
|
|
||||||
"home",
|
|
||||||
"http 1.4.0",
|
"http 1.4.0",
|
||||||
"jsonwebtoken 9.3.1",
|
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"quick-xml 0.37.5",
|
"quick-xml 0.39.4",
|
||||||
"rand 0.8.6",
|
"reqsign-core",
|
||||||
"reqwest",
|
|
||||||
"rsa",
|
|
||||||
"rust-ini",
|
"rust-ini",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"sha1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reqsign-core"
|
||||||
|
version = "3.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b10302cf0a7d7e7352ba211fc92c3c5bebf1286153e49cc5aa87348078a8e102"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"base64 0.22.1",
|
||||||
|
"bytes",
|
||||||
|
"form_urlencoded",
|
||||||
|
"futures",
|
||||||
|
"hex",
|
||||||
|
"hmac 0.12.1",
|
||||||
|
"http 1.4.0",
|
||||||
|
"jiff",
|
||||||
|
"log",
|
||||||
|
"percent-encoding",
|
||||||
"sha1",
|
"sha1",
|
||||||
"sha2 0.10.9",
|
"sha2 0.10.9",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reqsign-file-read-tokio"
|
||||||
|
version = "3.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2d89295b3d17abea31851cc8de55d843d89c52132c864963c38d41920613dc5"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"reqsign-core",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml 0.8.23",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.28"
|
version = "0.13.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -4370,10 +4266,9 @@ dependencies = [
|
|||||||
"mime",
|
"mime",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"quinn",
|
|
||||||
"rustls 0.23.40",
|
"rustls 0.23.40",
|
||||||
"rustls-native-certs",
|
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
"rustls-platform-verifier",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
@@ -4389,7 +4284,6 @@ dependencies = [
|
|||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"wasm-streams",
|
"wasm-streams",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"webpki-roots",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4560,7 +4454,6 @@ dependencies = [
|
|||||||
"pkcs1",
|
"pkcs1",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"sha2 0.10.9",
|
|
||||||
"signature",
|
"signature",
|
||||||
"spki",
|
"spki",
|
||||||
"subtle",
|
"subtle",
|
||||||
@@ -4597,12 +4490,6 @@ dependencies = [
|
|||||||
"ordered-multimap",
|
"ordered-multimap",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-hash"
|
|
||||||
version = "2.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -4688,10 +4575,36 @@ version = "1.14.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
|
checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"web-time",
|
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-platform-verifier"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation 0.10.1",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"jni",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"rustls 0.23.40",
|
||||||
|
"rustls-native-certs",
|
||||||
|
"rustls-platform-verifier-android",
|
||||||
|
"rustls-webpki 0.103.13",
|
||||||
|
"security-framework",
|
||||||
|
"security-framework-sys",
|
||||||
|
"webpki-root-certs",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-platform-verifier-android"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.101.7"
|
version = "0.101.7"
|
||||||
@@ -4725,15 +4638,6 @@ version = "1.0.23"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "salsa20"
|
|
||||||
version = "0.10.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
|
|
||||||
dependencies = [
|
|
||||||
"cipher",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@@ -4797,17 +4701,6 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scrypt"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f"
|
|
||||||
dependencies = [
|
|
||||||
"pbkdf2",
|
|
||||||
"salsa20",
|
|
||||||
"sha2 0.10.9",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sct"
|
name = "sct"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -5869,7 +5762,6 @@ checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0"
|
|||||||
name = "vaultwarden"
|
name = "vaultwarden"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
|
||||||
"argon2",
|
"argon2",
|
||||||
"aws-config",
|
"aws-config",
|
||||||
"aws-credential-types",
|
"aws-credential-types",
|
||||||
@@ -5899,7 +5791,7 @@ dependencies = [
|
|||||||
"html5gum",
|
"html5gum",
|
||||||
"http 1.4.0",
|
"http 1.4.0",
|
||||||
"job_scheduler_ng",
|
"job_scheduler_ng",
|
||||||
"jsonwebtoken 10.3.0",
|
"jsonwebtoken",
|
||||||
"lettre",
|
"lettre",
|
||||||
"libsqlite3-sys",
|
"libsqlite3-sys",
|
||||||
"log",
|
"log",
|
||||||
@@ -5916,13 +5808,15 @@ dependencies = [
|
|||||||
"pico-args",
|
"pico-args",
|
||||||
"rand 0.10.1",
|
"rand 0.10.1",
|
||||||
"regex",
|
"regex",
|
||||||
"reqsign",
|
"reqsign-aws-v4",
|
||||||
|
"reqsign-core",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"ring",
|
"ring",
|
||||||
"rmpv",
|
"rmpv",
|
||||||
"rocket",
|
"rocket",
|
||||||
"rocket_ws",
|
"rocket_ws",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
|
"rustls 0.23.40",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -6083,9 +5977,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-streams"
|
name = "wasm-streams"
|
||||||
version = "0.4.2"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
|
checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@@ -6195,10 +6089,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-root-certs"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d"
|
checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
@@ -6346,15 +6240,6 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.60.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.53.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.61.2"
|
version = "0.61.2"
|
||||||
@@ -6388,30 +6273,13 @@ dependencies = [
|
|||||||
"windows_aarch64_gnullvm 0.52.6",
|
"windows_aarch64_gnullvm 0.52.6",
|
||||||
"windows_aarch64_msvc 0.52.6",
|
"windows_aarch64_msvc 0.52.6",
|
||||||
"windows_i686_gnu 0.52.6",
|
"windows_i686_gnu 0.52.6",
|
||||||
"windows_i686_gnullvm 0.52.6",
|
"windows_i686_gnullvm",
|
||||||
"windows_i686_msvc 0.52.6",
|
"windows_i686_msvc 0.52.6",
|
||||||
"windows_x86_64_gnu 0.52.6",
|
"windows_x86_64_gnu 0.52.6",
|
||||||
"windows_x86_64_gnullvm 0.52.6",
|
"windows_x86_64_gnullvm 0.52.6",
|
||||||
"windows_x86_64_msvc 0.52.6",
|
"windows_x86_64_msvc 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.53.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
"windows_aarch64_gnullvm 0.53.1",
|
|
||||||
"windows_aarch64_msvc 0.53.1",
|
|
||||||
"windows_i686_gnu 0.53.1",
|
|
||||||
"windows_i686_gnullvm 0.53.1",
|
|
||||||
"windows_i686_msvc 0.53.1",
|
|
||||||
"windows_x86_64_gnu 0.53.1",
|
|
||||||
"windows_x86_64_gnullvm 0.53.1",
|
|
||||||
"windows_x86_64_msvc 0.53.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6424,12 +6292,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6442,12 +6304,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6460,24 +6316,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnullvm"
|
name = "windows_i686_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnullvm"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6490,12 +6334,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6508,12 +6346,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6526,12 +6358,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6544,12 +6370,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.26"
|
version = "0.6.26"
|
||||||
@@ -6691,6 +6511,16 @@ dependencies = [
|
|||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xattr"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rustix",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml"
|
name = "xml"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@@ -6737,9 +6567,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yubico_ng"
|
name = "yubico_ng"
|
||||||
version = "0.14.1"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "929981f5b46b8fb8ee54b144de6b55c3a94fbe26635ee25b0e126e184250867c"
|
checksum = "228e2862e3c66f3224102d9a00d9d3646b271a05cc6c4819fea195fa8b5c00e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
|
|||||||
+8
-7
@@ -39,7 +39,7 @@ vendored_openssl = ["openssl/vendored"]
|
|||||||
# Enable MiMalloc memory allocator to replace the default malloc
|
# Enable MiMalloc memory allocator to replace the default malloc
|
||||||
# This can improve performance for Alpine builds
|
# This can improve performance for Alpine builds
|
||||||
enable_mimalloc = ["dep:mimalloc"]
|
enable_mimalloc = ["dep:mimalloc"]
|
||||||
s3 = ["opendal/services-s3", "dep:aws-config", "dep:aws-credential-types", "dep:aws-smithy-runtime-api", "dep:anyhow", "dep:http", "dep:reqsign"]
|
s3 = ["opendal/services-s3", "dep:aws-config", "dep:aws-credential-types", "dep:aws-smithy-runtime-api", "dep:http", "dep:reqsign-aws-v4", "dep:reqsign-core"]
|
||||||
|
|
||||||
# OIDC specific features
|
# OIDC specific features
|
||||||
oidc-accept-rfc3339-timestamps = ["openidconnect/accept-rfc3339-timestamps"]
|
oidc-accept-rfc3339-timestamps = ["openidconnect/accept-rfc3339-timestamps"]
|
||||||
@@ -102,6 +102,7 @@ libsqlite3-sys = { version = "0.37.0", optional = true }
|
|||||||
# Crypto-related libraries
|
# Crypto-related libraries
|
||||||
rand = "0.10.1"
|
rand = "0.10.1"
|
||||||
ring = "0.17.14"
|
ring = "0.17.14"
|
||||||
|
rustls = { version = "0.23.40", features = ["ring", "std"], default-features = false }
|
||||||
subtle = "2.6.1"
|
subtle = "2.6.1"
|
||||||
|
|
||||||
# UUID generation
|
# UUID generation
|
||||||
@@ -125,7 +126,7 @@ jsonwebtoken = { version = "10.3.0", features = ["use_pem", "rust_crypto"], defa
|
|||||||
totp-lite = "2.0.1"
|
totp-lite = "2.0.1"
|
||||||
|
|
||||||
# Yubico Library
|
# Yubico Library
|
||||||
yubico = { package = "yubico_ng", version = "0.14.1", features = ["online-tokio"], default-features = false }
|
yubico = { package = "yubico_ng", version = "0.15.0", features = ["online-tokio"], default-features = false }
|
||||||
|
|
||||||
# WebAuthn libraries
|
# WebAuthn libraries
|
||||||
# danger-allow-state-serialisation is needed to save the state in the db
|
# danger-allow-state-serialisation is needed to save the state in the db
|
||||||
@@ -146,7 +147,7 @@ email_address = "0.2.9"
|
|||||||
handlebars = { version = "6.4.0", features = ["dir_source"] }
|
handlebars = { version = "6.4.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.28", features = ["rustls-tls", "rustls-tls-native-roots", "stream", "json", "deflate", "gzip", "brotli", "zstd", "socks", "cookies", "charset", "http2", "system-proxy"], default-features = false}
|
reqwest = { version = "0.13.3", features = ["rustls-no-provider", "stream", "json", "form", "deflate", "gzip", "brotli", "zstd", "socks", "cookies", "charset", "http2", "system-proxy"], default-features = false}
|
||||||
hickory-resolver = "0.26.1"
|
hickory-resolver = "0.26.1"
|
||||||
|
|
||||||
# Favicon extraction libraries
|
# Favicon extraction libraries
|
||||||
@@ -174,7 +175,7 @@ pastey = "0.2.2"
|
|||||||
governor = "0.10.4"
|
governor = "0.10.4"
|
||||||
|
|
||||||
# OIDC for SSO
|
# OIDC for SSO
|
||||||
openidconnect = { version = "4.0.1", features = ["reqwest", "rustls-tls"] }
|
openidconnect = { version = "4.0.1", default-features = false }
|
||||||
moka = { version = "0.12.15", features = ["future"] }
|
moka = { version = "0.12.15", features = ["future"] }
|
||||||
|
|
||||||
# Check client versions for specific features.
|
# Check client versions for specific features.
|
||||||
@@ -196,15 +197,15 @@ rpassword = "7.5.1"
|
|||||||
grass_compiler = { version = "0.13.4", default-features = false }
|
grass_compiler = { version = "0.13.4", default-features = false }
|
||||||
|
|
||||||
# File are accessed through Apache OpenDAL
|
# File are accessed through Apache OpenDAL
|
||||||
opendal = { version = "0.55.0", features = ["services-fs"], default-features = false }
|
opendal = { version = "0.56.0", features = ["services-fs"], default-features = false }
|
||||||
|
|
||||||
# For retrieving AWS credentials, including temporary SSO credentials
|
# For retrieving AWS credentials, including temporary SSO credentials
|
||||||
anyhow = { version = "1.0.102", optional = true }
|
|
||||||
aws-config = { version = "1.8.16", features = ["behavior-version-latest", "rt-tokio", "credentials-process", "sso"], default-features = false, optional = true }
|
aws-config = { version = "1.8.16", features = ["behavior-version-latest", "rt-tokio", "credentials-process", "sso"], default-features = false, optional = true }
|
||||||
aws-credential-types = { version = "1.2.14", optional = true }
|
aws-credential-types = { version = "1.2.14", optional = true }
|
||||||
aws-smithy-runtime-api = { version = "1.12.0", optional = true }
|
aws-smithy-runtime-api = { version = "1.12.0", optional = true }
|
||||||
http = { version = "1.4.0", optional = true }
|
http = { version = "1.4.0", optional = true }
|
||||||
reqsign = { version = "0.16.5", optional = true }
|
reqsign-aws-v4 = { version = "3.0.0", optional = true }
|
||||||
|
reqsign-core = { version = "3.0.0", optional = true }
|
||||||
|
|
||||||
# Strip debuginfo from the release builds
|
# Strip debuginfo from the release builds
|
||||||
# The debug symbols are to provide better panic traces
|
# The debug symbols are to provide better panic traces
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ ENV DEBIAN_FRONTEND=noninteractive \
|
|||||||
# Debian Trixie uses libpq v17
|
# Debian Trixie uses libpq v17
|
||||||
PQ_LIB_DIR="/usr/local/musl/pq17/lib"
|
PQ_LIB_DIR="/usr/local/musl/pq17/lib"
|
||||||
|
|
||||||
|
|
||||||
# Create CARGO_HOME folder and don't download rust docs
|
# Create CARGO_HOME folder and don't download rust docs
|
||||||
RUN mkdir -pv "${CARGO_HOME}" && \
|
RUN mkdir -pv "${CARGO_HOME}" && \
|
||||||
rustup set profile minimal
|
rustup set profile minimal
|
||||||
|
|||||||
+11
-33
@@ -51,7 +51,7 @@ ENV DEBIAN_FRONTEND=noninteractive \
|
|||||||
TERM=xterm-256color \
|
TERM=xterm-256color \
|
||||||
CARGO_HOME="/root/.cargo" \
|
CARGO_HOME="/root/.cargo" \
|
||||||
USER="root"
|
USER="root"
|
||||||
# Install clang to get `xx-cargo` working
|
# Install clang && xx-c-essentials to get `xx-cargo` working
|
||||||
# Install pkg-config to allow amd64 builds to find all libraries
|
# Install pkg-config to allow amd64 builds to find all libraries
|
||||||
# Install git so build.rs can determine the correct version
|
# Install git so build.rs can determine the correct version
|
||||||
# Install the libc cross packages based upon the debian-arch
|
# Install the libc cross packages based upon the debian-arch
|
||||||
@@ -59,19 +59,16 @@ RUN apt-get update && \
|
|||||||
apt-get install -y \
|
apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
clang \
|
clang \
|
||||||
pkg-config \
|
git && \
|
||||||
git \
|
|
||||||
"libc6-$(xx-info debian-arch)-cross" \
|
|
||||||
"libc6-dev-$(xx-info debian-arch)-cross" \
|
|
||||||
"linux-libc-dev-$(xx-info debian-arch)-cross" && \
|
|
||||||
xx-apt-get install -y \
|
xx-apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
gcc \
|
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
libpq5 \
|
libpq5 \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
libmariadb-dev \
|
libmariadb-dev \
|
||||||
zlib1g-dev && \
|
pkg-config \
|
||||||
|
zlib1g-dev \
|
||||||
|
xx-c-essentials && \
|
||||||
# Run xx-cargo early, since it sometimes seems to break when run at a later stage
|
# Run xx-cargo early, since it sometimes seems to break when run at a later stage
|
||||||
echo "export CARGO_TARGET=$(xx-cargo --print-target-triple)" >> /env-cargo
|
echo "export CARGO_TARGET=$(xx-cargo --print-target-triple)" >> /env-cargo
|
||||||
|
|
||||||
@@ -83,29 +80,6 @@ RUN mkdir -pv "${CARGO_HOME}" && \
|
|||||||
RUN USER=root cargo new --bin /app
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Environment variables for Cargo on Debian based builds
|
|
||||||
ARG TARGET_PKG_CONFIG_PATH
|
|
||||||
|
|
||||||
RUN source /env-cargo && \
|
|
||||||
if xx-info is-cross ; then \
|
|
||||||
# We can't use xx-cargo since that uses clang, which doesn't work for our libraries.
|
|
||||||
# Because of this we generate the needed environment variables here which we can load in the needed steps.
|
|
||||||
echo "export CC_$(echo "${CARGO_TARGET}" | tr '[:upper:]' '[:lower:]' | tr - _)=/usr/bin/$(xx-info)-gcc" >> /env-cargo && \
|
|
||||||
echo "export CARGO_TARGET_$(echo "${CARGO_TARGET}" | tr '[:lower:]' '[:upper:]' | tr - _)_LINKER=/usr/bin/$(xx-info)-gcc" >> /env-cargo && \
|
|
||||||
echo "export CROSS_COMPILE=1" >> /env-cargo && \
|
|
||||||
echo "export PKG_CONFIG_ALLOW_CROSS=1" >> /env-cargo && \
|
|
||||||
# For some architectures `xx-info` returns a triple which doesn't matches the path on disk
|
|
||||||
# In those cases you can override this by setting the `TARGET_PKG_CONFIG_PATH` build-arg
|
|
||||||
if [[ -n "${TARGET_PKG_CONFIG_PATH}" ]]; then \
|
|
||||||
echo "export TARGET_PKG_CONFIG_PATH=${TARGET_PKG_CONFIG_PATH}" >> /env-cargo ; \
|
|
||||||
else \
|
|
||||||
echo "export PKG_CONFIG_PATH=/usr/lib/$(xx-info)/pkgconfig" >> /env-cargo ; \
|
|
||||||
fi && \
|
|
||||||
echo "# End of env-cargo" >> /env-cargo ; \
|
|
||||||
fi && \
|
|
||||||
# Output the current contents of the file
|
|
||||||
cat /env-cargo
|
|
||||||
|
|
||||||
RUN source /env-cargo && \
|
RUN source /env-cargo && \
|
||||||
rustup target add "${CARGO_TARGET}"
|
rustup target add "${CARGO_TARGET}"
|
||||||
|
|
||||||
@@ -122,7 +96,9 @@ ARG DB=sqlite,mysql,postgresql
|
|||||||
# dummy project, except the target folder
|
# dummy project, except the target folder
|
||||||
# This folder contains the compiled dependencies
|
# This folder contains the compiled dependencies
|
||||||
RUN source /env-cargo && \
|
RUN source /env-cargo && \
|
||||||
cargo build --features ${DB} --profile "${CARGO_PROFILE}" --target="${CARGO_TARGET}" && \
|
# Workaround for xx related build issues
|
||||||
|
# https://github.com/tonistiigi/xx/pull/108#issuecomment-3700635977
|
||||||
|
PKG_CONFIG="$(command -v "$(xx-info)-pkg-config")" xx-cargo build --features ${DB} --profile "${CARGO_PROFILE}" && \
|
||||||
find . -not -path "./target*" -delete
|
find . -not -path "./target*" -delete
|
||||||
|
|
||||||
# Copies the complete project
|
# Copies the complete project
|
||||||
@@ -137,7 +113,9 @@ RUN source /env-cargo && \
|
|||||||
# Also do this for build.rs to ensure the version is rechecked
|
# Also do this for build.rs to ensure the version is rechecked
|
||||||
touch build.rs src/main.rs && \
|
touch build.rs src/main.rs && \
|
||||||
# Create a symlink to the binary target folder to easy copy the binary in the final stage
|
# Create a symlink to the binary target folder to easy copy the binary in the final stage
|
||||||
cargo build --features ${DB} --profile "${CARGO_PROFILE}" --target="${CARGO_TARGET}" && \
|
# Workaround for xx related build issues
|
||||||
|
# https://github.com/tonistiigi/xx/pull/108#issuecomment-3700635977
|
||||||
|
PKG_CONFIG="$(command -v "$(xx-info)-pkg-config")" xx-cargo build --features ${DB} --profile "${CARGO_PROFILE}" && \
|
||||||
if [[ "${CARGO_PROFILE}" == "dev" ]] ; then \
|
if [[ "${CARGO_PROFILE}" == "dev" ]] ; then \
|
||||||
ln -vfsr "/app/target/${CARGO_TARGET}/debug" /app/target/final ; \
|
ln -vfsr "/app/target/${CARGO_TARGET}/debug" /app/target/final ; \
|
||||||
else \
|
else \
|
||||||
|
|||||||
+20
-34
@@ -27,6 +27,11 @@
|
|||||||
# $ docker image inspect --format "{{ '{{' }}.RepoTags}}" docker.io/vaultwarden/web-vault@{{ vault_image_digest }}
|
# $ docker image inspect --format "{{ '{{' }}.RepoTags}}" docker.io/vaultwarden/web-vault@{{ vault_image_digest }}
|
||||||
# [docker.io/vaultwarden/web-vault:{{ vault_version | replace('+', '_') }}]
|
# [docker.io/vaultwarden/web-vault:{{ vault_version | replace('+', '_') }}]
|
||||||
#
|
#
|
||||||
|
{% macro xx_cargo_config() -%}
|
||||||
|
# Workaround for xx related build issues
|
||||||
|
# https://github.com/tonistiigi/xx/pull/108#issuecomment-3700635977
|
||||||
|
PKG_CONFIG="$(command -v "$(xx-info)-pkg-config")" xx-cargo build --features ${DB} --profile "${CARGO_PROFILE}"
|
||||||
|
{%- endmacro %}
|
||||||
FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@{{ vault_image_digest }} AS vault
|
FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@{{ vault_image_digest }} AS vault
|
||||||
|
|
||||||
{% if base == "debian" %}
|
{% if base == "debian" %}
|
||||||
@@ -66,10 +71,10 @@ ENV DEBIAN_FRONTEND=noninteractive \
|
|||||||
# Use PostgreSQL v17 during Alpine/MUSL builds instead of the default v16
|
# Use PostgreSQL v17 during Alpine/MUSL builds instead of the default v16
|
||||||
# Debian Trixie uses libpq v17
|
# Debian Trixie uses libpq v17
|
||||||
PQ_LIB_DIR="/usr/local/musl/pq17/lib"
|
PQ_LIB_DIR="/usr/local/musl/pq17/lib"
|
||||||
{% endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{% if base == "debian" %}
|
{% if base == "debian" %}
|
||||||
# Install clang to get `xx-cargo` working
|
# Install clang && xx-c-essentials to get `xx-cargo` working
|
||||||
# Install pkg-config to allow amd64 builds to find all libraries
|
# Install pkg-config to allow amd64 builds to find all libraries
|
||||||
# Install git so build.rs can determine the correct version
|
# Install git so build.rs can determine the correct version
|
||||||
# Install the libc cross packages based upon the debian-arch
|
# Install the libc cross packages based upon the debian-arch
|
||||||
@@ -77,19 +82,16 @@ RUN apt-get update && \
|
|||||||
apt-get install -y \
|
apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
clang \
|
clang \
|
||||||
pkg-config \
|
git && \
|
||||||
git \
|
|
||||||
"libc6-$(xx-info debian-arch)-cross" \
|
|
||||||
"libc6-dev-$(xx-info debian-arch)-cross" \
|
|
||||||
"linux-libc-dev-$(xx-info debian-arch)-cross" && \
|
|
||||||
xx-apt-get install -y \
|
xx-apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
gcc \
|
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
libpq5 \
|
libpq5 \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
libmariadb-dev \
|
libmariadb-dev \
|
||||||
zlib1g-dev && \
|
pkg-config \
|
||||||
|
zlib1g-dev \
|
||||||
|
xx-c-essentials && \
|
||||||
# Run xx-cargo early, since it sometimes seems to break when run at a later stage
|
# Run xx-cargo early, since it sometimes seems to break when run at a later stage
|
||||||
echo "export CARGO_TARGET=$(xx-cargo --print-target-triple)" >> /env-cargo
|
echo "export CARGO_TARGET=$(xx-cargo --print-target-triple)" >> /env-cargo
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -102,31 +104,7 @@ RUN mkdir -pv "${CARGO_HOME}" && \
|
|||||||
RUN USER=root cargo new --bin /app
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
{% if base == "debian" %}
|
{% if base == "alpine" %}
|
||||||
# Environment variables for Cargo on Debian based builds
|
|
||||||
ARG TARGET_PKG_CONFIG_PATH
|
|
||||||
|
|
||||||
RUN source /env-cargo && \
|
|
||||||
if xx-info is-cross ; then \
|
|
||||||
# We can't use xx-cargo since that uses clang, which doesn't work for our libraries.
|
|
||||||
# Because of this we generate the needed environment variables here which we can load in the needed steps.
|
|
||||||
echo "export CC_$(echo "${CARGO_TARGET}" | tr '[:upper:]' '[:lower:]' | tr - _)=/usr/bin/$(xx-info)-gcc" >> /env-cargo && \
|
|
||||||
echo "export CARGO_TARGET_$(echo "${CARGO_TARGET}" | tr '[:lower:]' '[:upper:]' | tr - _)_LINKER=/usr/bin/$(xx-info)-gcc" >> /env-cargo && \
|
|
||||||
echo "export CROSS_COMPILE=1" >> /env-cargo && \
|
|
||||||
echo "export PKG_CONFIG_ALLOW_CROSS=1" >> /env-cargo && \
|
|
||||||
# For some architectures `xx-info` returns a triple which doesn't matches the path on disk
|
|
||||||
# In those cases you can override this by setting the `TARGET_PKG_CONFIG_PATH` build-arg
|
|
||||||
if [[ -n "${TARGET_PKG_CONFIG_PATH}" ]]; then \
|
|
||||||
echo "export TARGET_PKG_CONFIG_PATH=${TARGET_PKG_CONFIG_PATH}" >> /env-cargo ; \
|
|
||||||
else \
|
|
||||||
echo "export PKG_CONFIG_PATH=/usr/lib/$(xx-info)/pkgconfig" >> /env-cargo ; \
|
|
||||||
fi && \
|
|
||||||
echo "# End of env-cargo" >> /env-cargo ; \
|
|
||||||
fi && \
|
|
||||||
# Output the current contents of the file
|
|
||||||
cat /env-cargo
|
|
||||||
|
|
||||||
{% elif base == "alpine" %}
|
|
||||||
# Environment variables for Cargo on Alpine based builds
|
# Environment variables for Cargo on Alpine based builds
|
||||||
RUN echo "export CARGO_TARGET=${RUST_MUSL_CROSS_TARGET}" >> /env-cargo && \
|
RUN echo "export CARGO_TARGET=${RUST_MUSL_CROSS_TARGET}" >> /env-cargo && \
|
||||||
# Output the current contents of the file
|
# Output the current contents of the file
|
||||||
@@ -154,7 +132,11 @@ ARG DB=sqlite,mysql,postgresql,enable_mimalloc
|
|||||||
# dummy project, except the target folder
|
# dummy project, except the target folder
|
||||||
# This folder contains the compiled dependencies
|
# This folder contains the compiled dependencies
|
||||||
RUN source /env-cargo && \
|
RUN source /env-cargo && \
|
||||||
|
{% if base == "debian" %}
|
||||||
|
{{ xx_cargo_config() }} && \
|
||||||
|
{% elif base == "alpine" %}
|
||||||
cargo build --features ${DB} --profile "${CARGO_PROFILE}" --target="${CARGO_TARGET}" && \
|
cargo build --features ${DB} --profile "${CARGO_PROFILE}" --target="${CARGO_TARGET}" && \
|
||||||
|
{% endif %}
|
||||||
find . -not -path "./target*" -delete
|
find . -not -path "./target*" -delete
|
||||||
|
|
||||||
# Copies the complete project
|
# Copies the complete project
|
||||||
@@ -169,7 +151,11 @@ RUN source /env-cargo && \
|
|||||||
# Also do this for build.rs to ensure the version is rechecked
|
# Also do this for build.rs to ensure the version is rechecked
|
||||||
touch build.rs src/main.rs && \
|
touch build.rs src/main.rs && \
|
||||||
# Create a symlink to the binary target folder to easy copy the binary in the final stage
|
# Create a symlink to the binary target folder to easy copy the binary in the final stage
|
||||||
|
{% if base == "debian" %}
|
||||||
|
{{ xx_cargo_config() }} && \
|
||||||
|
{% elif base == "alpine" %}
|
||||||
cargo build --features ${DB} --profile "${CARGO_PROFILE}" --target="${CARGO_TARGET}" && \
|
cargo build --features ${DB} --profile "${CARGO_PROFILE}" --target="${CARGO_TARGET}" && \
|
||||||
|
{% endif %}
|
||||||
if [[ "${CARGO_PROFILE}" == "dev" ]] ; then \
|
if [[ "${CARGO_PROFILE}" == "dev" ]] ; then \
|
||||||
ln -vfsr "/app/target/${CARGO_TARGET}/debug" /app/target/final ; \
|
ln -vfsr "/app/target/${CARGO_TARGET}/debug" /app/target/final ; \
|
||||||
else \
|
else \
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE sso_auth DROP COLUMN code_response_error;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE sso_auth ADD COLUMN code_response_error TEXT;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE sso_auth DROP COLUMN IF EXISTS code_response_error;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE sso_auth ADD COLUMN IF NOT EXISTS code_response_error TEXT;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE sso_auth DROP COLUMN code_response_error;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE sso_auth ADD COLUMN code_response_error TEXT;
|
||||||
@@ -568,7 +568,7 @@ async fn post_access_file(
|
|||||||
async fn download_url(host: &Host, send_id: &SendId, file_id: &SendFileId) -> Result<String, crate::Error> {
|
async fn download_url(host: &Host, send_id: &SendId, file_id: &SendFileId) -> Result<String, crate::Error> {
|
||||||
let operator = CONFIG.opendal_operator_for_path_type(&PathType::Sends)?;
|
let operator = CONFIG.opendal_operator_for_path_type(&PathType::Sends)?;
|
||||||
|
|
||||||
if operator.info().scheme() == <&'static str>::from(opendal::Scheme::Fs) {
|
if crate::storage::is_fs_operator(&operator) {
|
||||||
let token_claims = crate::auth::generate_send_claims(send_id, file_id);
|
let token_claims = crate::auth::generate_send_claims(send_id, file_id);
|
||||||
let token = crate::auth::encode_jwt(&token_claims);
|
let token = crate::auth::encode_jwt(&token_claims);
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ pub fn routes() -> Vec<Route> {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct SendEmailLoginData {
|
struct SendEmailLoginData {
|
||||||
#[serde(alias = "DeviceIdentifier")]
|
#[serde(alias = "DeviceIdentifier")]
|
||||||
device_identifier: DeviceId,
|
device_identifier: Option<DeviceId>,
|
||||||
#[serde(alias = "Email")]
|
#[serde(alias = "Email")]
|
||||||
email: Option<String>,
|
email: Option<String>,
|
||||||
#[serde(alias = "MasterPasswordHash")]
|
#[serde(alias = "MasterPasswordHash")]
|
||||||
@@ -91,8 +91,11 @@ async fn send_email_login(data: Json<SendEmailLoginData>, client_headers: Client
|
|||||||
|
|
||||||
user
|
user
|
||||||
} else {
|
} else {
|
||||||
|
let Some(device_identifier) = &data.device_identifier else {
|
||||||
|
err!("No device identifier has been submitted.")
|
||||||
|
};
|
||||||
// SSO login only sends device id, so we get the user by the most recently used device
|
// SSO login only sends device id, so we get the user by the most recently used device
|
||||||
let Some(user) = User::find_by_device_for_email2fa(&data.device_identifier, &conn).await else {
|
let Some(user) = User::find_by_device_for_email2fa(device_identifier, &conn).await else {
|
||||||
err!("Username or password is incorrect. Try again.")
|
err!("Username or password is incorrect. Try again.")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+22
-25
@@ -28,8 +28,9 @@ use crate::{
|
|||||||
crypto,
|
crypto,
|
||||||
db::{
|
db::{
|
||||||
models::{
|
models::{
|
||||||
AuthRequest, AuthRequestId, Device, DeviceId, EventType, Invitation, OIDCCodeWrapper, OrganizationApiKey,
|
AuthRequest, AuthRequestId, Device, DeviceId, EventType, Invitation, OIDCCodeResponseError,
|
||||||
OrganizationId, SsoAuth, SsoUser, TwoFactor, TwoFactorIncomplete, TwoFactorType, User, UserId,
|
OrganizationApiKey, OrganizationId, SsoAuth, SsoUser, TwoFactor, TwoFactorIncomplete, TwoFactorType, User,
|
||||||
|
UserId,
|
||||||
},
|
},
|
||||||
DbConn,
|
DbConn,
|
||||||
},
|
},
|
||||||
@@ -186,7 +187,7 @@ async fn _sso_login(
|
|||||||
// Ratelimit the login
|
// Ratelimit the login
|
||||||
crate::ratelimit::check_limit_login(&ip.ip)?;
|
crate::ratelimit::check_limit_login(&ip.ip)?;
|
||||||
|
|
||||||
let (state, code_verifier) = match (data.code.as_ref(), data.code_verifier.as_ref()) {
|
let (code, code_verifier) = match (data.code.as_ref(), data.code_verifier.as_ref()) {
|
||||||
(None, _) => err!(
|
(None, _) => err!(
|
||||||
"Got no code in OIDC data",
|
"Got no code in OIDC data",
|
||||||
ErrorEvent {
|
ErrorEvent {
|
||||||
@@ -202,7 +203,7 @@ async fn _sso_login(
|
|||||||
(Some(code), Some(code_verifier)) => (code, code_verifier.clone()),
|
(Some(code), Some(code_verifier)) => (code, code_verifier.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (sso_auth, user_infos) = sso::exchange_code(state, code_verifier, conn).await?;
|
let (sso_auth, user_infos) = sso::exchange_code(code, code_verifier, conn).await?;
|
||||||
let user_with_sso = match SsoUser::find_by_identifier(&user_infos.identifier, conn).await {
|
let user_with_sso = match SsoUser::find_by_identifier(&user_infos.identifier, conn).await {
|
||||||
None => match SsoUser::find_by_mail(&user_infos.email, conn).await {
|
None => match SsoUser::find_by_mail(&user_infos.email, conn).await {
|
||||||
None => None,
|
None => None,
|
||||||
@@ -1138,7 +1139,7 @@ struct ConnectData {
|
|||||||
|
|
||||||
// Needed for authorization code
|
// Needed for authorization code
|
||||||
#[field(name = uncased("code"))]
|
#[field(name = uncased("code"))]
|
||||||
code: Option<OIDCState>,
|
code: Option<OIDCCode>,
|
||||||
#[field(name = uncased("code_verifier"))]
|
#[field(name = uncased("code_verifier"))]
|
||||||
code_verifier: Option<OIDCCodeVerifier>,
|
code_verifier: Option<OIDCCodeVerifier>,
|
||||||
}
|
}
|
||||||
@@ -1165,19 +1166,12 @@ const SSO_BINDING_COOKIE: &str = "VW_SSO_BINDING";
|
|||||||
|
|
||||||
#[get("/connect/oidc-signin?<code>&<state>", rank = 1)]
|
#[get("/connect/oidc-signin?<code>&<state>", rank = 1)]
|
||||||
async fn oidcsignin(code: OIDCCode, state: String, cookies: &CookieJar<'_>, mut conn: DbConn) -> ApiResult<Redirect> {
|
async fn oidcsignin(code: OIDCCode, state: String, cookies: &CookieJar<'_>, mut conn: DbConn) -> ApiResult<Redirect> {
|
||||||
_oidcsignin_redirect(
|
_oidcsignin_redirect(state, code, None, cookies, &mut conn).await
|
||||||
state,
|
|
||||||
OIDCCodeWrapper::Ok {
|
|
||||||
code,
|
|
||||||
},
|
|
||||||
cookies,
|
|
||||||
&mut conn,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bitwarden client appear to only care for code and state so we pipe it through
|
// Bitwarden client appear to only care for code and state
|
||||||
// cf: https://github.com/bitwarden/clients/blob/80b74b3300e15b4ae414dc06044cc9b02b6c10a6/libs/auth/src/angular/sso/sso.component.ts#L141
|
// We save the error in the database and set the encoded state as the code to be able to retrieve them later on
|
||||||
|
// cf: https://github.com/bitwarden/clients/blob/afd36d290ce18fb0048e0575e7d5a8f78b5dbffc/libs/auth/src/angular/sso/sso.component.ts#L156
|
||||||
#[get("/connect/oidc-signin?<state>&<error>&<error_description>", rank = 2)]
|
#[get("/connect/oidc-signin?<state>&<error>&<error_description>", rank = 2)]
|
||||||
async fn oidcsignin_error(
|
async fn oidcsignin_error(
|
||||||
state: String,
|
state: String,
|
||||||
@@ -1187,11 +1181,12 @@ async fn oidcsignin_error(
|
|||||||
mut conn: DbConn,
|
mut conn: DbConn,
|
||||||
) -> ApiResult<Redirect> {
|
) -> ApiResult<Redirect> {
|
||||||
_oidcsignin_redirect(
|
_oidcsignin_redirect(
|
||||||
state,
|
state.clone(),
|
||||||
OIDCCodeWrapper::Error {
|
state.into(),
|
||||||
|
Some(OIDCCodeResponseError {
|
||||||
error,
|
error,
|
||||||
error_description,
|
error_description,
|
||||||
},
|
}),
|
||||||
cookies,
|
cookies,
|
||||||
&mut conn,
|
&mut conn,
|
||||||
)
|
)
|
||||||
@@ -1200,10 +1195,10 @@ async fn oidcsignin_error(
|
|||||||
|
|
||||||
// The state was encoded using Base64 to ensure no issue with providers.
|
// The state was encoded using Base64 to ensure no issue with providers.
|
||||||
// iss and scope parameters are needed for redirection to work on IOS.
|
// iss and scope parameters are needed for redirection to work on IOS.
|
||||||
// We pass the state as the code to get it back later on.
|
|
||||||
async fn _oidcsignin_redirect(
|
async fn _oidcsignin_redirect(
|
||||||
base64_state: String,
|
base64_state: String,
|
||||||
code_response: OIDCCodeWrapper,
|
code: OIDCCode,
|
||||||
|
error: Option<OIDCCodeResponseError>,
|
||||||
cookies: &CookieJar<'_>,
|
cookies: &CookieJar<'_>,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
) -> ApiResult<Redirect> {
|
) -> ApiResult<Redirect> {
|
||||||
@@ -1222,9 +1217,11 @@ async fn _oidcsignin_redirect(
|
|||||||
(Some(expected), Some(actual)) if crypto::ct_eq(expected, actual) => {}
|
(Some(expected), Some(actual)) if crypto::ct_eq(expected, actual) => {}
|
||||||
_ => err!(format!("SSO session binding mismatch for {state}")),
|
_ => err!(format!("SSO session binding mismatch for {state}")),
|
||||||
}
|
}
|
||||||
cookies.remove(Cookie::build(SSO_BINDING_COOKIE).path("/identity/connect/").build());
|
cookies
|
||||||
|
.remove(Cookie::build(SSO_BINDING_COOKIE).path(format!("{}/identity/connect/", CONFIG.domain_path())).build());
|
||||||
|
|
||||||
sso_auth.code_response = Some(code_response);
|
sso_auth.code_response = Some(code.clone());
|
||||||
|
sso_auth.code_response_error = error;
|
||||||
sso_auth.updated_at = Utc::now().naive_utc();
|
sso_auth.updated_at = Utc::now().naive_utc();
|
||||||
sso_auth.save(conn).await?;
|
sso_auth.save(conn).await?;
|
||||||
|
|
||||||
@@ -1234,7 +1231,7 @@ async fn _oidcsignin_redirect(
|
|||||||
};
|
};
|
||||||
|
|
||||||
url.query_pairs_mut()
|
url.query_pairs_mut()
|
||||||
.append_pair("code", &state)
|
.append_pair("code", &code)
|
||||||
.append_pair("state", &state)
|
.append_pair("state", &state)
|
||||||
.append_pair("scope", &AuthMethod::Sso.scope())
|
.append_pair("scope", &AuthMethod::Sso.scope())
|
||||||
.append_pair("iss", &CONFIG.domain());
|
.append_pair("iss", &CONFIG.domain());
|
||||||
@@ -1294,7 +1291,7 @@ async fn authorize(data: AuthorizeData, cookies: &CookieJar<'_>, secure: Secure,
|
|||||||
|
|
||||||
cookies.add(
|
cookies.add(
|
||||||
Cookie::build((SSO_BINDING_COOKIE, binding_token))
|
Cookie::build((SSO_BINDING_COOKIE, binding_token))
|
||||||
.path("/identity/connect/")
|
.path(format!("{}/identity/connect/", CONFIG.domain_path()))
|
||||||
.max_age(time::Duration::seconds(sso::SSO_AUTH_EXPIRATION.num_seconds()))
|
.max_age(time::Duration::seconds(sso::SSO_AUTH_EXPIRATION.num_seconds()))
|
||||||
.same_site(SameSite::Lax) // Lax is needed because the IdP runs on a different FQDN
|
.same_site(SameSite::Lax) // Lax is needed because the IdP runs on a different FQDN
|
||||||
.http_only(true)
|
.http_only(true)
|
||||||
|
|||||||
+2
-6
@@ -54,12 +54,8 @@ static PUBLIC_RSA_KEY: OnceLock<DecodingKey> = OnceLock::new();
|
|||||||
pub async fn initialize_keys() -> Result<(), Error> {
|
pub async fn initialize_keys() -> Result<(), Error> {
|
||||||
use std::io::Error;
|
use std::io::Error;
|
||||||
|
|
||||||
let rsa_key_filename = std::path::PathBuf::from(CONFIG.private_rsa_key())
|
let rsa_key_filename = crate::storage::file_name(&CONFIG.private_rsa_key())
|
||||||
.file_name()
|
.ok_or_else(|| Error::other("Private RSA key path missing filename"))?;
|
||||||
.ok_or_else(|| Error::other("Private RSA key path missing filename"))?
|
|
||||||
.to_str()
|
|
||||||
.ok_or_else(|| Error::other("Private RSA key path filename is not valid UTF-8"))?
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let operator = CONFIG.opendal_operator_for_path_type(&PathType::RsaKey).map_err(Error::other)?;
|
let operator = CONFIG.opendal_operator_for_path_type(&PathType::RsaKey).map_err(Error::other)?;
|
||||||
|
|
||||||
|
|||||||
+31
-119
@@ -14,6 +14,7 @@ use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
|
storage,
|
||||||
util::{
|
util::{
|
||||||
get_active_web_release, get_env, get_env_bool, is_valid_email, parse_experimental_client_feature_flags,
|
get_active_web_release, get_env, get_env_bool, is_valid_email, parse_experimental_client_feature_flags,
|
||||||
FeatureFlagFilter,
|
FeatureFlagFilter,
|
||||||
@@ -22,18 +23,14 @@ use crate::{
|
|||||||
|
|
||||||
static CONFIG_FILE: LazyLock<String> = LazyLock::new(|| {
|
static CONFIG_FILE: LazyLock<String> = LazyLock::new(|| {
|
||||||
let data_folder = get_env("DATA_FOLDER").unwrap_or_else(|| String::from("data"));
|
let data_folder = get_env("DATA_FOLDER").unwrap_or_else(|| String::from("data"));
|
||||||
get_env("CONFIG_FILE").unwrap_or_else(|| format!("{data_folder}/config.json"))
|
get_env("CONFIG_FILE").unwrap_or_else(|| storage::join_path(&data_folder, "config.json"))
|
||||||
});
|
});
|
||||||
|
|
||||||
static CONFIG_FILE_PARENT_DIR: LazyLock<String> = LazyLock::new(|| {
|
static CONFIG_FILE_PARENT_DIR: LazyLock<String> =
|
||||||
let path = std::path::PathBuf::from(&*CONFIG_FILE);
|
LazyLock::new(|| storage::parent(&CONFIG_FILE).unwrap_or_else(|| "data".to_string()));
|
||||||
path.parent().unwrap_or(std::path::Path::new("data")).to_str().unwrap_or("data").to_string()
|
|
||||||
});
|
|
||||||
|
|
||||||
static CONFIG_FILENAME: LazyLock<String> = LazyLock::new(|| {
|
static CONFIG_FILENAME: LazyLock<String> =
|
||||||
let path = std::path::PathBuf::from(&*CONFIG_FILE);
|
LazyLock::new(|| storage::file_name(&CONFIG_FILE).unwrap_or_else(|| "config.json".to_string()));
|
||||||
path.file_name().unwrap_or(std::ffi::OsStr::new("config.json")).to_str().unwrap_or("config.json").to_string()
|
|
||||||
});
|
|
||||||
|
|
||||||
pub static SKIP_CONFIG_VALIDATION: AtomicBool = AtomicBool::new(false);
|
pub static SKIP_CONFIG_VALIDATION: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
@@ -263,7 +260,7 @@ macro_rules! make_config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn from_file() -> Result<Self, Error> {
|
async fn from_file() -> Result<Self, Error> {
|
||||||
let operator = opendal_operator_for_path(&CONFIG_FILE_PARENT_DIR)?;
|
let operator = storage::operator_for_path(&CONFIG_FILE_PARENT_DIR)?;
|
||||||
let config_bytes = operator.read(&CONFIG_FILENAME).await?;
|
let config_bytes = operator.read(&CONFIG_FILENAME).await?;
|
||||||
println!("[INFO] Using saved config from `{}` for configuration.\n", *CONFIG_FILE);
|
println!("[INFO] Using saved config from `{}` for configuration.\n", *CONFIG_FILE);
|
||||||
serde_json::from_slice(&config_bytes.to_vec()).map_err(Into::into)
|
serde_json::from_slice(&config_bytes.to_vec()).map_err(Into::into)
|
||||||
@@ -507,19 +504,19 @@ make_config! {
|
|||||||
/// Data folder |> Main data folder
|
/// Data folder |> Main data folder
|
||||||
data_folder: String, false, def, "data".to_string();
|
data_folder: String, false, def, "data".to_string();
|
||||||
/// Database URL
|
/// Database URL
|
||||||
database_url: String, false, auto, |c| format!("{}/db.sqlite3", c.data_folder);
|
database_url: String, false, auto, |c| format!("sqlite://{}", storage::join_path(&c.data_folder, "db.sqlite3"));
|
||||||
/// Icon cache folder
|
/// Icon cache folder
|
||||||
icon_cache_folder: String, false, auto, |c| format!("{}/icon_cache", c.data_folder);
|
icon_cache_folder: String, false, auto, |c| storage::join_path(&c.data_folder, "icon_cache");
|
||||||
/// Attachments folder
|
/// Attachments folder
|
||||||
attachments_folder: String, false, auto, |c| format!("{}/attachments", c.data_folder);
|
attachments_folder: String, false, auto, |c| storage::join_path(&c.data_folder, "attachments");
|
||||||
/// Sends folder
|
/// Sends folder
|
||||||
sends_folder: String, false, auto, |c| format!("{}/sends", c.data_folder);
|
sends_folder: String, false, auto, |c| storage::join_path(&c.data_folder, "sends");
|
||||||
/// Temp folder |> Used for storing temporary file uploads
|
/// Temp folder |> Used for storing temporary file uploads
|
||||||
tmp_folder: String, false, auto, |c| format!("{}/tmp", c.data_folder);
|
tmp_folder: String, false, auto, |c| storage::join_path(&c.data_folder, "tmp");
|
||||||
/// Templates folder
|
/// Templates folder
|
||||||
templates_folder: String, false, auto, |c| format!("{}/templates", c.data_folder);
|
templates_folder: String, false, auto, |c| storage::join_path(&c.data_folder, "templates");
|
||||||
/// Session JWT key
|
/// Session JWT key
|
||||||
rsa_key_filename: String, false, auto, |c| format!("{}/rsa_key", c.data_folder);
|
rsa_key_filename: String, false, auto, |c| storage::join_path(&c.data_folder, "rsa_key");
|
||||||
/// Web vault folder
|
/// Web vault folder
|
||||||
web_vault_folder: String, false, def, "web-vault/".to_string();
|
web_vault_folder: String, false, def, "web-vault/".to_string();
|
||||||
},
|
},
|
||||||
@@ -929,14 +926,17 @@ fn validate_config(cfg: &ConfigItems, on_update: bool) -> Result<(), Error> {
|
|||||||
{
|
{
|
||||||
use crate::db::DbConnType;
|
use crate::db::DbConnType;
|
||||||
let url = &cfg.database_url;
|
let url = &cfg.database_url;
|
||||||
if DbConnType::from_url(url)? == DbConnType::Sqlite && url.contains('/') {
|
if DbConnType::from_url(url)? == DbConnType::Sqlite {
|
||||||
let path = std::path::Path::new(&url);
|
let file_path = url.strip_prefix("sqlite://").unwrap_or(url);
|
||||||
if let Some(parent) = path.parent() {
|
if file_path.contains('/') {
|
||||||
if !parent.is_dir() {
|
let path = std::path::Path::new(file_path);
|
||||||
err!(format!(
|
if let Some(parent) = path.parent() {
|
||||||
"SQLite database directory `{}` does not exist or is not a directory",
|
if !parent.is_dir() {
|
||||||
parent.display()
|
err!(format!(
|
||||||
));
|
"SQLite database directory `{}` does not exist or is not a directory",
|
||||||
|
parent.display()
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1366,90 +1366,6 @@ fn smtp_convert_deprecated_ssl_options(smtp_ssl: Option<bool>, smtp_explicit_tls
|
|||||||
"starttls".to_string()
|
"starttls".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opendal_operator_for_path(path: &str) -> Result<opendal::Operator, Error> {
|
|
||||||
// Cache of previously built operators by path
|
|
||||||
static OPERATORS_BY_PATH: LazyLock<dashmap::DashMap<String, opendal::Operator>> =
|
|
||||||
LazyLock::new(dashmap::DashMap::new);
|
|
||||||
|
|
||||||
if let Some(operator) = OPERATORS_BY_PATH.get(path) {
|
|
||||||
return Ok(operator.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let operator = if path.starts_with("s3://") {
|
|
||||||
#[cfg(not(s3))]
|
|
||||||
return Err(opendal::Error::new(opendal::ErrorKind::ConfigInvalid, "S3 support is not enabled").into());
|
|
||||||
|
|
||||||
#[cfg(s3)]
|
|
||||||
opendal_s3_operator_for_path(path)?
|
|
||||||
} else {
|
|
||||||
let builder = opendal::services::Fs::default().root(path);
|
|
||||||
opendal::Operator::new(builder)?.finish()
|
|
||||||
};
|
|
||||||
|
|
||||||
OPERATORS_BY_PATH.insert(path.to_string(), operator.clone());
|
|
||||||
|
|
||||||
Ok(operator)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(s3)]
|
|
||||||
fn opendal_s3_operator_for_path(path: &str) -> Result<opendal::Operator, Error> {
|
|
||||||
use crate::http_client::aws::AwsReqwestConnector;
|
|
||||||
use aws_config::{default_provider::credentials::DefaultCredentialsChain, provider_config::ProviderConfig};
|
|
||||||
|
|
||||||
// This is a custom AWS credential loader that uses the official AWS Rust
|
|
||||||
// SDK config crate to load credentials. This ensures maximum compatibility
|
|
||||||
// with AWS credential configurations. For example, OpenDAL doesn't support
|
|
||||||
// AWS SSO temporary credentials yet.
|
|
||||||
struct OpenDALS3CredentialLoader {}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl reqsign::AwsCredentialLoad for OpenDALS3CredentialLoader {
|
|
||||||
async fn load_credential(&self, _client: reqwest::Client) -> anyhow::Result<Option<reqsign::AwsCredential>> {
|
|
||||||
use aws_credential_types::provider::ProvideCredentials as _;
|
|
||||||
use tokio::sync::OnceCell;
|
|
||||||
|
|
||||||
static DEFAULT_CREDENTIAL_CHAIN: OnceCell<DefaultCredentialsChain> = OnceCell::const_new();
|
|
||||||
|
|
||||||
let chain = DEFAULT_CREDENTIAL_CHAIN
|
|
||||||
.get_or_init(|| {
|
|
||||||
let reqwest_client = reqwest::Client::builder().build().unwrap();
|
|
||||||
let connector = AwsReqwestConnector {
|
|
||||||
client: reqwest_client,
|
|
||||||
};
|
|
||||||
|
|
||||||
let conf = ProviderConfig::default().with_http_client(connector);
|
|
||||||
|
|
||||||
DefaultCredentialsChain::builder().configure(conf).build()
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let creds = chain.provide_credentials().await?;
|
|
||||||
|
|
||||||
Ok(Some(reqsign::AwsCredential {
|
|
||||||
access_key_id: creds.access_key_id().to_string(),
|
|
||||||
secret_access_key: creds.secret_access_key().to_string(),
|
|
||||||
session_token: creds.session_token().map(|s| s.to_string()),
|
|
||||||
expires_in: creds.expiry().map(|expiration| expiration.into()),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const OPEN_DAL_S3_CREDENTIAL_LOADER: OpenDALS3CredentialLoader = OpenDALS3CredentialLoader {};
|
|
||||||
|
|
||||||
let url = Url::parse(path).map_err(|e| format!("Invalid path S3 URL path {path:?}: {e}"))?;
|
|
||||||
|
|
||||||
let bucket = url.host_str().ok_or_else(|| format!("Missing Bucket name in data folder S3 URL {path:?}"))?;
|
|
||||||
|
|
||||||
let builder = opendal::services::S3::default()
|
|
||||||
.customized_credential_load(Box::new(OPEN_DAL_S3_CREDENTIAL_LOADER))
|
|
||||||
.enable_virtual_host_style()
|
|
||||||
.bucket(bucket)
|
|
||||||
.root(url.path())
|
|
||||||
.default_storage_class("INTELLIGENT_TIERING");
|
|
||||||
|
|
||||||
Ok(opendal::Operator::new(builder)?.finish())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum PathType {
|
pub enum PathType {
|
||||||
Data,
|
Data,
|
||||||
IconCache,
|
IconCache,
|
||||||
@@ -1547,7 +1463,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Save to file
|
//Save to file
|
||||||
let operator = opendal_operator_for_path(&CONFIG_FILE_PARENT_DIR)?;
|
let operator = storage::operator_for_path(&CONFIG_FILE_PARENT_DIR)?;
|
||||||
operator.write(&CONFIG_FILENAME, config_str).await?;
|
operator.write(&CONFIG_FILENAME, config_str).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1612,7 +1528,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_user_config(&self) -> Result<(), Error> {
|
pub async fn delete_user_config(&self) -> Result<(), Error> {
|
||||||
let operator = opendal_operator_for_path(&CONFIG_FILE_PARENT_DIR)?;
|
let operator = storage::operator_for_path(&CONFIG_FILE_PARENT_DIR)?;
|
||||||
operator.delete(&CONFIG_FILENAME).await?;
|
operator.delete(&CONFIG_FILENAME).await?;
|
||||||
|
|
||||||
// Empty user config
|
// Empty user config
|
||||||
@@ -1636,7 +1552,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn private_rsa_key(&self) -> String {
|
pub fn private_rsa_key(&self) -> String {
|
||||||
format!("{}.pem", self.rsa_key_filename())
|
storage::with_extension(&self.rsa_key_filename(), "pem")
|
||||||
}
|
}
|
||||||
pub fn mail_enabled(&self) -> bool {
|
pub fn mail_enabled(&self) -> bool {
|
||||||
let inner = &self.inner.read().unwrap().config;
|
let inner = &self.inner.read().unwrap().config;
|
||||||
@@ -1677,15 +1593,11 @@ impl Config {
|
|||||||
PathType::IconCache => self.icon_cache_folder(),
|
PathType::IconCache => self.icon_cache_folder(),
|
||||||
PathType::Attachments => self.attachments_folder(),
|
PathType::Attachments => self.attachments_folder(),
|
||||||
PathType::Sends => self.sends_folder(),
|
PathType::Sends => self.sends_folder(),
|
||||||
PathType::RsaKey => std::path::Path::new(&self.rsa_key_filename())
|
PathType::RsaKey => storage::parent(&self.private_rsa_key())
|
||||||
.parent()
|
.ok_or_else(|| std::io::Error::other("Failed to get directory of RSA key file"))?,
|
||||||
.ok_or_else(|| std::io::Error::other("Failed to get directory of RSA key file"))?
|
|
||||||
.to_str()
|
|
||||||
.ok_or_else(|| std::io::Error::other("Failed to convert RSA key file directory to UTF-8 string"))?
|
|
||||||
.to_string(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
opendal_operator_for_path(&path)
|
storage::operator_for_path(&path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_template<T: serde::ser::Serialize>(&self, name: &str, data: &T) -> Result<String, Error> {
|
pub fn render_template<T: serde::ser::Serialize>(&self, name: &str, data: &T) -> Result<String, Error> {
|
||||||
|
|||||||
+27
-7
@@ -272,13 +272,32 @@ impl DbConnType {
|
|||||||
#[cfg(not(postgresql))]
|
#[cfg(not(postgresql))]
|
||||||
err!("`DATABASE_URL` is a PostgreSQL URL, but the 'postgresql' feature is not enabled")
|
err!("`DATABASE_URL` is a PostgreSQL URL, but the 'postgresql' feature is not enabled")
|
||||||
|
|
||||||
//Sqlite
|
// Sqlite (explicit)
|
||||||
} else {
|
} else if url.len() > 7 && &url[..7] == "sqlite:" {
|
||||||
#[cfg(sqlite)]
|
#[cfg(sqlite)]
|
||||||
return Ok(DbConnType::Sqlite);
|
return Ok(DbConnType::Sqlite);
|
||||||
|
|
||||||
#[cfg(not(sqlite))]
|
#[cfg(not(sqlite))]
|
||||||
err!("`DATABASE_URL` looks like a SQLite URL, but 'sqlite' feature is not enabled")
|
err!("`DATABASE_URL` is a SQLite URL, but the 'sqlite' feature is not enabled")
|
||||||
|
|
||||||
|
// No recognized scheme — assume legacy bare-path SQLite, but the database file must already exist.
|
||||||
|
// This prevents misconfigured URLs (typos, quoted strings) from silently creating a new empty SQLite database.
|
||||||
|
} else {
|
||||||
|
#[cfg(sqlite)]
|
||||||
|
{
|
||||||
|
if std::path::Path::new(url).exists() {
|
||||||
|
return Ok(DbConnType::Sqlite);
|
||||||
|
}
|
||||||
|
err!(format!(
|
||||||
|
"`DATABASE_URL` does not match any known database scheme (mysql://, postgresql://, sqlite://) \
|
||||||
|
and no existing SQLite database was found at '{url}'. \
|
||||||
|
If you intend to use SQLite, use an explicit `sqlite://` scheme in your `DATABASE_URL`. \
|
||||||
|
Otherwise, check your DATABASE_URL for typos or quoting issues."
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(sqlite))]
|
||||||
|
err!("`DATABASE_URL` does not match any known database scheme (mysql://, postgresql://, sqlite://)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,11 +409,12 @@ pub fn backup_sqlite() -> Result<String, Error> {
|
|||||||
|
|
||||||
let db_url = CONFIG.database_url();
|
let db_url = CONFIG.database_url();
|
||||||
if DbConnType::from_url(&CONFIG.database_url()).map(|t| t == DbConnType::Sqlite).unwrap_or(false) {
|
if DbConnType::from_url(&CONFIG.database_url()).map(|t| t == DbConnType::Sqlite).unwrap_or(false) {
|
||||||
// Since we do not allow any schema for sqlite database_url's like `file:` or `sqlite:` to be set, we can assume here it isn't
|
// Strip the sqlite:// prefix if present to get the raw file path
|
||||||
// This way we can set a readonly flag on the opening mode without issues.
|
let file_path = db_url.strip_prefix("sqlite://").unwrap_or(&db_url);
|
||||||
let mut conn = diesel::sqlite::SqliteConnection::establish(&format!("sqlite://{db_url}?mode=ro"))?;
|
// Open a read-only connection for the backup
|
||||||
|
let mut conn = diesel::sqlite::SqliteConnection::establish(&format!("sqlite://{file_path}?mode=ro"))?;
|
||||||
|
|
||||||
let db_path = std::path::Path::new(&db_url).parent().unwrap();
|
let db_path = std::path::Path::new(file_path).parent().unwrap();
|
||||||
let backup_file = db_path
|
let backup_file = db_path
|
||||||
.join(format!("db_{}.sqlite3", chrono::Utc::now().format("%Y%m%d_%H%M%S")))
|
.join(format!("db_{}.sqlite3", chrono::Utc::now().format("%Y%m%d_%H%M%S")))
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ impl Attachment {
|
|||||||
pub async fn get_url(&self, host: &str) -> Result<String, crate::Error> {
|
pub async fn get_url(&self, host: &str) -> Result<String, crate::Error> {
|
||||||
let operator = CONFIG.opendal_operator_for_path_type(&PathType::Attachments)?;
|
let operator = CONFIG.opendal_operator_for_path_type(&PathType::Attachments)?;
|
||||||
|
|
||||||
if operator.info().scheme() == <&'static str>::from(opendal::Scheme::Fs) {
|
if crate::storage::is_fs_operator(&operator) {
|
||||||
let token = encode_jwt(&generate_file_download_claims(self.cipher_uuid.clone(), self.id.clone()));
|
let token = encode_jwt(&generate_file_download_claims(self.cipher_uuid.clone(), self.id.clone()));
|
||||||
Ok(format!("{host}/attachments/{}/{}?token={token}", self.cipher_uuid, self.id))
|
Ok(format!("{host}/attachments/{}/{}?token={token}", self.cipher_uuid, self.id))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ pub use self::send::{
|
|||||||
id::{SendFileId, SendId},
|
id::{SendFileId, SendId},
|
||||||
Send, SendType,
|
Send, SendType,
|
||||||
};
|
};
|
||||||
pub use self::sso_auth::{OIDCAuthenticatedUser, OIDCCodeWrapper, SsoAuth};
|
pub use self::sso_auth::{OIDCAuthenticatedUser, OIDCCodeResponseError, SsoAuth};
|
||||||
pub use self::two_factor::{TwoFactor, TwoFactorType};
|
pub use self::two_factor::{TwoFactor, TwoFactorType};
|
||||||
pub use self::two_factor_duo_context::TwoFactorDuoContext;
|
pub use self::two_factor_duo_context::TwoFactorDuoContext;
|
||||||
pub use self::two_factor_incomplete::TwoFactorIncomplete;
|
pub use self::two_factor_incomplete::TwoFactorIncomplete;
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ impl Send {
|
|||||||
|
|
||||||
if self.atype == SendType::File as i32 {
|
if self.atype == SendType::File as i32 {
|
||||||
let operator = CONFIG.opendal_operator_for_path_type(&PathType::Sends)?;
|
let operator = CONFIG.opendal_operator_for_path_type(&PathType::Sends)?;
|
||||||
operator.remove_all(&self.uuid).await.ok();
|
operator.delete_with(&self.uuid).recursive(true).await.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
|
|||||||
+18
-10
@@ -15,17 +15,12 @@ use diesel::sql_types::Text;
|
|||||||
|
|
||||||
#[derive(AsExpression, Clone, Debug, Serialize, Deserialize, FromSqlRow)]
|
#[derive(AsExpression, Clone, Debug, Serialize, Deserialize, FromSqlRow)]
|
||||||
#[diesel(sql_type = Text)]
|
#[diesel(sql_type = Text)]
|
||||||
pub enum OIDCCodeWrapper {
|
pub struct OIDCCodeResponseError {
|
||||||
Ok {
|
pub error: String,
|
||||||
code: OIDCCode,
|
pub error_description: Option<String>,
|
||||||
},
|
|
||||||
Error {
|
|
||||||
error: String,
|
|
||||||
error_description: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_FromToSqlText!(OIDCCodeWrapper);
|
impl_FromToSqlText!(OIDCCodeResponseError);
|
||||||
|
|
||||||
#[derive(AsExpression, Clone, Debug, Serialize, Deserialize, FromSqlRow)]
|
#[derive(AsExpression, Clone, Debug, Serialize, Deserialize, FromSqlRow)]
|
||||||
#[diesel(sql_type = Text)]
|
#[diesel(sql_type = Text)]
|
||||||
@@ -50,7 +45,8 @@ pub struct SsoAuth {
|
|||||||
pub client_challenge: OIDCCodeChallenge,
|
pub client_challenge: OIDCCodeChallenge,
|
||||||
pub nonce: String,
|
pub nonce: String,
|
||||||
pub redirect_uri: String,
|
pub redirect_uri: String,
|
||||||
pub code_response: Option<OIDCCodeWrapper>,
|
pub code_response: Option<OIDCCode>,
|
||||||
|
pub code_response_error: Option<OIDCCodeResponseError>,
|
||||||
pub auth_response: Option<OIDCAuthenticatedUser>,
|
pub auth_response: Option<OIDCAuthenticatedUser>,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub updated_at: NaiveDateTime,
|
pub updated_at: NaiveDateTime,
|
||||||
@@ -76,6 +72,7 @@ impl SsoAuth {
|
|||||||
created_at: now,
|
created_at: now,
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
code_response: None,
|
code_response: None,
|
||||||
|
code_response_error: None,
|
||||||
auth_response: None,
|
auth_response: None,
|
||||||
binding_hash,
|
binding_hash,
|
||||||
}
|
}
|
||||||
@@ -118,6 +115,17 @@ impl SsoAuth {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn find_by_code(code: &OIDCCode, conn: &DbConn) -> Option<Self> {
|
||||||
|
let oldest = Utc::now().naive_utc() - *SSO_AUTH_EXPIRATION;
|
||||||
|
db_run! { conn: {
|
||||||
|
sso_auth::table
|
||||||
|
.filter(sso_auth::code_response.eq(code))
|
||||||
|
.filter(sso_auth::created_at.ge(oldest))
|
||||||
|
.first::<Self>(conn)
|
||||||
|
.ok()
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn delete(self, conn: &DbConn) -> EmptyResult {
|
pub async fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||||
db_run! {conn: {
|
db_run! {conn: {
|
||||||
diesel::delete(sso_auth::table.filter(sso_auth::state.eq(self.state)))
|
diesel::delete(sso_auth::table.filter(sso_auth::state.eq(self.state)))
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ table! {
|
|||||||
nonce -> Text,
|
nonce -> Text,
|
||||||
redirect_uri -> Text,
|
redirect_uri -> Text,
|
||||||
code_response -> Nullable<Text>,
|
code_response -> Nullable<Text>,
|
||||||
|
code_response_error -> Nullable<Text>,
|
||||||
auth_response -> Nullable<Text>,
|
auth_response -> Nullable<Text>,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
|
|||||||
+10
@@ -57,6 +57,7 @@ mod mail;
|
|||||||
mod ratelimit;
|
mod ratelimit;
|
||||||
mod sso;
|
mod sso;
|
||||||
mod sso_client;
|
mod sso_client;
|
||||||
|
mod storage;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use crate::api::core::two_factor::duo_oidc::purge_duo_contexts;
|
use crate::api::core::two_factor::duo_oidc::purge_duo_contexts;
|
||||||
@@ -70,6 +71,7 @@ pub use util::is_running_in_container;
|
|||||||
|
|
||||||
#[rocket::main]
|
#[rocket::main]
|
||||||
async fn main() -> Result<(), Error> {
|
async fn main() -> Result<(), Error> {
|
||||||
|
install_rustls_crypto_provider();
|
||||||
parse_args();
|
parse_args();
|
||||||
launch_info();
|
launch_info();
|
||||||
|
|
||||||
@@ -202,6 +204,14 @@ fn parse_args() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn install_rustls_crypto_provider() {
|
||||||
|
if rustls::crypto::CryptoProvider::get_default().is_none() {
|
||||||
|
rustls::crypto::ring::default_provider()
|
||||||
|
.install_default()
|
||||||
|
.expect("failed to install rustls ring crypto provider");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn launch_info() {
|
fn launch_info() {
|
||||||
println!(
|
println!(
|
||||||
"\
|
"\
|
||||||
|
|||||||
+14
-14
@@ -10,7 +10,7 @@ use crate::{
|
|||||||
auth,
|
auth,
|
||||||
auth::{AuthMethod, AuthTokens, TokenWrapper, BW_EXPIRATION, DEFAULT_REFRESH_VALIDITY},
|
auth::{AuthMethod, AuthTokens, TokenWrapper, BW_EXPIRATION, DEFAULT_REFRESH_VALIDITY},
|
||||||
db::{
|
db::{
|
||||||
models::{Device, OIDCAuthenticatedUser, OIDCCodeWrapper, SsoAuth, SsoUser, User},
|
models::{Device, OIDCAuthenticatedUser, SsoAuth, SsoUser, User},
|
||||||
DbConn,
|
DbConn,
|
||||||
},
|
},
|
||||||
sso_client::Client,
|
sso_client::Client,
|
||||||
@@ -240,14 +240,14 @@ impl OIDCIdentifier {
|
|||||||
// - second time we will rely on `SsoAuth.auth_response` since the `code` has already been exchanged.
|
// - second time we will rely on `SsoAuth.auth_response` since the `code` has already been exchanged.
|
||||||
// The `SsoAuth` will ensure that the user is authorized only once.
|
// The `SsoAuth` will ensure that the user is authorized only once.
|
||||||
pub async fn exchange_code(
|
pub async fn exchange_code(
|
||||||
state: &OIDCState,
|
code: &OIDCCode,
|
||||||
client_verifier: OIDCCodeVerifier,
|
client_verifier: OIDCCodeVerifier,
|
||||||
conn: &DbConn,
|
conn: &DbConn,
|
||||||
) -> ApiResult<(SsoAuth, OIDCAuthenticatedUser)> {
|
) -> ApiResult<(SsoAuth, OIDCAuthenticatedUser)> {
|
||||||
use openidconnect::OAuth2TokenResponse;
|
use openidconnect::OAuth2TokenResponse;
|
||||||
|
|
||||||
let mut sso_auth = match SsoAuth::find(state, conn).await {
|
let mut sso_auth = match SsoAuth::find_by_code(code, conn).await {
|
||||||
None => err!(format!("Invalid state cannot retrieve sso auth")),
|
None => err!(format!("Invalid code cannot retrieve sso auth")),
|
||||||
Some(sso_auth) => sso_auth,
|
Some(sso_auth) => sso_auth,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -255,18 +255,18 @@ pub async fn exchange_code(
|
|||||||
return Ok((sso_auth, authenticated_user));
|
return Ok((sso_auth, authenticated_user));
|
||||||
}
|
}
|
||||||
|
|
||||||
let code = match sso_auth.code_response.clone() {
|
let code = match (sso_auth.code_response.clone(), sso_auth.code_response_error.as_ref()) {
|
||||||
Some(OIDCCodeWrapper::Ok {
|
(Some(code), None) => code,
|
||||||
code,
|
(_, Some(re)) => {
|
||||||
}) => code.clone(),
|
let error_msg = format!(
|
||||||
Some(OIDCCodeWrapper::Error {
|
"SSO authorization failed: {}, {}",
|
||||||
error,
|
re.error,
|
||||||
error_description,
|
re.error_description.as_ref().unwrap_or(&String::new())
|
||||||
}) => {
|
);
|
||||||
sso_auth.delete(conn).await?;
|
sso_auth.delete(conn).await?;
|
||||||
err!(format!("SSO authorization failed: {error}, {}", error_description.as_ref().unwrap_or(&String::new())))
|
err!(error_msg);
|
||||||
}
|
}
|
||||||
None => {
|
(None, _) => {
|
||||||
sso_auth.delete(conn).await?;
|
sso_auth.delete(conn).await?;
|
||||||
err!("Missing authorization provider return");
|
err!("Missing authorization provider return");
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-4
@@ -1,12 +1,13 @@
|
|||||||
use std::{borrow::Cow, sync::LazyLock, time::Duration};
|
use std::{borrow::Cow, future::Future, pin::Pin, sync::LazyLock, time::Duration};
|
||||||
|
|
||||||
use openidconnect::{core::*, reqwest, *};
|
use openidconnect::{core::*, *};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{ApiResult, EmptyResult},
|
api::{ApiResult, EmptyResult},
|
||||||
db::models::SsoAuth,
|
db::models::SsoAuth,
|
||||||
|
http_client::get_reqwest_client_builder,
|
||||||
sso::{OIDCCode, OIDCCodeChallenge, OIDCCodeVerifier, OIDCState},
|
sso::{OIDCCode, OIDCCodeChallenge, OIDCCodeVerifier, OIDCState},
|
||||||
CONFIG,
|
CONFIG,
|
||||||
};
|
};
|
||||||
@@ -46,10 +47,42 @@ pub type RefreshTokenResponse = (Option<String>, String, Option<Duration>);
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
pub http_client: reqwest::Client,
|
pub http_client: OidcHttpClient,
|
||||||
pub core_client: CustomClient,
|
pub core_client: CustomClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct OidcHttpClient {
|
||||||
|
client: reqwest::Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OidcHttpClient {
|
||||||
|
fn new() -> Result<Self, reqwest::Error> {
|
||||||
|
get_reqwest_client_builder().redirect(reqwest::redirect::Policy::none()).build().map(|client| Self {
|
||||||
|
client,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'c> AsyncHttpClient<'c> for OidcHttpClient {
|
||||||
|
type Error = HttpClientError<reqwest::Error>;
|
||||||
|
type Future = Pin<Box<dyn Future<Output = Result<HttpResponse, Self::Error>> + Send + Sync + 'c>>;
|
||||||
|
|
||||||
|
fn call(&'c self, request: HttpRequest) -> Self::Future {
|
||||||
|
Box::pin(async move {
|
||||||
|
let response = self.client.execute(request.try_into().map_err(Box::new)?).await.map_err(Box::new)?;
|
||||||
|
|
||||||
|
let mut builder = http::Response::builder().status(response.status()).version(response.version());
|
||||||
|
|
||||||
|
for (name, value) in response.headers() {
|
||||||
|
builder = builder.header(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.body(response.bytes().await.map_err(Box::new)?.to_vec()).map_err(HttpClientError::Http)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
// Call the OpenId discovery endpoint to retrieve configuration
|
// Call the OpenId discovery endpoint to retrieve configuration
|
||||||
async fn _get_client() -> ApiResult<Self> {
|
async fn _get_client() -> ApiResult<Self> {
|
||||||
@@ -58,7 +91,7 @@ impl Client {
|
|||||||
|
|
||||||
let issuer_url = CONFIG.sso_issuer_url()?;
|
let issuer_url = CONFIG.sso_issuer_url()?;
|
||||||
|
|
||||||
let http_client = match reqwest::ClientBuilder::new().redirect(reqwest::redirect::Policy::none()).build() {
|
let http_client = match OidcHttpClient::new() {
|
||||||
Err(err) => err!(format!("Failed to build http client: {err}")),
|
Err(err) => err!(format!("Failed to build http client: {err}")),
|
||||||
Ok(client) => client,
|
Ok(client) => client,
|
||||||
};
|
};
|
||||||
|
|||||||
+297
@@ -0,0 +1,297 @@
|
|||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
pub(crate) fn join_path(base: &str, child: &str) -> String {
|
||||||
|
#[cfg(s3)]
|
||||||
|
if s3::is_uri(base) {
|
||||||
|
return s3::join_path(base, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
let base = base.trim_end_matches('/');
|
||||||
|
let child = child.trim_start_matches('/');
|
||||||
|
if base.is_empty() {
|
||||||
|
child.to_string()
|
||||||
|
} else if child.is_empty() {
|
||||||
|
base.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{base}/{child}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_extension(path: &str, extension: &str) -> String {
|
||||||
|
let extension = extension.trim_start_matches('.');
|
||||||
|
|
||||||
|
#[cfg(s3)]
|
||||||
|
if s3::is_uri(path) {
|
||||||
|
return s3::with_extension(path, extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
format!("{path}.{extension}")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parent(path: &str) -> Option<String> {
|
||||||
|
#[cfg(s3)]
|
||||||
|
if s3::is_uri(path) {
|
||||||
|
return s3::parent(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::path::Path::new(path).parent()?.to_str().map(ToString::to_string)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn file_name(path: &str) -> Option<String> {
|
||||||
|
#[cfg(s3)]
|
||||||
|
if s3::is_uri(path) {
|
||||||
|
return s3::file_name(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::path::Path::new(path).file_name()?.to_str().map(ToString::to_string)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_fs_operator(operator: &opendal::Operator) -> bool {
|
||||||
|
operator.info().scheme() == opendal::services::FS_SCHEME
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn operator_for_path(path: &str) -> Result<opendal::Operator, crate::Error> {
|
||||||
|
// Cache of previously built operators by path
|
||||||
|
static OPERATORS_BY_PATH: LazyLock<dashmap::DashMap<String, opendal::Operator>> =
|
||||||
|
LazyLock::new(dashmap::DashMap::new);
|
||||||
|
|
||||||
|
if let Some(operator) = OPERATORS_BY_PATH.get(path) {
|
||||||
|
return Ok(operator.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let operator = if path.starts_with("s3://") {
|
||||||
|
#[cfg(not(s3))]
|
||||||
|
return Err(opendal::Error::new(opendal::ErrorKind::ConfigInvalid, "S3 support is not enabled").into());
|
||||||
|
|
||||||
|
#[cfg(s3)]
|
||||||
|
s3::operator_for_path(path)?
|
||||||
|
} else {
|
||||||
|
let builder = opendal::services::Fs::default().root(path);
|
||||||
|
opendal::Operator::new(builder)?.finish()
|
||||||
|
};
|
||||||
|
|
||||||
|
OPERATORS_BY_PATH.insert(path.to_string(), operator.clone());
|
||||||
|
|
||||||
|
Ok(operator)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(s3)]
|
||||||
|
mod s3 {
|
||||||
|
use reqwest::Url;
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
|
pub(super) fn is_uri(path: &str) -> bool {
|
||||||
|
path.starts_with("s3://")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn join_path(base: &str, child: &str) -> String {
|
||||||
|
if let Ok(mut url) = Url::parse(base) {
|
||||||
|
let mut segments = path_segments(&url);
|
||||||
|
segments.extend(child.split('/').filter(|segment| !segment.is_empty()).map(ToString::to_string));
|
||||||
|
set_path_segments(&mut url, &segments);
|
||||||
|
return url.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let base = base.trim_end_matches('/');
|
||||||
|
let child = child.trim_start_matches('/');
|
||||||
|
if base.is_empty() {
|
||||||
|
child.to_string()
|
||||||
|
} else if child.is_empty() {
|
||||||
|
base.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{base}/{child}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn with_extension(path: &str, extension: &str) -> String {
|
||||||
|
if let Ok(mut url) = Url::parse(path) {
|
||||||
|
let mut segments = path_segments(&url);
|
||||||
|
if let Some(file_name) = segments.last_mut() {
|
||||||
|
file_name.push('.');
|
||||||
|
file_name.push_str(extension);
|
||||||
|
set_path_segments(&mut url, &segments);
|
||||||
|
return url.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
format!("{path}.{extension}")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn parent(path: &str) -> Option<String> {
|
||||||
|
if let Ok(mut url) = Url::parse(path) {
|
||||||
|
let mut segments = path_segments(&url);
|
||||||
|
segments.pop()?;
|
||||||
|
set_path_segments(&mut url, &segments);
|
||||||
|
return Some(url.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::path::Path::new(path).parent()?.to_str().map(ToString::to_string)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn file_name(path: &str) -> Option<String> {
|
||||||
|
if let Ok(url) = Url::parse(path) {
|
||||||
|
return path_segments(&url).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::path::Path::new(path).file_name()?.to_str().map(ToString::to_string)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_segments(url: &Url) -> Vec<String> {
|
||||||
|
url.path_segments()
|
||||||
|
.map(|segments| segments.filter(|segment| !segment.is_empty()).map(ToString::to_string).collect())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_path_segments(url: &mut Url, segments: &[String]) {
|
||||||
|
if segments.is_empty() {
|
||||||
|
url.set_path("");
|
||||||
|
} else {
|
||||||
|
url.set_path(&format!("/{}", segments.join("/")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn operator_for_path(path: &str) -> Result<opendal::Operator, Error> {
|
||||||
|
use crate::http_client::aws::AwsReqwestConnector;
|
||||||
|
use aws_config::{default_provider::credentials::DefaultCredentialsChain, provider_config::ProviderConfig};
|
||||||
|
use opendal::Configurator;
|
||||||
|
use reqsign_aws_v4::Credential;
|
||||||
|
use reqsign_core::{Context, ProvideCredential, ProvideCredentialChain};
|
||||||
|
|
||||||
|
// This is a custom AWS credential loader that uses the official AWS Rust
|
||||||
|
// SDK config crate to load credentials. This ensures maximum compatibility
|
||||||
|
// with AWS credential configurations. For example, OpenDAL doesn't support
|
||||||
|
// AWS SSO temporary credentials yet.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct OpenDALS3CredentialProvider;
|
||||||
|
|
||||||
|
impl ProvideCredential for OpenDALS3CredentialProvider {
|
||||||
|
type Credential = Credential;
|
||||||
|
|
||||||
|
async fn provide_credential(&self, _ctx: &Context) -> reqsign_core::Result<Option<Self::Credential>> {
|
||||||
|
use aws_credential_types::provider::ProvideCredentials as _;
|
||||||
|
use reqsign_core::time::Timestamp;
|
||||||
|
use tokio::sync::OnceCell;
|
||||||
|
|
||||||
|
static DEFAULT_CREDENTIAL_CHAIN: OnceCell<DefaultCredentialsChain> = OnceCell::const_new();
|
||||||
|
|
||||||
|
let chain = DEFAULT_CREDENTIAL_CHAIN
|
||||||
|
.get_or_init(|| {
|
||||||
|
let reqwest_client = reqwest::Client::builder().build().unwrap();
|
||||||
|
let connector = AwsReqwestConnector {
|
||||||
|
client: reqwest_client,
|
||||||
|
};
|
||||||
|
|
||||||
|
let conf = ProviderConfig::default().with_http_client(connector);
|
||||||
|
|
||||||
|
DefaultCredentialsChain::builder().configure(conf).build()
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let creds = chain.provide_credentials().await.map_err(|e| {
|
||||||
|
reqsign_core::Error::unexpected("failed to load AWS credentials via AWS SDK").with_source(e)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let expires_in = if let Some(expiration) = creds.expiry() {
|
||||||
|
let duration = expiration.duration_since(std::time::UNIX_EPOCH).map_err(|e| {
|
||||||
|
reqsign_core::Error::unexpected("AWS credential expiration is before the Unix epoch")
|
||||||
|
.with_source(e)
|
||||||
|
})?;
|
||||||
|
let seconds = i64::try_from(duration.as_secs()).map_err(|e| {
|
||||||
|
reqsign_core::Error::unexpected("AWS credential expiration is too large").with_source(e)
|
||||||
|
})?;
|
||||||
|
Some(Timestamp::from_second(seconds)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(Credential {
|
||||||
|
access_key_id: creds.access_key_id().to_string(),
|
||||||
|
secret_access_key: creds.secret_access_key().to_string(),
|
||||||
|
session_token: creds.session_token().map(|s| s.to_string()),
|
||||||
|
expires_in,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let uri = opendal::OperatorUri::new(path, std::iter::empty::<(String, String)>())?;
|
||||||
|
let mut config = opendal::services::S3Config::from_uri(&uri)?;
|
||||||
|
|
||||||
|
if !uri_has_option(&uri, &["default_storage_class"]) {
|
||||||
|
config.default_storage_class = Some("INTELLIGENT_TIERING".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !uri_has_option(
|
||||||
|
&uri,
|
||||||
|
&["enable_virtual_host_style", "aws_virtual_hosted_style_request", "virtual_hosted_style_request"],
|
||||||
|
) {
|
||||||
|
config.enable_virtual_host_style = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let use_aws_sdk_credentials = !uri_has_credential_options(&uri, &config);
|
||||||
|
let mut builder = config.into_builder();
|
||||||
|
|
||||||
|
if use_aws_sdk_credentials {
|
||||||
|
builder =
|
||||||
|
builder.credential_provider_chain(ProvideCredentialChain::new().push(OpenDALS3CredentialProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(opendal::Operator::new(builder)?.finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uri_has_option(uri: &opendal::OperatorUri, names: &[&str]) -> bool {
|
||||||
|
names.iter().any(|name| uri.options().contains_key(*name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uri_has_credential_options(uri: &opendal::OperatorUri, config: &opendal::services::S3Config) -> bool {
|
||||||
|
config.access_key_id.is_some()
|
||||||
|
|| config.secret_access_key.is_some()
|
||||||
|
|| config.session_token.is_some()
|
||||||
|
|| config.role_arn.is_some()
|
||||||
|
|| config.external_id.is_some()
|
||||||
|
|| config.role_session_name.is_some()
|
||||||
|
|| uri_has_option(uri, &["allow_anonymous", "disable_config_load", "disable_ec2_metadata"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn handles_local_paths() {
|
||||||
|
assert_eq!(join_path("data", "attachments"), "data/attachments");
|
||||||
|
assert_eq!(with_extension("data/rsa_key", "pem"), "data/rsa_key.pem");
|
||||||
|
assert_eq!(parent("data/rsa_key.pem").as_deref(), Some("data"));
|
||||||
|
assert_eq!(file_name("data/rsa_key.pem").as_deref(), Some("rsa_key.pem"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, s3))]
|
||||||
|
mod s3_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn joins_s3_path_before_query_string() {
|
||||||
|
assert_eq!(
|
||||||
|
join_path("s3://bucket/base?region=us-west-2", "attachments"),
|
||||||
|
"s3://bucket/base/attachments?region=us-west-2"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn appends_extension_before_s3_query_string() {
|
||||||
|
assert_eq!(
|
||||||
|
with_extension("s3://bucket/base/rsa_key?region=us-west-2", "pem"),
|
||||||
|
"s3://bucket/base/rsa_key.pem?region=us-west-2"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn splits_s3_parent_and_file_name_without_query_string() {
|
||||||
|
let path = "s3://bucket/base/config.json?region=us-west-2";
|
||||||
|
|
||||||
|
assert_eq!(parent(path).as_deref(), Some("s3://bucket/base?region=us-west-2"));
|
||||||
|
assert_eq!(file_name(path).as_deref(), Some("config.json"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user