mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
Configurable session timeouts (#1965)
* added `auth_session_expiry` and `auth_privilege_expiry` * Added `AcountPolicy` struct * spelling and stuff * added cli tools
This commit is contained in:
parent
17e4ad52f8
commit
eb7527379b
93
Cargo.lock
generated
93
Cargo.lock
generated
|
@ -155,7 +155,7 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rusticata-macros",
|
"rusticata-macros",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -426,11 +426,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.65.1"
|
version = "0.66.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5"
|
checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 2.3.3",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
@ -563,11 +563,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.79"
|
version = "1.0.82"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -771,7 +772,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -788,7 +789,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1088,9 +1089,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8810e7e2cf385b1e9b50d68264908ec367ba642c96d02edfe61c39e88e2a3c01"
|
checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -1343,7 +1344,7 @@ dependencies = [
|
||||||
"mime",
|
"mime",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
"webdriver",
|
"webdriver",
|
||||||
|
@ -2227,7 +2228,7 @@ dependencies = [
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -2273,7 +2274,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
|
@ -2300,7 +2301,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
@ -2386,7 +2387,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"sketching",
|
"sketching",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-openssl",
|
"tokio-openssl",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
@ -2439,7 +2440,7 @@ dependencies = [
|
||||||
"smartstring",
|
"smartstring",
|
||||||
"smolset",
|
"smolset",
|
||||||
"sshkeys",
|
"sshkeys",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"toml",
|
"toml",
|
||||||
|
@ -2484,7 +2485,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sketching",
|
"sketching",
|
||||||
"testkit-macros",
|
"testkit-macros",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
|
@ -2505,7 +2506,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde-wasm-bindgen 0.5.0",
|
"serde-wasm-bindgen 0.5.0",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
@ -2519,9 +2520,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kqueue"
|
name = "kqueue"
|
||||||
version = "1.0.7"
|
version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98"
|
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kqueue-sys",
|
"kqueue-sys",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -2529,9 +2530,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kqueue-sys"
|
name = "kqueue-sys"
|
||||||
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 = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
|
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -2750,9 +2751,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchit"
|
name = "matchit"
|
||||||
version = "0.7.1"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67827e6ea8ee8a7c4a72227ef4fc08957040acffdb5f122733b24fa12daff41b"
|
checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mathru"
|
name = "mathru"
|
||||||
|
@ -3310,9 +3311,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pest"
|
name = "pest"
|
||||||
version = "2.7.1"
|
version = "2.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5"
|
checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"ucd-trie",
|
"ucd-trie",
|
||||||
|
@ -3355,18 +3356,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.2"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842"
|
checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pin-project-internal",
|
"pin-project-internal",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-internal"
|
name = "pin-project-internal"
|
||||||
version = "1.1.2"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
|
checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -3824,9 +3825,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.4"
|
version = "0.38.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
|
checksum = "172891ebdceb05aa0005f533a6cbfca599ddd7d966f6f5d4d9b2e70478e70399"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.3.3",
|
"bitflags 2.3.3",
|
||||||
"errno",
|
"errno",
|
||||||
|
@ -3875,7 +3876,7 @@ dependencies = [
|
||||||
"peg",
|
"peg",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"url",
|
"url",
|
||||||
|
@ -3939,9 +3940,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selinux-sys"
|
name = "selinux-sys"
|
||||||
version = "0.6.5"
|
version = "0.6.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2b8dbf5dd0b21d466538786194081b2c4d61878e1427f7e52f99d5845432483"
|
checksum = "d56602385930248c57e45f6174a6a48e12b723d0cc2ae8f467fcbe80c0d06f41"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cc",
|
"cc",
|
||||||
|
@ -4085,7 +4086,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with_macros",
|
"serde_with_macros",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4331,9 +4332,9 @@ checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.7.0"
|
version = "3.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998"
|
checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
|
@ -4415,9 +4416,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.24"
|
version = "0.3.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b79eabcd964882a646b3584543ccabeae7869e9ac32a46f6f22b7a5bd405308b"
|
checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
@ -4719,7 +4720,7 @@ dependencies = [
|
||||||
"sharded-slab",
|
"sharded-slab",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log",
|
"tracing-log",
|
||||||
|
@ -5113,7 +5114,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
@ -5370,9 +5371,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.5.2"
|
version = "0.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7"
|
checksum = "acaaa1190073b2b101e15083c38ee8ec891b5e05cbee516521e94ec008f61e64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
@ -5401,7 +5402,7 @@ dependencies = [
|
||||||
"oid-registry",
|
"oid-registry",
|
||||||
"rusticata-macros",
|
"rusticata-macros",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -5536,5 +5537,5 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"quick-error",
|
"quick-error",
|
||||||
"regex",
|
"regex",
|
||||||
"time 0.3.24",
|
"time 0.3.25",
|
||||||
]
|
]
|
||||||
|
|
|
@ -61,6 +61,7 @@ pub enum ClientError {
|
||||||
TotpVerifyFailed(Uuid, TotpSecret),
|
TotpVerifyFailed(Uuid, TotpSecret),
|
||||||
TotpInvalidSha1(Uuid),
|
TotpInvalidSha1(Uuid),
|
||||||
JsonDecode(reqwest::Error, String),
|
JsonDecode(reqwest::Error, String),
|
||||||
|
InvalidResponseFormat(String),
|
||||||
JsonEncode(SerdeJsonError),
|
JsonEncode(SerdeJsonError),
|
||||||
SystemError,
|
SystemError,
|
||||||
ConfigParseIssue(String),
|
ConfigParseIssue(String),
|
||||||
|
|
|
@ -23,4 +23,40 @@ impl KanidmClient {
|
||||||
self.perform_delete_request_with_body("/v1/system/_attr/badlist_password", list)
|
self.perform_delete_request_with_body("/v1/system/_attr/badlist_password", list)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn system_authsession_expiry_get(&self) -> Result<u32, ClientError> {
|
||||||
|
let list: Option<[String; 1]> = self
|
||||||
|
.perform_get_request("/v1/system/_attr/authsession_expiry")
|
||||||
|
.await?;
|
||||||
|
list.ok_or(ClientError::EmptyResponse).and_then(|s| {
|
||||||
|
s[0].parse::<u32>()
|
||||||
|
.map_err(|err| ClientError::InvalidResponseFormat(err.to_string()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn system_authsession_expiry_set(&self, expiry: u32) -> Result<(), ClientError> {
|
||||||
|
self.perform_put_request(
|
||||||
|
"/v1/system/_attr/authsession_expiry",
|
||||||
|
vec![expiry.to_string()],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn system_auth_privilege_expiry_get(&self) -> Result<u32, ClientError> {
|
||||||
|
let list: Option<[String; 1]> = self
|
||||||
|
.perform_get_request("/v1/system/_attr/privilege_expiry")
|
||||||
|
.await?;
|
||||||
|
list.ok_or(ClientError::EmptyResponse).and_then(|s| {
|
||||||
|
s[0].parse::<u32>()
|
||||||
|
.map_err(|err| ClientError::InvalidResponseFormat(err.to_string()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn system_auth_privilege_expiry_set(&self, expiry: u32) -> Result<(), ClientError> {
|
||||||
|
self.perform_put_request(
|
||||||
|
"/v1/system/_attr/privilege_expiry",
|
||||||
|
vec![expiry.to_string()],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub const ATTR_ACP_TARGET_SCOPE: &str = "acp_targetscope";
|
||||||
pub const ATTR_API_TOKEN_SESSION: &str = "api_token_session";
|
pub const ATTR_API_TOKEN_SESSION: &str = "api_token_session";
|
||||||
pub const ATTR_ATTR: &str = "attr";
|
pub const ATTR_ATTR: &str = "attr";
|
||||||
pub const ATTR_ATTRIBUTENAME: &str = "attributename";
|
pub const ATTR_ATTRIBUTENAME: &str = "attributename";
|
||||||
|
pub const ATTR_AUTH_SESSION_EXPIRY: &str = "authsession_expiry";
|
||||||
pub const ATTR_BADLIST_PASSWORD: &str = "badlist_password";
|
pub const ATTR_BADLIST_PASSWORD: &str = "badlist_password";
|
||||||
pub const ATTR_CLAIM: &str = "claim";
|
pub const ATTR_CLAIM: &str = "claim";
|
||||||
pub const ATTR_CLASS: &str = "class";
|
pub const ATTR_CLASS: &str = "class";
|
||||||
|
@ -91,6 +92,7 @@ pub const ATTR_PASSWORD_IMPORT: &str = "password_import";
|
||||||
pub const ATTR_PHANTOM: &str = "phantom";
|
pub const ATTR_PHANTOM: &str = "phantom";
|
||||||
pub const ATTR_PRIMARY_CREDENTIAL: &str = "primary_credential";
|
pub const ATTR_PRIMARY_CREDENTIAL: &str = "primary_credential";
|
||||||
pub const ATTR_PRIVATE_COOKIE_KEY: &str = "private_cookie_key";
|
pub const ATTR_PRIVATE_COOKIE_KEY: &str = "private_cookie_key";
|
||||||
|
pub const ATTR_PRIVILEGE_EXPIRY: &str = "privilege_expiry";
|
||||||
pub const ATTR_RADIUS_SECRET: &str = "radius_secret";
|
pub const ATTR_RADIUS_SECRET: &str = "radius_secret";
|
||||||
pub const ATTR_RECYCLED: &str = "recycled";
|
pub const ATTR_RECYCLED: &str = "recycled";
|
||||||
pub const ATTR_REPLICATED: &str = "replicated";
|
pub const ATTR_REPLICATED: &str = "replicated";
|
||||||
|
|
|
@ -1219,6 +1219,24 @@ pub async fn system_delete_attr(
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn system_put_attr(
|
||||||
|
State(state): State<ServerState>,
|
||||||
|
Path(attr): Path<String>,
|
||||||
|
Extension(kopid): Extension<KOpId>,
|
||||||
|
Json(values): Json<Vec<String>>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SystemConfig.into()));
|
||||||
|
json_rest_event_put_attr(
|
||||||
|
state,
|
||||||
|
STR_UUID_SYSTEM_CONFIG.to_string(),
|
||||||
|
attr,
|
||||||
|
filter,
|
||||||
|
values,
|
||||||
|
kopid,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn recycle_bin_get(
|
pub async fn recycle_bin_get(
|
||||||
State(state): State<ServerState>,
|
State(state): State<ServerState>,
|
||||||
Extension(kopid): Extension<KOpId>,
|
Extension(kopid): Extension<KOpId>,
|
||||||
|
@ -1661,6 +1679,7 @@ pub fn router(state: ServerState) -> Router<ServerState> {
|
||||||
"/v1/system/_attr/:attr",
|
"/v1/system/_attr/:attr",
|
||||||
get(system_get_attr)
|
get(system_get_attr)
|
||||||
.post(system_post_attr)
|
.post(system_post_attr)
|
||||||
|
.put(system_put_attr)
|
||||||
.delete(system_delete_attr),
|
.delete(system_delete_attr),
|
||||||
)
|
)
|
||||||
.route("/v1/recycle_bin", get(recycle_bin_get))
|
.route("/v1/recycle_bin", get(recycle_bin_get))
|
||||||
|
|
|
@ -341,7 +341,6 @@ async fn main() -> ExitCode {
|
||||||
config.update_output_mode(opt.commands.commonopt().output_mode.to_owned().into());
|
config.update_output_mode(opt.commands.commonopt().output_mode.to_owned().into());
|
||||||
config.update_trust_x_forward_for(sconfig.trust_x_forward_for);
|
config.update_trust_x_forward_for(sconfig.trust_x_forward_for);
|
||||||
config.update_admin_bind_path(&sconfig.adminbindpath);
|
config.update_admin_bind_path(&sconfig.adminbindpath);
|
||||||
|
|
||||||
match &opt.commands {
|
match &opt.commands {
|
||||||
// we aren't going to touch the DB so we can carry on
|
// we aren't going to touch the DB so we can carry on
|
||||||
KanidmdOpt::HealthCheck(_) => (),
|
KanidmdOpt::HealthCheck(_) => (),
|
||||||
|
|
|
@ -1447,6 +1447,43 @@ lazy_static! {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref E_IDM_ACP_SYSTEM_CONFIG_SESSION_EXP_PRIV_V1: EntryInitNew = entry_init!(
|
||||||
|
(ATTR_CLASS, EntryClass::Object.to_value()),
|
||||||
|
(ATTR_CLASS, EntryClass::AccessControlProfile.to_value()),
|
||||||
|
(ATTR_CLASS, EntryClass::AccessControlModify.to_value()),
|
||||||
|
(ATTR_CLASS, EntryClass::AccessControlSearch.to_value()),
|
||||||
|
(ATTR_NAME, Value::new_iname("idm_acp_system_config_session_exp_priv")),
|
||||||
|
(ATTR_UUID, Value::Uuid(UUID_IDM_ACP_SYSTEM_CONFIG_SESSION_EXP_PRIV_V1)),
|
||||||
|
(
|
||||||
|
Attribute::Description.as_ref(),
|
||||||
|
Value::new_utf8s("Builtin IDM Control for granting session expiry configuration rights")
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ATTR_ACP_RECEIVER_GROUP,
|
||||||
|
Value::Refer(UUID_SYSTEM_ADMINS)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ATTR_ACP_TARGET_SCOPE,
|
||||||
|
Value::new_json_filter_s(
|
||||||
|
"{\"and\": [{\"eq\": [\"uuid\",\"00000000-0000-0000-0000-ffffff000027\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}"
|
||||||
|
)
|
||||||
|
.expect("Invalid JSON filter")
|
||||||
|
),
|
||||||
|
(ATTR_ACP_SEARCH_ATTR, Value::new_iutf8("class")),
|
||||||
|
(ATTR_ACP_SEARCH_ATTR, Value::new_iutf8("name")),
|
||||||
|
(ATTR_ACP_SEARCH_ATTR, Value::new_iutf8("uuid")),
|
||||||
|
(ATTR_ACP_SEARCH_ATTR, Value::new_iutf8("description")),
|
||||||
|
(ATTR_ACP_SEARCH_ATTR, Attribute::AuthSessionExpiry.to_value()),
|
||||||
|
(ATTR_ACP_MODIFY_PRESENTATTR, Attribute::AuthSessionExpiry.to_value()),
|
||||||
|
(ATTR_ACP_MODIFY_REMOVEDATTR, Attribute::AuthSessionExpiry.to_value()),
|
||||||
|
(ATTR_ACP_SEARCH_ATTR, Attribute::PrivilegeExpiry.to_value()),
|
||||||
|
(ATTR_ACP_MODIFY_PRESENTATTR, Attribute::PrivilegeExpiry.to_value()),
|
||||||
|
(ATTR_ACP_MODIFY_REMOVEDATTR, Attribute::PrivilegeExpiry.to_value())
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref E_IDM_ACP_ACCOUNT_UNIX_EXTEND_PRIV_V1: EntryInitNew = entry_init!(
|
pub static ref E_IDM_ACP_ACCOUNT_UNIX_EXTEND_PRIV_V1: EntryInitNew = entry_init!(
|
||||||
(ATTR_CLASS, EntryClass::Object.to_value()),
|
(ATTR_CLASS, EntryClass::Object.to_value()),
|
||||||
|
|
|
@ -50,6 +50,7 @@ pub enum Attribute {
|
||||||
ApiTokenSession,
|
ApiTokenSession,
|
||||||
Attr,
|
Attr,
|
||||||
AttributeName,
|
AttributeName,
|
||||||
|
AuthSessionExpiry,
|
||||||
BadlistPassword,
|
BadlistPassword,
|
||||||
Claim,
|
Claim,
|
||||||
Class,
|
Class,
|
||||||
|
@ -115,6 +116,7 @@ pub enum Attribute {
|
||||||
Phantom,
|
Phantom,
|
||||||
PrimaryCredential,
|
PrimaryCredential,
|
||||||
PrivateCookieKey,
|
PrivateCookieKey,
|
||||||
|
PrivilegeExpiry,
|
||||||
RadiusSecret,
|
RadiusSecret,
|
||||||
Replicated,
|
Replicated,
|
||||||
Rs256PrivateKeyDer,
|
Rs256PrivateKeyDer,
|
||||||
|
@ -189,6 +191,7 @@ impl TryFrom<String> for Attribute {
|
||||||
ATTR_API_TOKEN_SESSION => Attribute::ApiTokenSession,
|
ATTR_API_TOKEN_SESSION => Attribute::ApiTokenSession,
|
||||||
ATTR_ATTR => Attribute::Attr,
|
ATTR_ATTR => Attribute::Attr,
|
||||||
ATTR_ATTRIBUTENAME => Attribute::AttributeName,
|
ATTR_ATTRIBUTENAME => Attribute::AttributeName,
|
||||||
|
ATTR_AUTH_SESSION_EXPIRY => Attribute::AuthSessionExpiry,
|
||||||
ATTR_BADLIST_PASSWORD => Attribute::BadlistPassword,
|
ATTR_BADLIST_PASSWORD => Attribute::BadlistPassword,
|
||||||
ATTR_CLAIM => Attribute::Claim,
|
ATTR_CLAIM => Attribute::Claim,
|
||||||
ATTR_CLASS => Attribute::Class,
|
ATTR_CLASS => Attribute::Class,
|
||||||
|
@ -256,6 +259,7 @@ impl TryFrom<String> for Attribute {
|
||||||
ATTR_PHANTOM => Attribute::Phantom,
|
ATTR_PHANTOM => Attribute::Phantom,
|
||||||
ATTR_PRIMARY_CREDENTIAL => Attribute::PrimaryCredential,
|
ATTR_PRIMARY_CREDENTIAL => Attribute::PrimaryCredential,
|
||||||
ATTR_PRIVATE_COOKIE_KEY => Attribute::PrivateCookieKey,
|
ATTR_PRIVATE_COOKIE_KEY => Attribute::PrivateCookieKey,
|
||||||
|
ATTR_PRIVILEGE_EXPIRY => Attribute::PrivilegeExpiry,
|
||||||
ATTR_RADIUS_SECRET => Attribute::RadiusSecret,
|
ATTR_RADIUS_SECRET => Attribute::RadiusSecret,
|
||||||
ATTR_REPLICATED => Attribute::Replicated,
|
ATTR_REPLICATED => Attribute::Replicated,
|
||||||
ATTR_RS256_PRIVATE_KEY_DER => Attribute::Rs256PrivateKeyDer,
|
ATTR_RS256_PRIVATE_KEY_DER => Attribute::Rs256PrivateKeyDer,
|
||||||
|
@ -416,6 +420,8 @@ impl From<Attribute> for &'static str {
|
||||||
Attribute::TestAttr => TEST_ATTR_TEST_ATTR,
|
Attribute::TestAttr => TEST_ATTR_TEST_ATTR,
|
||||||
#[cfg(any(debug_assertions, test))]
|
#[cfg(any(debug_assertions, test))]
|
||||||
Attribute::Extra => TEST_ATTR_EXTRA,
|
Attribute::Extra => TEST_ATTR_EXTRA,
|
||||||
|
Attribute::AuthSessionExpiry => ATTR_AUTH_SESSION_EXPIRY,
|
||||||
|
Attribute::PrivilegeExpiry => ATTR_PRIVILEGE_EXPIRY,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,9 +77,9 @@ pub const MFAREG_SESSION_TIMEOUT: u64 = 300;
|
||||||
pub const PW_MIN_LENGTH: usize = 10;
|
pub const PW_MIN_LENGTH: usize = 10;
|
||||||
|
|
||||||
// Default - sessions last for 1 hour.
|
// Default - sessions last for 1 hour.
|
||||||
pub const AUTH_SESSION_EXPIRY: u64 = 3600;
|
pub const DEFAULT_AUTH_SESSION_EXPIRY: u32 = 3600;
|
||||||
// Default - privileges last for 10 minutes.
|
// Default - privileges last for 10 minutes.
|
||||||
pub const AUTH_PRIVILEGE_EXPIRY: u64 = 600;
|
pub const DEFAULT_AUTH_PRIVILEGE_EXPIRY: u32 = 600;
|
||||||
// Default - oauth refresh tokens last for 16 hours.
|
// Default - oauth refresh tokens last for 16 hours.
|
||||||
pub const OAUTH_REFRESH_TOKEN_EXPIRY: u64 = 3600 * 8;
|
pub const OAUTH_REFRESH_TOKEN_EXPIRY: u64 = 3600 * 8;
|
||||||
|
|
||||||
|
|
|
@ -193,6 +193,24 @@ pub static ref SCHEMA_ATTR_BADLIST_PASSWORD: SchemaAttribute = SchemaAttribute {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub static ref SCHEMA_ATTR_AUTH_SESSION_EXPIRY: SchemaAttribute = SchemaAttribute {
|
||||||
|
uuid: UUID_SCHEMA_ATTR_AUTH_SESSION_EXPIRY,
|
||||||
|
name: Attribute::AuthSessionExpiry.into(),
|
||||||
|
|
||||||
|
description: "An expiration time for an authentication session.".to_string(),
|
||||||
|
syntax: SyntaxType::Uint32,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static ref SCHEMA_ATTR_AUTH_PRIVILEGE_EXPIRY: SchemaAttribute = SchemaAttribute {
|
||||||
|
uuid: UUID_SCHEMA_ATTR_AUTH_PRIVILEGE_EXPIRY,
|
||||||
|
name: Attribute::PrivilegeExpiry.into(),
|
||||||
|
|
||||||
|
description: "An expiration time for a privileged authentication session.".to_string(),
|
||||||
|
syntax: SyntaxType::Uint32,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
pub static ref SCHEMA_ATTR_LOGINSHELL: SchemaAttribute = SchemaAttribute {
|
pub static ref SCHEMA_ATTR_LOGINSHELL: SchemaAttribute = SchemaAttribute {
|
||||||
uuid: UUID_SCHEMA_ATTR_LOGINSHELL,
|
uuid: UUID_SCHEMA_ATTR_LOGINSHELL,
|
||||||
name: Attribute::LoginShell.into(),
|
name: Attribute::LoginShell.into(),
|
||||||
|
@ -714,6 +732,8 @@ pub static ref SCHEMA_CLASS_SYSTEM_CONFIG: SchemaClass = SchemaClass {
|
||||||
systemmay: vec![
|
systemmay: vec![
|
||||||
Attribute::Description.into(),
|
Attribute::Description.into(),
|
||||||
Attribute::BadlistPassword.into(),
|
Attribute::BadlistPassword.into(),
|
||||||
|
Attribute::AuthSessionExpiry.into(),
|
||||||
|
Attribute::PrivilegeExpiry.into()
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::constants::uuids::*;
|
||||||
|
|
||||||
use crate::entry::{Entry, EntryInit, EntryInitNew, EntryNew};
|
use crate::entry::{Entry, EntryInit, EntryInitNew, EntryNew};
|
||||||
use crate::prelude::{Attribute, EntryClass};
|
use crate::prelude::{Attribute, EntryClass};
|
||||||
|
use crate::prelude::{DEFAULT_AUTH_PRIVILEGE_EXPIRY, DEFAULT_AUTH_SESSION_EXPIRY};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
// Default entries for system_config
|
// Default entries for system_config
|
||||||
|
@ -30,6 +31,14 @@ lazy_static! {
|
||||||
"demo_badlist_shohfie3aeci2oobur0aru9uushah6EiPi2woh4hohngoighaiRuepieN3ongoo1"
|
"demo_badlist_shohfie3aeci2oobur0aru9uushah6EiPi2woh4hohngoighaiRuepieN3ongoo1"
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
Attribute::AuthSessionExpiry.as_ref(),
|
||||||
|
Value::Uint32(DEFAULT_AUTH_SESSION_EXPIRY)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Attribute::PrivilegeExpiry.as_ref(),
|
||||||
|
Value::Uint32(DEFAULT_AUTH_PRIVILEGE_EXPIRY)
|
||||||
|
),
|
||||||
(
|
(
|
||||||
Attribute::BadlistPassword.as_ref(),
|
Attribute::BadlistPassword.as_ref(),
|
||||||
Value::new_iutf8("100preteamare")
|
Value::new_iutf8("100preteamare")
|
||||||
|
|
|
@ -233,6 +233,10 @@ pub const UUID_SCHEMA_ATTR_SYNC_YIELD_AUTHORITY: Uuid =
|
||||||
uuid!("00000000-0000-0000-0000-ffff00000138");
|
uuid!("00000000-0000-0000-0000-ffff00000138");
|
||||||
pub const UUID_SCHEMA_CLASS_CONFLICT: Uuid = uuid!("00000000-0000-0000-0000-ffff00000139");
|
pub const UUID_SCHEMA_CLASS_CONFLICT: Uuid = uuid!("00000000-0000-0000-0000-ffff00000139");
|
||||||
pub const UUID_SCHEMA_ATTR_SOURCE_UUID: Uuid = uuid!("00000000-0000-0000-0000-ffff00000140");
|
pub const UUID_SCHEMA_ATTR_SOURCE_UUID: Uuid = uuid!("00000000-0000-0000-0000-ffff00000140");
|
||||||
|
pub const UUID_SCHEMA_ATTR_AUTH_SESSION_EXPIRY: Uuid =
|
||||||
|
uuid!("00000000-0000-0000-0000-ffff00000141");
|
||||||
|
pub const UUID_SCHEMA_ATTR_AUTH_PRIVILEGE_EXPIRY: Uuid =
|
||||||
|
uuid!("00000000-0000-0000-0000-ffff00000142");
|
||||||
|
|
||||||
// System and domain infos
|
// System and domain infos
|
||||||
// I'd like to strongly criticise william of the past for making poor choices about these allocations.
|
// I'd like to strongly criticise william of the past for making poor choices about these allocations.
|
||||||
|
@ -312,6 +316,8 @@ pub const UUID_IDM_HP_ACP_SYNC_ACCOUNT_MANAGE_PRIV_V1: Uuid =
|
||||||
pub const UUID_IDM_ACP_ACCOUNT_MAIL_READ_PRIV_V1: Uuid =
|
pub const UUID_IDM_ACP_ACCOUNT_MAIL_READ_PRIV_V1: Uuid =
|
||||||
uuid!("00000000-0000-0000-0000-ffffff000045");
|
uuid!("00000000-0000-0000-0000-ffffff000045");
|
||||||
pub const UUID_IDM_ACCOUNT_SELF_ACP_WRITE_V1: Uuid = uuid!("00000000-0000-0000-0000-ffffff000046");
|
pub const UUID_IDM_ACCOUNT_SELF_ACP_WRITE_V1: Uuid = uuid!("00000000-0000-0000-0000-ffffff000046");
|
||||||
|
pub const UUID_IDM_ACP_SYSTEM_CONFIG_SESSION_EXP_PRIV_V1: Uuid =
|
||||||
|
uuid!("00000000-0000-0000-0000-ffffff000047");
|
||||||
|
|
||||||
// End of system ranges
|
// End of system ranges
|
||||||
pub const UUID_DOES_NOT_EXIST: Uuid = uuid!("00000000-0000-0000-0000-fffffffffffe");
|
pub const UUID_DOES_NOT_EXIST: Uuid = uuid!("00000000-0000-0000-0000-fffffffffffe");
|
||||||
|
|
|
@ -219,6 +219,7 @@ impl Account {
|
||||||
session_id: Uuid,
|
session_id: Uuid,
|
||||||
scope: SessionScope,
|
scope: SessionScope,
|
||||||
ct: Duration,
|
ct: Duration,
|
||||||
|
auth_session_expiry: u32,
|
||||||
) -> Option<UserAuthToken> {
|
) -> Option<UserAuthToken> {
|
||||||
// TODO: Apply policy to this expiry time.
|
// TODO: Apply policy to this expiry time.
|
||||||
// We have to remove the nanoseconds because when we transmit this / serialise it we drop
|
// We have to remove the nanoseconds because when we transmit this / serialise it we drop
|
||||||
|
@ -226,9 +227,8 @@ impl Account {
|
||||||
// ns value which breaks some checks.
|
// ns value which breaks some checks.
|
||||||
let ct = ct - Duration::from_nanos(ct.subsec_nanos() as u64);
|
let ct = ct - Duration::from_nanos(ct.subsec_nanos() as u64);
|
||||||
let issued_at = OffsetDateTime::UNIX_EPOCH + ct;
|
let issued_at = OffsetDateTime::UNIX_EPOCH + ct;
|
||||||
|
|
||||||
let expiry =
|
let expiry =
|
||||||
Some(OffsetDateTime::UNIX_EPOCH + ct + Duration::from_secs(AUTH_SESSION_EXPIRY));
|
Some(OffsetDateTime::UNIX_EPOCH + ct + Duration::from_secs(auth_session_expiry as u64));
|
||||||
|
|
||||||
let (purpose, expiry) = match scope {
|
let (purpose, expiry) = match scope {
|
||||||
// Issue an invalid/expired session.
|
// Issue an invalid/expired session.
|
||||||
|
@ -279,6 +279,7 @@ impl Account {
|
||||||
session_expiry: Option<OffsetDateTime>,
|
session_expiry: Option<OffsetDateTime>,
|
||||||
scope: SessionScope,
|
scope: SessionScope,
|
||||||
ct: Duration,
|
ct: Duration,
|
||||||
|
auth_privilege_expiry: u32,
|
||||||
) -> Option<UserAuthToken> {
|
) -> Option<UserAuthToken> {
|
||||||
let issued_at = OffsetDateTime::UNIX_EPOCH + ct;
|
let issued_at = OffsetDateTime::UNIX_EPOCH + ct;
|
||||||
|
|
||||||
|
@ -294,7 +295,9 @@ impl Account {
|
||||||
// Return a ReadWrite session with an inner expiry for the privileges
|
// Return a ReadWrite session with an inner expiry for the privileges
|
||||||
{
|
{
|
||||||
let expiry = Some(
|
let expiry = Some(
|
||||||
OffsetDateTime::UNIX_EPOCH + ct + Duration::from_secs(AUTH_PRIVILEGE_EXPIRY),
|
OffsetDateTime::UNIX_EPOCH
|
||||||
|
+ ct
|
||||||
|
+ Duration::from_secs(auth_privilege_expiry.into()),
|
||||||
);
|
);
|
||||||
(
|
(
|
||||||
UatPurpose::ReadWrite { expiry },
|
UatPurpose::ReadWrite { expiry },
|
||||||
|
@ -327,6 +330,7 @@ impl Account {
|
||||||
expire: Option<&OffsetDateTime>,
|
expire: Option<&OffsetDateTime>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let cot = OffsetDateTime::UNIX_EPOCH + ct;
|
let cot = OffsetDateTime::UNIX_EPOCH + ct;
|
||||||
|
trace!("Checking within valid time: {:?} {:?}", valid_from, expire);
|
||||||
|
|
||||||
let vmin = if let Some(vft) = valid_from {
|
let vmin = if let Some(vft) = valid_from {
|
||||||
// If current time greater than start time window
|
// If current time greater than start time window
|
||||||
|
@ -539,7 +543,7 @@ impl Account {
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Remember, token expiry is checked by validate_and_parse_token_to_token.
|
// Remember, token expiry is checked by validate_and_parse_token_to_token.
|
||||||
// If we wanted we could check other properties of the uat here?
|
// If we wanted we could check other properties of the uat here?
|
||||||
// Alternatelly, we could always store LESS in the uat because of this?
|
// Alternatively, we could always store LESS in the uat because of this?
|
||||||
|
|
||||||
let within_valid_window = Account::check_within_valid_time(
|
let within_valid_window = Account::check_within_valid_time(
|
||||||
ct,
|
ct,
|
||||||
|
@ -554,6 +558,7 @@ impl Account {
|
||||||
|
|
||||||
// Anonymous does NOT record it's sessions, so we simply check the expiry time
|
// Anonymous does NOT record it's sessions, so we simply check the expiry time
|
||||||
// of the token. This is already done for us as noted above.
|
// of the token. This is already done for us as noted above.
|
||||||
|
trace!("{}", &uat);
|
||||||
|
|
||||||
if uat.uuid == UUID_ANONYMOUS {
|
if uat.uuid == UUID_ANONYMOUS {
|
||||||
security_debug!("Anonymous sessions do not have session records, session is valid.");
|
security_debug!("Anonymous sessions do not have session records, session is valid.");
|
||||||
|
@ -845,7 +850,12 @@ mod tests {
|
||||||
.expect("account must exist");
|
.expect("account must exist");
|
||||||
let session_id = uuid::Uuid::new_v4();
|
let session_id = uuid::Uuid::new_v4();
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
|
|
||||||
// Check the ui hints are as expected.
|
// Check the ui hints are as expected.
|
||||||
|
@ -871,7 +881,12 @@ mod tests {
|
||||||
.expect("account must exist");
|
.expect("account must exist");
|
||||||
let session_id = uuid::Uuid::new_v4();
|
let session_id = uuid::Uuid::new_v4();
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_PRIVILEGE_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
|
|
||||||
assert!(uat.ui_hints.len() == 2);
|
assert!(uat.ui_hints.len() == 2);
|
||||||
|
@ -902,7 +917,12 @@ mod tests {
|
||||||
.expect("account must exist");
|
.expect("account must exist");
|
||||||
let session_id = uuid::Uuid::new_v4();
|
let session_id = uuid::Uuid::new_v4();
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
|
|
||||||
assert!(uat.ui_hints.len() == 3);
|
assert!(uat.ui_hints.len() == 3);
|
||||||
|
|
|
@ -37,6 +37,8 @@ use crate::prelude::*;
|
||||||
use crate::value::Session;
|
use crate::value::Session;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
|
use super::server::AccountPolicy;
|
||||||
|
|
||||||
// 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% fulfilled. This is where MFA or other
|
// handlers requirements can be 100% fulfilled. This is where MFA or other
|
||||||
// auth policies would exist, but each credHandler has to be a whole
|
// auth policies would exist, but each credHandler has to be a whole
|
||||||
|
@ -332,7 +334,7 @@ impl CredHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validate a singule password credential of the account.
|
/// Validate a single password credential of the account.
|
||||||
fn validate_password(
|
fn validate_password(
|
||||||
cred: &AuthCredential,
|
cred: &AuthCredential,
|
||||||
cred_id: Uuid,
|
cred_id: Uuid,
|
||||||
|
@ -1005,8 +1007,8 @@ impl AuthSession {
|
||||||
async_tx: &Sender<DelayedAction>,
|
async_tx: &Sender<DelayedAction>,
|
||||||
audit_tx: &Sender<AuditEvent>,
|
audit_tx: &Sender<AuditEvent>,
|
||||||
webauthn: &Webauthn,
|
webauthn: &Webauthn,
|
||||||
pw_badlist_set: Option<&HashSet<String>>,
|
|
||||||
uat_jwt_signer: &JwsSigner,
|
uat_jwt_signer: &JwsSigner,
|
||||||
|
account_policy: &AccountPolicy,
|
||||||
) -> Result<AuthState, OperationError> {
|
) -> Result<AuthState, OperationError> {
|
||||||
let (next_state, response) = match &mut self.state {
|
let (next_state, response) = match &mut self.state {
|
||||||
AuthSessionState::Init(_) | AuthSessionState::Success | AuthSessionState::Denied(_) => {
|
AuthSessionState::Init(_) | AuthSessionState::Success | AuthSessionState::Denied(_) => {
|
||||||
|
@ -1021,11 +1023,18 @@ impl AuthSession {
|
||||||
self.account.uuid,
|
self.account.uuid,
|
||||||
async_tx,
|
async_tx,
|
||||||
webauthn,
|
webauthn,
|
||||||
pw_badlist_set,
|
Some(&account_policy.pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
CredState::Success { auth_type, cred_id } => {
|
CredState::Success { auth_type, cred_id } => {
|
||||||
// Issue the uat based on a set of factors.
|
// Issue the uat based on a set of factors.
|
||||||
let uat = self.issue_uat(&auth_type, time, async_tx, cred_id)?;
|
let uat = self.issue_uat(
|
||||||
|
&auth_type,
|
||||||
|
time,
|
||||||
|
async_tx,
|
||||||
|
cred_id,
|
||||||
|
account_policy.authsession_expiry,
|
||||||
|
account_policy.privilege_expiry,
|
||||||
|
)?;
|
||||||
let jwt = Jws::new(uat);
|
let jwt = Jws::new(uat);
|
||||||
|
|
||||||
// Now encrypt and prepare the token for return to the client.
|
// Now encrypt and prepare the token for return to the client.
|
||||||
|
@ -1096,6 +1105,8 @@ impl AuthSession {
|
||||||
time: Duration,
|
time: Duration,
|
||||||
async_tx: &Sender<DelayedAction>,
|
async_tx: &Sender<DelayedAction>,
|
||||||
cred_id: Uuid,
|
cred_id: Uuid,
|
||||||
|
auth_session_expiry: u32,
|
||||||
|
auth_privilege_expiry: u32,
|
||||||
) -> Result<UserAuthToken, OperationError> {
|
) -> Result<UserAuthToken, OperationError> {
|
||||||
security_debug!("Successful cred handling");
|
security_debug!("Successful cred handling");
|
||||||
match self.intent {
|
match self.intent {
|
||||||
|
@ -1121,7 +1132,7 @@ impl AuthSession {
|
||||||
|
|
||||||
let uat = self
|
let uat = self
|
||||||
.account
|
.account
|
||||||
.to_userauthtoken(session_id, scope, time)
|
.to_userauthtoken(session_id, scope, time, auth_session_expiry)
|
||||||
.ok_or(OperationError::InvalidState)?;
|
.ok_or(OperationError::InvalidState)?;
|
||||||
|
|
||||||
// Queue the session info write.
|
// Queue the session info write.
|
||||||
|
@ -1181,7 +1192,13 @@ impl AuthSession {
|
||||||
|
|
||||||
let uat = self
|
let uat = self
|
||||||
.account
|
.account
|
||||||
.to_reissue_userauthtoken(session_id, session_expiry, scope, time)
|
.to_reissue_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
session_expiry,
|
||||||
|
scope,
|
||||||
|
time,
|
||||||
|
auth_privilege_expiry,
|
||||||
|
)
|
||||||
.ok_or(OperationError::InvalidState)?;
|
.ok_or(OperationError::InvalidState)?;
|
||||||
|
|
||||||
Ok(uat)
|
Ok(uat)
|
||||||
|
@ -1230,6 +1247,7 @@ mod tests {
|
||||||
BAD_WEBAUTHN_MSG, PW_BADLIST_MSG,
|
BAD_WEBAUTHN_MSG, PW_BADLIST_MSG,
|
||||||
};
|
};
|
||||||
use crate::idm::delayed::DelayedAction;
|
use crate::idm::delayed::DelayedAction;
|
||||||
|
use crate::idm::server::AccountPolicy;
|
||||||
use crate::idm::AuthState;
|
use crate::idm::AuthState;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::utils::{duration_from_epoch_now, readable_password_from_random};
|
use crate::utils::{duration_from_epoch_now, readable_password_from_random};
|
||||||
|
@ -1353,8 +1371,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(_)) => {}
|
Ok(AuthState::Denied(_)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1377,8 +1395,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1421,8 +1439,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == PW_BADLIST_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == PW_BADLIST_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1545,8 +1563,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1571,8 +1589,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1594,8 +1612,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1619,8 +1637,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1631,8 +1649,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1656,8 +1674,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1668,8 +1686,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1733,8 +1751,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1745,8 +1763,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == PW_BADLIST_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == PW_BADLIST_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1890,8 +1908,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
None,
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&Default::default(),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1918,8 +1936,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
None,
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&Default::default(),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -1953,8 +1971,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
None,
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&Default::default(),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2002,8 +2020,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
None,
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&Default::default(),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2055,8 +2073,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2079,8 +2097,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2114,8 +2132,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2144,8 +2162,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2156,8 +2174,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2192,8 +2210,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2204,8 +2222,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2273,8 +2291,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2297,8 +2315,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2330,8 +2348,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2360,8 +2378,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2372,8 +2390,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2402,8 +2420,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2414,8 +2432,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2438,8 +2456,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2450,8 +2468,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2480,8 +2498,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2492,8 +2510,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2572,8 +2590,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2595,8 +2613,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_BACKUPCODE_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_BACKUPCODE_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2619,8 +2637,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2631,8 +2649,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2661,8 +2679,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2673,8 +2691,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2705,8 +2723,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2717,8 +2735,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2788,8 +2806,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2800,8 +2818,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2824,8 +2842,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2836,8 +2854,8 @@ mod tests {
|
||||||
&async_tx,
|
&async_tx,
|
||||||
&audit_tx,
|
&audit_tx,
|
||||||
&webauthn,
|
&webauthn,
|
||||||
Some(&pw_badlist_cache),
|
|
||||||
&jws_signer,
|
&jws_signer,
|
||||||
|
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||||
) {
|
) {
|
||||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
|
|
@ -1280,7 +1280,7 @@ impl<'a> IdmServerCredUpdateTransaction<'a> {
|
||||||
// check a password badlist to eliminate more content
|
// check a password badlist to eliminate more content
|
||||||
// we check the password as "lower case" to help eliminate possibilities
|
// we check the password as "lower case" to help eliminate possibilities
|
||||||
// also, when pw_badlist_cache is read from DB, it is read as Value (iutf8 lowercase)
|
// also, when pw_badlist_cache is read from DB, it is read as Value (iutf8 lowercase)
|
||||||
if (*self.pw_badlist_cache).contains(&cleartext.to_lowercase()) {
|
if (self.account_policy.pw_badlist_cache).contains(&cleartext.to_lowercase()) {
|
||||||
security_info!("Password found in badlist, rejecting");
|
security_info!("Password found in badlist, rejecting");
|
||||||
Err(PasswordQuality::BadListed)
|
Err(PasswordQuality::BadListed)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2118,7 +2118,12 @@ mod tests {
|
||||||
.target_to_account(UUID_ADMIN)
|
.target_to_account(UUID_ADMIN)
|
||||||
.expect("account must exist");
|
.expect("account must exist");
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
|
|
||||||
// Need the uat first for expiry.
|
// Need the uat first for expiry.
|
||||||
|
@ -2232,7 +2237,12 @@ mod tests {
|
||||||
.target_to_account(UUID_ADMIN)
|
.target_to_account(UUID_ADMIN)
|
||||||
.expect("account must exist");
|
.expect("account must exist");
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
|
|
||||||
// Need the uat first for expiry.
|
// Need the uat first for expiry.
|
||||||
|
@ -2287,7 +2297,12 @@ mod tests {
|
||||||
.expect("account must exist");
|
.expect("account must exist");
|
||||||
let session_id = uuid::Uuid::new_v4();
|
let session_id = uuid::Uuid::new_v4();
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
let ident = idms_prox_write
|
let ident = idms_prox_write
|
||||||
.process_uat_to_identity(&uat, ct)
|
.process_uat_to_identity(&uat, ct)
|
||||||
|
@ -2612,7 +2627,12 @@ mod tests {
|
||||||
.expect("account must exist");
|
.expect("account must exist");
|
||||||
let session_id = uuid::Uuid::new_v4();
|
let session_id = uuid::Uuid::new_v4();
|
||||||
let uat2 = account
|
let uat2 = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
let ident2 = idms_prox_write
|
let ident2 = idms_prox_write
|
||||||
.process_uat_to_identity(&uat2, ct)
|
.process_uat_to_identity(&uat2, ct)
|
||||||
|
@ -3212,7 +3232,12 @@ mod tests {
|
||||||
.expect("account must exist");
|
.expect("account must exist");
|
||||||
let session_id = uuid::Uuid::new_v4();
|
let session_id = uuid::Uuid::new_v4();
|
||||||
let uat2 = account
|
let uat2 = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
let ident2 = idms_prox_write
|
let ident2 = idms_prox_write
|
||||||
.process_uat_to_identity(&uat2, ct)
|
.process_uat_to_identity(&uat2, ct)
|
||||||
|
|
|
@ -69,6 +69,44 @@ pub struct DomainKeys {
|
||||||
pub(crate) cookie_key: [u8; 64],
|
pub(crate) cookie_key: [u8; 64],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AccountPolicy {
|
||||||
|
pub(crate) privilege_expiry: u32,
|
||||||
|
pub(crate) authsession_expiry: u32,
|
||||||
|
pub(crate) pw_badlist_cache: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountPolicy {
|
||||||
|
pub fn new(
|
||||||
|
privilege_expiry: u32,
|
||||||
|
authsession_expiry: u32,
|
||||||
|
pw_badlist_cache: HashSet<String>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
privilege_expiry,
|
||||||
|
authsession_expiry,
|
||||||
|
pw_badlist_cache,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_pw_badlist_cache(pw_badlist_cache: HashSet<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
pw_badlist_cache,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AccountPolicy {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
privilege_expiry: DEFAULT_AUTH_PRIVILEGE_EXPIRY,
|
||||||
|
authsession_expiry: DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
pw_badlist_cache: Default::default(), // TODO: more thoughts on this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct IdmServer {
|
pub struct IdmServer {
|
||||||
// There is a good reason to keep this single thread - it
|
// There is a good reason to keep this single thread - it
|
||||||
// means that limits to sessions can be easily applied and checked to
|
// means that limits to sessions can be easily applied and checked to
|
||||||
|
@ -87,9 +125,9 @@ pub struct IdmServer {
|
||||||
audit_tx: Sender<AuditEvent>,
|
audit_tx: Sender<AuditEvent>,
|
||||||
/// [Webauthn] verifier/config
|
/// [Webauthn] verifier/config
|
||||||
webauthn: Webauthn,
|
webauthn: Webauthn,
|
||||||
pw_badlist_cache: Arc<CowCell<HashSet<String>>>,
|
|
||||||
oauth2rs: Arc<Oauth2ResourceServers>,
|
oauth2rs: Arc<Oauth2ResourceServers>,
|
||||||
domain_keys: Arc<CowCell<DomainKeys>>,
|
domain_keys: Arc<CowCell<DomainKeys>>,
|
||||||
|
account_policy: Arc<CowCell<AccountPolicy>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains methods that require writes, but in the context of writing to the idm in memory structures (maybe the query server too). This is things like authentication.
|
/// Contains methods that require writes, but in the context of writing to the idm in memory structures (maybe the query server too). This is things like authentication.
|
||||||
|
@ -105,7 +143,7 @@ pub struct IdmServerAuthTransaction<'a> {
|
||||||
pub(crate) async_tx: Sender<DelayedAction>,
|
pub(crate) async_tx: Sender<DelayedAction>,
|
||||||
pub(crate) audit_tx: Sender<AuditEvent>,
|
pub(crate) audit_tx: Sender<AuditEvent>,
|
||||||
pub(crate) webauthn: &'a Webauthn,
|
pub(crate) webauthn: &'a Webauthn,
|
||||||
pub(crate) pw_badlist_cache: CowCellReadTxn<HashSet<String>>,
|
pub(crate) account_policy: CowCellReadTxn<AccountPolicy>,
|
||||||
pub(crate) domain_keys: CowCellReadTxn<DomainKeys>,
|
pub(crate) domain_keys: CowCellReadTxn<DomainKeys>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +151,7 @@ pub struct IdmServerCredUpdateTransaction<'a> {
|
||||||
pub(crate) _qs_read: QueryServerReadTransaction<'a>,
|
pub(crate) _qs_read: QueryServerReadTransaction<'a>,
|
||||||
// sid: Sid,
|
// sid: Sid,
|
||||||
pub(crate) webauthn: &'a Webauthn,
|
pub(crate) webauthn: &'a Webauthn,
|
||||||
pub(crate) pw_badlist_cache: CowCellReadTxn<HashSet<String>>,
|
pub(crate) account_policy: CowCellReadTxn<AccountPolicy>,
|
||||||
pub(crate) cred_update_sessions: BptreeMapReadTxn<'a, Uuid, CredentialUpdateSessionMutex>,
|
pub(crate) cred_update_sessions: BptreeMapReadTxn<'a, Uuid, CredentialUpdateSessionMutex>,
|
||||||
pub(crate) domain_keys: CowCellReadTxn<DomainKeys>,
|
pub(crate) domain_keys: CowCellReadTxn<DomainKeys>,
|
||||||
pub(crate) crypto_policy: &'a CryptoPolicy,
|
pub(crate) crypto_policy: &'a CryptoPolicy,
|
||||||
|
@ -135,7 +173,7 @@ pub struct IdmServerProxyWriteTransaction<'a> {
|
||||||
pub(crate) sid: Sid,
|
pub(crate) sid: Sid,
|
||||||
crypto_policy: &'a CryptoPolicy,
|
crypto_policy: &'a CryptoPolicy,
|
||||||
webauthn: &'a Webauthn,
|
webauthn: &'a Webauthn,
|
||||||
pw_badlist_cache: CowCellWriteTxn<'a, HashSet<String>>,
|
account_policy: CowCellWriteTxn<'a, AccountPolicy>,
|
||||||
pub(crate) domain_keys: CowCellWriteTxn<'a, DomainKeys>,
|
pub(crate) domain_keys: CowCellWriteTxn<'a, DomainKeys>,
|
||||||
pub(crate) oauth2rs: Oauth2ResourceServersWriteTransaction<'a>,
|
pub(crate) oauth2rs: Oauth2ResourceServersWriteTransaction<'a>,
|
||||||
}
|
}
|
||||||
|
@ -168,6 +206,8 @@ impl IdmServer {
|
||||||
cookie_key,
|
cookie_key,
|
||||||
pw_badlist_set,
|
pw_badlist_set,
|
||||||
oauth2rs_set,
|
oauth2rs_set,
|
||||||
|
privilege_expiry,
|
||||||
|
authsession_expiry,
|
||||||
) = {
|
) = {
|
||||||
let mut qs_read = qs.read().await;
|
let mut qs_read = qs.read().await;
|
||||||
(
|
(
|
||||||
|
@ -179,6 +219,8 @@ impl IdmServer {
|
||||||
qs_read.get_password_badlist()?,
|
qs_read.get_password_badlist()?,
|
||||||
// Add a read/reload of all oauth2 configurations.
|
// Add a read/reload of all oauth2 configurations.
|
||||||
qs_read.get_oauth2rs_set()?,
|
qs_read.get_oauth2rs_set()?,
|
||||||
|
qs_read.get_privilege_expiry()?,
|
||||||
|
qs_read.get_authsession_expiry()?,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -253,7 +295,11 @@ impl IdmServer {
|
||||||
async_tx,
|
async_tx,
|
||||||
audit_tx,
|
audit_tx,
|
||||||
webauthn,
|
webauthn,
|
||||||
pw_badlist_cache: Arc::new(CowCell::new(pw_badlist_set)),
|
account_policy: Arc::new(CowCell::new(AccountPolicy::new(
|
||||||
|
privilege_expiry,
|
||||||
|
authsession_expiry,
|
||||||
|
pw_badlist_set,
|
||||||
|
))),
|
||||||
domain_keys,
|
domain_keys,
|
||||||
oauth2rs: Arc::new(oauth2rs),
|
oauth2rs: Arc::new(oauth2rs),
|
||||||
},
|
},
|
||||||
|
@ -283,7 +329,7 @@ impl IdmServer {
|
||||||
async_tx: self.async_tx.clone(),
|
async_tx: self.async_tx.clone(),
|
||||||
audit_tx: self.audit_tx.clone(),
|
audit_tx: self.audit_tx.clone(),
|
||||||
webauthn: &self.webauthn,
|
webauthn: &self.webauthn,
|
||||||
pw_badlist_cache: self.pw_badlist_cache.read(),
|
account_policy: self.account_policy.read(),
|
||||||
domain_keys: self.domain_keys.read(),
|
domain_keys: self.domain_keys.read(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,7 +359,7 @@ impl IdmServer {
|
||||||
sid,
|
sid,
|
||||||
crypto_policy: &self.crypto_policy,
|
crypto_policy: &self.crypto_policy,
|
||||||
webauthn: &self.webauthn,
|
webauthn: &self.webauthn,
|
||||||
pw_badlist_cache: self.pw_badlist_cache.write(),
|
account_policy: self.account_policy.write(),
|
||||||
domain_keys: self.domain_keys.write(),
|
domain_keys: self.domain_keys.write(),
|
||||||
oauth2rs: self.oauth2rs.write(),
|
oauth2rs: self.oauth2rs.write(),
|
||||||
}
|
}
|
||||||
|
@ -324,7 +370,7 @@ impl IdmServer {
|
||||||
_qs_read: self.qs.read().await,
|
_qs_read: self.qs.read().await,
|
||||||
// sid: Sid,
|
// sid: Sid,
|
||||||
webauthn: &self.webauthn,
|
webauthn: &self.webauthn,
|
||||||
pw_badlist_cache: self.pw_badlist_cache.read(),
|
account_policy: self.account_policy.read(),
|
||||||
cred_update_sessions: self.cred_update_sessions.read(),
|
cred_update_sessions: self.cred_update_sessions.read(),
|
||||||
domain_keys: self.domain_keys.read(),
|
domain_keys: self.domain_keys.read(),
|
||||||
crypto_policy: &self.crypto_policy,
|
crypto_policy: &self.crypto_policy,
|
||||||
|
@ -1120,7 +1166,6 @@ impl<'a> IdmServerAuthTransaction<'a> {
|
||||||
// Process the credentials here as required.
|
// Process the credentials here as required.
|
||||||
// Basically throw them at the auth_session and see what
|
// Basically throw them at the auth_session and see what
|
||||||
// falls out.
|
// falls out.
|
||||||
let pw_badlist_cache = Some(&(*self.pw_badlist_cache));
|
|
||||||
auth_session
|
auth_session
|
||||||
.validate_creds(
|
.validate_creds(
|
||||||
&creds.cred,
|
&creds.cred,
|
||||||
|
@ -1128,8 +1173,8 @@ impl<'a> IdmServerAuthTransaction<'a> {
|
||||||
&self.async_tx,
|
&self.async_tx,
|
||||||
&self.audit_tx,
|
&self.audit_tx,
|
||||||
self.webauthn,
|
self.webauthn,
|
||||||
pw_badlist_cache,
|
|
||||||
&self.domain_keys.uat_jwt_signer,
|
&self.domain_keys.uat_jwt_signer,
|
||||||
|
&self.account_policy,
|
||||||
)
|
)
|
||||||
.map(|aus| {
|
.map(|aus| {
|
||||||
// Inspect the result:
|
// Inspect the result:
|
||||||
|
@ -1563,7 +1608,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
||||||
// check a password badlist to eliminate more content
|
// check a password badlist to eliminate more content
|
||||||
// we check the password as "lower case" to help eliminate possibilities
|
// we check the password as "lower case" to help eliminate possibilities
|
||||||
// also, when pw_badlist_cache is read from DB, it is read as Value (iutf8 lowercase)
|
// also, when pw_badlist_cache is read from DB, it is read as Value (iutf8 lowercase)
|
||||||
if (*self.pw_badlist_cache).contains(&cleartext.to_lowercase()) {
|
if (self.account_policy.pw_badlist_cache).contains(&cleartext.to_lowercase()) {
|
||||||
security_info!("Password found in badlist, rejecting");
|
security_info!("Password found in badlist, rejecting");
|
||||||
Err(OperationError::PasswordQuality(vec![
|
Err(OperationError::PasswordQuality(vec![
|
||||||
PasswordFeedback::BadListed,
|
PasswordFeedback::BadListed,
|
||||||
|
@ -1979,7 +2024,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
||||||
.get_changed_uuids()
|
.get_changed_uuids()
|
||||||
.contains(&UUID_SYSTEM_CONFIG)
|
.contains(&UUID_SYSTEM_CONFIG)
|
||||||
{
|
{
|
||||||
self.reload_password_badlist()?;
|
// TODO: probably it would be more efficient to introduce a single check for each of the possible system configs
|
||||||
|
// but that would require changing what uuid the operation is assigned
|
||||||
|
self.reload_system_account_policy()?;
|
||||||
};
|
};
|
||||||
if self.qs_write.get_changed_ouath2() {
|
if self.qs_write.get_changed_ouath2() {
|
||||||
self.qs_write
|
self.qs_write
|
||||||
|
@ -2029,20 +2076,17 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
||||||
// Commit everything.
|
// Commit everything.
|
||||||
self.oauth2rs.commit();
|
self.oauth2rs.commit();
|
||||||
self.domain_keys.commit();
|
self.domain_keys.commit();
|
||||||
self.pw_badlist_cache.commit();
|
self.account_policy.commit();
|
||||||
self.cred_update_sessions.commit();
|
self.cred_update_sessions.commit();
|
||||||
trace!("cred_update_session.commit");
|
trace!("cred_update_session.commit");
|
||||||
self.qs_write.commit()
|
self.qs_write.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reload_password_badlist(&mut self) -> Result<(), OperationError> {
|
fn reload_system_account_policy(&mut self) -> Result<(), OperationError> {
|
||||||
match self.qs_write.get_password_badlist() {
|
self.account_policy.pw_badlist_cache = self.qs_write.get_password_badlist()?;
|
||||||
Ok(badlist_entry) => {
|
self.account_policy.authsession_expiry = self.qs_write.get_authsession_expiry()?;
|
||||||
*self.pw_badlist_cache = badlist_entry;
|
self.account_policy.privilege_expiry = self.qs_write.get_privilege_expiry()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2067,7 +2111,7 @@ mod tests {
|
||||||
PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
|
PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
|
||||||
UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent,
|
UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent,
|
||||||
};
|
};
|
||||||
use crate::idm::server::{IdmServer, IdmServerTransaction};
|
use crate::idm::server::{IdmServer, IdmServerTransaction, Token};
|
||||||
use crate::idm::AuthState;
|
use crate::idm::AuthState;
|
||||||
use crate::modify::{Modify, ModifyList};
|
use crate::modify::{Modify, ModifyList};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
@ -3396,7 +3440,7 @@ mod tests {
|
||||||
#[idm_test]
|
#[idm_test]
|
||||||
async fn test_idm_jwt_uat_expiry(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
|
async fn test_idm_jwt_uat_expiry(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
|
||||||
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
||||||
let expiry = ct + Duration::from_secs(AUTH_SESSION_EXPIRY + 1);
|
let expiry = ct + Duration::from_secs((DEFAULT_AUTH_SESSION_EXPIRY + 1).into());
|
||||||
// Do an authenticate
|
// Do an authenticate
|
||||||
init_admin_w_password(idms, TEST_PASSWORD)
|
init_admin_w_password(idms, TEST_PASSWORD)
|
||||||
.await
|
.await
|
||||||
|
@ -3431,8 +3475,8 @@ mod tests {
|
||||||
_idms_delayed: &mut IdmServerDelayed,
|
_idms_delayed: &mut IdmServerDelayed,
|
||||||
) {
|
) {
|
||||||
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
||||||
let expiry_a = ct + Duration::from_secs(AUTH_SESSION_EXPIRY + 1);
|
let expiry_a = ct + Duration::from_secs((DEFAULT_AUTH_SESSION_EXPIRY + 1).into());
|
||||||
let expiry_b = ct + Duration::from_secs((AUTH_SESSION_EXPIRY + 1) * 2);
|
let expiry_b = ct + Duration::from_secs(((DEFAULT_AUTH_SESSION_EXPIRY + 1) * 2).into());
|
||||||
|
|
||||||
let session_a = Uuid::new_v4();
|
let session_a = Uuid::new_v4();
|
||||||
let session_b = Uuid::new_v4();
|
let session_b = Uuid::new_v4();
|
||||||
|
@ -3533,7 +3577,7 @@ mod tests {
|
||||||
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
||||||
|
|
||||||
let post_grace = ct + GRACE_WINDOW + Duration::from_secs(1);
|
let post_grace = ct + GRACE_WINDOW + Duration::from_secs(1);
|
||||||
let expiry = ct + Duration::from_secs(AUTH_SESSION_EXPIRY + 1);
|
let expiry = ct + Duration::from_secs(DEFAULT_AUTH_SESSION_EXPIRY as u64 + 1);
|
||||||
|
|
||||||
// Assert that our grace time is less than expiry, so we know the failure is due to
|
// Assert that our grace time is less than expiry, so we know the failure is due to
|
||||||
// this.
|
// this.
|
||||||
|
@ -3592,6 +3636,137 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[idm_test]
|
||||||
|
async fn test_idm_account_session_expiry(
|
||||||
|
idms: &IdmServer,
|
||||||
|
_idms_delayed: &mut IdmServerDelayed,
|
||||||
|
) {
|
||||||
|
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
||||||
|
|
||||||
|
//we first set the expiry to a custom value
|
||||||
|
let mut idms_prox_write = idms.proxy_write(ct).await;
|
||||||
|
|
||||||
|
let new_authsession_expiry = 1000_u32;
|
||||||
|
idms_prox_write.account_policy.authsession_expiry = new_authsession_expiry;
|
||||||
|
assert!(idms_prox_write.commit().is_ok());
|
||||||
|
|
||||||
|
// Start anonymous auth.
|
||||||
|
let mut idms_auth = idms.auth().await;
|
||||||
|
// Send the initial auth event for initialising the session
|
||||||
|
let anon_init = AuthEvent::anonymous_init();
|
||||||
|
// Expect success
|
||||||
|
let r1 = idms_auth.auth(&anon_init, ct, Source::Internal).await;
|
||||||
|
/* Some weird lifetime things happen here ... */
|
||||||
|
|
||||||
|
let sid = match r1 {
|
||||||
|
Ok(ar) => {
|
||||||
|
let AuthResult { sessionid, state } = ar;
|
||||||
|
match state {
|
||||||
|
AuthState::Choose(mut conts) => {
|
||||||
|
// Should only be one auth mech
|
||||||
|
assert!(conts.len() == 1);
|
||||||
|
// And it should be anonymous
|
||||||
|
let m = conts.pop().expect("Should not fail");
|
||||||
|
assert!(m == AuthMech::Anonymous);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!("A critical error has occurred! We have a non-continue result!");
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Now pass back the sessionid, we are good to continue.
|
||||||
|
sessionid
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// Should not occur!
|
||||||
|
error!("A critical error has occurred! {:?}", e);
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
idms_auth.commit().expect("Must not fail");
|
||||||
|
|
||||||
|
let mut idms_auth = idms.auth().await;
|
||||||
|
let anon_begin = AuthEvent::begin_mech(sid, AuthMech::Anonymous);
|
||||||
|
|
||||||
|
let r2 = idms_auth.auth(&anon_begin, ct, Source::Internal).await;
|
||||||
|
|
||||||
|
match r2 {
|
||||||
|
Ok(ar) => {
|
||||||
|
let AuthResult {
|
||||||
|
sessionid: _,
|
||||||
|
state,
|
||||||
|
} = ar;
|
||||||
|
|
||||||
|
match state {
|
||||||
|
AuthState::Continue(allowed) => {
|
||||||
|
// Check the uat.
|
||||||
|
assert!(allowed.len() == 1);
|
||||||
|
assert!(allowed.first() == Some(&AuthAllowed::Anonymous));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!("A critical error has occurred! We have a non-continue result!");
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("A critical error has occurred! {:?}", e);
|
||||||
|
// Should not occur!
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
idms_auth.commit().expect("Must not fail");
|
||||||
|
|
||||||
|
let mut idms_auth = idms.auth().await;
|
||||||
|
// Now send the anonymous request, given the session id.
|
||||||
|
let anon_step = AuthEvent::cred_step_anonymous(sid);
|
||||||
|
|
||||||
|
// Expect success
|
||||||
|
let r2 = idms_auth.auth(&anon_step, ct, Source::Internal).await;
|
||||||
|
|
||||||
|
let token = match r2 {
|
||||||
|
Ok(ar) => {
|
||||||
|
let AuthResult {
|
||||||
|
sessionid: _,
|
||||||
|
state,
|
||||||
|
} = ar;
|
||||||
|
|
||||||
|
match state {
|
||||||
|
AuthState::Success(uat, AuthIssueSession::Token) => uat,
|
||||||
|
_ => {
|
||||||
|
error!("A critical error has occurred! We have a non-succcess result!");
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("A critical error has occurred! {:?}", e);
|
||||||
|
// Should not occur!
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
idms_auth.commit().expect("Must not fail");
|
||||||
|
|
||||||
|
// Token_str to uat
|
||||||
|
// we have to do it this way because anonymous doesn't have an ideantity for which we cam get the expiry value
|
||||||
|
let Token::UserAuthToken(uat) = idms
|
||||||
|
.proxy_read()
|
||||||
|
.await
|
||||||
|
.validate_and_parse_token_to_token(Some(&token), ct)
|
||||||
|
.expect("Must not fail") else {
|
||||||
|
panic!("Unexpected auth token type for anonymous auth");
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!(?uat);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
matches!(uat.expiry, Some(exp) if exp == OffsetDateTime::UNIX_EPOCH + ct + Duration::from_secs(new_authsession_expiry as u64))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[idm_test]
|
#[idm_test]
|
||||||
async fn test_idm_uat_claim_insertion(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) {
|
async fn test_idm_uat_claim_insertion(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) {
|
||||||
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
||||||
|
@ -3609,7 +3784,12 @@ mod tests {
|
||||||
|
|
||||||
// == anonymous
|
// == anonymous
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
let ident = idms_prox_write
|
let ident = idms_prox_write
|
||||||
.process_uat_to_identity(&uat, ct)
|
.process_uat_to_identity(&uat, ct)
|
||||||
|
@ -3623,7 +3803,12 @@ mod tests {
|
||||||
|
|
||||||
// == unixpassword
|
// == unixpassword
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
let ident = idms_prox_write
|
let ident = idms_prox_write
|
||||||
.process_uat_to_identity(&uat, ct)
|
.process_uat_to_identity(&uat, ct)
|
||||||
|
@ -3637,7 +3822,12 @@ mod tests {
|
||||||
|
|
||||||
// == password
|
// == password
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
let ident = idms_prox_write
|
let ident = idms_prox_write
|
||||||
.process_uat_to_identity(&uat, ct)
|
.process_uat_to_identity(&uat, ct)
|
||||||
|
@ -3651,7 +3841,12 @@ mod tests {
|
||||||
|
|
||||||
// == generatedpassword
|
// == generatedpassword
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
let ident = idms_prox_write
|
let ident = idms_prox_write
|
||||||
.process_uat_to_identity(&uat, ct)
|
.process_uat_to_identity(&uat, ct)
|
||||||
|
@ -3665,7 +3860,12 @@ mod tests {
|
||||||
|
|
||||||
// == webauthn
|
// == webauthn
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
let ident = idms_prox_write
|
let ident = idms_prox_write
|
||||||
.process_uat_to_identity(&uat, ct)
|
.process_uat_to_identity(&uat, ct)
|
||||||
|
@ -3679,7 +3879,12 @@ mod tests {
|
||||||
|
|
||||||
// == passwordmfa
|
// == passwordmfa
|
||||||
let uat = account
|
let uat = account
|
||||||
.to_userauthtoken(session_id, SessionScope::ReadWrite, ct)
|
.to_userauthtoken(
|
||||||
|
session_id,
|
||||||
|
SessionScope::ReadWrite,
|
||||||
|
ct,
|
||||||
|
DEFAULT_AUTH_SESSION_EXPIRY,
|
||||||
|
)
|
||||||
.expect("Unable to create uat");
|
.expect("Unable to create uat");
|
||||||
let ident = idms_prox_write
|
let ident = idms_prox_write
|
||||||
.process_uat_to_identity(&uat, ct)
|
.process_uat_to_identity(&uat, ct)
|
||||||
|
|
|
@ -32,6 +32,8 @@ lazy_static! {
|
||||||
m.insert("id_verification_eckey");
|
m.insert("id_verification_eckey");
|
||||||
m.insert("badlist_password");
|
m.insert("badlist_password");
|
||||||
m.insert("domain_display_name");
|
m.insert("domain_display_name");
|
||||||
|
m.insert("authsession_expiry");
|
||||||
|
m.insert("privilege_expiry");
|
||||||
m
|
m
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -490,6 +490,8 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
||||||
SCHEMA_ATTR_ACCOUNT_EXPIRE.clone().into(),
|
SCHEMA_ATTR_ACCOUNT_EXPIRE.clone().into(),
|
||||||
SCHEMA_ATTR_ACCOUNT_VALID_FROM.clone().into(),
|
SCHEMA_ATTR_ACCOUNT_VALID_FROM.clone().into(),
|
||||||
SCHEMA_ATTR_API_TOKEN_SESSION.clone().into(),
|
SCHEMA_ATTR_API_TOKEN_SESSION.clone().into(),
|
||||||
|
SCHEMA_ATTR_AUTH_SESSION_EXPIRY.clone().into(),
|
||||||
|
SCHEMA_ATTR_AUTH_PRIVILEGE_EXPIRY.clone().into(),
|
||||||
SCHEMA_ATTR_BADLIST_PASSWORD.clone().into(),
|
SCHEMA_ATTR_BADLIST_PASSWORD.clone().into(),
|
||||||
SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN.clone().into(),
|
SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN.clone().into(),
|
||||||
SCHEMA_ATTR_DEVICEKEYS.clone().into(),
|
SCHEMA_ATTR_DEVICEKEYS.clone().into(),
|
||||||
|
@ -701,6 +703,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
||||||
E_IDM_ACP_RADIUS_SERVERS_V1.clone(),
|
E_IDM_ACP_RADIUS_SERVERS_V1.clone(),
|
||||||
E_IDM_ACP_DOMAIN_ADMIN_PRIV_V1.clone(),
|
E_IDM_ACP_DOMAIN_ADMIN_PRIV_V1.clone(),
|
||||||
E_IDM_ACP_SYSTEM_CONFIG_PRIV_V1.clone(),
|
E_IDM_ACP_SYSTEM_CONFIG_PRIV_V1.clone(),
|
||||||
|
E_IDM_ACP_SYSTEM_CONFIG_SESSION_EXP_PRIV_V1.clone(),
|
||||||
E_IDM_ACP_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1.clone(),
|
E_IDM_ACP_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1.clone(),
|
||||||
E_IDM_ACP_PEOPLE_EXTEND_PRIV_V1.clone(),
|
E_IDM_ACP_PEOPLE_EXTEND_PRIV_V1.clone(),
|
||||||
E_IDM_ACP_HP_PEOPLE_READ_PRIV_V1.clone(),
|
E_IDM_ACP_HP_PEOPLE_READ_PRIV_V1.clone(),
|
||||||
|
|
|
@ -820,6 +820,36 @@ pub trait QueryServerTransaction<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_authsession_expiry(&mut self) -> Result<u32, OperationError> {
|
||||||
|
self.internal_search_uuid(UUID_SYSTEM_CONFIG)
|
||||||
|
.and_then(|e| {
|
||||||
|
if let Some(expiry_time) = e.get_ava_single_uint32("authsession_expiry") {
|
||||||
|
Ok(expiry_time)
|
||||||
|
} else {
|
||||||
|
Err(OperationError::NoMatchingAttributes)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|e| {
|
||||||
|
admin_error!(?e, "Failed to retrieve authsession_expiry from system configuration");
|
||||||
|
e
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_privilege_expiry(&mut self) -> Result<u32, OperationError> {
|
||||||
|
self.internal_search_uuid(UUID_SYSTEM_CONFIG)
|
||||||
|
.and_then(|e| {
|
||||||
|
if let Some(expiry_time) = e.get_ava_single_uint32("privilege_expiry") {
|
||||||
|
Ok(expiry_time)
|
||||||
|
} else {
|
||||||
|
Err(OperationError::NoMatchingAttributes)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|e| {
|
||||||
|
admin_error!(?e, "Failed to retrieve privilege_expiry from system configuration");
|
||||||
|
e
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn get_oauth2rs_set(&mut self) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
|
fn get_oauth2rs_set(&mut self) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
|
||||||
self.internal_search(filter!(f_eq(
|
self.internal_search(filter!(f_eq(
|
||||||
Attribute::Class,
|
Attribute::Class,
|
||||||
|
|
|
@ -323,6 +323,8 @@ async fn test_default_entries_rbac_admins_schema_entries(rsclient: KanidmClient)
|
||||||
"domain_ssid",
|
"domain_ssid",
|
||||||
"gidnumber",
|
"gidnumber",
|
||||||
"badlist_password",
|
"badlist_password",
|
||||||
|
"authsession_expiry",
|
||||||
|
"privilege_expiry",
|
||||||
"loginshell",
|
"loginshell",
|
||||||
"unix_password",
|
"unix_password",
|
||||||
"nsuniqueid",
|
"nsuniqueid",
|
||||||
|
|
|
@ -1460,3 +1460,34 @@ async fn test_server_user_auth_reauthentication(rsclient: KanidmClient) {
|
||||||
eprintln!("{:?} {:?}", now, uat.purpose);
|
eprintln!("{:?} {:?}", now, uat.purpose);
|
||||||
assert!(uat.purpose_readwrite_active(now));
|
assert!(uat.purpose_readwrite_active(now));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[kanidmd_testkit::test]
|
||||||
|
async fn test_authsession_expiry(rsclient: KanidmClient) {
|
||||||
|
let res = rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.await;
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let authsession_expiry = 2878_u32;
|
||||||
|
rsclient
|
||||||
|
.system_authsession_expiry_set(authsession_expiry)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let ans = rsclient.system_authsession_expiry_get().await.unwrap();
|
||||||
|
assert_eq!(authsession_expiry, ans);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[kanidmd_testkit::test]
|
||||||
|
async fn test_privilege_expiry(rsclient: KanidmClient) {
|
||||||
|
let res = rsclient
|
||||||
|
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||||
|
.await;
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let authsession_expiry = 2878_u32;
|
||||||
|
|
||||||
|
rsclient
|
||||||
|
.system_auth_privilege_expiry_set(authsession_expiry)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let ans = rsclient.system_auth_privilege_expiry_get().await.unwrap();
|
||||||
|
assert_eq!(authsession_expiry, ans);
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ pub mod raw;
|
||||||
pub mod recycle;
|
pub mod recycle;
|
||||||
pub mod serviceaccount;
|
pub mod serviceaccount;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
|
pub mod session_expiry;
|
||||||
pub mod synch;
|
pub mod synch;
|
||||||
mod webauthn;
|
mod webauthn;
|
||||||
|
|
||||||
|
@ -72,6 +73,8 @@ impl SystemOpt {
|
||||||
SystemOpt::Oauth2 { commands } => commands.debug(),
|
SystemOpt::Oauth2 { commands } => commands.debug(),
|
||||||
SystemOpt::Domain { commands } => commands.debug(),
|
SystemOpt::Domain { commands } => commands.debug(),
|
||||||
SystemOpt::Synch { commands } => commands.debug(),
|
SystemOpt::Synch { commands } => commands.debug(),
|
||||||
|
SystemOpt::AuthSessionExpiry { commands } => commands.debug(),
|
||||||
|
SystemOpt::PrivilegedSessionExpiry { commands } => commands.debug(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +84,8 @@ impl SystemOpt {
|
||||||
SystemOpt::Oauth2 { commands } => commands.exec().await,
|
SystemOpt::Oauth2 { commands } => commands.exec().await,
|
||||||
SystemOpt::Domain { commands } => commands.exec().await,
|
SystemOpt::Domain { commands } => commands.exec().await,
|
||||||
SystemOpt::Synch { commands } => commands.exec().await,
|
SystemOpt::Synch { commands } => commands.exec().await,
|
||||||
|
SystemOpt::AuthSessionExpiry { commands } => commands.exec().await,
|
||||||
|
SystemOpt::PrivilegedSessionExpiry { commands } => commands.exec().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
73
tools/cli/src/cli/session_expiry.rs
Normal file
73
tools/cli/src/cli/session_expiry.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
use crate::common::OpType;
|
||||||
|
|
||||||
|
use crate::{AuthSessionExpiryOpt, PrivilegedSessionExpiryOpt};
|
||||||
|
|
||||||
|
impl AuthSessionExpiryOpt {
|
||||||
|
pub fn debug(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
AuthSessionExpiryOpt::Get(copt) => copt.debug,
|
||||||
|
AuthSessionExpiryOpt::Set { copt, .. } => copt.debug,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn exec(&self) {
|
||||||
|
match self {
|
||||||
|
AuthSessionExpiryOpt::Get(copt) => {
|
||||||
|
let client = copt.to_client(OpType::Read).await;
|
||||||
|
match client.system_authsession_expiry_get().await {
|
||||||
|
Ok(exp_time) => {
|
||||||
|
println!(
|
||||||
|
"The current system auth session expiry time is: {exp_time} seconds."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("{:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AuthSessionExpiryOpt::Set { copt, expiry } => {
|
||||||
|
let client = copt.to_client(OpType::Write).await;
|
||||||
|
match client.system_authsession_expiry_set(*expiry).await {
|
||||||
|
Ok(()) => {
|
||||||
|
println!("The system auth session expiry has been successfully updated.")
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(e) => eprintln!("{:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrivilegedSessionExpiryOpt {
|
||||||
|
pub fn debug(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
PrivilegedSessionExpiryOpt::Get(copt) => copt.debug,
|
||||||
|
PrivilegedSessionExpiryOpt::Set { copt, .. } => copt.debug,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn exec(&self) {
|
||||||
|
match self {
|
||||||
|
PrivilegedSessionExpiryOpt::Get(copt) => {
|
||||||
|
let client = copt.to_client(OpType::Read).await;
|
||||||
|
match client.system_auth_privilege_expiry_get().await {
|
||||||
|
Ok(exp_time) => {
|
||||||
|
println!(
|
||||||
|
"The current system auth privilege expiry time is: {exp_time} seconds."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("{:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PrivilegedSessionExpiryOpt::Set { copt, expiry } => {
|
||||||
|
let client = copt.to_client(OpType::Write).await;
|
||||||
|
match client.system_auth_privilege_expiry_set(*expiry).await {
|
||||||
|
Ok(()) => {
|
||||||
|
println!("The system auth privilege expiry has been successfully updated.")
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(e) => eprintln!("{:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -904,6 +904,36 @@ pub enum SynchOpt {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
pub enum AuthSessionExpiryOpt {
|
||||||
|
#[clap[name = "get"]]
|
||||||
|
/// Show information about this system auth session expiry
|
||||||
|
Get(CommonOpt),
|
||||||
|
#[clap[name = "set"]]
|
||||||
|
/// Sets the system auth session expiry in seconds
|
||||||
|
Set {
|
||||||
|
#[clap(flatten)]
|
||||||
|
copt: CommonOpt,
|
||||||
|
#[clap(name = "expiry")]
|
||||||
|
expiry: u32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
pub enum PrivilegedSessionExpiryOpt {
|
||||||
|
#[clap[name = "get"]]
|
||||||
|
/// Show information about this system privileged session expiry
|
||||||
|
Get(CommonOpt),
|
||||||
|
#[clap[name = "set"]]
|
||||||
|
/// Sets the system auth privilege session expiry in seconds
|
||||||
|
Set {
|
||||||
|
#[clap(flatten)]
|
||||||
|
copt: CommonOpt,
|
||||||
|
#[clap(name = "expiry")]
|
||||||
|
expiry: u32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub enum SystemOpt {
|
pub enum SystemOpt {
|
||||||
#[clap(name = "pw-badlist")]
|
#[clap(name = "pw-badlist")]
|
||||||
|
@ -912,6 +942,18 @@ pub enum SystemOpt {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
commands: PwBadlistOpt,
|
commands: PwBadlistOpt,
|
||||||
},
|
},
|
||||||
|
/// Configure and display the system auth session expiry
|
||||||
|
#[clap(name = "auth-expiry")]
|
||||||
|
AuthSessionExpiry {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
commands: AuthSessionExpiryOpt,
|
||||||
|
},
|
||||||
|
/// Configure and display the system auth privilege session expiry
|
||||||
|
#[clap(name = "privilege-expiry")]
|
||||||
|
PrivilegedSessionExpiry {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
commands: PrivilegedSessionExpiryOpt,
|
||||||
|
},
|
||||||
#[clap(name = "oauth2")]
|
#[clap(name = "oauth2")]
|
||||||
/// Configure and display oauth2/oidc resource server configuration
|
/// Configure and display oauth2/oidc resource server configuration
|
||||||
Oauth2 {
|
Oauth2 {
|
||||||
|
|
Loading…
Reference in a new issue