On login pw upgrade (#315)

Fixes #295 on login pw upgrade. This adds support for SSHA512 to be imported at the request of @colbyprior, and adds a delayed action queue so that events can be run after-the-fact so that authentication does not need to exist under the write path.
This commit is contained in:
Firstyear 2020-08-24 12:15:21 +10:00 committed by GitHub
parent e6e89ed13a
commit dbfe87e675
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 889 additions and 422 deletions

403
Cargo.lock generated
View file

@ -78,7 +78,7 @@ dependencies = [
"log", "log",
"mime", "mime",
"mime_guess", "mime_guess",
"percent-encoding 2.1.0", "percent-encoding",
"v_htmlescape", "v_htmlescape",
] ]
@ -118,7 +118,7 @@ dependencies = [
"lazy_static", "lazy_static",
"log", "log",
"mime", "mime",
"percent-encoding 2.1.0", "percent-encoding",
"pin-project", "pin-project",
"rand", "rand",
"regex", "regex",
@ -191,9 +191,9 @@ dependencies = [
[[package]] [[package]]
name = "actix-service" name = "actix-service"
version = "1.0.5" version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3e4fc95dfa7e24171b2d0bb46b85f8ab0e8499e4e3caec691fc4ea65c287564" checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"pin-project", "pin-project",
@ -314,7 +314,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"time 0.1.43", "time 0.1.43",
"url 2.1.1", "url",
] ]
[[package]] [[package]]
@ -402,16 +402,63 @@ dependencies = [
] ]
[[package]] [[package]]
name = "async-std" name = "async-executor"
version = "1.6.2" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00d68a33ebc8b57800847d00787307f84a562224a14db069b0acefe4c2abbf5d" checksum = "90f47c78ea98277cb1f5e6f60ba4fc762f5eafe9f6511bc2f7dfd8b75c225650"
dependencies = [ dependencies = [
"async-io",
"futures-lite",
"multitask",
"parking 1.0.6",
"scoped-tls",
"waker-fn",
]
[[package]]
name = "async-io"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae22a338d28c75b53702b66f77979062cb29675db376d99e451af4fa79dedb3"
dependencies = [
"cfg-if",
"concurrent-queue",
"futures-lite",
"libc",
"once_cell",
"parking 2.0.0",
"polling",
"socket2",
"vec-arena",
"wepoll-sys-stjepang",
"winapi 0.3.9",
]
[[package]]
name = "async-mutex"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20e85981fc34e84cdff3fc2c9219189752633fdc538a06df8b5ac45b68a4f3a9"
dependencies = [
"event-listener",
]
[[package]]
name = "async-std"
version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c8da367da62b8ff2313c406c9ac091c1b31d67a165becdd2de380d846260f7"
dependencies = [
"async-executor",
"async-io",
"async-mutex",
"async-task", "async-task",
"blocking",
"crossbeam-utils", "crossbeam-utils",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-io", "futures-io",
"futures-lite",
"kv-log-macro", "kv-log-macro",
"log", "log",
"memchr", "memchr",
@ -420,7 +467,6 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"slab", "slab",
"smol",
"wasm-bindgen-futures", "wasm-bindgen-futures",
] ]
@ -432,9 +478,9 @@ checksum = "c17772156ef2829aadc587461c7753af20b7e8db1529bc66855add962a3b35d3"
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.36" version = "0.1.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a265e3abeffdce30b2e26b7a11b222fe37c6067404001b434101457d0385eb92" checksum = "6e1a4a2f97ce50c9d0282c1468816208588441492b40d813b2e0419c22c05e7f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -460,9 +506,9 @@ dependencies = [
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]] [[package]]
name = "awc" name = "awc"
@ -481,7 +527,7 @@ dependencies = [
"log", "log",
"mime", "mime",
"openssl", "openssl",
"percent-encoding 2.1.0", "percent-encoding",
"rand", "rand",
"serde", "serde",
"serde_json", "serde_json",
@ -581,15 +627,14 @@ dependencies = [
[[package]] [[package]]
name = "blocking" name = "blocking"
version = "0.4.7" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2468ff7bf85066b4a3678fede6fe66db31846d753ff0adfbfab2c6a6e81612b" checksum = "ea5800d29218fea137b0880387e5948694a23c93fcdde157006966693a865c7c"
dependencies = [ dependencies = [
"async-channel", "async-channel",
"atomic-waker", "atomic-waker",
"futures-lite", "futures-lite",
"once_cell", "once_cell",
"parking",
"waker-fn", "waker-fn",
] ]
@ -681,9 +726,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.58" version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -693,9 +738,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.13" version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6" checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b"
dependencies = [ dependencies = [
"num-integer", "num-integer",
"num-traits", "num-traits",
@ -704,9 +749,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.33.1" version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"textwrap", "textwrap",
@ -749,9 +794,9 @@ dependencies = [
[[package]] [[package]]
name = "concurrent-queue" name = "concurrent-queue"
version = "1.2.0" version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e296417c8154304ac70aceda41f05318f986f7c0c767bcb0a2366fbb890e78e1" checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
dependencies = [ dependencies = [
"cache-padded", "cache-padded",
] ]
@ -782,40 +827,31 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "cookie"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5"
dependencies = [
"time 0.1.43",
"url 1.7.2",
]
[[package]] [[package]]
name = "cookie" name = "cookie"
version = "0.14.2" version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1373a16a4937bc34efec7b391f9c1500c30b8478a701a4f44c9165cc0475a6e0" checksum = "1373a16a4937bc34efec7b391f9c1500c30b8478a701a4f44c9165cc0475a6e0"
dependencies = [ dependencies = [
"percent-encoding",
"time 0.2.16", "time 0.2.16",
"version_check 0.9.2", "version_check 0.9.2",
] ]
[[package]] [[package]]
name = "cookie_store" name = "cookie_store"
version = "0.11.0" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2a7e5bf5517bf3c3f4358f13d3ec2092419ac2332aa8593605c957da73d491" checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3"
dependencies = [ dependencies = [
"cookie 0.12.0", "cookie",
"idna 0.2.0", "idna",
"log", "log",
"publicsuffix", "publicsuffix",
"serde", "serde",
"serde_json", "serde_json",
"time 0.1.43", "time 0.2.16",
"url 2.1.1", "url",
] ]
[[package]] [[package]]
@ -1094,9 +1130,9 @@ checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
[[package]] [[package]]
name = "either" name = "either"
version = "1.5.3" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
@ -1109,9 +1145,9 @@ dependencies = [
[[package]] [[package]]
name = "enum-as-inner" name = "enum-as-inner"
version = "0.3.2" version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc4bfcfacb61d231109d1d55202c1f33263319668b168843e02ad4652725ec9c" checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -1134,18 +1170,18 @@ dependencies = [
[[package]] [[package]]
name = "error-chain" name = "error-chain"
version = "0.12.2" version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
dependencies = [ dependencies = [
"version_check 0.9.2", "version_check 0.9.2",
] ]
[[package]] [[package]]
name = "event-listener" name = "event-listener"
version = "2.3.0" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "298f00c3b04c1d9b4cb86aefaaa35348af0957d98b30a5306fc635f8e718923d" checksum = "e1cd41440ae7e4734bbd42302f63eaba892afc93a3912dad84006247f0dedb0e"
[[package]] [[package]]
name = "failure" name = "failure"
@ -1199,15 +1235,15 @@ dependencies = [
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.3.3" version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36a9cb09840f81cd211e435d00a4e487edd263dc3c8ff815c32dd76ad668ebed" checksum = "4bd3bdaaf0a72155260a1c098989b60db1cbb22d6a628e64f16237aa4da93cc7"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.16" version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68c90b0fc46cf89d227cc78b40e494ff81287a92dd07631e5af0d06fe3cf885e" checksum = "766d0e77a2c1502169d4a93ff3b8c15a71fd946cd0126309752104e5f3c46d94"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"crc32fast", "crc32fast",
@ -1302,15 +1338,15 @@ checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
[[package]] [[package]]
name = "futures-lite" name = "futures-lite"
version = "0.1.10" version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe71459749b2e8e66fb95df721b22fa08661ad384a0c5b519e11d3893b4692a" checksum = "97999970129b808f0ccba93211201d431fcc12d7e1ffae03a61b5cedd1a7ced2"
dependencies = [ dependencies = [
"fastrand", "fastrand",
"futures-core", "futures-core",
"futures-io", "futures-io",
"memchr", "memchr",
"parking", "parking 2.0.0",
"pin-project-lite", "pin-project-lite",
"waker-fn", "waker-fn",
] ]
@ -1424,9 +1460,9 @@ checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.8.1" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb" checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
dependencies = [ dependencies = [
"ahash", "ahash",
"autocfg", "autocfg",
@ -1552,17 +1588,6 @@ dependencies = [
"time 0.1.43", "time 0.1.43",
] ]
[[package]]
name = "idna"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.2.0" version = "0.2.0"
@ -1576,9 +1601,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.5.0" version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7" checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "hashbrown",
@ -1611,6 +1636,12 @@ dependencies = [
"winreg 0.6.2", "winreg 0.6.2",
] ]
[[package]]
name = "ipnet"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.8.2" version = "0.8.2"
@ -1637,9 +1668,9 @@ checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.42" version = "0.3.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52732a3d3ad72c58ad2dc70624f9c17b46ecd0943b9a4f1ee37c4c18c5d983e2" checksum = "85a7e2c92a4804dd459b86c339278d0fe87cf93757fae222c3fa3ae75458bc73"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
@ -1653,11 +1684,12 @@ dependencies = [
"actix-rt", "actix-rt",
"actix-session", "actix-session",
"actix-web", "actix-web",
"async-std",
"base64 0.12.3", "base64 0.12.3",
"cargo-husky", "cargo-husky",
"chrono", "chrono",
"concread", "concread",
"cookie 0.14.2", "cookie",
"criterion", "criterion",
"crossbeam", "crossbeam",
"env_logger", "env_logger",
@ -1829,9 +1861,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.73" version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9" checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
[[package]] [[package]]
name = "libnss" name = "libnss"
@ -1947,9 +1979,9 @@ dependencies = [
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.4.0" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f" checksum = "4d7559a8a40d0f97e1edea3220f698f78b1c5ab67532e49f68fde3910323b722"
dependencies = [ dependencies = [
"adler", "adler",
] ]
@ -2018,6 +2050,17 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "multitask"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c09c35271e7dcdb5f709779111f2c8e8ab8e06c1b587c1c6a9e179d865aaa5b4"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
]
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.4" version = "0.2.4"
@ -2169,9 +2212,9 @@ checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
[[package]] [[package]]
name = "once_cell" name = "once_cell"
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"
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
[[package]] [[package]]
name = "oorandom" name = "oorandom"
@ -2244,6 +2287,12 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cb300f271742d4a2a66c01b6b2fa0c83dfebd2e0bf11addb879a3547b4ed87c" checksum = "6cb300f271742d4a2a66c01b6b2fa0c83dfebd2e0bf11addb879a3547b4ed87c"
[[package]]
name = "parking"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.10.2" version = "0.10.2"
@ -2313,12 +2362,6 @@ dependencies = [
"proc-macro-hack", "proc-macro-hack",
] ]
[[package]]
name = "percent-encoding"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.1.0" version = "2.1.0"
@ -2327,18 +2370,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "0.4.22" version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa"
dependencies = [ dependencies = [
"pin-project-internal", "pin-project-internal",
] ]
[[package]] [[package]]
name = "pin-project-internal" name = "pin-project-internal"
version = "0.4.22" version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2376,16 +2419,28 @@ dependencies = [
] ]
[[package]] [[package]]
name = "ppv-lite86" name = "polling"
version = "0.2.8" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" checksum = "9e09dffb745feffca5be3dea51c02b7b368c4597ab0219a82acaf9799ab3e0d1"
dependencies = [
"cfg-if",
"libc",
"wepoll-sys-stjepang",
"winapi 0.3.9",
]
[[package]]
name = "ppv-lite86"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
[[package]] [[package]]
name = "proc-macro-error" name = "proc-macro-error"
version = "1.0.3" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc175e9777c3116627248584e8f8b3e2987405cabe1c0adf7d1dd28f09dc7880" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [ dependencies = [
"proc-macro-error-attr", "proc-macro-error-attr",
"proc-macro2", "proc-macro2",
@ -2396,22 +2451,20 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro-error-attr" name = "proc-macro-error-attr"
version = "1.0.3" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cc9795ca17eb581285ec44936da7fc2335a3f34f2ddd13118b6f4d515435c50" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn",
"syn-mid",
"version_check 0.9.2", "version_check 0.9.2",
] ]
[[package]] [[package]]
name = "proc-macro-hack" name = "proc-macro-hack"
version = "0.5.16" version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
[[package]] [[package]]
name = "proc-macro-nested" name = "proc-macro-nested"
@ -2435,10 +2488,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b"
dependencies = [ dependencies = [
"error-chain", "error-chain",
"idna 0.2.0", "idna",
"lazy_static", "lazy_static",
"regex", "regex",
"url 2.1.1", "url",
] ]
[[package]] [[package]]
@ -2598,13 +2651,13 @@ dependencies = [
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.10.6" version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680" checksum = "12427a5577082c24419c9c417db35cfeb65962efc7675bb6b0d5f1f9d315bfe6"
dependencies = [ dependencies = [
"base64 0.12.3", "base64 0.12.3",
"bytes", "bytes",
"cookie 0.12.0", "cookie",
"cookie_store", "cookie_store",
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
@ -2613,21 +2666,22 @@ dependencies = [
"http-body", "http-body",
"hyper", "hyper",
"hyper-tls", "hyper-tls",
"ipnet",
"js-sys", "js-sys",
"lazy_static", "lazy_static",
"log", "log",
"mime", "mime",
"mime_guess", "mime_guess",
"native-tls", "native-tls",
"percent-encoding 2.1.0", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"time 0.1.43", "time 0.2.16",
"tokio", "tokio",
"tokio-tls", "tokio-tls",
"url 2.1.1", "url",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
@ -2798,9 +2852,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.114" version = "1.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
@ -2817,9 +2871,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.114" version = "1.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2828,9 +2882,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.56" version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -2846,7 +2900,7 @@ dependencies = [
"dtoa", "dtoa",
"itoa", "itoa",
"serde", "serde",
"url 2.1.1", "url",
] ]
[[package]] [[package]]
@ -2878,9 +2932,9 @@ dependencies = [
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.2.0" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"libc", "libc",
@ -2894,34 +2948,13 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.4.1" version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f" checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "smol"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "620cbb3c6e34da57d3a248cda0cd01cd5848164dc062e764e65d06fe3ea7aed5"
dependencies = [
"async-task",
"blocking",
"concurrent-queue",
"fastrand",
"futures-io",
"futures-util",
"libc",
"once_cell",
"scoped-tls",
"slab",
"socket2",
"wepoll-sys-stjepang",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.3.12" version = "0.3.12"
@ -3017,9 +3050,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]] [[package]]
name = "structopt" name = "structopt"
version = "0.3.15" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c" checksum = "de5472fb24d7e80ae84a7801b7978f95a19ec32cb1876faea59ab711eb901976"
dependencies = [ dependencies = [
"clap", "clap",
"lazy_static", "lazy_static",
@ -3028,9 +3061,9 @@ dependencies = [
[[package]] [[package]]
name = "structopt-derive" name = "structopt-derive"
version = "0.4.8" version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118" checksum = "1e0eb37335aeeebe51be42e2dc07f031163fbabfa6ac67d7ea68b5c2f68d5f99"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error", "proc-macro-error",
@ -3041,26 +3074,15 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.35" version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0" checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "syn-mid"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "synstructure" name = "synstructure"
version = "0.12.4" version = "0.12.4"
@ -3203,9 +3225,9 @@ dependencies = [
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "0.3.3" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
[[package]] [[package]]
name = "tokio" name = "tokio"
@ -3307,9 +3329,9 @@ checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.17" version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbdf4ccd1652592b01286a5dbe1e2a77d78afaa34beadd9872a5f7396f92aaa9" checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"log", "log",
@ -3318,9 +3340,9 @@ dependencies = [
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.11" version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94ae75f0d28ae10786f3b1895c55fe72e79928fd5ccdebb5438c75e93fec178f" checksum = "4f0e00789804e99b20f12bc7003ca416309d28a6f495d6af58d1e2c2842461b5"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
] ]
@ -3335,14 +3357,14 @@ dependencies = [
"enum-as-inner", "enum-as-inner",
"failure", "failure",
"futures", "futures",
"idna 0.2.0", "idna",
"lazy_static", "lazy_static",
"log", "log",
"rand", "rand",
"smallvec", "smallvec",
"socket2", "socket2",
"tokio", "tokio",
"url 2.1.1", "url",
] ]
[[package]] [[package]]
@ -3427,26 +3449,15 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "url"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
dependencies = [
"idna 0.1.5",
"matches",
"percent-encoding 1.0.1",
]
[[package]] [[package]]
name = "url" name = "url"
version = "2.1.1" version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
dependencies = [ dependencies = [
"idna 0.2.0", "idna",
"matches", "matches",
"percent-encoding 2.1.0", "percent-encoding",
] ]
[[package]] [[package]]
@ -3506,6 +3517,12 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
[[package]]
name = "vec-arena"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cb18268690309760d59ee1a9b21132c126ba384f374c59a94db4bc03adeb561"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.1.5" version = "0.1.5"
@ -3553,9 +3570,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.65" version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3edbcc9536ab7eababcc6d2374a0b7bfe13a2b6d562c5e07f370456b1a8f33d" checksum = "f0563a9a4b071746dd5aedbc3a28c6fe9be4586fb3fbadb67c400d4f53c6b16c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"serde", "serde",
@ -3565,9 +3582,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.65" version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ed2fb8c84bfad20ea66b26a3743f3e7ba8735a69fe7d95118c33ec8fc1244d" checksum = "bc71e4c5efa60fb9e74160e89b93353bc24059999c0ae0fb03affc39770310b0"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"lazy_static", "lazy_static",
@ -3580,9 +3597,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.15" version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ad6e4e8b2b7f8c90b6e09a9b590ea15cb0d1dbe28502b5a405cd95d1981671" checksum = "95f8d235a77f880bcef268d379810ea6c0af2eacfa90b1ad5af731776e0c4699"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
@ -3592,9 +3609,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.65" version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb071268b031a64d92fc6cf691715ca5a40950694d8f683c5bb43db7c730929e" checksum = "97c57cefa5fa80e2ba15641578b44d36e7a64279bc5ed43c6dbaf329457a2ed2"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -3602,9 +3619,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.65" version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf592c807080719d1ff2f245a687cbadb3ed28b2077ed7084b47aba8b691f2c6" checksum = "841a6d1c35c6f596ccea1f82504a192a60378f64b3bb0261904ad8f2f5657556"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3615,15 +3632,15 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.65" version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b6c0220ded549d63860c78c38f3bcc558d1ca3f4efa74942c536ddbbb55e87" checksum = "93b162580e34310e5931c4b792560108b10fd14d64915d7fff8ff00180e70092"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.42" version = "0.3.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be2398f326b7ba09815d0b403095f34dd708579220d099caae89be0b32137b2" checksum = "dda38f4e5ca63eda02c059d243aa25b5f35ab98451e518c51612cd0f1bd19a47"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",

9
GETTING_STARTED.md Normal file
View file

@ -0,0 +1,9 @@
## Getting Started (for Administrators)
If you want to deploy kanidm, or to see what it can do, you should read the [kanidm book]
[kanidm book]: https://github.com/kanidm/kanidm/blob/master/kanidm_book/src/SUMMARY.md
## Getting Started (for Developers)
See the README.md file.

View file

@ -16,34 +16,25 @@ Today the project is still under heavy development to achieve these goals - we d
functional release before early 2020. It is important to note that not all needed security features functional release before early 2020. It is important to note that not all needed security features
of the system have been completed yet! of the system have been completed yet!
## Code of Conduct ## Code of Conduct / Ethics
See our [code of conduct] See our [code of conduct]
[code of conduct]: https://github.com/kanidm/kanidm/blob/master/CODE_OF_CONDUCT.md
## Ethics / Rights
See our documentation on [rights and ethics] See our documentation on [rights and ethics]
[code of conduct]: https://github.com/kanidm/kanidm/blob/master/CODE_OF_CONDUCT.md
[rights and ethics]: https://github.com/kanidm/kanidm/blob/master/ethics/README.md [rights and ethics]: https://github.com/kanidm/kanidm/blob/master/ethics/README.md
## Some key ideas ## Documentation / Getting Started / Install
* All people should be respected and able to be respresented securely.
* Devices represent users and their identities - they are part of the authentication.
* Human error occurs - we should be designed to minimise human mistakes and empower people.
* The system should be easy to understand and reason about for users and admins.
## Documentation
If you want to deploy kanidm, or to see what it can do, you should read the [kanidm book] If you want to deploy kanidm, or to see what it can do, you should read the [kanidm book]
[kanidm book]: https://github.com/kanidm/kanidm/blob/master/kanidm_book/src/SUMMARY.md [kanidm book]: https://github.com/kanidm/kanidm/blob/master/kanidm_book/src/SUMMARY.md
## Getting in Contact ## Getting in Contact / Questions
We have a [gitter community channel] where we can talk. Firstyear is also happy to answer questions via email, which can be found on their github profile. We have a [gitter community channel] where we can talk. Firstyear is also happy to
answer questions via email, which can be found on their github profile.
[gitter community channel]: https://gitter.im/kanidm/community [gitter community channel]: https://gitter.im/kanidm/community
@ -70,6 +61,13 @@ We have a [gitter community channel] where we can talk. Firstyear is also happy
* Generic database: We don't want to be another NoSQL database, we want to be an IDM solution. * Generic database: We don't want to be another NoSQL database, we want to be an IDM solution.
* Being LDAP/GSSAPI/Kerberos: These are all legacy protocols that are hard to use and confine our thinking - we should avoid "being like them". * Being LDAP/GSSAPI/Kerberos: These are all legacy protocols that are hard to use and confine our thinking - we should avoid "being like them".
## Some key ideas
* All people should be respected and able to be respresented securely.
* Devices represent users and their identities - they are part of the authentication.
* Human error occurs - we should be designed to minimise human mistakes and empower people.
* The system should be easy to understand and reason about for users and admins.
## Development and Testing ## Development and Testing
### Designs ### Designs

View file

@ -102,6 +102,7 @@ pub enum OperationError {
PasswordBadListed, PasswordBadListed,
CryptographyError, CryptographyError,
ResourceLimit, ResourceLimit,
QueueDisconnected,
} }
impl PartialEq for OperationError { impl PartialEq for OperationError {

View file

@ -34,6 +34,8 @@ actix-web = { version = "2.0", features = ["openssl"] }
actix-session = "0.3" actix-session = "0.3"
actix-files = "0.2" actix-files = "0.2"
async-std = "1.6"
log = "0.4" log = "0.4"
env_logger = "0.7" env_logger = "0.7"
rand = "0.7" rand = "0.7"

View file

@ -11,6 +11,7 @@ pub struct DbCidV1 {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub enum DbPasswordV1 { pub enum DbPasswordV1 {
PBKDF2(usize, Vec<u8>, Vec<u8>), PBKDF2(usize, Vec<u8>, Vec<u8>),
SSHA512(Vec<u8>, Vec<u8>),
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]

View file

@ -36,7 +36,7 @@ use crate::audit::AuditScope;
use crate::be::{Backend, BackendTransaction, FsType}; use crate::be::{Backend, BackendTransaction, FsType};
use crate::crypto::setup_tls; use crate::crypto::setup_tls;
use crate::filter::{Filter, FilterInvalid}; use crate::filter::{Filter, FilterInvalid};
use crate::idm::server::IdmServer; use crate::idm::server::{IdmServer, IdmServerDelayed};
use crate::interval::IntervalActor; use crate::interval::IntervalActor;
use crate::ldap::LdapServer; use crate::ldap::LdapServer;
use crate::schema::Schema; use crate::schema::Schema;
@ -1301,7 +1301,7 @@ fn setup_qs_idms(
audit: &mut AuditScope, audit: &mut AuditScope,
be: Backend, be: Backend,
schema: Schema, schema: Schema,
) -> Result<(QueryServer, IdmServer), OperationError> { ) -> Result<(QueryServer, IdmServer, IdmServerDelayed), OperationError> {
// Create a query_server implementation // Create a query_server implementation
let query_server = QueryServer::new(be, schema); let query_server = QueryServer::new(be, schema);
@ -1317,9 +1317,9 @@ fn setup_qs_idms(
// We generate a SINGLE idms only! // We generate a SINGLE idms only!
let idms = IdmServer::new(query_server.clone()); let (idms, idms_delayed) = IdmServer::new(query_server.clone());
Ok((query_server, idms)) Ok((query_server, idms, idms_delayed))
} }
pub fn backup_server_core(config: &Configuration, dst_path: &str) { pub fn backup_server_core(config: &Configuration, dst_path: &str) {
@ -1389,7 +1389,7 @@ pub fn restore_server_core(config: &Configuration, dst_path: &str) {
info!("Attempting to init query server ..."); info!("Attempting to init query server ...");
let (qs, _idms) = match setup_qs_idms(&mut audit, be, schema) { let (qs, _idms, _idms_delayed) = match setup_qs_idms(&mut audit, be, schema) {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
audit.write_log(); audit.write_log();
@ -1457,7 +1457,7 @@ pub fn reindex_server_core(config: &Configuration) {
eprintln!("Attempting to init query server ..."); eprintln!("Attempting to init query server ...");
let (qs, _idms) = match setup_qs_idms(&mut audit, be, schema) { let (qs, _idms, _idms_delayed) = match setup_qs_idms(&mut audit, be, schema) {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
audit.write_log(); audit.write_log();
@ -1508,7 +1508,7 @@ pub fn domain_rename_core(config: &Configuration, new_domain_name: &str) {
} }
}; };
// setup the qs - *with* init of the migrations and schema. // setup the qs - *with* init of the migrations and schema.
let (qs, _idms) = match setup_qs_idms(&mut audit, be, schema) { let (qs, _idms, _idms_delayed) = match setup_qs_idms(&mut audit, be, schema) {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
audit.write_log(); audit.write_log();
@ -1606,7 +1606,7 @@ pub fn recover_account_core(config: &Configuration, name: &str, password: &str)
} }
}; };
// setup the qs - *with* init of the migrations and schema. // setup the qs - *with* init of the migrations and schema.
let (_qs, idms) = match setup_qs_idms(&mut audit, be, schema) { let (_qs, idms, _idms_delayed) = match setup_qs_idms(&mut audit, be, schema) {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
audit.write_log(); audit.write_log();
@ -1689,7 +1689,7 @@ pub async fn create_server_core(config: Configuration) -> Result<ServerCtx, ()>
} }
}; };
// Start the IDM server. // Start the IDM server.
let (qs, idms) = match setup_qs_idms(&mut audit, be, schema) { let (qs, idms, mut idms_delayed) = match setup_qs_idms(&mut audit, be, schema) {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
audit.write_log(); audit.write_log();
@ -1756,6 +1756,9 @@ pub async fn create_server_core(config: Configuration) -> Result<ServerCtx, ()>
let server_write_addr = let server_write_addr =
QueryServerWriteV1::start(log_tx.clone(), config.log_level, qs, idms_arc); QueryServerWriteV1::start(log_tx.clone(), config.log_level, qs, idms_arc);
// TODO #314: For now we just drop everything from the delayed queue until we rewrite to be async.
tokio::spawn(async move { idms_delayed.temp_drop_all().await; });
// Setup timed events associated to the write thread // Setup timed events associated to the write thread
let _int_addr = IntervalActor::new(server_write_addr.clone()).start(); let _int_addr = IntervalActor::new(server_write_addr.clone()).start();

View file

@ -2,6 +2,7 @@ use crate::be::dbvalue::{DbCredV1, DbPasswordV1};
use kanidm_proto::v1::OperationError; use kanidm_proto::v1::OperationError;
use openssl::hash::MessageDigest; use openssl::hash::MessageDigest;
use openssl::pkcs5::pbkdf2_hmac; use openssl::pkcs5::pbkdf2_hmac;
use openssl::sha::Sha512;
use rand::prelude::*; use rand::prelude::*;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -20,6 +21,9 @@ const PBKDF2_SALT_LEN: usize = 24;
const PBKDF2_KEY_LEN: usize = 64; const PBKDF2_KEY_LEN: usize = 64;
const PBKDF2_IMPORT_MIN_LEN: usize = 32; const PBKDF2_IMPORT_MIN_LEN: usize = 32;
const DS_SSHA512_SALT_LEN: usize = 8;
const DS_SSHA512_HASH_LEN: usize = 64;
// These are in order of "relative" strength. // These are in order of "relative" strength.
/* /*
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -38,6 +42,8 @@ pub enum Policy {
enum KDF { enum KDF {
// cost, salt, hash // cost, salt, hash
PBKDF2(usize, Vec<u8>, Vec<u8>), PBKDF2(usize, Vec<u8>, Vec<u8>),
// salt hash
SSHA512(Vec<u8>, Vec<u8>),
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -53,6 +59,9 @@ impl TryFrom<DbPasswordV1> for Password {
DbPasswordV1::PBKDF2(c, s, h) => Ok(Password { DbPasswordV1::PBKDF2(c, s, h) => Ok(Password {
material: KDF::PBKDF2(c, s, h), material: KDF::PBKDF2(c, s, h),
}), }),
DbPasswordV1::SSHA512(s, h) => Ok(Password {
material: KDF::SSHA512(s, h),
}),
} }
} }
} }
@ -88,6 +97,18 @@ impl TryFrom<&str> for Password {
} }
} }
// Test 389ds formats
if let Some(ds_ssha512) = value.strip_prefix("{SSHA512}") {
let sh = base64::decode(ds_ssha512).map_err(|_| ())?;
let (h, s) = sh.split_at(DS_SSHA512_HASH_LEN);
if s.len() != DS_SSHA512_SALT_LEN {
return Err(());
}
return Ok(Password {
material: KDF::SSHA512(s.to_vec(), h.to_vec()),
});
}
// Nothing matched to this point. // Nothing matched to this point.
Err(()) Err(())
} }
@ -160,6 +181,13 @@ impl Password {
&chal_key == key &chal_key == key
}) })
} }
KDF::SSHA512(salt, key) => {
let mut hasher = Sha512::new();
hasher.update(cleartext.as_bytes());
hasher.update(&salt);
let r = hasher.finish();
Ok(key == &(r.to_vec()))
}
} }
} }
@ -168,6 +196,14 @@ impl Password {
KDF::PBKDF2(cost, salt, hash) => { KDF::PBKDF2(cost, salt, hash) => {
DbPasswordV1::PBKDF2(*cost, salt.clone(), hash.clone()) DbPasswordV1::PBKDF2(*cost, salt.clone(), hash.clone())
} }
KDF::SSHA512(salt, hash) => DbPasswordV1::SSHA512(salt.clone(), hash.clone()),
}
}
pub fn requires_upgrade(&self) -> bool {
match &self.material {
KDF::PBKDF2(_, _, _) => false,
_ => true,
} }
} }
} }
@ -352,4 +388,14 @@ mod tests {
let r = Password::try_from(im_pw).expect("Failed to parse"); let r = Password::try_from(im_pw).expect("Failed to parse");
assert!(r.verify(password).unwrap_or(false)); assert!(r.verify(password).unwrap_or(false));
} }
#[test]
fn test_password_from_ds_ssha512() {
let im_pw = "{SSHA512}JwrSUHkI7FTAfHRVR6KoFlSN0E3dmaQWARjZ+/UsShYlENOqDtFVU77HJLLrY2MuSp0jve52+pwtdVl2QUAHukQ0XUf5LDtM";
let password = "password";
let r = Password::try_from(im_pw).expect("Failed to parse");
// Known weak, require upgrade.
assert!(r.requires_upgrade());
assert!(r.verify(password).unwrap_or(false));
}
} }

View file

@ -200,6 +200,31 @@ impl Account {
} }
} }
pub(crate) fn check_credential_pw(
&self,
cleartext: &str,
appid: &Option<String>,
) -> Result<bool, OperationError> {
match appid {
Some(_) => Err(OperationError::InvalidState),
None => {
match &self.primary {
// Check the cred's associated pw.
Some(ref primary) => {
primary.password.as_ref()
.ok_or(OperationError::InvalidState)
.and_then(|pw| {
pw.verify(cleartext)
})
}
None => {
Err(OperationError::InvalidState)
}
}
} // no appid
}
}
pub(crate) fn regenerate_radius_secret_mod( pub(crate) fn regenerate_radius_secret_mod(
&self, &self,
cleartext: &str, cleartext: &str,

View file

@ -6,8 +6,13 @@ use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthState};
use crate::credential::{totp::TOTP, Credential, Password}; use crate::credential::{totp::TOTP, Credential, Password};
use crate::idm::delayed::{DelayedAction, PasswordUpgrade};
// use crossbeam::channel::Sender;
use tokio::sync::mpsc::UnboundedSender as Sender;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::time::Duration; use std::time::Duration;
use uuid::Uuid;
// Each CredHandler takes one or more credentials and determines if the // Each CredHandler takes one or more credentials and determines if the
// handlers requirements can be 100% fufilled. This is where MFA or other // handlers requirements can be 100% fufilled. This is where MFA or other
@ -72,14 +77,216 @@ impl TryFrom<&Credential> for CredHandler {
} }
} }
// TODO: Can this be improved?
#[allow(clippy::cognitive_complexity)]
impl CredHandler { impl CredHandler {
fn maybe_pw_upgrade(
au: &mut AuditScope,
pw: &Password,
who: Uuid,
cleartext: &str,
async_tx: &Sender<DelayedAction>
)
{
if pw.requires_upgrade() {
if let Err(_e) = async_tx.send(DelayedAction::PwUpgrade(PasswordUpgrade {
target_uuid: who,
existing_password: cleartext.to_string(),
appid: None,
})) {
ladmin_warning!(au, "unable to queue delayed pwupgrade, continuing ... ");
};
}
}
fn validate_anonymous(
au: &mut AuditScope,
creds: &[AuthCredential],
) -> CredState {
creds.iter().fold(
CredState::Continue(vec![AuthAllowed::Anonymous]),
|acc, cred| {
// There is no "continuation" from this type - we only set it at
// the start assuming there is no values in the iter so we can tell
// the session to continue up to some timelimit.
match acc {
// If denied, continue returning denied.
CredState::Denied(_) => {
lsecurity!(au, "Handler::Anonymous -> Result::Denied - already denied");
acc
}
// We have a continue or success, it's important we keep checking here
// after the success, because if they sent "multiple" anonymous or
// they sent anon + password, we need to handle both cases. Double anon
// is okay, but anything else is instant failure, even if we already
// had a success.
_ => {
match cred {
AuthCredential::Anonymous => {
// For anonymous, no claims will ever be issued.
lsecurity!(au, "Handler::Anonymous -> Result::Success");
CredState::Success(Vec::new())
}
_ => {
lsecurity!(au, "Handler::Anonymous -> Result::Denied - invalid cred type for handler");
CredState::Denied(BAD_AUTH_TYPE_MSG)
}
}
}
} // end match acc
},
)
}
fn validate_password(
au: &mut AuditScope,
creds: &[AuthCredential],
pw: &mut Password,
who: Uuid,
async_tx: &Sender<DelayedAction>,
) -> CredState {
creds.iter().fold(
// If no creds, remind that we want pw ...
CredState::Continue(vec![AuthAllowed::Password]),
|acc, cred| {
match acc {
// If failed, continue to fail.
CredState::Denied(_) => {
lsecurity!(au, "Handler::Password -> Result::Denied - already denied");
acc
}
_ => {
match cred {
AuthCredential::Password(cleartext) => {
if pw.verify(cleartext.as_str()).unwrap_or(false) {
lsecurity!(au, "Handler::Password -> Result::Success");
Self::maybe_pw_upgrade(au, pw, who, cleartext.as_str(), async_tx);
CredState::Success(Vec::new())
} else {
lsecurity!(au, "Handler::Password -> Result::Denied - incorrect password");
CredState::Denied(BAD_PASSWORD_MSG)
}
}
// All other cases fail.
_ => {
lsecurity!(au, "Handler::Anonymous -> Result::Denied - invalid cred type for handler");
CredState::Denied(BAD_AUTH_TYPE_MSG)
}
}
}
} // end match acc
},
)
}
fn validate_totp_password(
au: &mut AuditScope,
creds: &[AuthCredential],
ts: &Duration,
pw_totp: &mut CredTotpPw,
who: Uuid,
async_tx: &Sender<DelayedAction>,
) -> CredState {
// Set the default reminder to both pw + totp
creds.iter().fold(
// If no creds, remind that we want pw ...
CredState::Continue(vec![AuthAllowed::TOTP, AuthAllowed::Password]),
|acc, cred| {
match acc {
CredState::Denied(_) => {
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - already denied");
acc
}
_ => {
match cred {
AuthCredential::Password(cleartext) => {
// if pw -> check
if pw_totp.pw.verify(cleartext.as_str()).unwrap_or(false) {
pw_totp.pw_state = CredVerifyState::Success;
Self::maybe_pw_upgrade(au, &pw_totp.pw, who, cleartext.as_str(), async_tx);
match pw_totp.totp_state {
CredVerifyState::Init => {
// TOTP hasn't been run yet, we need it before
// we indicate the pw status.
lsecurity!(au, "Handler::TOTPPassword -> Result::Continue - TOTP -, password OK");
CredState::Continue(vec![AuthAllowed::TOTP])
}
CredVerifyState::Success => {
// The totp is success, and password good, let's go!
lsecurity!(au, "Handler::TOTPPassword -> Result::Success - TOTP OK, password OK");
CredState::Success(Vec::new())
}
CredVerifyState::Fail => {
// The totp already failed, send that message now.
// Should be impossible state.
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - TOTP Fail, password OK");
CredState::Denied(BAD_TOTP_MSG)
}
}
} else {
pw_totp.pw_state = CredVerifyState::Fail;
match pw_totp.totp_state {
CredVerifyState::Init => {
// TOTP hasn't been run yet, we need it before
// we indicate the pw status.
lsecurity!(au, "Handler::TOTPPassword -> Result::Continue - TOTP -, password Fail");
CredState::Continue(vec![AuthAllowed::TOTP])
}
CredVerifyState::Success => {
// The totp is success, but password bad.
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - TOTP OK, password Fail");
CredState::Denied(BAD_PASSWORD_MSG)
}
CredVerifyState::Fail => {
// The totp already failed, remind.
// this should be an impossible state.
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - TOTP Fail, password Fail");
CredState::Denied(BAD_TOTP_MSG)
}
}
}
}
AuthCredential::TOTP(totp_chal) => {
// if totp -> check
if pw_totp.totp.verify(*totp_chal, ts) {
pw_totp.totp_state = CredVerifyState::Success;
match pw_totp.pw_state {
CredVerifyState::Init => {
lsecurity!(au, "Handler::TOTPPassword -> Result::Continue - TOTP OK, password -");
CredState::Continue(vec![AuthAllowed::Password])
}
CredVerifyState::Success => {
lsecurity!(au, "Handler::TOTPPassword -> Result::Success - TOTP OK, password OK");
CredState::Success(Vec::new())
}
CredVerifyState::Fail => {
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - TOTP OK, password Fail");
CredState::Denied(BAD_PASSWORD_MSG)
}
}
} else {
pw_totp.totp_state = CredVerifyState::Fail;
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - TOTP Fail, password -");
CredState::Denied(BAD_TOTP_MSG)
}
}
// All other cases fail.
_ => {
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - invalid cred type for handler");
CredState::Denied(BAD_AUTH_TYPE_MSG)
}
} // end match cred
}
} // end match acc
},
) // end fold
} // end CredHandler::TOTPPassword
pub fn validate( pub fn validate(
&mut self, &mut self,
au: &mut AuditScope, au: &mut AuditScope,
creds: &[AuthCredential], creds: &[AuthCredential],
ts: &Duration, ts: &Duration,
who: Uuid,
async_tx: &Sender<DelayedAction>,
) -> CredState { ) -> CredState {
match self { match self {
CredHandler::Denied => { CredHandler::Denied => {
@ -87,168 +294,9 @@ impl CredHandler {
lsecurity!(au, "Handler::Denied -> Result::Denied"); lsecurity!(au, "Handler::Denied -> Result::Denied");
CredState::Denied("authentication denied") CredState::Denied("authentication denied")
} }
CredHandler::Anonymous => { CredHandler::Anonymous => Self::validate_anonymous(au, creds),
creds.iter().fold( CredHandler::Password(ref mut pw) => Self::validate_password(au, creds, pw, who, async_tx),
CredState::Continue(vec![AuthAllowed::Anonymous]), CredHandler::TOTPPassword(ref mut pw_totp) => Self::validate_totp_password(au, creds, ts, pw_totp, who, async_tx),
|acc, cred| {
// There is no "continuation" from this type - we only set it at
// the start assuming there is no values in the iter so we can tell
// the session to continue up to some timelimit.
match acc {
// If denied, continue returning denied.
CredState::Denied(_) => {
lsecurity!(au, "Handler::Anonymous -> Result::Denied - already denied");
acc
}
// We have a continue or success, it's important we keep checking here
// after the success, because if they sent "multiple" anonymous or
// they sent anon + password, we need to handle both cases. Double anon
// is okay, but anything else is instant failure, even if we already
// had a success.
_ => {
match cred {
AuthCredential::Anonymous => {
// For anonymous, no claims will ever be issued.
lsecurity!(au, "Handler::Anonymous -> Result::Success");
CredState::Success(Vec::new())
}
_ => {
lsecurity!(au, "Handler::Anonymous -> Result::Denied - invalid cred type for handler");
CredState::Denied(BAD_AUTH_TYPE_MSG)
}
}
}
} // end match acc
},
)
} // end credhandler::anonymous
CredHandler::Password(pw) => {
creds.iter().fold(
// If no creds, remind that we want pw ...
CredState::Continue(vec![AuthAllowed::Password]),
|acc, cred| {
match acc {
// If failed, continue to fail.
CredState::Denied(_) => {
lsecurity!(au, "Handler::Password -> Result::Denied - already denied");
acc
}
_ => {
match cred {
AuthCredential::Password(cleartext) => {
if pw.verify(cleartext.as_str()).unwrap_or(false) {
lsecurity!(au, "Handler::Password -> Result::Success");
CredState::Success(Vec::new())
} else {
lsecurity!(au, "Handler::Password -> Result::Denied - incorrect password");
CredState::Denied(BAD_PASSWORD_MSG)
}
}
// All other cases fail.
_ => {
lsecurity!(au, "Handler::Anonymous -> Result::Denied - invalid cred type for handler");
CredState::Denied(BAD_AUTH_TYPE_MSG)
}
}
}
} // end match acc
},
)
} // end credhandler::password
CredHandler::TOTPPassword(pw_totp) => {
// Set the default reminder to both pw + totp
creds.iter().fold(
// If no creds, remind that we want pw ...
CredState::Continue(vec![AuthAllowed::TOTP, AuthAllowed::Password]),
|acc, cred| {
match acc {
CredState::Denied(_) => {
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - already denied");
acc
}
_ => {
match cred {
AuthCredential::Password(cleartext) => {
// if pw -> check
if pw_totp.pw.verify(cleartext.as_str()).unwrap_or(false) {
pw_totp.pw_state = CredVerifyState::Success;
match pw_totp.totp_state {
CredVerifyState::Init => {
// TOTP hasn't been run yet, we need it before
// we indicate the pw status.
lsecurity!(au, "Handler::TOTPPassword -> Result::Continue - TOTP -, password OK");
CredState::Continue(vec![AuthAllowed::TOTP])
}
CredVerifyState::Success => {
// The totp is success, and password good, let's go!
lsecurity!(au, "Handler::TOTPPassword -> Result::Success - TOTP OK, password OK");
CredState::Success(Vec::new())
}
CredVerifyState::Fail => {
// The totp already failed, send that message now.
// Should be impossible state.
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - TOTP Fail, password OK");
CredState::Denied(BAD_TOTP_MSG)
}
}
} else {
pw_totp.pw_state = CredVerifyState::Fail;
match pw_totp.totp_state {
CredVerifyState::Init => {
// TOTP hasn't been run yet, we need it before
// we indicate the pw status.
lsecurity!(au, "Handler::TOTPPassword -> Result::Continue - TOTP -, password Fail");
CredState::Continue(vec![AuthAllowed::TOTP])
}
CredVerifyState::Success => {
// The totp is success, but password bad.
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - TOTP OK, password Fail");
CredState::Denied(BAD_PASSWORD_MSG)
}
CredVerifyState::Fail => {
// The totp already failed, remind.
// this should be an impossible state.
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - TOTP Fail, password Fail");
CredState::Denied(BAD_TOTP_MSG)
}
}
}
}
AuthCredential::TOTP(totp_chal) => {
// if totp -> check
if pw_totp.totp.verify(*totp_chal, ts) {
pw_totp.totp_state = CredVerifyState::Success;
match pw_totp.pw_state {
CredVerifyState::Init => {
lsecurity!(au, "Handler::TOTPPassword -> Result::Continue - TOTP OK, password -");
CredState::Continue(vec![AuthAllowed::Password])
}
CredVerifyState::Success => {
lsecurity!(au, "Handler::TOTPPassword -> Result::Success - TOTP OK, password OK");
CredState::Success(Vec::new())
}
CredVerifyState::Fail => {
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - TOTP OK, password Fail");
CredState::Denied(BAD_PASSWORD_MSG)
}
}
} else {
pw_totp.totp_state = CredVerifyState::Fail;
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - TOTP Fail, password -");
CredState::Denied(BAD_TOTP_MSG)
}
}
// All other cases fail.
_ => {
lsecurity!(au, "Handler::TOTPPassword -> Result::Denied - invalid cred type for handler");
CredState::Denied(BAD_AUTH_TYPE_MSG)
}
} // end match cred
}
} // end match acc
},
) // end fold
} // end CredHandler::TOTPPassword
} }
} }
@ -344,6 +392,7 @@ impl AuthSession {
au: &mut AuditScope, au: &mut AuditScope,
creds: &[AuthCredential], creds: &[AuthCredential],
time: &Duration, time: &Duration,
async_tx: &Sender<DelayedAction>,
) -> Result<AuthState, OperationError> { ) -> Result<AuthState, OperationError> {
if self.finished { if self.finished {
return Err(OperationError::InvalidAuthState( return Err(OperationError::InvalidAuthState(
@ -360,7 +409,7 @@ impl AuthSession {
return Ok(AuthState::Denied(BAD_CREDENTIALS.to_string())); return Ok(AuthState::Denied(BAD_CREDENTIALS.to_string()));
} }
match self.handler.validate(au, creds, time) { match self.handler.validate(au, creds, time, self.account.uuid, async_tx) {
CredState::Success(claims) => { CredState::Success(claims) => {
lsecurity!(au, "Successful cred handling"); lsecurity!(au, "Successful cred handling");
self.finished = true; self.finished = true;
@ -414,6 +463,10 @@ mod tests {
}; };
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthState}; use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthState};
use std::time::Duration; use std::time::Duration;
// use async_std::task;
use tokio::sync::mpsc::unbounded_channel as unbounded;
// , UnboundedSender as Sender, UnboundedReceiver as Receiver};
#[test] #[test]
fn test_idm_authsession_anonymous_auth_mech() { fn test_idm_authsession_anonymous_auth_mech() {
@ -422,6 +475,7 @@ mod tests {
uuid::Uuid::new_v4(), uuid::Uuid::new_v4(),
None, None,
); );
let anon_account = entry_str_to_account!(JSON_ANONYMOUS_V1); let anon_account = entry_str_to_account!(JSON_ANONYMOUS_V1);
let session = AuthSession::new(&mut audit, anon_account, None); let session = AuthSession::new(&mut audit, anon_account, None);
@ -445,6 +499,7 @@ mod tests {
); );
let anon_account = entry_str_to_account!(JSON_ANONYMOUS_V1); let anon_account = entry_str_to_account!(JSON_ANONYMOUS_V1);
let mut session = AuthSession::new(&mut audit, anon_account, None); let mut session = AuthSession::new(&mut audit, anon_account, None);
let (async_tx, mut async_rx) = unbounded();
let attempt = vec![ let attempt = vec![
AuthCredential::Anonymous, AuthCredential::Anonymous,
@ -453,12 +508,13 @@ mod tests {
AuthCredential::Anonymous, AuthCredential::Anonymous,
AuthCredential::Anonymous, AuthCredential::Anonymous,
]; ];
match session.validate_creds(&mut audit, &attempt, &Duration::from_secs(0)) { match session.validate_creds(&mut audit, &attempt, &Duration::from_secs(0), &async_tx) {
Ok(AuthState::Denied(msg)) => { Ok(AuthState::Denied(msg)) => {
assert!(msg == BAD_CREDENTIALS); assert!(msg == BAD_CREDENTIALS);
} }
_ => panic!(), _ => panic!(),
}; };
assert!(async_rx.try_recv().is_err());
audit.write_log(); audit.write_log();
} }
@ -499,6 +555,7 @@ mod tests {
// now check // now check
let mut session = AuthSession::new(&mut audit, account.clone(), None); let mut session = AuthSession::new(&mut audit, account.clone(), None);
let (async_tx, mut async_rx) = unbounded();
let auth_mechs = session.valid_auth_mechs(); let auth_mechs = session.valid_auth_mechs();
assert!( assert!(
@ -509,17 +566,18 @@ mod tests {
); );
let attempt = vec![AuthCredential::Password("bad_password".to_string())]; let attempt = vec![AuthCredential::Password("bad_password".to_string())];
match session.validate_creds(&mut audit, &attempt, &Duration::from_secs(0)) { match session.validate_creds(&mut audit, &attempt, &Duration::from_secs(0), &async_tx) {
Ok(AuthState::Denied(_)) => {} Ok(AuthState::Denied(_)) => {}
_ => panic!(), _ => panic!(),
}; };
let mut session = AuthSession::new(&mut audit, account, None); let mut session = AuthSession::new(&mut audit, account, None);
let attempt = vec![AuthCredential::Password("test_password".to_string())]; let attempt = vec![AuthCredential::Password("test_password".to_string())];
match session.validate_creds(&mut audit, &attempt, &Duration::from_secs(0)) { match session.validate_creds(&mut audit, &attempt, &Duration::from_secs(0), &async_tx) {
Ok(AuthState::Success(_)) => {} Ok(AuthState::Success(_)) => {}
_ => panic!(), _ => panic!(),
}; };
assert!(async_rx.try_recv().is_err());
audit.write_log(); audit.write_log();
} }
@ -560,6 +618,7 @@ mod tests {
// now check // now check
let session = AuthSession::new(&mut audit, account.clone(), None); let session = AuthSession::new(&mut audit, account.clone(), None);
let (async_tx, mut async_rx) = unbounded();
let auth_mechs = session.valid_auth_mechs(); let auth_mechs = session.valid_auth_mechs();
assert!(auth_mechs.iter().fold(true, |acc, x| match x { assert!(auth_mechs.iter().fold(true, |acc, x| match x {
AuthAllowed::Password => acc, AuthAllowed::Password => acc,
@ -572,7 +631,7 @@ mod tests {
// check send anon (fail) // check send anon (fail)
{ {
let mut session = AuthSession::new(&mut audit, account.clone(), None); let mut session = AuthSession::new(&mut audit, account.clone(), None);
match session.validate_creds(&mut audit, &vec![AuthCredential::Anonymous], &ts) { match session.validate_creds(&mut audit, &vec![AuthCredential::Anonymous], &ts, &async_tx) {
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG), Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
_ => panic!(), _ => panic!(),
}; };
@ -588,11 +647,12 @@ mod tests {
&mut audit, &mut audit,
&vec![AuthCredential::Password(pw_bad.to_string())], &vec![AuthCredential::Password(pw_bad.to_string())],
&ts, &ts,
&async_tx
) { ) {
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::TOTP]), Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::TOTP]),
_ => panic!(), _ => panic!(),
}; };
match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_good)], &ts) { match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_good)], &ts, &async_tx) {
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG), Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
_ => panic!(), _ => panic!(),
}; };
@ -605,11 +665,12 @@ mod tests {
&mut audit, &mut audit,
&vec![AuthCredential::Password(pw_bad.to_string())], &vec![AuthCredential::Password(pw_bad.to_string())],
&ts, &ts,
&async_tx
) { ) {
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::TOTP]), Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::TOTP]),
_ => panic!(), _ => panic!(),
}; };
match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_bad)], &ts) { match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_bad)], &ts, &async_tx) {
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG), Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG),
_ => panic!(), _ => panic!(),
}; };
@ -623,11 +684,12 @@ mod tests {
&mut audit, &mut audit,
&vec![AuthCredential::Password(pw_good.to_string())], &vec![AuthCredential::Password(pw_good.to_string())],
&ts, &ts,
&async_tx
) { ) {
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::TOTP]), Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::TOTP]),
_ => panic!(), _ => panic!(),
}; };
match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_good)], &ts) { match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_good)], &ts, &async_tx) {
Ok(AuthState::Success(_)) => {} Ok(AuthState::Success(_)) => {}
_ => panic!(), _ => panic!(),
}; };
@ -641,11 +703,12 @@ mod tests {
&mut audit, &mut audit,
&vec![AuthCredential::Password(pw_good.to_string())], &vec![AuthCredential::Password(pw_good.to_string())],
&ts, &ts,
&async_tx
) { ) {
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::TOTP]), Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::TOTP]),
_ => panic!(), _ => panic!(),
}; };
match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_bad)], &ts) { match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_bad)], &ts, &async_tx) {
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG), Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG),
_ => panic!(), _ => panic!(),
}; };
@ -654,7 +717,7 @@ mod tests {
// check send bad totp, should fail immediate // check send bad totp, should fail immediate
{ {
let mut session = AuthSession::new(&mut audit, account.clone(), None); let mut session = AuthSession::new(&mut audit, account.clone(), None);
match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_bad)], &ts) { match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_bad)], &ts, &async_tx) {
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG), Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG),
_ => panic!(), _ => panic!(),
}; };
@ -664,7 +727,7 @@ mod tests {
// then bad pw, fail pw // then bad pw, fail pw
{ {
let mut session = AuthSession::new(&mut audit, account.clone(), None); let mut session = AuthSession::new(&mut audit, account.clone(), None);
match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_good)], &ts) { match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_good)], &ts, &async_tx) {
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]), Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
_ => panic!(), _ => panic!(),
}; };
@ -672,6 +735,7 @@ mod tests {
&mut audit, &mut audit,
&vec![AuthCredential::Password(pw_bad.to_string())], &vec![AuthCredential::Password(pw_bad.to_string())],
&ts, &ts,
&async_tx
) { ) {
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG), Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
_ => panic!(), _ => panic!(),
@ -682,7 +746,7 @@ mod tests {
// then good pw, success // then good pw, success
{ {
let mut session = AuthSession::new(&mut audit, account.clone(), None); let mut session = AuthSession::new(&mut audit, account.clone(), None);
match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_good)], &ts) { match session.validate_creds(&mut audit, &vec![AuthCredential::TOTP(totp_good)], &ts, &async_tx) {
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]), Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
_ => panic!(), _ => panic!(),
}; };
@ -690,6 +754,7 @@ mod tests {
&mut audit, &mut audit,
&vec![AuthCredential::Password(pw_good.to_string())], &vec![AuthCredential::Password(pw_good.to_string())],
&ts, &ts,
&async_tx
) { ) {
Ok(AuthState::Success(_)) => {} Ok(AuthState::Success(_)) => {}
_ => panic!(), _ => panic!(),
@ -708,6 +773,7 @@ mod tests {
AuthCredential::TOTP(totp_bad), AuthCredential::TOTP(totp_bad),
], ],
&ts, &ts,
&async_tx
) { ) {
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG), Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG),
_ => panic!(), _ => panic!(),
@ -723,6 +789,7 @@ mod tests {
AuthCredential::Password(pw_bad.to_string()), AuthCredential::Password(pw_bad.to_string()),
], ],
&ts, &ts,
&async_tx
) { ) {
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG), Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
_ => panic!(), _ => panic!(),
@ -738,6 +805,7 @@ mod tests {
AuthCredential::Password(pw_good.to_string()), AuthCredential::Password(pw_good.to_string()),
], ],
&ts, &ts,
&async_tx
) { ) {
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG), Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG),
_ => panic!(), _ => panic!(),
@ -753,12 +821,14 @@ mod tests {
AuthCredential::Password(pw_good.to_string()), AuthCredential::Password(pw_good.to_string()),
], ],
&ts, &ts,
&async_tx
) { ) {
Ok(AuthState::Success(_)) => {} Ok(AuthState::Success(_)) => {}
_ => panic!(), _ => panic!(),
}; };
} }
assert!(async_rx.try_recv().is_err());
audit.write_log(); audit.write_log();
} }
} }

View file

@ -0,0 +1,17 @@
use uuid::Uuid;
pub(crate) enum DelayedAction {
PwUpgrade(PasswordUpgrade),
UnixPwUpgrade(UnixPasswordUpgrade),
}
pub(crate) struct PasswordUpgrade {
pub target_uuid: Uuid,
pub existing_password: String,
pub appid: Option<String>,
}
pub(crate) struct UnixPasswordUpgrade {
pub target_uuid: Uuid,
pub existing_password: String,
}

View file

@ -1,3 +1,4 @@
pub(crate) mod delayed;
pub(crate) mod account; pub(crate) mod account;
pub(crate) mod authsession; pub(crate) mod authsession;
pub(crate) mod claim; pub(crate) mod claim;

View file

@ -19,6 +19,8 @@ use crate::server::{QueryServer, QueryServerTransaction, QueryServerWriteTransac
use crate::utils::{password_from_random, readable_password_from_random, uuid_from_duration, SID}; use crate::utils::{password_from_random, readable_password_from_random, uuid_from_duration, SID};
use crate::value::PartialValue; use crate::value::PartialValue;
use crate::idm::delayed::{DelayedAction, PasswordUpgrade, UnixPasswordUpgrade};
use kanidm_proto::v1::AuthState; use kanidm_proto::v1::AuthState;
use kanidm_proto::v1::OperationError; use kanidm_proto::v1::OperationError;
use kanidm_proto::v1::RadiusAuthToken; use kanidm_proto::v1::RadiusAuthToken;
@ -27,6 +29,12 @@ use kanidm_proto::v1::SetCredentialResponse;
use kanidm_proto::v1::UnixGroupToken; use kanidm_proto::v1::UnixGroupToken;
use kanidm_proto::v1::UnixUserToken; use kanidm_proto::v1::UnixUserToken;
// use crossbeam::channel::{unbounded, Sender, Receiver, TryRecvError};
use tokio::sync::mpsc::{unbounded_channel as unbounded, UnboundedSender as Sender, UnboundedReceiver as Receiver};
#[cfg(test)]
use tokio::sync::mpsc::error::TryRecvError;
use concread::collections::bptree::*; use concread::collections::bptree::*;
use rand::prelude::*; use rand::prelude::*;
use std::time::Duration; use std::time::Duration;
@ -45,6 +53,7 @@ pub struct IdmServer {
// The configured crypto policy for the IDM server. Later this could be transactional // The configured crypto policy for the IDM server. Later this could be transactional
// and loaded from the db similar to access. But today it's just to allow dynamic pbkdf2rounds // and loaded from the db similar to access. But today it's just to allow dynamic pbkdf2rounds
crypto_policy: CryptoPolicy, crypto_policy: CryptoPolicy,
async_tx: Sender<DelayedAction>,
} }
pub struct IdmServerWriteTransaction<'a> { pub struct IdmServerWriteTransaction<'a> {
@ -55,6 +64,8 @@ pub struct IdmServerWriteTransaction<'a> {
pub qs_read: QueryServerReadTransaction<'a>, pub qs_read: QueryServerReadTransaction<'a>,
// thread/server id // thread/server id
sid: SID, sid: SID,
// For flagging eventual actions.
async_tx: Sender<DelayedAction>,
} }
pub struct IdmServerProxyReadTransaction<'a> { pub struct IdmServerProxyReadTransaction<'a> {
@ -73,9 +84,13 @@ pub struct IdmServerProxyWriteTransaction<'a> {
crypto_policy: &'a CryptoPolicy, crypto_policy: &'a CryptoPolicy,
} }
pub struct IdmServerDelayed {
async_rx: Receiver<DelayedAction>,
}
impl IdmServer { impl IdmServer {
// TODO #59: Make number of authsessions configurable!!! // TODO #59: Make number of authsessions configurable!!!
pub fn new(qs: QueryServer) -> IdmServer { pub fn new(qs: QueryServer) -> (IdmServer, IdmServerDelayed) {
// This is calculated back from: // This is calculated back from:
// 500 auths / thread -> 0.002 sec per op // 500 auths / thread -> 0.002 sec per op
// we can then spend up to ~0.001s hashing // we can then spend up to ~0.001s hashing
@ -84,12 +99,16 @@ impl IdmServer {
// overtime, we could increase this as auth parallelism // overtime, we could increase this as auth parallelism
// improves. // improves.
let crypto_policy = CryptoPolicy::time_target(Duration::from_millis(1)); let crypto_policy = CryptoPolicy::time_target(Duration::from_millis(1));
IdmServer { let (async_tx, async_rx) = unbounded();
(IdmServer {
sessions: BptreeMap::new(), sessions: BptreeMap::new(),
mfareg_sessions: BptreeMap::new(), mfareg_sessions: BptreeMap::new(),
qs, qs,
crypto_policy, crypto_policy,
} async_tx
}, IdmServerDelayed {
async_rx
})
} }
pub fn write(&self) -> IdmServerWriteTransaction { pub fn write(&self) -> IdmServerWriteTransaction {
@ -102,6 +121,7 @@ impl IdmServer {
// qs: &self.qs, // qs: &self.qs,
qs_read: self.qs.read(), qs_read: self.qs.read(),
sid, sid,
async_tx: self.async_tx.clone(),
} }
} }
@ -123,6 +143,47 @@ impl IdmServer {
crypto_policy: &self.crypto_policy, crypto_policy: &self.crypto_policy,
} }
} }
pub(crate) fn delayed_action(&self,
au: &mut AuditScope,
ts: Duration,
da: DelayedAction,
) -> Result<bool, OperationError> {
let mut pw = self.proxy_write(ts);
pw.process_delayedaction(au, da)
.and_then(|_| {
pw.commit(au)
})
.map(|()| true)
}
}
impl IdmServerDelayed {
#[cfg(test)]
pub fn is_empty_or_panic(&mut self) {
assert!(self.async_rx.try_recv().is_err());
}
#[cfg(test)]
pub(crate) fn try_recv(&mut self) -> Result<DelayedAction, OperationError> {
self.async_rx.try_recv().map_err(|e|
match e {
TryRecvError::Empty => OperationError::InvalidState,
TryRecvError::Closed => OperationError::QueueDisconnected,
}
)
}
pub(crate) async fn temp_drop_all(&mut self) {
loop {
match self.async_rx.recv().await {
// Drop it
Some(_) => {},
// Channel has closed
None => return,
}
}
}
} }
impl<'a> IdmServerWriteTransaction<'a> { impl<'a> IdmServerWriteTransaction<'a> {
@ -229,7 +290,7 @@ impl<'a> IdmServerWriteTransaction<'a> {
// Basically throw them at the auth_session and see what // Basically throw them at the auth_session and see what
// falls out. // falls out.
auth_session auth_session
.validate_creds(au, &creds.creds, &ct) .validate_creds(au, &creds.creds, &ct, &self.async_tx)
.map(|aus| { .map(|aus| {
AuthResult { AuthResult {
// Is this right? // Is this right?
@ -263,7 +324,7 @@ impl<'a> IdmServerWriteTransaction<'a> {
})?; })?;
// Validate the unix_pw - this checks the account/cred lock states. // Validate the unix_pw - this checks the account/cred lock states.
account.verify_unix_credential(au, uae.cleartext.as_str()) account.verify_unix_credential(au, uae.cleartext.as_str(), &self.async_tx)
} }
pub fn auth_ldap( pub fn auth_ldap(
@ -303,7 +364,7 @@ impl<'a> IdmServerWriteTransaction<'a> {
let account = let account =
UnixUserAccount::try_from_entry_ro(au, &account_entry, &mut self.qs_read)?; UnixUserAccount::try_from_entry_ro(au, &account_entry, &mut self.qs_read)?;
if account if account
.verify_unix_credential(au, lae.cleartext.as_str())? .verify_unix_credential(au, lae.cleartext.as_str(), &self.async_tx)?
.is_some() .is_some()
{ {
// Get the anon uat // Get the anon uat
@ -797,6 +858,85 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
Ok(next) Ok(next)
} }
// -- delayed action processing --
fn process_pwupgrade(
&mut self,
au: &mut AuditScope,
pwu: PasswordUpgrade,
) -> Result<(), OperationError> {
// get the account
let account = self.target_to_account(au, &pwu.target_uuid)?;
// check, does the pw still match?
let same = account.check_credential_pw(pwu.existing_password.as_str(), &pwu.appid)?;
// if yes, gen the pw mod and apply.
if same {
let modlist = account
.gen_password_mod(pwu.existing_password.as_str(), &pwu.appid, self.crypto_policy)
.map_err(|e| {
ladmin_error!(au, "Unable to generate password mod {:?}", e);
e
})?;
self.qs_write.internal_modify(
au,
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&pwu.target_uuid))),
&modlist)
} else {
// No action needed, it's probably been changed/updated already.
Ok(())
}
}
fn process_unixpwupgrade(
&mut self,
au: &mut AuditScope,
pwu: UnixPasswordUpgrade,
) -> Result<(), OperationError> {
let account = self
.qs_write
.internal_search_uuid(au, &pwu.target_uuid)
.and_then(|account_entry| {
UnixUserAccount::try_from_entry_rw(au, &account_entry, &mut self.qs_write)
})
.map_err(|e| {
ladmin_error!(au, "Failed to start unix pw upgrade -> {:?}", e);
e
})?;
let same = account.check_existing_pw(pwu.existing_password.as_str())?;
if same {
let modlist = account
.gen_password_mod(pwu.existing_password.as_str(), self.crypto_policy)
.map_err(|e| {
ladmin_error!(au, "Unable to generate password mod {:?}", e);
e
})?;
self.qs_write.internal_modify(
au,
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&pwu.target_uuid))),
&modlist)
} else {
Ok(())
}
}
fn process_delayedaction(
&mut self,
au: &mut AuditScope,
da: DelayedAction,
) -> Result<(), OperationError> {
match da {
DelayedAction::PwUpgrade(pwu) =>
self.process_pwupgrade(au, pwu),
DelayedAction::UnixPwUpgrade(upwu) =>
self.process_unixpwupgrade(au, upwu),
}
}
pub fn commit(self, au: &mut AuditScope) -> Result<(), OperationError> { pub fn commit(self, au: &mut AuditScope) -> Result<(), OperationError> {
lperf_trace_segment!(au, "idm::server::IdmServerWriteTransaction::commit", || { lperf_trace_segment!(au, "idm::server::IdmServerWriteTransaction::commit", || {
self.mfareg_sessions.commit(); self.mfareg_sessions.commit();
@ -814,7 +954,7 @@ mod tests {
}; };
use crate::credential::policy::CryptoPolicy; use crate::credential::policy::CryptoPolicy;
use crate::credential::totp::TOTP; use crate::credential::totp::TOTP;
use crate::credential::Credential; use crate::credential::{Credential, Password};
use crate::entry::{Entry, EntryInit, EntryNew}; use crate::entry::{Entry, EntryInit, EntryNew};
use crate::event::{AuthEvent, AuthResult, CreateEvent, ModifyEvent}; use crate::event::{AuthEvent, AuthResult, CreateEvent, ModifyEvent};
use crate::idm::event::{ use crate::idm::event::{
@ -824,15 +964,19 @@ mod tests {
}; };
use crate::modify::{Modify, ModifyList}; use crate::modify::{Modify, ModifyList};
use crate::value::{PartialValue, Value}; use crate::value::{PartialValue, Value};
// use crate::idm::delayed::{PasswordUpgrade, DelayedAction};
use kanidm_proto::v1::OperationError; use kanidm_proto::v1::OperationError;
use kanidm_proto::v1::SetCredentialResponse; use kanidm_proto::v1::SetCredentialResponse;
use kanidm_proto::v1::{AuthAllowed, AuthState}; use kanidm_proto::v1::{AuthAllowed, AuthState};
use crate::audit::AuditScope; use crate::audit::AuditScope;
use crate::idm::server::IdmServer; use crate::idm::server::IdmServer;
// , IdmServerDelayed;
use crate::server::QueryServer; use crate::server::QueryServer;
use crate::utils::duration_from_epoch_now; use crate::utils::duration_from_epoch_now;
use std::time::Duration; use std::time::Duration;
use std::convert::TryFrom;
use uuid::Uuid; use uuid::Uuid;
const TEST_PASSWORD: &'static str = "ntaoeuntnaoeuhraohuercahu😍"; const TEST_PASSWORD: &'static str = "ntaoeuntnaoeuhraohuercahu😍";
@ -842,7 +986,8 @@ mod tests {
#[test] #[test]
fn test_idm_anonymous_auth() { fn test_idm_anonymous_auth() {
run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed,
au: &mut AuditScope| {
let sid = { let sid = {
// Start and test anonymous auth. // Start and test anonymous auth.
let mut idms_write = idms.write(); let mut idms_write = idms.write();
@ -928,7 +1073,7 @@ mod tests {
// Test sending anonymous but with no session init. // Test sending anonymous but with no session init.
#[test] #[test]
fn test_idm_anonymous_auth_invalid_states() { fn test_idm_anonymous_auth_invalid_states() {
run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
{ {
let mut idms_write = idms.write(); let mut idms_write = idms.write();
let sid = Uuid::new_v4(); let sid = Uuid::new_v4();
@ -999,14 +1144,13 @@ mod tests {
sessionid sessionid
} }
#[test] fn check_admin_password(
fn test_idm_simple_password_auth() { idms: &IdmServer, au: &mut AuditScope, pw: &str
run_idm_test!(|qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { ) {
init_admin_w_password(au, qs, TEST_PASSWORD).expect("Failed to setup admin account");
let sid = init_admin_authsession_sid(idms, au); let sid = init_admin_authsession_sid(idms, au);
let mut idms_write = idms.write(); let mut idms_write = idms.write();
let anon_step = AuthEvent::cred_step_password(sid, TEST_PASSWORD); let anon_step = AuthEvent::cred_step_password(sid, pw);
// Expect success // Expect success
let r2 = idms_write.auth(au, &anon_step, Duration::from_secs(TEST_CURRENT_TIME)); let r2 = idms_write.auth(au, &anon_step, Duration::from_secs(TEST_CURRENT_TIME));
@ -1036,12 +1180,19 @@ mod tests {
}; };
idms_write.commit(au).expect("Must not fail"); idms_write.commit(au).expect("Must not fail");
}
#[test]
fn test_idm_simple_password_auth() {
run_idm_test!(|qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
init_admin_w_password(au, qs, TEST_PASSWORD).expect("Failed to setup admin account");
check_admin_password(idms, au, TEST_PASSWORD);
}) })
} }
#[test] #[test]
fn test_idm_simple_password_spn_auth() { fn test_idm_simple_password_spn_auth() {
run_idm_test!(|qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
init_admin_w_password(au, qs, TEST_PASSWORD).expect("Failed to setup admin account"); init_admin_w_password(au, qs, TEST_PASSWORD).expect("Failed to setup admin account");
let mut idms_write = idms.write(); let mut idms_write = idms.write();
let admin_init = AuthEvent::named_init("admin@example.com"); let admin_init = AuthEvent::named_init("admin@example.com");
@ -1098,7 +1249,7 @@ mod tests {
#[test] #[test]
fn test_idm_simple_password_invalid() { fn test_idm_simple_password_invalid() {
run_idm_test!(|qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
init_admin_w_password(au, qs, TEST_PASSWORD).expect("Failed to setup admin account"); init_admin_w_password(au, qs, TEST_PASSWORD).expect("Failed to setup admin account");
let sid = init_admin_authsession_sid(idms, au); let sid = init_admin_authsession_sid(idms, au);
let mut idms_write = idms.write(); let mut idms_write = idms.write();
@ -1137,7 +1288,7 @@ mod tests {
#[test] #[test]
fn test_idm_simple_password_reset() { fn test_idm_simple_password_reset() {
run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, TEST_PASSWORD, None); let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, TEST_PASSWORD, None);
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()); let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());
@ -1149,7 +1300,7 @@ mod tests {
#[test] #[test]
fn test_idm_anonymous_set_password_denied() { fn test_idm_anonymous_set_password_denied() {
run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
let pce = PasswordChangeEvent::new_internal(&UUID_ANONYMOUS, TEST_PASSWORD, None); let pce = PasswordChangeEvent::new_internal(&UUID_ANONYMOUS, TEST_PASSWORD, None);
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()); let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());
@ -1160,7 +1311,7 @@ mod tests {
#[test] #[test]
fn test_idm_session_expire() { fn test_idm_session_expire() {
run_idm_test!(|qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
init_admin_w_password(au, qs, TEST_PASSWORD).expect("Failed to setup admin account"); init_admin_w_password(au, qs, TEST_PASSWORD).expect("Failed to setup admin account");
let sid = init_admin_authsession_sid(idms, au); let sid = init_admin_authsession_sid(idms, au);
let mut idms_write = idms.write(); let mut idms_write = idms.write();
@ -1179,7 +1330,7 @@ mod tests {
#[test] #[test]
fn test_idm_regenerate_radius_secret() { fn test_idm_regenerate_radius_secret() {
run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()); let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());
let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_ADMIN.clone()); let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_ADMIN.clone());
@ -1197,7 +1348,7 @@ mod tests {
#[test] #[test]
fn test_idm_radiusauthtoken() { fn test_idm_radiusauthtoken() {
run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()); let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());
let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_ADMIN.clone()); let rrse = RegenerateRadiusSecretEvent::new_internal(UUID_ADMIN.clone());
let r1 = idms_prox_write let r1 = idms_prox_write
@ -1218,7 +1369,7 @@ mod tests {
#[test] #[test]
fn test_idm_simple_password_reject_weak() { fn test_idm_simple_password_reject_weak() {
run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
// len check // len check
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()); let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());
@ -1251,7 +1402,7 @@ mod tests {
#[test] #[test]
fn test_idm_unixusertoken() { fn test_idm_unixusertoken() {
run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
let idms_prox_write = idms.proxy_write(duration_from_epoch_now()); let idms_prox_write = idms.proxy_write(duration_from_epoch_now());
// Modify admin to have posixaccount // Modify admin to have posixaccount
let me_posix = unsafe { let me_posix = unsafe {
@ -1323,7 +1474,7 @@ mod tests {
#[test] #[test]
fn test_idm_simple_unix_password_reset() { fn test_idm_simple_unix_password_reset() {
run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()); let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());
// make the admin a valid posix account // make the admin a valid posix account
let me_posix = unsafe { let me_posix = unsafe {
@ -1385,7 +1536,7 @@ mod tests {
#[test] #[test]
fn test_idm_totp_registration() { fn test_idm_totp_registration() {
run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
let ct = duration_from_epoch_now(); let ct = duration_from_epoch_now();
let expire = Duration::from_secs(ct.as_secs() + MFAREG_SESSION_TIMEOUT + 2); let expire = Duration::from_secs(ct.as_secs() + MFAREG_SESSION_TIMEOUT + 2);
let mut idms_prox_write = idms.proxy_write(ct.clone()); let mut idms_prox_write = idms.proxy_write(ct.clone());
@ -1513,4 +1664,92 @@ mod tests {
assert!(idms_prox_write.commit(au).is_ok()); assert!(idms_prox_write.commit(au).is_ok());
}) })
} }
#[test]
fn test_idm_simple_password_upgrade() {
run_idm_test!(|qs: &QueryServer, idms: &IdmServer, idms_delayed: &mut IdmServerDelayed, au: &mut AuditScope| {
// Assert the delayed action queue is empty
idms_delayed.is_empty_or_panic();
// Setup the admin w_ an imported password.
{
let qs_write = qs.write(duration_from_epoch_now());
// now modify and provide a primary credential.
let me_inv_m = unsafe {
ModifyEvent::new_internal_invalid(
filter!(f_eq("name", PartialValue::new_iname("admin"))),
ModifyList::new_list(vec![Modify::Present(
"password_import".to_string(),
Value::from("{SSHA512}JwrSUHkI7FTAfHRVR6KoFlSN0E3dmaQWARjZ+/UsShYlENOqDtFVU77HJLLrY2MuSp0jve52+pwtdVl2QUAHukQ0XUf5LDtM")
)]),
)
};
// go!
assert!(qs_write.modify(au, &me_inv_m).is_ok());
qs_write.commit(au).expect("failed to commit");
}
// Still empty
idms_delayed.is_empty_or_panic();
// Do an auth, this will trigger the action to send.
check_admin_password(idms, au, "password");
// process it.
let da = idms_delayed.try_recv().expect("invalid");
assert!(Ok(true) == idms.delayed_action(au, duration_from_epoch_now(), da));
// Check the admin pw still matches
check_admin_password(idms, au, "password");
// No delayed action was queued.
idms_delayed.is_empty_or_panic();
})
}
#[test]
fn test_idm_unix_password_upgrade() {
run_idm_test!(|qs: &QueryServer, idms: &IdmServer, idms_delayed: &mut IdmServerDelayed, au: &mut AuditScope| {
// Assert the delayed action queue is empty
idms_delayed.is_empty_or_panic();
// Setup the admin with an imported unix pw.
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());
let im_pw = "{SSHA512}JwrSUHkI7FTAfHRVR6KoFlSN0E3dmaQWARjZ+/UsShYlENOqDtFVU77HJLLrY2MuSp0jve52+pwtdVl2QUAHukQ0XUf5LDtM";
let pw = Password::try_from(im_pw).expect("failed to parse");
let cred = Credential::new_from_password(pw);
let v_cred = Value::new_credential("unix", cred);
let me_posix = unsafe {
ModifyEvent::new_internal_invalid(
filter!(f_eq("name", PartialValue::new_iname("admin"))),
ModifyList::new_list(vec![
Modify::Present("class".to_string(), Value::new_class("posixaccount")),
Modify::Present("gidnumber".to_string(), Value::new_uint32(2001)),
Modify::Present("unix_password".to_string(), v_cred),
]),
)
};
assert!(idms_prox_write.qs_write.modify(au, &me_posix).is_ok());
assert!(idms_prox_write.commit(au).is_ok());
idms_delayed.is_empty_or_panic();
// Get the auth ready.
let uuae = UnixUserAuthEvent::new_internal(&UUID_ADMIN, "password");
let mut idms_write = idms.write();
let a1 = idms_write.auth_unix(au, &uuae, Duration::from_secs(TEST_CURRENT_TIME));
match a1 {
Ok(Some(_tok)) => {}
_ => assert!(false),
};
idms_write.commit(au).expect("Must not fail");
// The upgrade was queued
// Process it.
let da = idms_delayed.try_recv().expect("invalid");
assert!(Ok(true) == idms.delayed_action(au, duration_from_epoch_now(), da));
// Go again
let mut idms_write = idms.write();
let a2 = idms_write.auth_unix(au, &uuae, Duration::from_secs(TEST_CURRENT_TIME));
match a2 {
Ok(Some(_tok)) => {}
_ => assert!(false),
};
idms_write.commit(au).expect("Must not fail");
// No delayed action was queued.
idms_delayed.is_empty_or_panic();
})
}
} }

View file

@ -13,6 +13,11 @@ use crate::value::{PartialValue, Value};
use kanidm_proto::v1::OperationError; use kanidm_proto::v1::OperationError;
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken}; use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
use crate::idm::delayed::{DelayedAction, UnixPasswordUpgrade};
// use crossbeam::channel::Sender;
use tokio::sync::mpsc::UnboundedSender as Sender;
use std::iter; use std::iter;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -165,6 +170,7 @@ impl UnixUserAccount {
&self, &self,
au: &mut AuditScope, au: &mut AuditScope,
cleartext: &str, cleartext: &str,
async_tx: &Sender<DelayedAction>,
) -> Result<Option<UnixUserToken>, OperationError> { ) -> Result<Option<UnixUserToken>, OperationError> {
// TODO #59: Is the cred locked? // TODO #59: Is the cred locked?
// is the cred some or none? // is the cred some or none?
@ -173,6 +179,18 @@ impl UnixUserAccount {
Some(pw) => { Some(pw) => {
if pw.verify(cleartext)? { if pw.verify(cleartext)? {
lsecurity!(au, "Successful unix cred handling"); lsecurity!(au, "Successful unix cred handling");
if pw.requires_upgrade() {
async_tx.send(
DelayedAction::UnixPwUpgrade(UnixPasswordUpgrade {
target_uuid: self.uuid,
existing_password: cleartext.to_string(),
})
).map_err(|_| {
ladmin_error!(au, "failed to queue delayed action - unix password upgrade");
OperationError::InvalidState
})?;
}
Some(self.to_unixusertoken()).transpose() Some(self.to_unixusertoken()).transpose()
} else { } else {
// Failed to auth // Failed to auth
@ -195,6 +213,25 @@ impl UnixUserAccount {
} }
} }
} }
pub(crate) fn check_existing_pw(
&self,
cleartext: &str,
) -> Result<bool, OperationError> {
match &self.cred {
Some(cred) => match &cred.password {
Some(pw) => {
pw.verify(cleartext)
}
None => {
Err(OperationError::InvalidState)
}
}
None => {
Err(OperationError::InvalidState)
}
}
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -490,7 +490,7 @@ mod tests {
#[test] #[test]
fn test_ldap_simple_bind() { fn test_ldap_simple_bind() {
run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed, au: &mut AuditScope| {
let ldaps = LdapServer::new(au, idms).expect("failed to start ldap"); let ldaps = LdapServer::new(au, idms).expect("failed to start ldap");
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()); let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());

View file

@ -117,7 +117,7 @@ macro_rules! run_idm_test {
($test_fn:expr) => {{ ($test_fn:expr) => {{
use crate::audit::AuditScope; use crate::audit::AuditScope;
use crate::be::{Backend, FsType}; use crate::be::{Backend, FsType};
use crate::idm::server::IdmServer; use crate::idm::server::{IdmServer, IdmServerDelayed};
use crate::schema::Schema; use crate::schema::Schema;
use crate::server::QueryServer; use crate::server::QueryServer;
use crate::utils::duration_from_epoch_now; use crate::utils::duration_from_epoch_now;
@ -145,12 +145,13 @@ macro_rules! run_idm_test {
.initialise_helper(&mut audit, duration_from_epoch_now()) .initialise_helper(&mut audit, duration_from_epoch_now())
.expect("init failed"); .expect("init failed");
let test_idm_server = IdmServer::new(test_server.clone()); let (test_idm_server, mut idms_delayed) = IdmServer::new(test_server.clone());
$test_fn(&test_server, &test_idm_server, &mut audit); $test_fn(&test_server, &test_idm_server, &mut idms_delayed, &mut audit);
// Any needed teardown? // Any needed teardown?
// Make sure there are no errors. // Make sure there are no errors.
assert!(test_server.verify(&mut audit).len() == 0); assert!(test_server.verify(&mut audit).len() == 0);
idms_delayed.is_empty_or_panic();
audit.write_log(); audit.write_log();
}}; }};
} }