mirror of
https://github.com/ViViDboarder/bitwarden_rs.git
synced 2024-12-22 11:37:45 +00:00
Change config to thread-safe system, needed for a future config panel.
Improved some two factor methods.
This commit is contained in:
parent
86de0ca17b
commit
a1dc47b826
99
Cargo.lock
generated
99
Cargo.lock
generated
@ -95,7 +95,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -119,7 +119,7 @@ dependencies = [
|
||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"diesel 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"diesel 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"diesel_migrations 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fern 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -135,6 +135,7 @@ dependencies = [
|
||||
"num-derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"oath 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paste 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmpv 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -427,7 +428,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "diesel"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -739,7 +740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.1.15"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -833,13 +834,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.12.22"
|
||||
version = "0.12.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"h2 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"h2 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -875,7 +876,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.12.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.12.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -1108,7 +1109,7 @@ name = "migrations_internals"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"diesel 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"diesel 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1453,6 +1454,26 @@ dependencies = [
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"paste-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-hack 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste-impl"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro-hack 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pear"
|
||||
version = "0.1.2"
|
||||
@ -1565,6 +1586,16 @@ dependencies = [
|
||||
"typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.26"
|
||||
@ -1608,7 +1639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -1633,7 +1664,7 @@ dependencies = [
|
||||
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1648,7 +1679,7 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1656,12 +1687,20 @@ name = "rand_core"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -1669,7 +1708,7 @@ name = "rand_hc"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1677,7 +1716,7 @@ name = "rand_isaac"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1688,7 +1727,7 @@ dependencies = [
|
||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -1698,7 +1737,7 @@ name = "rand_pcg"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -1707,7 +1746,7 @@ name = "rand_xorshift"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1715,7 +1754,7 @@ name = "rdrand"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1756,12 +1795,12 @@ name = "reqwest"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encoding_rs 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.12.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.12.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libflate 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1814,7 +1853,7 @@ name = "rocket"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2640,7 +2679,7 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"block-modes 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2666,7 +2705,7 @@ dependencies = [
|
||||
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
|
||||
"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5"
|
||||
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
|
||||
"checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2"
|
||||
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
|
||||
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
|
||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||
@ -2704,7 +2743,7 @@ dependencies = [
|
||||
"checksum devise 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74e04ba2d03c5fa0d954c061fc8c9c288badadffc272ebb87679a89846de3ed3"
|
||||
"checksum devise_codegen 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "066ceb7928ca93a9bedc6d0e612a8a0424048b0ab1f75971b203d01420c055d7"
|
||||
"checksum devise_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf41c59b22b5e3ec0ea55c7847e5f358d340f3a8d6d53a5cf4f1564967f96487"
|
||||
"checksum diesel 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "66d7d3a2f8a24763a1a52b5324737b4d24141bb294440ed9094db60bd6cd29ee"
|
||||
"checksum diesel 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a2469cbcf1dfb9446e491cac4c493c2554133f87f7d041e892ac82e5cd36e863"
|
||||
"checksum diesel_derives 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "62a27666098617d52c487a41f70de23d44a1dc1f3aa5877ceba2790fb1f1cab4"
|
||||
"checksum diesel_migrations 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c"
|
||||
"checksum digest 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a68d759d7a66a4f63d5bd2a2b14ad7e8cf93fe8c9be227031cd4e72ab0e9ee8"
|
||||
@ -2742,7 +2781,7 @@ dependencies = [
|
||||
"checksum generic-array 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe043cf9b85297937897087de81f590361686e1ac2d4d471b45435de5dfb6a6"
|
||||
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
|
||||
"checksum groupable 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32619942b8be646939eaf3db0602b39f5229b74575b67efc897811ded1db4e57"
|
||||
"checksum h2 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "30e0b8e55b4d7ffedade2b9605851f8e85f5010663e7ad170ef3c0f0681bc43f"
|
||||
"checksum h2 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ddb2b25a33e231484694267af28fec74ac63b5ccf51ee2065a5e313b834d836e"
|
||||
"checksum handlebars 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d82e5750d8027a97b9640e3fefa66bbaf852a35228e1c90790efd13c4b09c166"
|
||||
"checksum hmac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb5aa9647ba4711e9d6968dc1c810cd23989ed435443ca962e1bf6d8b8b83ff"
|
||||
"checksum hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f127a908633569f208325f86f71255d3363c79721d7f9fe31cd5569908819771"
|
||||
@ -2750,7 +2789,7 @@ dependencies = [
|
||||
"checksum http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1a10e5b573b9a0146545010f50772b9e8b1dd0a256564cc4307694c68832a2f5"
|
||||
"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83"
|
||||
"checksum hyper 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)" = "df0caae6b71d266b91b4a83111a61d2b94ed2e2bea024c532b933dcff867e58c"
|
||||
"checksum hyper 0.12.22 (registry+https://github.com/rust-lang/crates.io-index)" = "622db8120387cf64e3159e57343233b5a37253176fcda7f8a7e48f5a9c4c98fd"
|
||||
"checksum hyper 0.12.23 (registry+https://github.com/rust-lang/crates.io-index)" = "860faf61a9957c9cb0e23e69f1c8290e92f6eb660fcdd1f2d6777043a2ae1a46"
|
||||
"checksum hyper-sync-rustls 0.3.0-rc.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d1a443a90413a118ac6739e024f6a5180aa3b3f43f7de65f9d388a961cff19b"
|
||||
"checksum hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cd73f14ad370d3b4d4b7dce08f69b81536c82e39fcc89731930fe5788cd661"
|
||||
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
|
||||
@ -2812,6 +2851,8 @@ dependencies = [
|
||||
"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337"
|
||||
"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"
|
||||
"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
|
||||
"checksum paste 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f50392d1265092fbee9273414cc40eb6d47d307bd66222c477bb8450c8504f9d"
|
||||
"checksum paste-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a3cd512fe3a55e8933b2dcad913e365639db86d512e4004c3084b86864d9467a"
|
||||
"checksum pear 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c26d2b92e47063ffce70d3e3b1bd097af121a9e0db07ca38a6cc1cf0cc85ff25"
|
||||
"checksum pear_codegen 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "336db4a192cc7f54efeb0c4e11a9245394824cc3bcbd37ba3ff51240c35d7a6e"
|
||||
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
||||
@ -2825,6 +2866,7 @@ dependencies = [
|
||||
"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
|
||||
"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
|
||||
"checksum plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0"
|
||||
"checksum proc-macro-hack 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3e90aa19cd73dedc2d0e1e8407473f073d735fef0ab521438de6da8ee449ab66"
|
||||
"checksum proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "38fddd23d98b2144d197c0eca5705632d4fe2667d14a6be5df8934f8d74f1978"
|
||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
@ -2835,7 +2877,8 @@ dependencies = [
|
||||
"checksum rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3906503e80ac6cbcacb2c2973fa8e473f24d7e2747c8c92bb230c2441cad96b5"
|
||||
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||
"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
|
||||
"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
|
||||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
|
||||
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
||||
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
||||
"checksum rand_os 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46fbd5550acf75b0c2730f5dd1873751daf9beb8f11b44027778fae50d7feca"
|
||||
|
@ -95,6 +95,9 @@ native-tls = "0.2.2"
|
||||
# Template library
|
||||
handlebars = "1.1.0"
|
||||
|
||||
# Macro ident-combining library
|
||||
paste = "0.1.4"
|
||||
|
||||
[patch.crates-io]
|
||||
# Add support for Timestamp type
|
||||
rmp = { git = 'https://github.com/dani-garcia/msgpack-rust' }
|
||||
|
@ -1,4 +1,3 @@
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json::Value;
|
||||
|
||||
use rocket::http::{Cookie, Cookies, SameSite};
|
||||
@ -6,7 +5,7 @@ use rocket::request::{self, FlashMessage, Form, FromRequest, Request};
|
||||
use rocket::response::{content::Html, Flash, Redirect};
|
||||
use rocket::{Outcome, Route};
|
||||
|
||||
use crate::api::{JsonResult, JsonUpcase};
|
||||
use crate::api::{ApiResult, EmptyResult, JsonUpcase};
|
||||
use crate::auth::{decode_admin, encode_jwt, generate_admin_claims, ClientIp};
|
||||
use crate::db::{models::*, DbConn};
|
||||
use crate::error::Error;
|
||||
@ -14,7 +13,7 @@ use crate::mail;
|
||||
use crate::CONFIG;
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
if CONFIG.admin_token.is_none() {
|
||||
if CONFIG.admin_token().is_none() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
@ -54,7 +53,7 @@ impl AdminTemplateData {
|
||||
}
|
||||
|
||||
#[get("/", rank = 2)]
|
||||
fn admin_login(flash: Option<FlashMessage>) -> Result<Html<String>, Error> {
|
||||
fn admin_login(flash: Option<FlashMessage>) -> ApiResult<Html<String>> {
|
||||
// If there is an error, show it
|
||||
let msg = flash.map(|msg| format!("{}: {}", msg.name(), msg.msg()));
|
||||
|
||||
@ -97,14 +96,14 @@ fn post_admin_login(data: Form<LoginForm>, mut cookies: Cookies, ip: ClientIp) -
|
||||
}
|
||||
|
||||
fn _validate_token(token: &str) -> bool {
|
||||
match CONFIG.admin_token.as_ref() {
|
||||
match CONFIG.admin_token().as_ref() {
|
||||
None => false,
|
||||
Some(t) => t == token,
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/", rank = 1)]
|
||||
fn admin_page(_token: AdminToken, conn: DbConn) -> Result<Html<String>, Error> {
|
||||
fn admin_page(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
|
||||
let users = User::get_all(&conn);
|
||||
let users_json: Vec<Value> = users.iter().map(|u| u.to_json(&conn)).collect();
|
||||
|
||||
@ -119,39 +118,36 @@ struct InviteData {
|
||||
}
|
||||
|
||||
#[post("/invite", data = "<data>")]
|
||||
fn invite_user(data: JsonUpcase<InviteData>, _token: AdminToken, conn: DbConn) -> JsonResult {
|
||||
fn invite_user(data: JsonUpcase<InviteData>, _token: AdminToken, conn: DbConn) -> EmptyResult {
|
||||
let data: InviteData = data.into_inner().data;
|
||||
let email = data.Email.clone();
|
||||
if User::find_by_mail(&data.Email, &conn).is_some() {
|
||||
err!("User already exists")
|
||||
}
|
||||
|
||||
if !CONFIG.invitations_allowed {
|
||||
if !CONFIG.invitations_allowed() {
|
||||
err!("Invitations are not allowed")
|
||||
}
|
||||
|
||||
if let Some(ref mail_config) = CONFIG.mail {
|
||||
if let Some(ref mail_config) = CONFIG.mail() {
|
||||
let mut user = User::new(email);
|
||||
user.save(&conn)?;
|
||||
let org_name = "bitwarden_rs";
|
||||
mail::send_invite(&user.email, &user.uuid, None, None, &org_name, None, mail_config)?;
|
||||
mail::send_invite(&user.email, &user.uuid, None, None, &org_name, None, mail_config)
|
||||
} else {
|
||||
let mut invitation = Invitation::new(data.Email);
|
||||
invitation.save(&conn)?;
|
||||
invitation.save(&conn)
|
||||
}
|
||||
|
||||
Ok(Json(json!({})))
|
||||
}
|
||||
|
||||
#[post("/users/<uuid>/delete")]
|
||||
fn delete_user(uuid: String, _token: AdminToken, conn: DbConn) -> JsonResult {
|
||||
fn delete_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
|
||||
let user = match User::find_by_uuid(&uuid, &conn) {
|
||||
Some(user) => user,
|
||||
None => err!("User doesn't exist"),
|
||||
};
|
||||
|
||||
user.delete(&conn)?;
|
||||
Ok(Json(json!({})))
|
||||
user.delete(&conn)
|
||||
}
|
||||
|
||||
pub struct AdminToken {}
|
||||
|
@ -79,14 +79,14 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
|
||||
}
|
||||
|
||||
user
|
||||
} else if CONFIG.signups_allowed {
|
||||
} else if CONFIG.signups_allowed() {
|
||||
err!("Account with this email already exists")
|
||||
} else {
|
||||
err!("Registration not allowed")
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if CONFIG.signups_allowed || Invitation::take(&data.Email, &conn) {
|
||||
if CONFIG.signups_allowed() || Invitation::take(&data.Email, &conn) {
|
||||
User::new(data.Email.clone())
|
||||
} else {
|
||||
err!("Registration not allowed")
|
||||
@ -419,9 +419,9 @@ fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResul
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
if let Some(ref mail_config) = CONFIG.mail {
|
||||
if let Some(ref mail_config) = CONFIG.mail() {
|
||||
mail::send_password_hint(&data.Email, hint, mail_config)?;
|
||||
} else if CONFIG.show_password_hint {
|
||||
} else if CONFIG.show_password_hint() {
|
||||
if let Some(hint) = hint {
|
||||
err!(format!("Your password hint is: {}", &hint));
|
||||
} else {
|
||||
|
@ -302,7 +302,7 @@ pub fn update_cipher_from_data(
|
||||
cipher.fields = data.Fields.map(|f| f.to_string());
|
||||
cipher.data = type_data.to_string();
|
||||
cipher.password_history = data.PasswordHistory.map(|f| f.to_string());
|
||||
|
||||
|
||||
cipher.save(&conn)?;
|
||||
cipher.move_to_folder(data.FolderId, &headers.user.uuid, &conn)?;
|
||||
|
||||
@ -651,7 +651,7 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers
|
||||
let boundary_pair = params.next().expect("No boundary provided");
|
||||
let boundary = boundary_pair.1;
|
||||
|
||||
let base_path = Path::new(&CONFIG.attachments_folder).join(&cipher.uuid);
|
||||
let base_path = Path::new(&CONFIG.attachments_folder()).join(&cipher.uuid);
|
||||
|
||||
let mut attachment_key = None;
|
||||
|
||||
|
@ -486,17 +486,17 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
|
||||
}
|
||||
|
||||
for email in data.Emails.iter() {
|
||||
let mut user_org_status = match CONFIG.mail {
|
||||
let mut user_org_status = match CONFIG.mail() {
|
||||
Some(_) => UserOrgStatus::Invited as i32,
|
||||
None => UserOrgStatus::Accepted as i32, // Automatically mark user as accepted if no email invites
|
||||
};
|
||||
let user = match User::find_by_mail(&email, &conn) {
|
||||
None => {
|
||||
if !CONFIG.invitations_allowed {
|
||||
if !CONFIG.invitations_allowed() {
|
||||
err!(format!("User email does not exist: {}", email))
|
||||
}
|
||||
|
||||
if CONFIG.mail.is_none() {
|
||||
if CONFIG.mail().is_none() {
|
||||
let mut invitation = Invitation::new(email.clone());
|
||||
invitation.save(&conn)?;
|
||||
}
|
||||
@ -535,7 +535,7 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
|
||||
|
||||
new_user.save(&conn)?;
|
||||
|
||||
if let Some(ref mail_config) = CONFIG.mail {
|
||||
if let Some(ref mail_config) = CONFIG.mail() {
|
||||
let org_name = match Organization::find_by_uuid(&org_id, &conn) {
|
||||
Some(org) => org.name,
|
||||
None => err!("Error looking up organization"),
|
||||
@ -558,11 +558,11 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
|
||||
|
||||
#[post("/organizations/<org_id>/users/<user_org>/reinvite")]
|
||||
fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
|
||||
if !CONFIG.invitations_allowed {
|
||||
if !CONFIG.invitations_allowed() {
|
||||
err!("Invitations are not allowed.")
|
||||
}
|
||||
|
||||
if CONFIG.mail.is_none() {
|
||||
if CONFIG.mail().is_none() {
|
||||
err!("SMTP is not configured.")
|
||||
}
|
||||
|
||||
@ -585,7 +585,7 @@ fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn:
|
||||
None => err!("Error looking up organization."),
|
||||
};
|
||||
|
||||
if let Some(ref mail_config) = CONFIG.mail {
|
||||
if let Some(ref mail_config) = CONFIG.mail() {
|
||||
mail::send_invite(
|
||||
&user.email,
|
||||
&user.uuid,
|
||||
@ -637,7 +637,7 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptD
|
||||
None => err!("Invited user not found"),
|
||||
}
|
||||
|
||||
if let Some(ref mail_config) = CONFIG.mail {
|
||||
if let Some(ref mail_config) = CONFIG.mail() {
|
||||
let mut org_name = String::from("bitwarden_rs");
|
||||
if let Some(org_id) = &claims.org_id {
|
||||
org_name = match Organization::find_by_uuid(&org_id, &conn) {
|
||||
@ -686,7 +686,7 @@ fn confirm_invite(
|
||||
None => err!("Invalid key provided"),
|
||||
};
|
||||
|
||||
if let Some(ref mail_config) = CONFIG.mail {
|
||||
if let Some(ref mail_config) = CONFIG.mail() {
|
||||
let org_name = match Organization::find_by_uuid(&org_id, &conn) {
|
||||
Some(org) => org.name,
|
||||
None => err!("Error looking up organization."),
|
||||
|
@ -179,7 +179,7 @@ fn activate_authenticator(data: JsonUpcase<EnableAuthenticatorData>, headers: He
|
||||
Some(n) => n as u64,
|
||||
None => err!("Malformed token"),
|
||||
};
|
||||
|
||||
|
||||
let mut user = headers.user;
|
||||
|
||||
if !user.check_valid_password(&password_hash) {
|
||||
@ -236,13 +236,13 @@ use crate::CONFIG;
|
||||
const U2F_VERSION: &str = "U2F_V2";
|
||||
|
||||
lazy_static! {
|
||||
static ref APP_ID: String = format!("{}/app-id.json", &CONFIG.domain);
|
||||
static ref APP_ID: String = format!("{}/app-id.json", &CONFIG.domain());
|
||||
static ref U2F: U2f = U2f::new(APP_ID.clone());
|
||||
}
|
||||
|
||||
#[post("/two-factor/get-u2f", data = "<data>")]
|
||||
fn generate_u2f(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
if !CONFIG.domain_set {
|
||||
if !CONFIG.domain_set() {
|
||||
err!("`DOMAIN` environment variable is not set. U2F disabled")
|
||||
}
|
||||
|
||||
@ -286,6 +286,8 @@ fn generate_u2f_challenge(data: JsonUpcase<PasswordData>, headers: Headers, conn
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
struct EnableU2FData {
|
||||
Id: NumberOrString, // 1..5
|
||||
Name: String,
|
||||
MasterPasswordHash: String,
|
||||
DeviceResponse: String,
|
||||
}
|
||||
@ -321,54 +323,52 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
|
||||
err!("Invalid password");
|
||||
}
|
||||
|
||||
let tf_challenge =
|
||||
TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::U2fRegisterChallenge as i32, &conn);
|
||||
let tf_type = TwoFactorType::U2fRegisterChallenge as i32;
|
||||
let tf_challenge = match TwoFactor::find_by_user_and_type(&user.uuid, tf_type, &conn) {
|
||||
Some(c) => c,
|
||||
None => err!("Can't recover challenge"),
|
||||
};
|
||||
|
||||
if let Some(tf_challenge) = tf_challenge {
|
||||
let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
|
||||
let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
|
||||
tf_challenge.delete(&conn)?;
|
||||
|
||||
tf_challenge.delete(&conn)?;
|
||||
let response_copy: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse)?;
|
||||
|
||||
let response_copy: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse)?;
|
||||
let error_code = response_copy
|
||||
.error_code
|
||||
.clone()
|
||||
.map_or("0".into(), NumberOrString::into_string);
|
||||
|
||||
let error_code = response_copy
|
||||
.error_code
|
||||
.clone()
|
||||
.map_or("0".into(), NumberOrString::into_string);
|
||||
|
||||
if error_code != "0" {
|
||||
err!("Error registering U2F token")
|
||||
}
|
||||
|
||||
let response = response_copy.into_response();
|
||||
|
||||
let registration = U2F.register_response(challenge.clone(), response)?;
|
||||
// TODO: Allow more than one U2F device
|
||||
let mut registrations = Vec::new();
|
||||
registrations.push(registration);
|
||||
|
||||
let tf_registration = TwoFactor::new(
|
||||
user.uuid.clone(),
|
||||
TwoFactorType::U2f,
|
||||
serde_json::to_string(®istrations).unwrap(),
|
||||
);
|
||||
tf_registration.save(&conn)?;
|
||||
|
||||
_generate_recover_code(&mut user, &conn);
|
||||
|
||||
Ok(Json(json!({
|
||||
"Enabled": true,
|
||||
"Challenge": {
|
||||
"UserId": user.uuid,
|
||||
"AppId": APP_ID.to_string(),
|
||||
"Challenge": challenge,
|
||||
"Version": U2F_VERSION,
|
||||
},
|
||||
"Object": "twoFactorU2f"
|
||||
})))
|
||||
} else {
|
||||
err!("Can't recover challenge")
|
||||
if error_code != "0" {
|
||||
err!("Error registering U2F token")
|
||||
}
|
||||
|
||||
let response = response_copy.into_response();
|
||||
|
||||
let registration = U2F.register_response(challenge.clone(), response)?;
|
||||
// TODO: Allow more than one U2F device
|
||||
let mut registrations = Vec::new();
|
||||
registrations.push(registration);
|
||||
|
||||
let tf_registration = TwoFactor::new(
|
||||
user.uuid.clone(),
|
||||
TwoFactorType::U2f,
|
||||
serde_json::to_string(®istrations).unwrap(),
|
||||
);
|
||||
tf_registration.save(&conn)?;
|
||||
|
||||
_generate_recover_code(&mut user, &conn);
|
||||
|
||||
Ok(Json(json!({
|
||||
"Enabled": true,
|
||||
"Challenge": {
|
||||
"UserId": user.uuid,
|
||||
"AppId": APP_ID.to_string(),
|
||||
"Challenge": challenge,
|
||||
"Version": U2F_VERSION,
|
||||
},
|
||||
"Object": "twoFactorU2f"
|
||||
})))
|
||||
}
|
||||
|
||||
#[put("/two-factor/u2f", data = "<data>")]
|
||||
@ -493,29 +493,9 @@ use yubico::config::Config;
|
||||
use yubico::Yubico;
|
||||
|
||||
fn parse_yubikeys(data: &EnableYubikeyData) -> Vec<String> {
|
||||
let mut yubikeys: Vec<String> = Vec::new();
|
||||
let data_keys = [&data.Key1, &data.Key2, &data.Key3, &data.Key4, &data.Key5];
|
||||
|
||||
if data.Key1.is_some() {
|
||||
yubikeys.push(data.Key1.as_ref().unwrap().to_owned());
|
||||
}
|
||||
|
||||
if data.Key2.is_some() {
|
||||
yubikeys.push(data.Key2.as_ref().unwrap().to_owned());
|
||||
}
|
||||
|
||||
if data.Key3.is_some() {
|
||||
yubikeys.push(data.Key3.as_ref().unwrap().to_owned());
|
||||
}
|
||||
|
||||
if data.Key4.is_some() {
|
||||
yubikeys.push(data.Key4.as_ref().unwrap().to_owned());
|
||||
}
|
||||
|
||||
if data.Key5.is_some() {
|
||||
yubikeys.push(data.Key5.as_ref().unwrap().to_owned());
|
||||
}
|
||||
|
||||
yubikeys
|
||||
data_keys.into_iter().filter_map(|e| e.as_ref().cloned()).collect()
|
||||
}
|
||||
|
||||
fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value {
|
||||
@ -529,17 +509,17 @@ fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value {
|
||||
}
|
||||
|
||||
fn verify_yubikey_otp(otp: String) -> JsonResult {
|
||||
if !CONFIG.yubico_cred_set {
|
||||
if !CONFIG.yubico_cred_set() {
|
||||
err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
|
||||
}
|
||||
|
||||
let yubico = Yubico::new();
|
||||
let config = Config::default()
|
||||
.set_client_id(CONFIG.yubico_client_id.to_owned())
|
||||
.set_key(CONFIG.yubico_secret_key.to_owned());
|
||||
.set_client_id(CONFIG.yubico_client_id())
|
||||
.set_key(CONFIG.yubico_secret_key());
|
||||
|
||||
let result = match CONFIG.yubico_server {
|
||||
Some(ref server) => yubico.verify(otp, config.set_api_hosts(vec![server.to_owned()])),
|
||||
let result = match CONFIG.yubico_server() {
|
||||
Some(server) => yubico.verify(otp, config.set_api_hosts(vec![server])),
|
||||
None => yubico.verify(otp, config),
|
||||
};
|
||||
|
||||
@ -551,7 +531,7 @@ fn verify_yubikey_otp(otp: String) -> JsonResult {
|
||||
|
||||
#[post("/two-factor/get-yubikey", data = "<data>")]
|
||||
fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
if !CONFIG.yubico_cred_set {
|
||||
if !CONFIG.yubico_cred_set() {
|
||||
err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
|
||||
}
|
||||
|
||||
@ -637,7 +617,7 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
|
||||
serde_json::to_string(&yubikey_metadata).unwrap(),
|
||||
);
|
||||
yubikey_registration.save(&conn)?;
|
||||
|
||||
|
||||
_generate_recover_code(&mut user, &conn);
|
||||
|
||||
let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
|
||||
|
@ -32,7 +32,7 @@ fn icon(domain: String) -> Content<Vec<u8>> {
|
||||
}
|
||||
|
||||
fn get_icon(domain: &str) -> Vec<u8> {
|
||||
let path = format!("{}/{}.png", CONFIG.icon_cache_folder, domain);
|
||||
let path = format!("{}/{}.png", CONFIG.icon_cache_folder(), domain);
|
||||
|
||||
if let Some(icon) = get_cached_icon(&path) {
|
||||
return icon;
|
||||
@ -87,7 +87,7 @@ fn file_is_expired(path: &str, ttl: u64) -> Result<bool, Error> {
|
||||
|
||||
fn icon_is_negcached(path: &str) -> bool {
|
||||
let miss_indicator = path.to_owned() + ".miss";
|
||||
let expired = file_is_expired(&miss_indicator, CONFIG.icon_cache_negttl);
|
||||
let expired = file_is_expired(&miss_indicator, CONFIG.icon_cache_negttl());
|
||||
|
||||
match expired {
|
||||
// No longer negatively cached, drop the marker
|
||||
@ -110,12 +110,12 @@ fn mark_negcache(path: &str) {
|
||||
}
|
||||
|
||||
fn icon_is_expired(path: &str) -> bool {
|
||||
let expired = file_is_expired(path, CONFIG.icon_cache_ttl);
|
||||
let expired = file_is_expired(path, CONFIG.icon_cache_ttl());
|
||||
expired.unwrap_or(true)
|
||||
}
|
||||
|
||||
fn get_icon_url(domain: &str) -> String {
|
||||
if CONFIG.local_icon_extractor {
|
||||
if CONFIG.local_icon_extractor() {
|
||||
format!("http://{}/favicon.ico", domain)
|
||||
} else {
|
||||
format!("https://icons.bitwarden.com/{}/icon.png", domain)
|
||||
@ -135,7 +135,7 @@ fn download_icon(url: &str) -> Result<Vec<u8>, Error> {
|
||||
}
|
||||
|
||||
fn save_icon(path: &str, icon: &[u8]) {
|
||||
create_dir_all(&CONFIG.icon_cache_folder).expect("Error creating icon cache");
|
||||
create_dir_all(&CONFIG.icon_cache_folder()).expect("Error creating icon cache");
|
||||
|
||||
if let Ok(mut f) = File::create(path) {
|
||||
f.write_all(icon).expect("Error writing icon file");
|
||||
|
@ -234,7 +234,7 @@ fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &DbConn) -> Api
|
||||
match TwoFactorType::from_i32(*provider) {
|
||||
Some(TwoFactorType::Authenticator) => { /* Nothing to do for TOTP */ }
|
||||
|
||||
Some(TwoFactorType::U2f) if CONFIG.domain_set => {
|
||||
Some(TwoFactorType::U2f) if CONFIG.domain_set() => {
|
||||
let request = two_factor::generate_u2f_login(user_uuid, conn)?;
|
||||
let mut challenge_list = Vec::new();
|
||||
|
||||
|
@ -25,7 +25,7 @@ fn negotiate(_headers: Headers, _conn: DbConn) -> JsonResult {
|
||||
let conn_id = BASE64URL.encode(&crypto::get_random(vec![0u8; 16]));
|
||||
let mut available_transports: Vec<JsonValue> = Vec::new();
|
||||
|
||||
if CONFIG.websocket_enabled {
|
||||
if CONFIG.websocket_enabled() {
|
||||
available_transports.push(json!({"transport":"WebSockets", "transferFormats":["Text","Binary"]}));
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ fn serialize_date(date: NaiveDateTime) -> Value {
|
||||
let seconds: i64 = date.timestamp();
|
||||
let nanos: i64 = date.timestamp_subsec_nanos() as i64;
|
||||
let timestamp = nanos << 34 | seconds;
|
||||
|
||||
|
||||
let bs = timestamp.to_be_bytes();
|
||||
|
||||
// -1 is Timestamp
|
||||
@ -349,9 +349,12 @@ pub fn start_notification_server() -> WebSocketUsers {
|
||||
let factory = WSFactory::init();
|
||||
let users = factory.users.clone();
|
||||
|
||||
if CONFIG.websocket_enabled {
|
||||
if CONFIG.websocket_enabled() {
|
||||
thread::spawn(move || {
|
||||
WebSocket::new(factory).unwrap().listen(&CONFIG.websocket_url).unwrap();
|
||||
WebSocket::new(factory)
|
||||
.unwrap()
|
||||
.listen(&CONFIG.websocket_url())
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,11 @@ use rocket::Route;
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::CONFIG;
|
||||
use crate::util::Cached;
|
||||
use crate::CONFIG;
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
if CONFIG.web_vault_enabled {
|
||||
if CONFIG.web_vault_enabled() {
|
||||
routes![web_index, app_id, web_files, attachments, alive]
|
||||
} else {
|
||||
routes![attachments, alive]
|
||||
@ -21,7 +21,9 @@ pub fn routes() -> Vec<Route> {
|
||||
|
||||
#[get("/")]
|
||||
fn web_index() -> Cached<io::Result<NamedFile>> {
|
||||
Cached::short(NamedFile::open(Path::new(&CONFIG.web_vault_folder).join("index.html")))
|
||||
Cached::short(NamedFile::open(
|
||||
Path::new(&CONFIG.web_vault_folder()).join("index.html"),
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/app-id.json")]
|
||||
@ -35,7 +37,7 @@ fn app_id() -> Cached<Content<Json<Value>>> {
|
||||
{
|
||||
"version": { "major": 1, "minor": 0 },
|
||||
"ids": [
|
||||
&CONFIG.domain,
|
||||
&CONFIG.domain(),
|
||||
"ios:bundle-id:com.8bit.bitwarden",
|
||||
"android:apk-key-hash:dUGFzUzf3lmHSLBDBIv+WaFyZMI" ]
|
||||
}]
|
||||
@ -45,12 +47,12 @@ fn app_id() -> Cached<Content<Json<Value>>> {
|
||||
|
||||
#[get("/<p..>", rank = 10)] // Only match this if the other routes don't match
|
||||
fn web_files(p: PathBuf) -> Cached<io::Result<NamedFile>> {
|
||||
Cached::long(NamedFile::open(Path::new(&CONFIG.web_vault_folder).join(p)))
|
||||
Cached::long(NamedFile::open(Path::new(&CONFIG.web_vault_folder()).join(p)))
|
||||
}
|
||||
|
||||
#[get("/attachments/<uuid>/<file..>")]
|
||||
fn attachments(uuid: String, file: PathBuf) -> io::Result<NamedFile> {
|
||||
NamedFile::open(Path::new(&CONFIG.attachments_folder).join(uuid).join(file))
|
||||
NamedFile::open(Path::new(&CONFIG.attachments_folder()).join(uuid).join(file))
|
||||
}
|
||||
|
||||
#[get("/alive")]
|
||||
|
18
src/auth.rs
18
src/auth.rs
@ -16,21 +16,21 @@ const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
|
||||
lazy_static! {
|
||||
pub static ref DEFAULT_VALIDITY: Duration = Duration::hours(2);
|
||||
static ref JWT_HEADER: Header = Header::new(JWT_ALGORITHM);
|
||||
pub static ref JWT_LOGIN_ISSUER: String = format!("{}|login", CONFIG.domain);
|
||||
pub static ref JWT_INVITE_ISSUER: String = format!("{}|invite", CONFIG.domain);
|
||||
pub static ref JWT_ADMIN_ISSUER: String = format!("{}|admin", CONFIG.domain);
|
||||
static ref PRIVATE_RSA_KEY: Vec<u8> = match read_file(&CONFIG.private_rsa_key) {
|
||||
pub static ref JWT_LOGIN_ISSUER: String = format!("{}|login", CONFIG.domain());
|
||||
pub static ref JWT_INVITE_ISSUER: String = format!("{}|invite", CONFIG.domain());
|
||||
pub static ref JWT_ADMIN_ISSUER: String = format!("{}|admin", CONFIG.domain());
|
||||
static ref PRIVATE_RSA_KEY: Vec<u8> = match read_file(&CONFIG.private_rsa_key()) {
|
||||
Ok(key) => key,
|
||||
Err(e) => panic!(
|
||||
"Error loading private RSA Key from {}\n Error: {}",
|
||||
CONFIG.private_rsa_key, e
|
||||
CONFIG.private_rsa_key(), e
|
||||
),
|
||||
};
|
||||
static ref PUBLIC_RSA_KEY: Vec<u8> = match read_file(&CONFIG.public_rsa_key) {
|
||||
static ref PUBLIC_RSA_KEY: Vec<u8> = match read_file(&CONFIG.public_rsa_key()) {
|
||||
Ok(key) => key,
|
||||
Err(e) => panic!(
|
||||
"Error loading public RSA Key from {}\n Error: {}",
|
||||
CONFIG.public_rsa_key, e
|
||||
CONFIG.public_rsa_key(), e
|
||||
),
|
||||
};
|
||||
}
|
||||
@ -185,8 +185,8 @@ impl<'a, 'r> FromRequest<'a, 'r> for Headers {
|
||||
let headers = request.headers();
|
||||
|
||||
// Get host
|
||||
let host = if CONFIG.domain_set {
|
||||
CONFIG.domain.clone()
|
||||
let host = if CONFIG.domain_set() {
|
||||
CONFIG.domain()
|
||||
} else if let Some(referer) = headers.get_one("Referer") {
|
||||
referer.to_string()
|
||||
} else {
|
||||
|
245
src/config.rs
Normal file
245
src/config.rs
Normal file
@ -0,0 +1,245 @@
|
||||
use std::process::exit;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use handlebars::Handlebars;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref CONFIG: Config = Config::load();
|
||||
}
|
||||
|
||||
macro_rules! make_config {
|
||||
( $( $name:ident: $ty:ty ),+ $(,)* ) => {
|
||||
|
||||
pub struct Config { inner: RwLock<_Config> }
|
||||
|
||||
#[derive(Default)]
|
||||
struct _Config {
|
||||
_templates: Handlebars,
|
||||
$(pub $name: $ty),+
|
||||
}
|
||||
|
||||
paste::item! {
|
||||
#[allow(unused)]
|
||||
impl Config {
|
||||
$(
|
||||
pub fn $name(&self) -> $ty {
|
||||
self.inner.read().unwrap().$name.clone()
|
||||
}
|
||||
pub fn [<set_ $name>](&self, value: $ty) {
|
||||
self.inner.write().unwrap().$name = value;
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
make_config! {
|
||||
database_url: String,
|
||||
icon_cache_folder: String,
|
||||
attachments_folder: String,
|
||||
|
||||
icon_cache_ttl: u64,
|
||||
icon_cache_negttl: u64,
|
||||
|
||||
private_rsa_key: String,
|
||||
private_rsa_key_pem: String,
|
||||
public_rsa_key: String,
|
||||
|
||||
web_vault_folder: String,
|
||||
web_vault_enabled: bool,
|
||||
|
||||
websocket_enabled: bool,
|
||||
websocket_url: String,
|
||||
|
||||
extended_logging: bool,
|
||||
log_file: Option<String>,
|
||||
|
||||
local_icon_extractor: bool,
|
||||
signups_allowed: bool,
|
||||
invitations_allowed: bool,
|
||||
admin_token: Option<String>,
|
||||
password_iterations: i32,
|
||||
show_password_hint: bool,
|
||||
|
||||
domain: String,
|
||||
domain_set: bool,
|
||||
|
||||
yubico_cred_set: bool,
|
||||
yubico_client_id: String,
|
||||
yubico_secret_key: String,
|
||||
yubico_server: Option<String>,
|
||||
|
||||
mail: Option<MailConfig>,
|
||||
templates_folder: String,
|
||||
reload_templates: bool,
|
||||
}
|
||||
|
||||
fn load_templates(path: &str) -> Handlebars {
|
||||
let mut hb = Handlebars::new();
|
||||
// Error on missing params
|
||||
hb.set_strict_mode(true);
|
||||
|
||||
macro_rules! reg {
|
||||
($name:expr) => {{
|
||||
let template = include_str!(concat!("static/templates/", $name, ".hbs"));
|
||||
hb.register_template_string($name, template).unwrap();
|
||||
}};
|
||||
}
|
||||
|
||||
// First register default templates here
|
||||
reg!("email/invite_accepted");
|
||||
reg!("email/invite_confirmed");
|
||||
reg!("email/pw_hint_none");
|
||||
reg!("email/pw_hint_some");
|
||||
reg!("email/send_org_invite");
|
||||
|
||||
reg!("admin/base");
|
||||
reg!("admin/login");
|
||||
reg!("admin/page");
|
||||
|
||||
// And then load user templates to overwrite the defaults
|
||||
// Use .hbs extension for the files
|
||||
// Templates get registered with their relative name
|
||||
hb.register_templates_directory(".hbs", path).unwrap();
|
||||
|
||||
hb
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn render_template<T: serde::ser::Serialize>(
|
||||
&self,
|
||||
name: &str,
|
||||
data: &T,
|
||||
) -> Result<String, crate::error::Error> {
|
||||
if CONFIG.reload_templates() {
|
||||
warn!("RELOADING TEMPLATES");
|
||||
let hb = load_templates(CONFIG.templates_folder().as_ref());
|
||||
hb.render(name, data).map_err(Into::into)
|
||||
} else {
|
||||
let hb = &CONFIG.inner.read().unwrap()._templates;
|
||||
hb.render(name, data).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn load() -> Self {
|
||||
use crate::util::{get_env, get_env_or};
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
let df = get_env_or("DATA_FOLDER", "data".to_string());
|
||||
let key = get_env_or("RSA_KEY_FILENAME", format!("{}/{}", &df, "rsa_key"));
|
||||
|
||||
let domain = get_env("DOMAIN");
|
||||
|
||||
let yubico_client_id = get_env("YUBICO_CLIENT_ID");
|
||||
let yubico_secret_key = get_env("YUBICO_SECRET_KEY");
|
||||
|
||||
let templates_folder = get_env_or("TEMPLATES_FOLDER", format!("{}/{}", &df, "templates"));
|
||||
|
||||
let cfg = _Config {
|
||||
database_url: get_env_or("DATABASE_URL", format!("{}/{}", &df, "db.sqlite3")),
|
||||
icon_cache_folder: get_env_or("ICON_CACHE_FOLDER", format!("{}/{}", &df, "icon_cache")),
|
||||
attachments_folder: get_env_or("ATTACHMENTS_FOLDER", format!("{}/{}", &df, "attachments")),
|
||||
_templates: load_templates(&templates_folder),
|
||||
templates_folder,
|
||||
reload_templates: get_env_or("RELOAD_TEMPLATES", false),
|
||||
|
||||
// icon_cache_ttl defaults to 30 days (30 * 24 * 60 * 60 seconds)
|
||||
icon_cache_ttl: get_env_or("ICON_CACHE_TTL", 2_592_000),
|
||||
// icon_cache_negttl defaults to 3 days (3 * 24 * 60 * 60 seconds)
|
||||
icon_cache_negttl: get_env_or("ICON_CACHE_NEGTTL", 259_200),
|
||||
|
||||
private_rsa_key: format!("{}.der", &key),
|
||||
private_rsa_key_pem: format!("{}.pem", &key),
|
||||
public_rsa_key: format!("{}.pub.der", &key),
|
||||
|
||||
web_vault_folder: get_env_or("WEB_VAULT_FOLDER", "web-vault/".into()),
|
||||
web_vault_enabled: get_env_or("WEB_VAULT_ENABLED", true),
|
||||
|
||||
websocket_enabled: get_env_or("WEBSOCKET_ENABLED", false),
|
||||
websocket_url: format!(
|
||||
"{}:{}",
|
||||
get_env_or("WEBSOCKET_ADDRESS", "0.0.0.0".to_string()),
|
||||
get_env_or("WEBSOCKET_PORT", 3012)
|
||||
),
|
||||
|
||||
extended_logging: get_env_or("EXTENDED_LOGGING", true),
|
||||
log_file: get_env("LOG_FILE"),
|
||||
|
||||
local_icon_extractor: get_env_or("LOCAL_ICON_EXTRACTOR", false),
|
||||
signups_allowed: get_env_or("SIGNUPS_ALLOWED", true),
|
||||
admin_token: get_env("ADMIN_TOKEN"),
|
||||
invitations_allowed: get_env_or("INVITATIONS_ALLOWED", true),
|
||||
password_iterations: get_env_or("PASSWORD_ITERATIONS", 100_000),
|
||||
show_password_hint: get_env_or("SHOW_PASSWORD_HINT", true),
|
||||
|
||||
domain_set: domain.is_some(),
|
||||
domain: domain.unwrap_or("http://localhost".into()),
|
||||
|
||||
yubico_cred_set: yubico_client_id.is_some() && yubico_secret_key.is_some(),
|
||||
yubico_client_id: yubico_client_id.unwrap_or("00000".into()),
|
||||
yubico_secret_key: yubico_secret_key.unwrap_or("AAAAAAA".into()),
|
||||
yubico_server: get_env("YUBICO_SERVER"),
|
||||
|
||||
mail: MailConfig::load(),
|
||||
};
|
||||
|
||||
Config {
|
||||
inner: RwLock::new(cfg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MailConfig {
|
||||
pub smtp_host: String,
|
||||
pub smtp_port: u16,
|
||||
pub smtp_ssl: bool,
|
||||
pub smtp_from: String,
|
||||
pub smtp_from_name: String,
|
||||
pub smtp_username: Option<String>,
|
||||
pub smtp_password: Option<String>,
|
||||
}
|
||||
|
||||
impl MailConfig {
|
||||
fn load() -> Option<Self> {
|
||||
use crate::util::{get_env, get_env_or};
|
||||
|
||||
// When SMTP_HOST is absent, we assume the user does not want to enable it.
|
||||
let smtp_host = match get_env("SMTP_HOST") {
|
||||
Some(host) => host,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let smtp_from = get_env("SMTP_FROM").unwrap_or_else(|| {
|
||||
error!("Please specify SMTP_FROM to enable SMTP support.");
|
||||
exit(1);
|
||||
});
|
||||
|
||||
let smtp_from_name = get_env_or("SMTP_FROM_NAME", "Bitwarden_RS".into());
|
||||
|
||||
let smtp_ssl = get_env_or("SMTP_SSL", true);
|
||||
let smtp_port = get_env("SMTP_PORT").unwrap_or_else(|| if smtp_ssl { 587u16 } else { 25u16 });
|
||||
|
||||
let smtp_username = get_env("SMTP_USERNAME");
|
||||
let smtp_password = get_env("SMTP_PASSWORD").or_else(|| {
|
||||
if smtp_username.as_ref().is_some() {
|
||||
error!("SMTP_PASSWORD is mandatory when specifying SMTP_USERNAME.");
|
||||
exit(1);
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
Some(MailConfig {
|
||||
smtp_host,
|
||||
smtp_port,
|
||||
smtp_ssl,
|
||||
smtp_from,
|
||||
smtp_from_name,
|
||||
smtp_username,
|
||||
smtp_password,
|
||||
})
|
||||
}
|
||||
}
|
@ -25,13 +25,13 @@ pub mod schema;
|
||||
|
||||
/// Initializes a database pool.
|
||||
pub fn init_pool() -> Pool {
|
||||
let manager = ConnectionManager::new(&*CONFIG.database_url);
|
||||
let manager = ConnectionManager::new(CONFIG.database_url());
|
||||
|
||||
r2d2::Pool::builder().build(manager).expect("Failed to create pool")
|
||||
}
|
||||
|
||||
pub fn get_connection() -> Result<Connection, ConnectionError> {
|
||||
Connection::establish(&CONFIG.database_url)
|
||||
Connection::establish(&CONFIG.database_url())
|
||||
}
|
||||
|
||||
/// Attempts to retrieve a single connection from the managed database pool. If
|
||||
|
@ -28,7 +28,7 @@ impl Attachment {
|
||||
}
|
||||
|
||||
pub fn get_file_path(&self) -> String {
|
||||
format!("{}/{}/{}", CONFIG.attachments_folder, self.cipher_uuid, self.id)
|
||||
format!("{}/{}/{}", CONFIG.attachments_folder(), self.cipher_uuid, self.id)
|
||||
}
|
||||
|
||||
pub fn to_json(&self, host: &str) -> Value {
|
||||
@ -86,7 +86,7 @@ impl Attachment {
|
||||
|
||||
pub fn find_by_id(id: &str, conn: &DbConn) -> Option<Self> {
|
||||
let id = id.to_lowercase();
|
||||
|
||||
|
||||
attachments::table
|
||||
.filter(attachments::id.eq(id))
|
||||
.first::<Self>(&**conn)
|
||||
|
@ -56,7 +56,7 @@ impl User {
|
||||
|
||||
password_hash: Vec::new(),
|
||||
salt: crypto::get_random_64(),
|
||||
password_iterations: CONFIG.password_iterations,
|
||||
password_iterations: CONFIG.password_iterations(),
|
||||
|
||||
security_stamp: crate::util::get_uuid(),
|
||||
|
||||
@ -242,7 +242,7 @@ impl Invitation {
|
||||
}
|
||||
|
||||
pub fn take(mail: &str, conn: &DbConn) -> bool {
|
||||
CONFIG.invitations_allowed
|
||||
CONFIG.invitations_allowed()
|
||||
&& match Self::find_by_mail(mail, &conn) {
|
||||
Some(invitation) => invitation.delete(&conn).is_ok(),
|
||||
None => false,
|
||||
|
@ -6,8 +6,8 @@ use native_tls::{Protocol, TlsConnector};
|
||||
|
||||
use crate::api::EmptyResult;
|
||||
use crate::auth::{encode_jwt, generate_invite_claims};
|
||||
use crate::config::MailConfig;
|
||||
use crate::error::Error;
|
||||
use crate::MailConfig;
|
||||
use crate::CONFIG;
|
||||
|
||||
fn mailer(config: &MailConfig) -> SmtpTransport {
|
||||
@ -85,7 +85,7 @@ pub fn send_invite(
|
||||
let (subject, body) = get_text(
|
||||
"email/send_org_invite",
|
||||
json!({
|
||||
"url": CONFIG.domain,
|
||||
"url": CONFIG.domain(),
|
||||
"org_id": org_id.unwrap_or("_".to_string()),
|
||||
"org_user_id": org_user_id.unwrap_or("_".to_string()),
|
||||
"email": address,
|
||||
@ -101,7 +101,7 @@ pub fn send_invite_accepted(new_user_email: &str, address: &str, org_name: &str,
|
||||
let (subject, body) = get_text(
|
||||
"email/invite_accepted",
|
||||
json!({
|
||||
"url": CONFIG.domain,
|
||||
"url": CONFIG.domain(),
|
||||
"email": new_user_email,
|
||||
"org_name": org_name,
|
||||
}),
|
||||
@ -114,7 +114,7 @@ pub fn send_invite_confirmed(address: &str, org_name: &str, config: &MailConfig)
|
||||
let (subject, body) = get_text(
|
||||
"email/invite_confirmed",
|
||||
json!({
|
||||
"url": CONFIG.domain,
|
||||
"url": CONFIG.domain(),
|
||||
"org_name": org_name,
|
||||
}),
|
||||
)?;
|
||||
|
239
src/main.rs
239
src/main.rs
@ -20,7 +20,6 @@ extern crate derive_more;
|
||||
#[macro_use]
|
||||
extern crate num_derive;
|
||||
|
||||
use handlebars::Handlebars;
|
||||
use rocket::{fairing::AdHoc, Rocket};
|
||||
|
||||
use std::{
|
||||
@ -32,11 +31,14 @@ use std::{
|
||||
mod error;
|
||||
mod api;
|
||||
mod auth;
|
||||
mod config;
|
||||
mod crypto;
|
||||
mod db;
|
||||
mod mail;
|
||||
mod util;
|
||||
|
||||
pub use config::CONFIG;
|
||||
|
||||
fn init_rocket() -> Rocket {
|
||||
rocket::ignite()
|
||||
.mount("/", api::web_routes())
|
||||
@ -68,7 +70,7 @@ mod migrations {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if CONFIG.extended_logging {
|
||||
if CONFIG.extended_logging() {
|
||||
init_logging().ok();
|
||||
}
|
||||
|
||||
@ -99,7 +101,7 @@ fn init_logging() -> Result<(), fern::InitError> {
|
||||
.level_for("multipart", log::LevelFilter::Info)
|
||||
.chain(std::io::stdout());
|
||||
|
||||
if let Some(log_file) = CONFIG.log_file.as_ref() {
|
||||
if let Some(log_file) = CONFIG.log_file() {
|
||||
logger = logger.chain(fern::log_file(log_file)?);
|
||||
}
|
||||
|
||||
@ -133,7 +135,8 @@ fn chain_syslog(logger: fern::Dispatch) -> fern::Dispatch {
|
||||
}
|
||||
|
||||
fn check_db() {
|
||||
let path = Path::new(&CONFIG.database_url);
|
||||
let url = CONFIG.database_url();
|
||||
let path = Path::new(&url);
|
||||
|
||||
if let Some(parent) = path.parent() {
|
||||
use std::fs;
|
||||
@ -153,7 +156,7 @@ fn check_db() {
|
||||
|
||||
fn check_rsa_keys() {
|
||||
// If the RSA keys don't exist, try to create them
|
||||
if !util::file_exists(&CONFIG.private_rsa_key) || !util::file_exists(&CONFIG.public_rsa_key) {
|
||||
if !util::file_exists(&CONFIG.private_rsa_key()) || !util::file_exists(&CONFIG.public_rsa_key()) {
|
||||
info!("JWT keys don't exist, checking if OpenSSL is available...");
|
||||
|
||||
Command::new("openssl").arg("version").output().unwrap_or_else(|_| {
|
||||
@ -166,7 +169,7 @@ fn check_rsa_keys() {
|
||||
let mut success = Command::new("openssl")
|
||||
.arg("genrsa")
|
||||
.arg("-out")
|
||||
.arg(&CONFIG.private_rsa_key_pem)
|
||||
.arg(&CONFIG.private_rsa_key_pem())
|
||||
.output()
|
||||
.expect("Failed to create private pem file")
|
||||
.status
|
||||
@ -175,11 +178,11 @@ fn check_rsa_keys() {
|
||||
success &= Command::new("openssl")
|
||||
.arg("rsa")
|
||||
.arg("-in")
|
||||
.arg(&CONFIG.private_rsa_key_pem)
|
||||
.arg(&CONFIG.private_rsa_key_pem())
|
||||
.arg("-outform")
|
||||
.arg("DER")
|
||||
.arg("-out")
|
||||
.arg(&CONFIG.private_rsa_key)
|
||||
.arg(&CONFIG.private_rsa_key())
|
||||
.output()
|
||||
.expect("Failed to create private der file")
|
||||
.status
|
||||
@ -188,14 +191,14 @@ fn check_rsa_keys() {
|
||||
success &= Command::new("openssl")
|
||||
.arg("rsa")
|
||||
.arg("-in")
|
||||
.arg(&CONFIG.private_rsa_key)
|
||||
.arg(&CONFIG.private_rsa_key())
|
||||
.arg("-inform")
|
||||
.arg("DER")
|
||||
.arg("-RSAPublicKey_out")
|
||||
.arg("-outform")
|
||||
.arg("DER")
|
||||
.arg("-out")
|
||||
.arg(&CONFIG.public_rsa_key)
|
||||
.arg(&CONFIG.public_rsa_key())
|
||||
.output()
|
||||
.expect("Failed to create public der file")
|
||||
.status
|
||||
@ -211,11 +214,11 @@ fn check_rsa_keys() {
|
||||
}
|
||||
|
||||
fn check_web_vault() {
|
||||
if !CONFIG.web_vault_enabled {
|
||||
if !CONFIG.web_vault_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
let index_path = Path::new(&CONFIG.web_vault_folder).join("index.html");
|
||||
let index_path = Path::new(&CONFIG.web_vault_folder()).join("index.html");
|
||||
|
||||
if !index_path.exists() {
|
||||
error!("Web vault is not found. Please follow the steps in the README to install it");
|
||||
@ -232,215 +235,3 @@ fn unofficial_warning() -> AdHoc {
|
||||
warn!("\\--------------------------------------------------------------------/");
|
||||
})
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
// Load the config from .env or from environment variables
|
||||
static ref CONFIG: Config = Config::load();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MailConfig {
|
||||
smtp_host: String,
|
||||
smtp_port: u16,
|
||||
smtp_ssl: bool,
|
||||
smtp_from: String,
|
||||
smtp_from_name: String,
|
||||
smtp_username: Option<String>,
|
||||
smtp_password: Option<String>,
|
||||
}
|
||||
|
||||
impl MailConfig {
|
||||
fn load() -> Option<Self> {
|
||||
use crate::util::{get_env, get_env_or};
|
||||
|
||||
// When SMTP_HOST is absent, we assume the user does not want to enable it.
|
||||
let smtp_host = match get_env("SMTP_HOST") {
|
||||
Some(host) => host,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let smtp_from = get_env("SMTP_FROM").unwrap_or_else(|| {
|
||||
error!("Please specify SMTP_FROM to enable SMTP support.");
|
||||
exit(1);
|
||||
});
|
||||
|
||||
let smtp_from_name = get_env_or("SMTP_FROM_NAME", "Bitwarden_RS".into());
|
||||
|
||||
let smtp_ssl = get_env_or("SMTP_SSL", true);
|
||||
let smtp_port = get_env("SMTP_PORT").unwrap_or_else(|| if smtp_ssl { 587u16 } else { 25u16 });
|
||||
|
||||
let smtp_username = get_env("SMTP_USERNAME");
|
||||
let smtp_password = get_env("SMTP_PASSWORD").or_else(|| {
|
||||
if smtp_username.as_ref().is_some() {
|
||||
error!("SMTP_PASSWORD is mandatory when specifying SMTP_USERNAME.");
|
||||
exit(1);
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
Some(MailConfig {
|
||||
smtp_host,
|
||||
smtp_port,
|
||||
smtp_ssl,
|
||||
smtp_from,
|
||||
smtp_from_name,
|
||||
smtp_username,
|
||||
smtp_password,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
database_url: String,
|
||||
icon_cache_folder: String,
|
||||
attachments_folder: String,
|
||||
|
||||
icon_cache_ttl: u64,
|
||||
icon_cache_negttl: u64,
|
||||
|
||||
private_rsa_key: String,
|
||||
private_rsa_key_pem: String,
|
||||
public_rsa_key: String,
|
||||
|
||||
web_vault_folder: String,
|
||||
web_vault_enabled: bool,
|
||||
|
||||
websocket_enabled: bool,
|
||||
websocket_url: String,
|
||||
|
||||
extended_logging: bool,
|
||||
log_file: Option<String>,
|
||||
|
||||
local_icon_extractor: bool,
|
||||
signups_allowed: bool,
|
||||
invitations_allowed: bool,
|
||||
admin_token: Option<String>,
|
||||
password_iterations: i32,
|
||||
show_password_hint: bool,
|
||||
|
||||
domain: String,
|
||||
domain_set: bool,
|
||||
|
||||
yubico_cred_set: bool,
|
||||
yubico_client_id: String,
|
||||
yubico_secret_key: String,
|
||||
yubico_server: Option<String>,
|
||||
|
||||
mail: Option<MailConfig>,
|
||||
templates: Handlebars,
|
||||
templates_folder: String,
|
||||
reload_templates: bool,
|
||||
}
|
||||
|
||||
fn load_templates(path: &str) -> Handlebars {
|
||||
let mut hb = Handlebars::new();
|
||||
// Error on missing params
|
||||
hb.set_strict_mode(true);
|
||||
|
||||
macro_rules! reg {
|
||||
($name:expr) => {{
|
||||
let template = include_str!(concat!("static/templates/", $name, ".hbs"));
|
||||
hb.register_template_string($name, template).unwrap();
|
||||
}};
|
||||
}
|
||||
|
||||
// First register default templates here
|
||||
reg!("email/invite_accepted");
|
||||
reg!("email/invite_confirmed");
|
||||
reg!("email/pw_hint_none");
|
||||
reg!("email/pw_hint_some");
|
||||
reg!("email/send_org_invite");
|
||||
|
||||
reg!("admin/base");
|
||||
reg!("admin/login");
|
||||
reg!("admin/page");
|
||||
|
||||
// And then load user templates to overwrite the defaults
|
||||
// Use .hbs extension for the files
|
||||
// Templates get registered with their relative name
|
||||
hb.register_templates_directory(".hbs", path).unwrap();
|
||||
|
||||
hb
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn render_template<T: serde::ser::Serialize>(&self, name: &str, data: &T) -> Result<String, error::Error> {
|
||||
// We add this to signal the compiler not to drop the result of 'load_templates'
|
||||
let hb_owned;
|
||||
|
||||
let hb = if CONFIG.reload_templates {
|
||||
warn!("RELOADING TEMPLATES");
|
||||
hb_owned = load_templates(&self.templates_folder);
|
||||
&hb_owned
|
||||
} else {
|
||||
&self.templates
|
||||
};
|
||||
|
||||
hb.render(name, data).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn load() -> Self {
|
||||
use crate::util::{get_env, get_env_or};
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
let df = get_env_or("DATA_FOLDER", "data".to_string());
|
||||
let key = get_env_or("RSA_KEY_FILENAME", format!("{}/{}", &df, "rsa_key"));
|
||||
|
||||
let domain = get_env("DOMAIN");
|
||||
|
||||
let yubico_client_id = get_env("YUBICO_CLIENT_ID");
|
||||
let yubico_secret_key = get_env("YUBICO_SECRET_KEY");
|
||||
|
||||
let templates_folder = get_env_or("TEMPLATES_FOLDER", format!("{}/{}", &df, "templates"));
|
||||
|
||||
Config {
|
||||
database_url: get_env_or("DATABASE_URL", format!("{}/{}", &df, "db.sqlite3")),
|
||||
icon_cache_folder: get_env_or("ICON_CACHE_FOLDER", format!("{}/{}", &df, "icon_cache")),
|
||||
attachments_folder: get_env_or("ATTACHMENTS_FOLDER", format!("{}/{}", &df, "attachments")),
|
||||
templates: load_templates(&templates_folder),
|
||||
templates_folder,
|
||||
reload_templates: get_env_or("RELOAD_TEMPLATES", false),
|
||||
|
||||
// icon_cache_ttl defaults to 30 days (30 * 24 * 60 * 60 seconds)
|
||||
icon_cache_ttl: get_env_or("ICON_CACHE_TTL", 2_592_000),
|
||||
// icon_cache_negttl defaults to 3 days (3 * 24 * 60 * 60 seconds)
|
||||
icon_cache_negttl: get_env_or("ICON_CACHE_NEGTTL", 259_200),
|
||||
|
||||
private_rsa_key: format!("{}.der", &key),
|
||||
private_rsa_key_pem: format!("{}.pem", &key),
|
||||
public_rsa_key: format!("{}.pub.der", &key),
|
||||
|
||||
web_vault_folder: get_env_or("WEB_VAULT_FOLDER", "web-vault/".into()),
|
||||
web_vault_enabled: get_env_or("WEB_VAULT_ENABLED", true),
|
||||
|
||||
websocket_enabled: get_env_or("WEBSOCKET_ENABLED", false),
|
||||
websocket_url: format!(
|
||||
"{}:{}",
|
||||
get_env_or("WEBSOCKET_ADDRESS", "0.0.0.0".to_string()),
|
||||
get_env_or("WEBSOCKET_PORT", 3012)
|
||||
),
|
||||
|
||||
extended_logging: get_env_or("EXTENDED_LOGGING", true),
|
||||
log_file: get_env("LOG_FILE"),
|
||||
|
||||
local_icon_extractor: get_env_or("LOCAL_ICON_EXTRACTOR", false),
|
||||
signups_allowed: get_env_or("SIGNUPS_ALLOWED", true),
|
||||
admin_token: get_env("ADMIN_TOKEN"),
|
||||
invitations_allowed: get_env_or("INVITATIONS_ALLOWED", true),
|
||||
password_iterations: get_env_or("PASSWORD_ITERATIONS", 100_000),
|
||||
show_password_hint: get_env_or("SHOW_PASSWORD_HINT", true),
|
||||
|
||||
domain_set: domain.is_some(),
|
||||
domain: domain.unwrap_or("http://localhost".into()),
|
||||
|
||||
yubico_cred_set: yubico_client_id.is_some() && yubico_secret_key.is_some(),
|
||||
yubico_client_id: yubico_client_id.unwrap_or("00000".into()),
|
||||
yubico_secret_key: yubico_secret_key.unwrap_or("AAAAAAA".into()),
|
||||
yubico_server: get_env("YUBICO_SERVER"),
|
||||
|
||||
mail: MailConfig::load(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
// Web Headers and caching
|
||||
//
|
||||
use rocket::fairing::{Fairing, Info, Kind};
|
||||
use rocket::{Request, Response};
|
||||
use rocket::response::{self, Responder};
|
||||
use rocket::{Request, Response};
|
||||
|
||||
pub struct AppHeaders();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user