mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-01-25 21:53:16 -05:00
initial db layer work for authentication (#26)
This commit is contained in:
parent
aa81eae65a
commit
75f233da79
282
Cargo.lock
generated
282
Cargo.lock
generated
@ -16,6 +16,15 @@ name = "arc-swap"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "argon2rs"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "array-macro"
|
||||
version = "1.0.2"
|
||||
@ -69,6 +78,15 @@ name = "bitflags"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "blake2-rfc"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "build_const"
|
||||
version = "0.2.1"
|
||||
@ -88,6 +106,14 @@ dependencies = [
|
||||
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargon"
|
||||
version = "0.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.22"
|
||||
@ -116,6 +142,11 @@ dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.2.3"
|
||||
@ -153,6 +184,15 @@ dependencies = [
|
||||
"smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.6.1"
|
||||
@ -162,6 +202,20 @@ dependencies = [
|
||||
"crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.5.2"
|
||||
@ -175,6 +229,14 @@ dependencies = [
|
||||
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.5.0"
|
||||
@ -202,6 +264,11 @@ dependencies = [
|
||||
"xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "docopt"
|
||||
version = "1.0.1"
|
||||
@ -219,6 +286,11 @@ name = "dtoa"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.6"
|
||||
@ -255,6 +327,14 @@ dependencies = [
|
||||
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-chain"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.2"
|
||||
@ -330,6 +410,11 @@ dependencies = [
|
||||
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gcc"
|
||||
version = "0.3.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.1.12"
|
||||
@ -474,6 +559,14 @@ dependencies = [
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.2"
|
||||
@ -526,6 +619,29 @@ dependencies = [
|
||||
"crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libpasta"
|
||||
version = "0.1.0-rc0"
|
||||
source = "git+https://github.com/scottlamb/libpasta?branch=pr-default-ring#074ce9d815b1d5e9639b3a8b4b1be5051fe5f074"
|
||||
dependencies = [
|
||||
"argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cargon 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring-pwhash 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_mcf 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_yaml 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.9.3"
|
||||
@ -541,6 +657,11 @@ name = "linked-hash-map"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.1.3"
|
||||
@ -680,10 +801,12 @@ dependencies = [
|
||||
name = "moonfire-db"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libpasta 0.1.0-rc0 (git+https://github.com/scottlamb/libpasta?branch=pr-default-ring)",
|
||||
"log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"moonfire-base 0.0.1",
|
||||
@ -997,6 +1120,16 @@ dependencies = [
|
||||
"proc-macro2 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.4.3"
|
||||
@ -1024,6 +1157,25 @@ name = "rand_core"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.40"
|
||||
@ -1099,6 +1251,38 @@ dependencies = [
|
||||
"uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring-pwhash"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rpassword"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.14.0"
|
||||
@ -1110,11 +1294,28 @@ dependencies = [
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-crypto"
|
||||
version = "0.2.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
@ -1147,6 +1348,11 @@ name = "scoped-tls"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "scoped_threadpool"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "0.3.3"
|
||||
@ -1190,6 +1396,14 @@ name = "serde"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.75"
|
||||
@ -1205,11 +1419,27 @@ name = "serde_json"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_mcf"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.5.3"
|
||||
@ -1221,6 +1451,17 @@ dependencies = [
|
||||
"url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.1.4"
|
||||
@ -1600,6 +1841,11 @@ dependencies = [
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "1.7.1"
|
||||
@ -1703,10 +1949,19 @@ name = "xi-unicode"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c"
|
||||
"checksum aho-corasick 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c6d463cbe7ed28720b5b489e7c083eeb8f90d08be2a0d6bb9e1ffea9ce1afa"
|
||||
"checksum arc-swap 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f753d9b7c861f9f426fdb10479e35ffef7eaa4359d7c3595610645459df8849a"
|
||||
"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392"
|
||||
"checksum array-macro 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8b1b1a00de235e9f2cc0e650423dc249d875c116a5934188c08fdd0c02d840ef"
|
||||
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
|
||||
"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a"
|
||||
@ -1714,27 +1969,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "85415d2594767338a74a30c1d370b2f3262ec1b4ed2d7bba5b3faf4de40467d9"
|
||||
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
|
||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||
"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
|
||||
"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
|
||||
"checksum byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8389c509ec62b9fe8eca58c502a0acaf017737355615243496cde4994f8fa4f9"
|
||||
"checksum bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e178b8e0e239e844b083d5a0d4a156b2654e67f9f80144d48398fcd736a24fb8"
|
||||
"checksum cargon 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "828332c08f2453409faf99af40e4a9e26c9b28790a9445c135e34c7996a25fb3"
|
||||
"checksum cc 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4a6007c146fdd28d4512a794b07ffe9d8e89e6bf86e2e0c4ddff2e1fb54a0007"
|
||||
"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3"
|
||||
"checksum chrono 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e48d85528df61dc964aa43c5f6ca681a19cfa74939b2348d204bd08a981f2fb0"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
|
||||
"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67"
|
||||
"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d"
|
||||
"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
|
||||
"checksum crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6c0a94250b0278d7fc5a894c3d276b11ea164edc8bf8feb10ca1ea517b44a649"
|
||||
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
|
||||
"checksum crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3486aefc4c0487b9cb52372c97df0a48b8c249514af1ee99703bf70d2f2ceda1"
|
||||
"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
|
||||
"checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9"
|
||||
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
|
||||
"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015"
|
||||
"checksum cursive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7024f124e4645e285879af7b54ea466dabfed91d16c3d8049df7a4e4c36d8bd"
|
||||
"checksum data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67df0571a74bf0d97fb8b2ed22abdd9a48475c96bd327db968b7d9cace99655e"
|
||||
"checksum docopt 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d60c92df70dfaaabecc14b409fd79f55ba0f247780529db1d73bfa601e1d3ac0"
|
||||
"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd"
|
||||
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
|
||||
"checksum encoding_rs 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2a91912d6f37c6a8fef8a2316a862542d036f13c923ad518b5aca7bcaac7544c"
|
||||
"checksum enum-map 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "caa1769f019df7ccd8f9a741d2d608309688d0f1bd8a8747c14ac993660c761c"
|
||||
"checksum enum-map-derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f915c8ef505ce27b6fa51515463938aa2e9135081fefc93aef786539a646a365"
|
||||
"checksum enumset 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "81193cae36d896445dae43f89be4fd6a171aad6bd674acb7e7074d50bbce6940"
|
||||
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
|
||||
"checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9"
|
||||
"checksum failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "946d0e98a50d9831f5d589038d2ca7f8f455b1c21028c0db0e84116a12696426"
|
||||
"checksum flate2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "37847f133aae7acf82bb9577ccd8bda241df836787642654286e79679826a54b"
|
||||
@ -1745,6 +2009,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
"checksum futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "884dbe32a6ae4cd7da5c6db9b78114449df9953b8d490c9d7e1b51720b922c62"
|
||||
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
|
||||
"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
|
||||
"checksum h2 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a27e7ed946e8335bdf9a191bc1b9b14a03ba822d013d2f58437f4fabcbd7fc2c"
|
||||
"checksum http 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "dca621d0fa606a5ff2850b6e337b57ad6137ee4d67e940449643ff45af6874c6"
|
||||
"checksum http-serve 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393ee8e71920168e84fa80efcd300bf90435118eb037f674aae92e28f82ec5ef"
|
||||
@ -1756,6 +2021,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
|
||||
"checksum indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08173ba1e906efb6538785a8844dd496f5d34f0a2d88038e95195172fc667220"
|
||||
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
|
||||
"checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450"
|
||||
"checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||
@ -1764,8 +2030,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef"
|
||||
"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
|
||||
"checksum libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7d4b4c7aff5bac19b956f693d0ea0eade8066deb092186ae954fa6ba14daab98"
|
||||
"checksum libpasta 0.1.0-rc0 (git+https://github.com/scottlamb/libpasta?branch=pr-default-ring)" = "<none>"
|
||||
"checksum libsqlite3-sys 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d3711dfd91a1081d2458ad2d06ea30a8755256e74038be2ad927d94e1c955ca8"
|
||||
"checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939"
|
||||
"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e"
|
||||
"checksum lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "949826a5ccf18c1b3a7c3d57692778d21768b79e46eb9dd07bfc4c2160036c54"
|
||||
"checksum log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cba860f648db8e6f269df990180c2217f333472b4a6e901e97446858487971e2"
|
||||
"checksum lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d06ff7ff06f729ce5f4e227876cb88d10bc59cd4ae1e09fbb2bde15c850dc21"
|
||||
@ -1808,9 +2076,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum proc-macro2 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "ee5697238f0d893c7f0ecc59c0999f18d2af85e424de441178bcacc9f9e6cf67"
|
||||
"checksum protobuf 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "128a4f37a2df739a567a8685b17f54aa19b9c3c4a6a5b8731e97a419b3db451c"
|
||||
"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5"
|
||||
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
|
||||
"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd"
|
||||
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
|
||||
"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2"
|
||||
"checksum rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b614fe08b6665cb9a231d07ac1364b0ef3cb3698f1239ee0c4c3a88a524f54c8"
|
||||
"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356"
|
||||
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
|
||||
"checksum reexport-proc-macro 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "438fe63770eda15baf98e30b4d27ada49b932866307fa04fec24d9043fe63324"
|
||||
"checksum reffers 0.4.2 (git+https://github.com/diwic/reffers-rs)" = "<none>"
|
||||
@ -1819,22 +2090,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a"
|
||||
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
|
||||
"checksum reqwest 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "738769ec83daf6c1929dc9dae7d69ed3779b55ae5c356e989dcd3aa677d8486e"
|
||||
"checksum ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f7d28b30a72c01b458428e0ae988d4149c20d902346902be881e3edc4bb325c"
|
||||
"checksum ring-pwhash 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "394374d86779b376a2a0ff4fa4d080f3993420d3a2c2342492df9852ff78c053"
|
||||
"checksum rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d127299b02abda51634f14025aec43ae87a7aa7a95202b6a868ec852607d1451"
|
||||
"checksum rusqlite 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9d9118f1ce84d8d0b67f9779936432fb42bb620cef2122409d786892cce9a3c"
|
||||
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
|
||||
"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
|
||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum ryu 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e7c066b8e2923f05d4718a06d2622f189ff362bc642bfade6c6629f0440f3827"
|
||||
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
|
||||
"checksum schannel 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "dc1fabf2a7b6483a141426e1afd09ad543520a77ac49bd03c286e7696ccfd77f"
|
||||
"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
|
||||
"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
|
||||
"checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332"
|
||||
"checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "22d340507cea0b7e6632900a176101fea959c7065d93ba555072da90aaaafc87"
|
||||
"checksum serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "adb6e51a6b3696b301bc221d785f898b4457c619b51d7ce195a6d20baecb37b3"
|
||||
"checksum serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "234fc8b737737b148ccd625175fc6390f5e4dacfdaa543cb93a3430d984a9119"
|
||||
"checksum serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "44dd2cfde475037451fa99b7e5df77aa3cfd1536575fa8e7a538ab36dcde49ae"
|
||||
"checksum serde_mcf 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8e71f1e2ddef2fb0451a9fcf2d975d05aac7fb6e452de01da13bff1b4c46cd"
|
||||
"checksum serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aaed41d9fb1e2f587201b863356590c90c1157495d811430a0c0325fe8169650"
|
||||
"checksum serde_yaml 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8099d3df28273c99a1728190c7a9f19d444c941044f64adf986bee7ec53051"
|
||||
"checksum signal-hook 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bfc9a251608ddaef559cd5b08b539cbda8c36d0efe0506f9a864765d75dbd665"
|
||||
"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
|
||||
"checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d"
|
||||
@ -1876,6 +2156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
||||
"checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae"
|
||||
"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6"
|
||||
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
|
||||
"checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363"
|
||||
@ -1891,3 +2172,4 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||
"checksum xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12ea8eda4b1eb72f02d148402e23832d56a33f55d8c1b2d5bcdde91d79d47cb1"
|
||||
"checksum yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95acf0db5515d07da9965ec0e0ba6cc2d825e2caeb7303b66ca441729801254e"
|
||||
|
@ -11,10 +11,12 @@ nightly = []
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
blake2-rfc = "0.2.18"
|
||||
failure = "0.1.1"
|
||||
fnv = "1.0"
|
||||
lazy_static = "1.0"
|
||||
libc = "0.2"
|
||||
libpasta = { git = "https://github.com/scottlamb/libpasta", branch = "pr-default-ring" }
|
||||
log = "0.4"
|
||||
lru-cache = "0.1"
|
||||
moonfire-base = { path = "../base" }
|
||||
|
897
db/auth.rs
Normal file
897
db/auth.rs
Normal file
@ -0,0 +1,897 @@
|
||||
// This file is part of Moonfire NVR, a security camera digital video recorder.
|
||||
// Copyright (C) 2018 Scott Lamb <slamb@slamb.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations including
|
||||
// the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for all
|
||||
// of the code used other than OpenSSL. If you modify file(s) with this
|
||||
// exception, you may extend this exception to your version of the
|
||||
// file(s), but you are not obligated to do so. If you do not wish to do
|
||||
// so, delete this exception statement from your version. If you delete
|
||||
// this exception statement from all source files in the program, then
|
||||
// also delete it here.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use base::strutil;
|
||||
use blake2_rfc::blake2b::blake2b;
|
||||
use failure::Error;
|
||||
use fnv::FnvHashMap;
|
||||
use libpasta;
|
||||
use rusqlite::{Connection, Transaction};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::net::IpAddr;
|
||||
|
||||
lazy_static! {
|
||||
static ref PASTA_CONFIG: libpasta::Config = if cfg!(test) {
|
||||
// In test builds, use bcrypt with the cost turned down. Password hash functions are
|
||||
// designed to be slow; when run un-optimized, they're unpleasantly slow with default
|
||||
// settings. Security doesn't matter for cfg(test).
|
||||
libpasta::Config::with_primitive(libpasta::primitives::Bcrypt::new(2))
|
||||
} else {
|
||||
libpasta::Config::default()
|
||||
};
|
||||
}
|
||||
|
||||
enum UserFlags {
|
||||
Disabled = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct User {
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
pub flags: i32,
|
||||
password_hash: Option<String>,
|
||||
pub password_id: i32,
|
||||
pub password_failure_count: i64,
|
||||
pub unix_uid: Option<i32>,
|
||||
|
||||
/// True iff this `User` has changed since the last flush.
|
||||
/// Only a couple things are flushed lazily: `password_failure_count` and (on upgrade to a new
|
||||
/// algorithm) `password_hash`.
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn change(&self) -> UserChange {
|
||||
UserChange {
|
||||
id: Some(self.id),
|
||||
username: self.username.to_string(),
|
||||
flags: self.flags,
|
||||
set_password_hash: None,
|
||||
unix_uid: self.unix_uid,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_password(&self) -> bool { self.password_hash.is_some() }
|
||||
fn disabled(&self) -> bool { (self.flags & UserFlags::Disabled as i32) != 0 }
|
||||
}
|
||||
|
||||
/// A change to a user.
|
||||
///
|
||||
/// * an insertion returned via `UserChange::add_user`.
|
||||
/// * an update returned via `User::change`.
|
||||
///
|
||||
/// Apply via `DatabaseGuard::apply_user_change` (which internally calls `auth::State::apply`).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserChange {
|
||||
id: Option<i32>,
|
||||
pub username: String,
|
||||
pub flags: i32,
|
||||
set_password_hash: Option<Option<String>>,
|
||||
pub unix_uid: Option<i32>,
|
||||
}
|
||||
|
||||
impl UserChange {
|
||||
pub fn add_user(username: String) -> Self {
|
||||
UserChange {
|
||||
id: None,
|
||||
username,
|
||||
flags: 0,
|
||||
set_password_hash: None,
|
||||
unix_uid: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_password(&mut self, pwd: String) {
|
||||
self.set_password_hash = Some(Some(PASTA_CONFIG.hash_password(&pwd)));
|
||||
}
|
||||
|
||||
pub fn clear_password(&mut self) {
|
||||
self.set_password_hash = Some(None);
|
||||
}
|
||||
|
||||
pub fn disable(&mut self) {
|
||||
self.flags |= UserFlags::Disabled as i32;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Request {
|
||||
pub when_sec: Option<i64>,
|
||||
pub user_agent: Option<Vec<u8>>,
|
||||
pub addr: Option<IpAddr>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
fn addr_buf(&self) -> Option<IpAddrBuf> {
|
||||
match self.addr {
|
||||
None => None,
|
||||
Some(IpAddr::V4(ref a)) => Some(IpAddrBuf::V4(a.octets())),
|
||||
Some(IpAddr::V6(ref a)) => Some(IpAddrBuf::V6(a.octets())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum IpAddrBuf {
|
||||
V4([u8; 4]),
|
||||
V6([u8; 16]),
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for IpAddrBuf {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
match *self {
|
||||
IpAddrBuf::V4(ref s) => &s[..],
|
||||
IpAddrBuf::V6(ref s) => &s[..],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FromSqlIpAddr(Option<IpAddr>);
|
||||
|
||||
impl rusqlite::types::FromSql for FromSqlIpAddr {
|
||||
fn column_result(value: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
|
||||
use rusqlite::types::ValueRef;
|
||||
match value {
|
||||
ValueRef::Null => Ok(FromSqlIpAddr(None)),
|
||||
ValueRef::Blob(ref b) => {
|
||||
match b.len() {
|
||||
4 => {
|
||||
let mut buf = [0u8; 4];
|
||||
buf.copy_from_slice(b);
|
||||
Ok(FromSqlIpAddr(Some(buf.into())))
|
||||
},
|
||||
16 => {
|
||||
let mut buf = [0u8; 16];
|
||||
buf.copy_from_slice(b);
|
||||
Ok(FromSqlIpAddr(Some(buf.into())))
|
||||
},
|
||||
_ => Err(rusqlite::types::FromSqlError::InvalidType),
|
||||
}
|
||||
},
|
||||
_ => Err(rusqlite::types::FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SessionFlags {
|
||||
HttpOnly = 1,
|
||||
Secure = 2,
|
||||
SameSite = 4,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum RevocationReason {
|
||||
LoggedOut = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Session {
|
||||
user_id: i32,
|
||||
flags: i32, // bitmask of SessionFlags enum values
|
||||
domain: Vec<u8>,
|
||||
description: Option<String>,
|
||||
|
||||
creation_password_id: Option<i32>,
|
||||
creation: Request,
|
||||
|
||||
revocation: Request,
|
||||
revocation_reason: Option<i32>, // see RevocationReason enum
|
||||
revocation_reason_detail: Option<String>,
|
||||
|
||||
last_use: Request,
|
||||
use_count: i32,
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
/// A raw session id (not base64-encoded). Sensitive. Never stored in the database.
|
||||
pub struct RawSessionId([u8; 48]);
|
||||
|
||||
impl RawSessionId {
|
||||
pub fn new() -> Self { RawSessionId([0u8; 48]) }
|
||||
|
||||
fn hash(&self) -> SessionHash {
|
||||
let r = blake2b(32, &[], &self.0[..]);
|
||||
let mut h = SessionHash([0u8; 32]);
|
||||
h.0.copy_from_slice(r.as_bytes());
|
||||
h
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for RawSessionId {
|
||||
fn as_ref(&self) -> &[u8] { &self.0[..] }
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for RawSessionId {
|
||||
fn as_mut(&mut self) -> &mut [u8] { &mut self.0[..] }
|
||||
}
|
||||
|
||||
impl fmt::Debug for RawSessionId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "RawSessionId(\"{}\")", &strutil::hex(&self.0[..]))
|
||||
}
|
||||
}
|
||||
|
||||
/// Blake2b-256 of the raw (not base64-encoded) 48-byte session id.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SessionHash([u8; 32]);
|
||||
|
||||
impl fmt::Debug for SessionHash {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "SessionHash(\"{}\")", &strutil::hex(&self.0[..]))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct State {
|
||||
users_by_id: BTreeMap<i32, User>,
|
||||
users_by_name: BTreeMap<String, i32>,
|
||||
|
||||
/// Some of the sessions stored in the database.
|
||||
/// Guaranteed to contain all "dirty" sessions (ones with unflushed changes); may contain
|
||||
/// others.
|
||||
///
|
||||
/// TODO: Add eviction of clean sessions. Keep a linked hash set of clean session hashes and
|
||||
/// evict the oldest when its size exceeds a threshold. Or just evict everything on every flush
|
||||
/// (and accept more frequent database accesses).
|
||||
sessions: FnvHashMap<SessionHash, Session>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn init(conn: &Connection) -> Result<Self, Error> {
|
||||
let mut state = State {
|
||||
users_by_id: BTreeMap::new(),
|
||||
users_by_name: BTreeMap::new(),
|
||||
sessions: FnvHashMap::default(),
|
||||
};
|
||||
let mut stmt = conn.prepare(r#"
|
||||
select
|
||||
id,
|
||||
username,
|
||||
flags,
|
||||
password_hash,
|
||||
password_id,
|
||||
password_failure_count,
|
||||
unix_uid
|
||||
from
|
||||
user
|
||||
"#)?;
|
||||
let mut rows = stmt.query(&[])?;
|
||||
while let Some(row) = rows.next() {
|
||||
let row = row?;
|
||||
let id = row.get_checked(0)?;
|
||||
let name: String = row.get_checked(1)?;
|
||||
state.users_by_id.insert(id, User {
|
||||
id,
|
||||
username: name.clone(),
|
||||
flags: row.get_checked(2)?,
|
||||
password_hash: row.get_checked(3)?,
|
||||
password_id: row.get_checked(4)?,
|
||||
password_failure_count: row.get_checked(5)?,
|
||||
unix_uid: row.get_checked(6)?,
|
||||
dirty: false,
|
||||
});
|
||||
state.users_by_name.insert(name, id);
|
||||
}
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub fn apply(&mut self, conn: &Connection, change: UserChange) -> Result<&User, Error> {
|
||||
if let Some(id) = change.id {
|
||||
self.update_user(conn, id, change)
|
||||
} else {
|
||||
self.add_user(conn, change)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn users_by_id(&self) -> &BTreeMap<i32, User> { &self.users_by_id }
|
||||
|
||||
fn update_user(&mut self, conn: &Connection, id: i32, change: UserChange)
|
||||
-> Result<&User, Error> {
|
||||
let mut stmt = conn.prepare_cached(r#"
|
||||
update user
|
||||
set
|
||||
username = :username,
|
||||
password_hash = :password_hash,
|
||||
password_id = :password_id,
|
||||
password_failure_count = :password_failure_count,
|
||||
flags = :flags,
|
||||
unix_uid = :unix_uid
|
||||
where
|
||||
id = :id
|
||||
"#)?;
|
||||
let e = self.users_by_id.entry(id);
|
||||
let e = match e {
|
||||
::std::collections::btree_map::Entry::Vacant(_) => panic!("missing uid {}!", id),
|
||||
::std::collections::btree_map::Entry::Occupied(e) => e,
|
||||
};
|
||||
{
|
||||
let (phash, pid, pcount) = match change.set_password_hash.as_ref() {
|
||||
None => {
|
||||
let u = e.get();
|
||||
(&u.password_hash, u.password_id, u.password_failure_count)
|
||||
},
|
||||
Some(h) => (h, e.get().password_id + 1, 0),
|
||||
};
|
||||
stmt.execute_named(&[
|
||||
(":username", &&change.username[..]),
|
||||
(":password_hash", phash),
|
||||
(":password_id", &pid),
|
||||
(":password_failure_count", &pcount),
|
||||
(":flags", &change.flags),
|
||||
(":unix_uid", &change.unix_uid),
|
||||
(":id", &id),
|
||||
])?;
|
||||
}
|
||||
let u = e.into_mut();
|
||||
u.username = change.username;
|
||||
if let Some(h) = change.set_password_hash {
|
||||
u.password_hash = h;
|
||||
u.password_id += 1;
|
||||
u.password_failure_count = 0;
|
||||
}
|
||||
u.flags = change.flags;
|
||||
u.unix_uid = change.unix_uid;
|
||||
Ok(u)
|
||||
}
|
||||
|
||||
fn add_user(&mut self, conn: &Connection, change: UserChange) -> Result<&User, Error> {
|
||||
let mut stmt = conn.prepare_cached(r#"
|
||||
insert into user (username, password_hash, flags, unix_uid)
|
||||
values (:username, :password_hash, :flags, :unix_uid)
|
||||
"#)?;
|
||||
let password_hash = change.set_password_hash.unwrap_or(None);
|
||||
stmt.execute_named(&[
|
||||
(":username", &&change.username[..]),
|
||||
(":password_hash", &password_hash),
|
||||
(":flags", &change.flags),
|
||||
(":unix_uid", &change.unix_uid),
|
||||
])?;
|
||||
let id = conn.last_insert_rowid() as i32;
|
||||
self.users_by_name.insert(change.username.clone(), id);
|
||||
let e = self.users_by_id.entry(id);
|
||||
let e = match e {
|
||||
::std::collections::btree_map::Entry::Vacant(e) => e,
|
||||
::std::collections::btree_map::Entry::Occupied(_) => panic!("uid {} conflict!", id),
|
||||
};
|
||||
Ok(e.insert(User {
|
||||
id,
|
||||
username: change.username,
|
||||
flags: change.flags,
|
||||
password_hash,
|
||||
password_id: 0,
|
||||
password_failure_count: 0,
|
||||
unix_uid: change.unix_uid,
|
||||
dirty: false,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn delete_user(&mut self, conn: &mut Connection, id: i32) -> Result<(), Error> {
|
||||
let tx = conn.transaction()?;
|
||||
tx.execute("delete from user_session where user_id = ?", &[&id])?;
|
||||
{
|
||||
let mut user_stmt = tx.prepare_cached("delete from user where id = ?")?;
|
||||
if user_stmt.execute(&[&id])? != 1 {
|
||||
bail!("user {} not found", id);
|
||||
}
|
||||
}
|
||||
tx.commit()?;
|
||||
let name = self.users_by_id.remove(&id).unwrap().username;
|
||||
self.users_by_name.remove(&name).unwrap();
|
||||
self.sessions.retain(|_k, ref mut v| v.user_id != id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn login_by_password(&mut self, conn: &Connection, req: Request, username: &str,
|
||||
password: String, domain: Vec<u8>, session_flags: i32)
|
||||
-> Result<(RawSessionId, &Session), Error> {
|
||||
let id = match self.users_by_name.get(username) {
|
||||
None => bail!("no such user {:?}", username),
|
||||
Some(&id) => id,
|
||||
};
|
||||
let u = self.users_by_id.get_mut(&id).expect("users_by_name implies users_by_id");
|
||||
if u.disabled() {
|
||||
bail!("user {:?} is disabled", username);
|
||||
}
|
||||
let new_hash = {
|
||||
let hash = match u.password_hash.as_ref() {
|
||||
None => bail!("no password set for user {:?}", username),
|
||||
Some(h) => h,
|
||||
};
|
||||
match PASTA_CONFIG.verify_password_update_hash(hash, &password) {
|
||||
libpasta::HashUpdate::Failed => {
|
||||
u.dirty = true;
|
||||
u.password_failure_count += 1;
|
||||
bail!("incorrect password for user {:?}", username);
|
||||
},
|
||||
libpasta::HashUpdate::Verified(new_pwd) => new_pwd,
|
||||
}
|
||||
};
|
||||
if let Some(h) = new_hash {
|
||||
u.password_hash = Some(h);
|
||||
u.dirty = true;
|
||||
}
|
||||
let password_id = u.password_id;
|
||||
State::make_session(conn, req, u, domain, Some(password_id), session_flags,
|
||||
&mut self.sessions)
|
||||
}
|
||||
|
||||
fn make_session<'s>(conn: &Connection, creation: Request, user: &mut User, domain: Vec<u8>,
|
||||
creation_password_id: Option<i32>, flags: i32,
|
||||
sessions: &'s mut FnvHashMap<SessionHash, Session>)
|
||||
-> Result<(RawSessionId, &'s Session), Error> {
|
||||
let mut session_id = RawSessionId::new();
|
||||
::openssl::rand::rand_bytes(&mut session_id.0).unwrap();
|
||||
let mut seed = [0u8; 32];
|
||||
::openssl::rand::rand_bytes(&mut seed).unwrap();
|
||||
let hash = session_id.hash();
|
||||
let mut stmt = conn.prepare_cached(r#"
|
||||
insert into user_session (session_id_hash, user_id, seed, flags, domain,
|
||||
creation_password_id, creation_time_sec,
|
||||
creation_user_agent, creation_peer_addr)
|
||||
values (:session_id_hash, :user_id, :seed, :flags, :domain,
|
||||
:creation_password_id, :creation_time_sec,
|
||||
:creation_user_agent, :creation_peer_addr)
|
||||
"#)?;
|
||||
let addr = creation.addr_buf();
|
||||
let addr: Option<&[u8]> = addr.as_ref().map(|a| a.as_ref());
|
||||
stmt.execute_named(&[
|
||||
(":session_id_hash", &&hash.0[..]),
|
||||
(":user_id", &user.id),
|
||||
(":seed", &&seed[..]),
|
||||
(":flags", &flags),
|
||||
(":domain", &domain),
|
||||
(":creation_password_id", &creation_password_id),
|
||||
(":creation_time_sec", &creation.when_sec),
|
||||
(":creation_user_agent", &creation.user_agent),
|
||||
(":creation_peer_addr", &addr),
|
||||
])?;
|
||||
let e = match sessions.entry(hash) {
|
||||
::std::collections::hash_map::Entry::Occupied(_) => panic!("duplicate session hash!"),
|
||||
::std::collections::hash_map::Entry::Vacant(e) => e,
|
||||
};
|
||||
let session = e.insert(Session {
|
||||
user_id: user.id,
|
||||
flags,
|
||||
domain,
|
||||
creation_password_id,
|
||||
creation,
|
||||
..Default::default()
|
||||
});
|
||||
Ok((session_id, session))
|
||||
}
|
||||
|
||||
pub fn authenticate_session(&mut self, conn: &Connection, req: Request,
|
||||
session: &RawSessionId) -> Result<(SessionHash, &User), Error> {
|
||||
let hash = session.hash();
|
||||
let s = match self.sessions.entry(hash) {
|
||||
::std::collections::hash_map::Entry::Occupied(e) => e.into_mut(),
|
||||
::std::collections::hash_map::Entry::Vacant(e) => e.insert(lookup_session(conn, hash)?),
|
||||
};
|
||||
let u = match self.users_by_id.get(&s.user_id) {
|
||||
None => bail!("session references nonexistent user!"),
|
||||
Some(u) => u,
|
||||
};
|
||||
if let Some(r) = s.revocation_reason {
|
||||
bail!("session is no longer valid (reason={})", r);
|
||||
}
|
||||
s.last_use = req;
|
||||
s.use_count += 1;
|
||||
s.dirty = true;
|
||||
if u.disabled() {
|
||||
bail!("user {:?} is disabled", &u.username);
|
||||
}
|
||||
Ok((hash, u))
|
||||
}
|
||||
|
||||
pub fn revoke_session(&mut self, conn: &Connection, reason: RevocationReason,
|
||||
detail: Option<String>, req: Request, hash: SessionHash)
|
||||
-> Result<(), Error> {
|
||||
let s = match self.sessions.entry(hash) {
|
||||
::std::collections::hash_map::Entry::Occupied(e) => e.into_mut(),
|
||||
::std::collections::hash_map::Entry::Vacant(e) => e.insert(lookup_session(conn, hash)?),
|
||||
};
|
||||
if s.revocation_reason.is_none() {
|
||||
let mut stmt = conn.prepare(r#"
|
||||
update user_session
|
||||
set
|
||||
revocation_time_sec = ?,
|
||||
revocation_user_agent = ?,
|
||||
revocation_peer_addr = ?,
|
||||
revocation_reason = ?,
|
||||
revocation_reason_detail = ?
|
||||
where
|
||||
session_id_hash = ?
|
||||
"#)?;
|
||||
let addr = req.addr_buf();
|
||||
let addr: Option<&[u8]> = addr.as_ref().map(|a| a.as_ref());
|
||||
stmt.execute(&[
|
||||
&req.when_sec,
|
||||
&req.user_agent,
|
||||
&addr,
|
||||
&(reason as i32),
|
||||
&detail,
|
||||
&&hash.0[..],
|
||||
])?;
|
||||
s.revocation = req;
|
||||
s.revocation_reason = Some(reason as i32);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flush(&mut self, tx: &Transaction) -> Result<(), Error> {
|
||||
let mut u_stmt = tx.prepare(r#"
|
||||
update user
|
||||
set
|
||||
password_failure_count = :password_failure_count,
|
||||
password_hash = :password_hash
|
||||
where
|
||||
id = :id
|
||||
"#)?;
|
||||
let mut s_stmt = tx.prepare(r#"
|
||||
update user_session
|
||||
set
|
||||
last_use_time_sec = :last_use_time_sec,
|
||||
last_use_user_agent = :last_use_user_agent,
|
||||
last_use_peer_addr = :last_use_peer_addr,
|
||||
use_count = :use_count
|
||||
where
|
||||
session_id_hash = :hash
|
||||
"#)?;
|
||||
for (&id, u) in &self.users_by_id {
|
||||
if !u.dirty {
|
||||
continue;
|
||||
}
|
||||
info!("flushing user with hash: {}", u.password_hash.as_ref().unwrap());
|
||||
u_stmt.execute_named(&[
|
||||
(":password_failure_count", &u.password_failure_count),
|
||||
(":password_hash", &u.password_hash),
|
||||
(":id", &id),
|
||||
])?;
|
||||
}
|
||||
for (_, s) in &self.sessions {
|
||||
if !s.dirty {
|
||||
continue;
|
||||
}
|
||||
let addr = s.last_use.addr_buf();
|
||||
let addr: Option<&[u8]> = addr.as_ref().map(|a| a.as_ref());
|
||||
s_stmt.execute_named(&[
|
||||
(":last_use_time_sec", &s.last_use.when_sec),
|
||||
(":last_use_user_agent", &s.last_use.user_agent),
|
||||
(":last_use_peer_addr", &addr),
|
||||
(":use_count", &s.use_count),
|
||||
])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn post_flush(&mut self) {
|
||||
for (_, u) in &mut self.users_by_id {
|
||||
u.dirty = false;
|
||||
}
|
||||
for (_, s) in &mut self.sessions {
|
||||
s.dirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_session(conn: &Connection, hash: SessionHash) -> Result<Session, Error> {
|
||||
let mut stmt = conn.prepare_cached(r#"
|
||||
select
|
||||
user_id,
|
||||
flags,
|
||||
domain,
|
||||
description,
|
||||
creation_password_id,
|
||||
creation_time_sec,
|
||||
creation_user_agent,
|
||||
creation_peer_addr,
|
||||
revocation_time_sec,
|
||||
revocation_user_agent,
|
||||
revocation_peer_addr,
|
||||
revocation_reason,
|
||||
revocation_reason_detail,
|
||||
last_use_time_sec,
|
||||
last_use_user_agent,
|
||||
last_use_peer_addr,
|
||||
use_count
|
||||
from
|
||||
user_session
|
||||
where
|
||||
session_id_hash = ?
|
||||
"#)?;
|
||||
let mut rows = stmt.query(&[&&hash.0[..]])?;
|
||||
let row = match rows.next() {
|
||||
None => bail!("no such session"),
|
||||
Some(Err(e)) => return Err(e.into()),
|
||||
Some(Ok(r)) => r,
|
||||
};
|
||||
let creation_addr: FromSqlIpAddr = row.get_checked(7)?;
|
||||
let revocation_addr: FromSqlIpAddr = row.get_checked(10)?;
|
||||
let last_use_addr: FromSqlIpAddr = row.get_checked(15)?;
|
||||
Ok(Session {
|
||||
user_id: row.get_checked(0)?,
|
||||
flags: row.get_checked(1)?,
|
||||
domain: row.get_checked(2)?,
|
||||
description: row.get_checked(3)?,
|
||||
creation_password_id: row.get_checked(4)?,
|
||||
creation: Request {
|
||||
when_sec: row.get_checked(5)?,
|
||||
user_agent: row.get_checked(6)?,
|
||||
addr: creation_addr.0,
|
||||
},
|
||||
revocation: Request {
|
||||
when_sec: row.get_checked(8)?,
|
||||
user_agent: row.get_checked(9)?,
|
||||
addr: revocation_addr.0,
|
||||
},
|
||||
revocation_reason: row.get_checked(11)?,
|
||||
revocation_reason_detail: row.get_checked(12)?,
|
||||
last_use: Request {
|
||||
when_sec: row.get_checked(13)?,
|
||||
user_agent: row.get_checked(14)?,
|
||||
addr: last_use_addr.0,
|
||||
},
|
||||
use_count: row.get_checked(16)?,
|
||||
dirty: false,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use db;
|
||||
use rusqlite::Connection;
|
||||
use super::*;
|
||||
use testutil;
|
||||
|
||||
#[test]
|
||||
fn open_empty_db() {
|
||||
testutil::init();
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
db::init(&mut conn).unwrap();
|
||||
State::init(&conn).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_login_use_logout() {
|
||||
testutil::init();
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
db::init(&mut conn).unwrap();
|
||||
let mut state = State::init(&conn).unwrap();
|
||||
let req = Request {
|
||||
when_sec: Some(42),
|
||||
addr: Some(::std::net::IpAddr::V4(::std::net::Ipv4Addr::new(127, 0, 0, 1))),
|
||||
user_agent: Some(b"some ua".to_vec()),
|
||||
};
|
||||
let (uid, mut c) = {
|
||||
let u = state.apply(&conn, UserChange::add_user("slamb".to_owned())).unwrap();
|
||||
(u.id, u.change())
|
||||
};
|
||||
let e = state.login_by_password(&conn, req.clone(), "slamb", "hunter2".to_owned(),
|
||||
b"nvr.example.com".to_vec(), 0).unwrap_err();
|
||||
assert_eq!(format!("{}", e), "no password set for user \"slamb\"");
|
||||
c.set_password("hunter2".to_owned());
|
||||
state.apply(&conn, c).unwrap();
|
||||
let e = state.login_by_password(&conn, req.clone(), "slamb",
|
||||
"hunter3".to_owned(),
|
||||
b"nvr.example.com".to_vec(), 0).unwrap_err();
|
||||
assert_eq!(format!("{}", e), "incorrect password for user \"slamb\"");
|
||||
let sid = {
|
||||
let (sid, s) = state.login_by_password(&conn, req.clone(), "slamb",
|
||||
"hunter2".to_owned(),
|
||||
b"nvr.example.com".to_vec(), 0).unwrap();
|
||||
assert_eq!(s.user_id, uid);
|
||||
sid
|
||||
};
|
||||
let sid_hash = {
|
||||
let (sid_hash, u) = state.authenticate_session(&conn, req.clone(), &sid).unwrap();
|
||||
assert_eq!(u.id, uid);
|
||||
sid_hash
|
||||
};
|
||||
state.revoke_session(&conn, RevocationReason::LoggedOut, None, req.clone(),
|
||||
sid_hash).unwrap();
|
||||
let e = state.authenticate_session(&conn, req.clone(), &sid).unwrap_err();
|
||||
assert_eq!(format!("{}", e), "session is no longer valid (reason=1)");
|
||||
|
||||
// Everything should persist across reload.
|
||||
drop(state);
|
||||
let mut state = State::init(&conn).unwrap();
|
||||
let e = state.authenticate_session(&conn, req, &sid).unwrap_err();
|
||||
assert_eq!(format!("{}", e), "session is no longer valid (reason=1)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn revoke_not_in_cache() {
|
||||
testutil::init();
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
db::init(&mut conn).unwrap();
|
||||
let mut state = State::init(&conn).unwrap();
|
||||
let req = Request {
|
||||
when_sec: Some(42),
|
||||
addr: Some(::std::net::IpAddr::V4(::std::net::Ipv4Addr::new(127, 0, 0, 1))),
|
||||
user_agent: Some(b"some ua".to_vec()),
|
||||
};
|
||||
{
|
||||
let mut c = UserChange::add_user("slamb".to_owned());
|
||||
c.set_password("hunter2".to_owned());
|
||||
state.apply(&conn, c).unwrap();
|
||||
};
|
||||
let sid = {
|
||||
let (sid, _s) = state.login_by_password(&conn, req.clone(), "slamb",
|
||||
"hunter2".to_owned(),
|
||||
b"nvr.example.com".to_vec(), 0).unwrap();
|
||||
sid
|
||||
};
|
||||
state.authenticate_session(&conn, req.clone(), &sid).unwrap();
|
||||
|
||||
// Reload.
|
||||
drop(state);
|
||||
let mut state = State::init(&conn).unwrap();
|
||||
state.revoke_session(&conn, RevocationReason::LoggedOut, None, req.clone(),
|
||||
sid.hash()).unwrap();
|
||||
let e = state.authenticate_session(&conn, req, &sid).unwrap_err();
|
||||
assert_eq!(format!("{}", e), "session is no longer valid (reason=1)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_hash() {
|
||||
// This hash is generated with cost=1 vs the cost=2 of PASTA_CONFIG.
|
||||
let insecure_hash =
|
||||
libpasta::Config::with_primitive(libpasta::primitives::Bcrypt::new(1))
|
||||
.hash_password("hunter2");
|
||||
testutil::init();
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
db::init(&mut conn).unwrap();
|
||||
let mut state = State::init(&conn).unwrap();
|
||||
let mut change = UserChange::add_user("slamb".to_owned());
|
||||
|
||||
// hunter2, in insecure MD5.
|
||||
change.set_password_hash = Some(Some(insecure_hash.clone()));
|
||||
let uid = {
|
||||
let u = state.apply(&conn, change).unwrap();
|
||||
assert_eq!(&insecure_hash, u.password_hash.as_ref().unwrap());
|
||||
u.id
|
||||
};
|
||||
|
||||
let req = Request {
|
||||
when_sec: Some(42),
|
||||
addr: Some(::std::net::IpAddr::V4(::std::net::Ipv4Addr::new(127, 0, 0, 1))),
|
||||
user_agent: Some(b"some ua".to_vec()),
|
||||
};
|
||||
state.login_by_password(&conn, req.clone(), "slamb", "hunter2".to_owned(),
|
||||
b"nvr.example.com".to_vec(), 0).unwrap();
|
||||
let new_hash = {
|
||||
// Password should have been automatically upgraded.
|
||||
let u = state.users_by_id().get(&uid).unwrap();
|
||||
assert!(u.dirty);
|
||||
assert_ne!(u.password_hash.as_ref().unwrap(), &insecure_hash);
|
||||
u.password_hash.as_ref().unwrap().clone()
|
||||
};
|
||||
|
||||
{
|
||||
let tx = conn.transaction().unwrap();
|
||||
state.flush(&tx).unwrap();
|
||||
tx.commit().unwrap();
|
||||
}
|
||||
|
||||
// On reload, the new hash should still be visible.
|
||||
drop(state);
|
||||
let mut state = State::init(&conn).unwrap();
|
||||
{
|
||||
let u = state.users_by_id().get(&uid).unwrap();
|
||||
assert!(!u.dirty);
|
||||
assert_eq!(u.password_hash.as_ref().unwrap(), &new_hash);
|
||||
}
|
||||
|
||||
// Login should still work.
|
||||
state.login_by_password(&conn, req.clone(), "slamb", "hunter2".to_owned(),
|
||||
b"nvr.example.com".to_vec(), 0).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disable() {
|
||||
testutil::init();
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
db::init(&mut conn).unwrap();
|
||||
let mut state = State::init(&conn).unwrap();
|
||||
let req = Request {
|
||||
when_sec: Some(42),
|
||||
addr: Some(::std::net::IpAddr::V4(::std::net::Ipv4Addr::new(127, 0, 0, 1))),
|
||||
user_agent: Some(b"some ua".to_vec()),
|
||||
};
|
||||
let uid = {
|
||||
let mut c = UserChange::add_user("slamb".to_owned());
|
||||
c.set_password("hunter2".to_owned());
|
||||
state.apply(&conn, c).unwrap().id
|
||||
};
|
||||
|
||||
// Get a session for later.
|
||||
let sid = state.login_by_password(&conn, req.clone(), "slamb",
|
||||
"hunter2".to_owned(),
|
||||
b"nvr.example.com".to_vec(), 0).unwrap().0;
|
||||
|
||||
// Disable the user.
|
||||
{
|
||||
let mut c = state.users_by_id().get(&uid).unwrap().change();
|
||||
c.disable();
|
||||
state.apply(&conn, c).unwrap();
|
||||
}
|
||||
|
||||
// Fresh logins shouldn't work.
|
||||
let e = state.login_by_password(&conn, req.clone(), "slamb",
|
||||
"hunter2".to_owned(),
|
||||
b"nvr.example.com".to_vec(), 0).unwrap_err();
|
||||
assert_eq!(format!("{}", e), "user \"slamb\" is disabled");
|
||||
|
||||
// Authenticating existing sessions shouldn't work either.
|
||||
let e = state.authenticate_session(&conn, req.clone(), &sid).unwrap_err();
|
||||
assert_eq!(format!("{}", e), "user \"slamb\" is disabled");
|
||||
|
||||
// The user should still be disabled after reload.
|
||||
drop(state);
|
||||
let mut state = State::init(&conn).unwrap();
|
||||
let e = state.authenticate_session(&conn, req, &sid).unwrap_err();
|
||||
assert_eq!(format!("{}", e), "user \"slamb\" is disabled");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete() {
|
||||
testutil::init();
|
||||
let mut conn = Connection::open_in_memory().unwrap();
|
||||
db::init(&mut conn).unwrap();
|
||||
let mut state = State::init(&conn).unwrap();
|
||||
let req = Request {
|
||||
when_sec: Some(42),
|
||||
addr: Some(::std::net::IpAddr::V4(::std::net::Ipv4Addr::new(127, 0, 0, 1))),
|
||||
user_agent: Some(b"some ua".to_vec()),
|
||||
};
|
||||
let uid = {
|
||||
let mut c = UserChange::add_user("slamb".to_owned());
|
||||
c.set_password("hunter2".to_owned());
|
||||
state.apply(&conn, c).unwrap().id
|
||||
};
|
||||
|
||||
// Get a session for later.
|
||||
let sid = state.login_by_password(&conn, req.clone(), "slamb",
|
||||
"hunter2".to_owned(),
|
||||
b"nvr.example.com".to_vec(), 0).unwrap().0;
|
||||
|
||||
state.delete_user(&mut conn, uid).unwrap();
|
||||
assert!(state.users_by_id().get(&uid).is_none());
|
||||
let e = state.authenticate_session(&conn, req.clone(), &sid).unwrap_err();
|
||||
assert_eq!(format!("{}", e), "no such session");
|
||||
|
||||
// The user should still be deleted after reload.
|
||||
drop(state);
|
||||
let mut state = State::init(&conn).unwrap();
|
||||
assert!(state.users_by_id().get(&uid).is_none());
|
||||
let e = state.authenticate_session(&conn, req.clone(), &sid).unwrap_err();
|
||||
assert_eq!(format!("{}", e), "no such session");
|
||||
}
|
||||
}
|
42
db/db.rs
42
db/db.rs
@ -52,6 +52,7 @@
|
||||
//! A list of mutations is built up in-memory and occasionally flushed to reduce SSD write
|
||||
//! cycles.
|
||||
|
||||
use auth;
|
||||
use base::clock::{self, Clocks};
|
||||
use dir;
|
||||
use failure::Error;
|
||||
@ -301,6 +302,12 @@ impl SampleFileDir {
|
||||
}
|
||||
}
|
||||
|
||||
pub use auth::Request;
|
||||
pub use auth::RawSessionId;
|
||||
pub use auth::Session;
|
||||
pub use auth::User;
|
||||
pub use auth::UserChange;
|
||||
|
||||
/// In-memory state about a camera.
|
||||
#[derive(Debug)]
|
||||
pub struct Camera {
|
||||
@ -559,6 +566,8 @@ pub struct LockedDatabase {
|
||||
/// mode).
|
||||
open_monotonic: recording::Time,
|
||||
|
||||
auth: auth::State,
|
||||
|
||||
sample_file_dirs_by_id: BTreeMap<i32, SampleFileDir>,
|
||||
cameras_by_id: BTreeMap<i32, Camera>,
|
||||
streams_by_id: BTreeMap<i32, Stream>,
|
||||
@ -855,6 +864,7 @@ impl LockedDatabase {
|
||||
bail!("unable to find current open {}", o.id);
|
||||
}
|
||||
}
|
||||
self.auth.flush(&tx)?;
|
||||
tx.commit()?;
|
||||
|
||||
// Process delete_garbage.
|
||||
@ -898,6 +908,7 @@ impl LockedDatabase {
|
||||
// Fix the range.
|
||||
s.range = new_range;
|
||||
}
|
||||
self.auth.post_flush();
|
||||
info!("Flush (why: {}): added {} recordings, deleted {}, marked {} files GCed.",
|
||||
reason, added, deleted, gced);
|
||||
for cb in &self.on_flush {
|
||||
@ -1663,12 +1674,41 @@ impl LockedDatabase {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ---- auth ----
|
||||
|
||||
pub fn users_by_id(&self) -> &BTreeMap<i32, User> { self.auth.users_by_id() }
|
||||
|
||||
pub fn apply_user_change(&mut self, change: UserChange) -> Result<&User, Error> {
|
||||
self.auth.apply(&self.conn, change)
|
||||
}
|
||||
|
||||
pub fn delete_user(&mut self, id: i32) -> Result<(), Error> {
|
||||
self.auth.delete_user(&mut self.conn, id)
|
||||
}
|
||||
|
||||
pub fn login_by_password(&mut self, req: auth::Request, username: &str, password: String,
|
||||
domain: Vec<u8>, session_flags: i32)
|
||||
-> Result<(RawSessionId, &Session), Error> {
|
||||
self.auth.login_by_password(&self.conn, req, username, password, domain, session_flags)
|
||||
}
|
||||
|
||||
pub fn authenticate_session(&mut self, req: auth::Request, sid: &auth::RawSessionId)
|
||||
-> Result<(auth::SessionHash, &User), Error> {
|
||||
self.auth.authenticate_session(&self.conn, req, sid)
|
||||
}
|
||||
|
||||
pub fn revoke_session(&mut self, reason: auth::RevocationReason, detail: Option<String>,
|
||||
req: auth::Request, hash: auth::SessionHash) -> Result<(), Error> {
|
||||
self.auth.revoke_session(&self.conn, reason, detail, req, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes a database.
|
||||
/// Note this doesn't set journal options, so that it can be used on in-memory databases for
|
||||
/// test code.
|
||||
pub fn init(conn: &mut rusqlite::Connection) -> Result<(), Error> {
|
||||
conn.execute("pragma foreign_keys = on", &[])?;
|
||||
let tx = conn.transaction()?;
|
||||
tx.execute_batch(include_str!("schema.sql"))?;
|
||||
{
|
||||
@ -1764,12 +1804,14 @@ impl<C: Clocks + Clone> Database<C> {
|
||||
uuid,
|
||||
})
|
||||
} else { None };
|
||||
let auth = auth::State::init(&conn)?;
|
||||
let db = Database {
|
||||
db: Some(Mutex::new(LockedDatabase {
|
||||
conn,
|
||||
uuid,
|
||||
open,
|
||||
open_monotonic,
|
||||
auth,
|
||||
sample_file_dirs_by_id: BTreeMap::new(),
|
||||
cameras_by_id: BTreeMap::new(),
|
||||
cameras_by_uuid: BTreeMap::new(),
|
||||
|
@ -30,10 +30,12 @@
|
||||
|
||||
#![cfg_attr(all(feature="nightly", test), feature(test))]
|
||||
|
||||
extern crate blake2_rfc;
|
||||
#[macro_use] extern crate failure;
|
||||
extern crate fnv;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
extern crate libc;
|
||||
extern crate libpasta;
|
||||
#[macro_use] extern crate log;
|
||||
extern crate lru_cache;
|
||||
extern crate moonfire_base as base;
|
||||
@ -47,6 +49,7 @@ extern crate tempdir;
|
||||
extern crate time;
|
||||
extern crate uuid;
|
||||
|
||||
pub mod auth;
|
||||
pub mod check;
|
||||
mod coding;
|
||||
pub mod db;
|
||||
|
Loading…
x
Reference in New Issue
Block a user