geforkt von mirrored/vaultwarden
Change config to thread-safe system, needed for a future config panel.
Improved some two factor methods.
Dieser Commit ist enthalten in:
Ursprung
86de0ca17b
Commit
a1dc47b826
19 geänderte Dateien mit 457 neuen und 394 gelöschten Zeilen
99
Cargo.lock
generiert
99
Cargo.lock
generiert
|
@ -95,7 +95,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.10.0"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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-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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"rmpv 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -427,7 +428,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diesel"
|
name = "diesel"
|
||||||
version = "1.4.0"
|
version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.1.15"
|
version = "0.1.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -833,13 +834,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.12.22"
|
version = "0.12.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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 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)",
|
"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)",
|
"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)",
|
"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)",
|
"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 = [
|
dependencies = [
|
||||||
"bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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 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)",
|
"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)",
|
"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"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
|
@ -1453,6 +1454,26 @@ dependencies = [
|
||||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "pear"
|
name = "pear"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -1565,6 +1586,16 @@ dependencies = [
|
||||||
"typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "0.4.26"
|
version = "0.4.26"
|
||||||
|
@ -1608,7 +1639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fuchsia-zircon 0.3.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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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_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_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_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)",
|
"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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
|
@ -1656,12 +1687,20 @@ name = "rand_core"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
name = "rand_core"
|
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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1669,7 +1708,7 @@ name = "rand_hc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
|
@ -1677,7 +1716,7 @@ name = "rand_isaac"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
|
@ -1688,7 +1727,7 @@ dependencies = [
|
||||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -1698,7 +1737,7 @@ name = "rand_pcg"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
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)",
|
"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"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
|
@ -1715,7 +1754,7 @@ name = "rdrand"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
|
@ -1756,12 +1795,12 @@ name = "reqwest"
|
||||||
version = "0.9.9"
|
version = "0.9.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1814,7 +1853,7 @@ name = "rocket"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
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)",
|
"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)",
|
"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)",
|
"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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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 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 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 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 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 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"
|
"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 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_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 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_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 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"
|
"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.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 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 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 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.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"
|
"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 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 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.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-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 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"
|
"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 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.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 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 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 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"
|
"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 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 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 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 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 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"
|
"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 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_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.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_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_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"
|
"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
|
# Template library
|
||||||
handlebars = "1.1.0"
|
handlebars = "1.1.0"
|
||||||
|
|
||||||
|
# Macro ident-combining library
|
||||||
|
paste = "0.1.4"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# Add support for Timestamp type
|
# Add support for Timestamp type
|
||||||
rmp = { git = 'https://github.com/dani-garcia/msgpack-rust' }
|
rmp = { git = 'https://github.com/dani-garcia/msgpack-rust' }
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use rocket_contrib::json::Json;
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use rocket::http::{Cookie, Cookies, SameSite};
|
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::response::{content::Html, Flash, Redirect};
|
||||||
use rocket::{Outcome, Route};
|
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::auth::{decode_admin, encode_jwt, generate_admin_claims, ClientIp};
|
||||||
use crate::db::{models::*, DbConn};
|
use crate::db::{models::*, DbConn};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
@ -14,7 +13,7 @@ use crate::mail;
|
||||||
use crate::CONFIG;
|
use crate::CONFIG;
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
if CONFIG.admin_token.is_none() {
|
if CONFIG.admin_token().is_none() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ impl AdminTemplateData {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/", rank = 2)]
|
#[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
|
// If there is an error, show it
|
||||||
let msg = flash.map(|msg| format!("{}: {}", msg.name(), msg.msg()));
|
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 {
|
fn _validate_token(token: &str) -> bool {
|
||||||
match CONFIG.admin_token.as_ref() {
|
match CONFIG.admin_token().as_ref() {
|
||||||
None => false,
|
None => false,
|
||||||
Some(t) => t == token,
|
Some(t) => t == token,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/", rank = 1)]
|
#[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 = User::get_all(&conn);
|
||||||
let users_json: Vec<Value> = users.iter().map(|u| u.to_json(&conn)).collect();
|
let users_json: Vec<Value> = users.iter().map(|u| u.to_json(&conn)).collect();
|
||||||
|
|
||||||
|
@ -119,39 +118,36 @@ struct InviteData {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/invite", data = "<data>")]
|
#[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 data: InviteData = data.into_inner().data;
|
||||||
let email = data.Email.clone();
|
let email = data.Email.clone();
|
||||||
if User::find_by_mail(&data.Email, &conn).is_some() {
|
if User::find_by_mail(&data.Email, &conn).is_some() {
|
||||||
err!("User already exists")
|
err!("User already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !CONFIG.invitations_allowed {
|
if !CONFIG.invitations_allowed() {
|
||||||
err!("Invitations are not 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);
|
let mut user = User::new(email);
|
||||||
user.save(&conn)?;
|
user.save(&conn)?;
|
||||||
let org_name = "bitwarden_rs";
|
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 {
|
} else {
|
||||||
let mut invitation = Invitation::new(data.Email);
|
let mut invitation = Invitation::new(data.Email);
|
||||||
invitation.save(&conn)?;
|
invitation.save(&conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Json(json!({})))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/users/<uuid>/delete")]
|
#[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) {
|
let user = match User::find_by_uuid(&uuid, &conn) {
|
||||||
Some(user) => user,
|
Some(user) => user,
|
||||||
None => err!("User doesn't exist"),
|
None => err!("User doesn't exist"),
|
||||||
};
|
};
|
||||||
|
|
||||||
user.delete(&conn)?;
|
user.delete(&conn)
|
||||||
Ok(Json(json!({})))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AdminToken {}
|
pub struct AdminToken {}
|
||||||
|
|
|
@ -79,14 +79,14 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
user
|
user
|
||||||
} else if CONFIG.signups_allowed {
|
} else if CONFIG.signups_allowed() {
|
||||||
err!("Account with this email already exists")
|
err!("Account with this email already exists")
|
||||||
} else {
|
} else {
|
||||||
err!("Registration not allowed")
|
err!("Registration not allowed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if CONFIG.signups_allowed || Invitation::take(&data.Email, &conn) {
|
if CONFIG.signups_allowed() || Invitation::take(&data.Email, &conn) {
|
||||||
User::new(data.Email.clone())
|
User::new(data.Email.clone())
|
||||||
} else {
|
} else {
|
||||||
err!("Registration not allowed")
|
err!("Registration not allowed")
|
||||||
|
@ -419,9 +419,9 @@ fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResul
|
||||||
None => return Ok(()),
|
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)?;
|
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 {
|
if let Some(hint) = hint {
|
||||||
err!(format!("Your password hint is: {}", &hint));
|
err!(format!("Your password hint is: {}", &hint));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -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_pair = params.next().expect("No boundary provided");
|
||||||
let boundary = boundary_pair.1;
|
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;
|
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() {
|
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,
|
Some(_) => UserOrgStatus::Invited as i32,
|
||||||
None => UserOrgStatus::Accepted as i32, // Automatically mark user as accepted if no email invites
|
None => UserOrgStatus::Accepted as i32, // Automatically mark user as accepted if no email invites
|
||||||
};
|
};
|
||||||
let user = match User::find_by_mail(&email, &conn) {
|
let user = match User::find_by_mail(&email, &conn) {
|
||||||
None => {
|
None => {
|
||||||
if !CONFIG.invitations_allowed {
|
if !CONFIG.invitations_allowed() {
|
||||||
err!(format!("User email does not exist: {}", email))
|
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());
|
let mut invitation = Invitation::new(email.clone());
|
||||||
invitation.save(&conn)?;
|
invitation.save(&conn)?;
|
||||||
}
|
}
|
||||||
|
@ -535,7 +535,7 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
|
||||||
|
|
||||||
new_user.save(&conn)?;
|
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) {
|
let org_name = match Organization::find_by_uuid(&org_id, &conn) {
|
||||||
Some(org) => org.name,
|
Some(org) => org.name,
|
||||||
None => err!("Error looking up organization"),
|
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")]
|
#[post("/organizations/<org_id>/users/<user_org>/reinvite")]
|
||||||
fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
|
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.")
|
err!("Invitations are not allowed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if CONFIG.mail.is_none() {
|
if CONFIG.mail().is_none() {
|
||||||
err!("SMTP is not configured.")
|
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."),
|
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(
|
mail::send_invite(
|
||||||
&user.email,
|
&user.email,
|
||||||
&user.uuid,
|
&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"),
|
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");
|
let mut org_name = String::from("bitwarden_rs");
|
||||||
if let Some(org_id) = &claims.org_id {
|
if let Some(org_id) = &claims.org_id {
|
||||||
org_name = match Organization::find_by_uuid(&org_id, &conn) {
|
org_name = match Organization::find_by_uuid(&org_id, &conn) {
|
||||||
|
@ -686,7 +686,7 @@ fn confirm_invite(
|
||||||
None => err!("Invalid key provided"),
|
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) {
|
let org_name = match Organization::find_by_uuid(&org_id, &conn) {
|
||||||
Some(org) => org.name,
|
Some(org) => org.name,
|
||||||
None => err!("Error looking up organization."),
|
None => err!("Error looking up organization."),
|
||||||
|
|
|
@ -236,13 +236,13 @@ use crate::CONFIG;
|
||||||
const U2F_VERSION: &str = "U2F_V2";
|
const U2F_VERSION: &str = "U2F_V2";
|
||||||
|
|
||||||
lazy_static! {
|
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());
|
static ref U2F: U2f = U2f::new(APP_ID.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/two-factor/get-u2f", data = "<data>")]
|
#[post("/two-factor/get-u2f", data = "<data>")]
|
||||||
fn generate_u2f(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
|
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")
|
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)]
|
#[derive(Deserialize, Debug)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
struct EnableU2FData {
|
struct EnableU2FData {
|
||||||
|
Id: NumberOrString, // 1..5
|
||||||
|
Name: String,
|
||||||
MasterPasswordHash: String,
|
MasterPasswordHash: String,
|
||||||
DeviceResponse: String,
|
DeviceResponse: String,
|
||||||
}
|
}
|
||||||
|
@ -321,54 +323,52 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
|
||||||
err!("Invalid password");
|
err!("Invalid password");
|
||||||
}
|
}
|
||||||
|
|
||||||
let tf_challenge =
|
let tf_type = TwoFactorType::U2fRegisterChallenge as i32;
|
||||||
TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::U2fRegisterChallenge as i32, &conn);
|
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
|
if error_code != "0" {
|
||||||
.error_code
|
err!("Error registering U2F token")
|
||||||
.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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>")]
|
#[put("/two-factor/u2f", data = "<data>")]
|
||||||
|
@ -493,29 +493,9 @@ use yubico::config::Config;
|
||||||
use yubico::Yubico;
|
use yubico::Yubico;
|
||||||
|
|
||||||
fn parse_yubikeys(data: &EnableYubikeyData) -> Vec<String> {
|
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() {
|
data_keys.into_iter().filter_map(|e| e.as_ref().cloned()).collect()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value {
|
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 {
|
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")
|
err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
let yubico = Yubico::new();
|
let yubico = Yubico::new();
|
||||||
let config = Config::default()
|
let config = Config::default()
|
||||||
.set_client_id(CONFIG.yubico_client_id.to_owned())
|
.set_client_id(CONFIG.yubico_client_id())
|
||||||
.set_key(CONFIG.yubico_secret_key.to_owned());
|
.set_key(CONFIG.yubico_secret_key());
|
||||||
|
|
||||||
let result = match CONFIG.yubico_server {
|
let result = match CONFIG.yubico_server() {
|
||||||
Some(ref server) => yubico.verify(otp, config.set_api_hosts(vec![server.to_owned()])),
|
Some(server) => yubico.verify(otp, config.set_api_hosts(vec![server])),
|
||||||
None => yubico.verify(otp, config),
|
None => yubico.verify(otp, config),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -551,7 +531,7 @@ fn verify_yubikey_otp(otp: String) -> JsonResult {
|
||||||
|
|
||||||
#[post("/two-factor/get-yubikey", data = "<data>")]
|
#[post("/two-factor/get-yubikey", data = "<data>")]
|
||||||
fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
|
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")
|
err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ fn icon(domain: String) -> Content<Vec<u8>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_icon(domain: &str) -> 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) {
|
if let Some(icon) = get_cached_icon(&path) {
|
||||||
return icon;
|
return icon;
|
||||||
|
@ -87,7 +87,7 @@ fn file_is_expired(path: &str, ttl: u64) -> Result<bool, Error> {
|
||||||
|
|
||||||
fn icon_is_negcached(path: &str) -> bool {
|
fn icon_is_negcached(path: &str) -> bool {
|
||||||
let miss_indicator = path.to_owned() + ".miss";
|
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 {
|
match expired {
|
||||||
// No longer negatively cached, drop the marker
|
// No longer negatively cached, drop the marker
|
||||||
|
@ -110,12 +110,12 @@ fn mark_negcache(path: &str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn icon_is_expired(path: &str) -> bool {
|
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)
|
expired.unwrap_or(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_icon_url(domain: &str) -> String {
|
fn get_icon_url(domain: &str) -> String {
|
||||||
if CONFIG.local_icon_extractor {
|
if CONFIG.local_icon_extractor() {
|
||||||
format!("http://{}/favicon.ico", domain)
|
format!("http://{}/favicon.ico", domain)
|
||||||
} else {
|
} else {
|
||||||
format!("https://icons.bitwarden.com/{}/icon.png", domain)
|
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]) {
|
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) {
|
if let Ok(mut f) = File::create(path) {
|
||||||
f.write_all(icon).expect("Error writing icon file");
|
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) {
|
match TwoFactorType::from_i32(*provider) {
|
||||||
Some(TwoFactorType::Authenticator) => { /* Nothing to do for TOTP */ }
|
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 request = two_factor::generate_u2f_login(user_uuid, conn)?;
|
||||||
let mut challenge_list = Vec::new();
|
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 conn_id = BASE64URL.encode(&crypto::get_random(vec![0u8; 16]));
|
||||||
let mut available_transports: Vec<JsonValue> = Vec::new();
|
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"]}));
|
available_transports.push(json!({"transport":"WebSockets", "transferFormats":["Text","Binary"]}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,9 +349,12 @@ pub fn start_notification_server() -> WebSocketUsers {
|
||||||
let factory = WSFactory::init();
|
let factory = WSFactory::init();
|
||||||
let users = factory.users.clone();
|
let users = factory.users.clone();
|
||||||
|
|
||||||
if CONFIG.websocket_enabled {
|
if CONFIG.websocket_enabled() {
|
||||||
thread::spawn(move || {
|
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 rocket_contrib::json::Json;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::CONFIG;
|
|
||||||
use crate::util::Cached;
|
use crate::util::Cached;
|
||||||
|
use crate::CONFIG;
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
if CONFIG.web_vault_enabled {
|
if CONFIG.web_vault_enabled() {
|
||||||
routes![web_index, app_id, web_files, attachments, alive]
|
routes![web_index, app_id, web_files, attachments, alive]
|
||||||
} else {
|
} else {
|
||||||
routes![attachments, alive]
|
routes![attachments, alive]
|
||||||
|
@ -21,7 +21,9 @@ pub fn routes() -> Vec<Route> {
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn web_index() -> Cached<io::Result<NamedFile>> {
|
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")]
|
#[get("/app-id.json")]
|
||||||
|
@ -35,7 +37,7 @@ fn app_id() -> Cached<Content<Json<Value>>> {
|
||||||
{
|
{
|
||||||
"version": { "major": 1, "minor": 0 },
|
"version": { "major": 1, "minor": 0 },
|
||||||
"ids": [
|
"ids": [
|
||||||
&CONFIG.domain,
|
&CONFIG.domain(),
|
||||||
"ios:bundle-id:com.8bit.bitwarden",
|
"ios:bundle-id:com.8bit.bitwarden",
|
||||||
"android:apk-key-hash:dUGFzUzf3lmHSLBDBIv+WaFyZMI" ]
|
"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
|
#[get("/<p..>", rank = 10)] // Only match this if the other routes don't match
|
||||||
fn web_files(p: PathBuf) -> Cached<io::Result<NamedFile>> {
|
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..>")]
|
#[get("/attachments/<uuid>/<file..>")]
|
||||||
fn attachments(uuid: String, file: PathBuf) -> io::Result<NamedFile> {
|
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")]
|
#[get("/alive")]
|
||||||
|
|
18
src/auth.rs
18
src/auth.rs
|
@ -16,21 +16,21 @@ const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref DEFAULT_VALIDITY: Duration = Duration::hours(2);
|
pub static ref DEFAULT_VALIDITY: Duration = Duration::hours(2);
|
||||||
static ref JWT_HEADER: Header = Header::new(JWT_ALGORITHM);
|
static ref JWT_HEADER: Header = Header::new(JWT_ALGORITHM);
|
||||||
pub static ref JWT_LOGIN_ISSUER: String = format!("{}|login", CONFIG.domain);
|
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_INVITE_ISSUER: String = format!("{}|invite", CONFIG.domain());
|
||||||
pub static ref JWT_ADMIN_ISSUER: String = format!("{}|admin", 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) {
|
static ref PRIVATE_RSA_KEY: Vec<u8> = match read_file(&CONFIG.private_rsa_key()) {
|
||||||
Ok(key) => key,
|
Ok(key) => key,
|
||||||
Err(e) => panic!(
|
Err(e) => panic!(
|
||||||
"Error loading private RSA Key from {}\n Error: {}",
|
"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,
|
Ok(key) => key,
|
||||||
Err(e) => panic!(
|
Err(e) => panic!(
|
||||||
"Error loading public RSA Key from {}\n Error: {}",
|
"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();
|
let headers = request.headers();
|
||||||
|
|
||||||
// Get host
|
// Get host
|
||||||
let host = if CONFIG.domain_set {
|
let host = if CONFIG.domain_set() {
|
||||||
CONFIG.domain.clone()
|
CONFIG.domain()
|
||||||
} else if let Some(referer) = headers.get_one("Referer") {
|
} else if let Some(referer) = headers.get_one("Referer") {
|
||||||
referer.to_string()
|
referer.to_string()
|
||||||
} else {
|
} else {
|
||||||
|
|
245
src/config.rs
Normale Datei
245
src/config.rs
Normale Datei
|
@ -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.
|
/// Initializes a database pool.
|
||||||
pub fn init_pool() -> 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")
|
r2d2::Pool::builder().build(manager).expect("Failed to create pool")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_connection() -> Result<Connection, ConnectionError> {
|
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
|
/// 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 {
|
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 {
|
pub fn to_json(&self, host: &str) -> Value {
|
||||||
|
|
|
@ -56,7 +56,7 @@ impl User {
|
||||||
|
|
||||||
password_hash: Vec::new(),
|
password_hash: Vec::new(),
|
||||||
salt: crypto::get_random_64(),
|
salt: crypto::get_random_64(),
|
||||||
password_iterations: CONFIG.password_iterations,
|
password_iterations: CONFIG.password_iterations(),
|
||||||
|
|
||||||
security_stamp: crate::util::get_uuid(),
|
security_stamp: crate::util::get_uuid(),
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ impl Invitation {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take(mail: &str, conn: &DbConn) -> bool {
|
pub fn take(mail: &str, conn: &DbConn) -> bool {
|
||||||
CONFIG.invitations_allowed
|
CONFIG.invitations_allowed()
|
||||||
&& match Self::find_by_mail(mail, &conn) {
|
&& match Self::find_by_mail(mail, &conn) {
|
||||||
Some(invitation) => invitation.delete(&conn).is_ok(),
|
Some(invitation) => invitation.delete(&conn).is_ok(),
|
||||||
None => false,
|
None => false,
|
||||||
|
|
|
@ -6,8 +6,8 @@ use native_tls::{Protocol, TlsConnector};
|
||||||
|
|
||||||
use crate::api::EmptyResult;
|
use crate::api::EmptyResult;
|
||||||
use crate::auth::{encode_jwt, generate_invite_claims};
|
use crate::auth::{encode_jwt, generate_invite_claims};
|
||||||
|
use crate::config::MailConfig;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::MailConfig;
|
|
||||||
use crate::CONFIG;
|
use crate::CONFIG;
|
||||||
|
|
||||||
fn mailer(config: &MailConfig) -> SmtpTransport {
|
fn mailer(config: &MailConfig) -> SmtpTransport {
|
||||||
|
@ -85,7 +85,7 @@ pub fn send_invite(
|
||||||
let (subject, body) = get_text(
|
let (subject, body) = get_text(
|
||||||
"email/send_org_invite",
|
"email/send_org_invite",
|
||||||
json!({
|
json!({
|
||||||
"url": CONFIG.domain,
|
"url": CONFIG.domain(),
|
||||||
"org_id": org_id.unwrap_or("_".to_string()),
|
"org_id": org_id.unwrap_or("_".to_string()),
|
||||||
"org_user_id": org_user_id.unwrap_or("_".to_string()),
|
"org_user_id": org_user_id.unwrap_or("_".to_string()),
|
||||||
"email": address,
|
"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(
|
let (subject, body) = get_text(
|
||||||
"email/invite_accepted",
|
"email/invite_accepted",
|
||||||
json!({
|
json!({
|
||||||
"url": CONFIG.domain,
|
"url": CONFIG.domain(),
|
||||||
"email": new_user_email,
|
"email": new_user_email,
|
||||||
"org_name": org_name,
|
"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(
|
let (subject, body) = get_text(
|
||||||
"email/invite_confirmed",
|
"email/invite_confirmed",
|
||||||
json!({
|
json!({
|
||||||
"url": CONFIG.domain,
|
"url": CONFIG.domain(),
|
||||||
"org_name": org_name,
|
"org_name": org_name,
|
||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
|
|
239
src/main.rs
239
src/main.rs
|
@ -20,7 +20,6 @@ extern crate derive_more;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate num_derive;
|
extern crate num_derive;
|
||||||
|
|
||||||
use handlebars::Handlebars;
|
|
||||||
use rocket::{fairing::AdHoc, Rocket};
|
use rocket::{fairing::AdHoc, Rocket};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -32,11 +31,14 @@ use std::{
|
||||||
mod error;
|
mod error;
|
||||||
mod api;
|
mod api;
|
||||||
mod auth;
|
mod auth;
|
||||||
|
mod config;
|
||||||
mod crypto;
|
mod crypto;
|
||||||
mod db;
|
mod db;
|
||||||
mod mail;
|
mod mail;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
pub use config::CONFIG;
|
||||||
|
|
||||||
fn init_rocket() -> Rocket {
|
fn init_rocket() -> Rocket {
|
||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.mount("/", api::web_routes())
|
.mount("/", api::web_routes())
|
||||||
|
@ -68,7 +70,7 @@ mod migrations {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if CONFIG.extended_logging {
|
if CONFIG.extended_logging() {
|
||||||
init_logging().ok();
|
init_logging().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +101,7 @@ fn init_logging() -> Result<(), fern::InitError> {
|
||||||
.level_for("multipart", log::LevelFilter::Info)
|
.level_for("multipart", log::LevelFilter::Info)
|
||||||
.chain(std::io::stdout());
|
.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)?);
|
logger = logger.chain(fern::log_file(log_file)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +135,8 @@ fn chain_syslog(logger: fern::Dispatch) -> fern::Dispatch {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_db() {
|
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() {
|
if let Some(parent) = path.parent() {
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
@ -153,7 +156,7 @@ fn check_db() {
|
||||||
|
|
||||||
fn check_rsa_keys() {
|
fn check_rsa_keys() {
|
||||||
// If the RSA keys don't exist, try to create them
|
// 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...");
|
info!("JWT keys don't exist, checking if OpenSSL is available...");
|
||||||
|
|
||||||
Command::new("openssl").arg("version").output().unwrap_or_else(|_| {
|
Command::new("openssl").arg("version").output().unwrap_or_else(|_| {
|
||||||
|
@ -166,7 +169,7 @@ fn check_rsa_keys() {
|
||||||
let mut success = Command::new("openssl")
|
let mut success = Command::new("openssl")
|
||||||
.arg("genrsa")
|
.arg("genrsa")
|
||||||
.arg("-out")
|
.arg("-out")
|
||||||
.arg(&CONFIG.private_rsa_key_pem)
|
.arg(&CONFIG.private_rsa_key_pem())
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create private pem file")
|
.expect("Failed to create private pem file")
|
||||||
.status
|
.status
|
||||||
|
@ -175,11 +178,11 @@ fn check_rsa_keys() {
|
||||||
success &= Command::new("openssl")
|
success &= Command::new("openssl")
|
||||||
.arg("rsa")
|
.arg("rsa")
|
||||||
.arg("-in")
|
.arg("-in")
|
||||||
.arg(&CONFIG.private_rsa_key_pem)
|
.arg(&CONFIG.private_rsa_key_pem())
|
||||||
.arg("-outform")
|
.arg("-outform")
|
||||||
.arg("DER")
|
.arg("DER")
|
||||||
.arg("-out")
|
.arg("-out")
|
||||||
.arg(&CONFIG.private_rsa_key)
|
.arg(&CONFIG.private_rsa_key())
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create private der file")
|
.expect("Failed to create private der file")
|
||||||
.status
|
.status
|
||||||
|
@ -188,14 +191,14 @@ fn check_rsa_keys() {
|
||||||
success &= Command::new("openssl")
|
success &= Command::new("openssl")
|
||||||
.arg("rsa")
|
.arg("rsa")
|
||||||
.arg("-in")
|
.arg("-in")
|
||||||
.arg(&CONFIG.private_rsa_key)
|
.arg(&CONFIG.private_rsa_key())
|
||||||
.arg("-inform")
|
.arg("-inform")
|
||||||
.arg("DER")
|
.arg("DER")
|
||||||
.arg("-RSAPublicKey_out")
|
.arg("-RSAPublicKey_out")
|
||||||
.arg("-outform")
|
.arg("-outform")
|
||||||
.arg("DER")
|
.arg("DER")
|
||||||
.arg("-out")
|
.arg("-out")
|
||||||
.arg(&CONFIG.public_rsa_key)
|
.arg(&CONFIG.public_rsa_key())
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to create public der file")
|
.expect("Failed to create public der file")
|
||||||
.status
|
.status
|
||||||
|
@ -211,11 +214,11 @@ fn check_rsa_keys() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_web_vault() {
|
fn check_web_vault() {
|
||||||
if !CONFIG.web_vault_enabled {
|
if !CONFIG.web_vault_enabled() {
|
||||||
return;
|
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() {
|
if !index_path.exists() {
|
||||||
error!("Web vault is not found. Please follow the steps in the README to install it");
|
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!("\\--------------------------------------------------------------------/");
|
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
|
// Web Headers and caching
|
||||||
//
|
//
|
||||||
use rocket::fairing::{Fairing, Info, Kind};
|
use rocket::fairing::{Fairing, Info, Kind};
|
||||||
use rocket::{Request, Response};
|
|
||||||
use rocket::response::{self, Responder};
|
use rocket::response::{self, Responder};
|
||||||
|
use rocket::{Request, Response};
|
||||||
|
|
||||||
pub struct AppHeaders();
|
pub struct AppHeaders();
|
||||||
|
|
||||||
|
|
Laden …
In neuem Issue referenzieren