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",
|
||||
"rusticata-macros",
|
||||
"thiserror",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -426,11 +426,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.65.1"
|
||||
version = "0.66.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5"
|
||||
checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 2.3.3",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"lazy_static",
|
||||
|
@ -563,11 +563,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -771,7 +772,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
|
@ -788,7 +789,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
@ -1088,9 +1089,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8810e7e2cf385b1e9b50d68264908ec367ba642c96d02edfe61c39e88e2a3c01"
|
||||
checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -1343,7 +1344,7 @@ dependencies = [
|
|||
"mime",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"tokio",
|
||||
"url",
|
||||
"webdriver",
|
||||
|
@ -2227,7 +2228,7 @@ dependencies = [
|
|||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tracing",
|
||||
|
@ -2273,7 +2274,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"tracing",
|
||||
"url",
|
||||
"urlencoding",
|
||||
|
@ -2300,7 +2301,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
|
@ -2386,7 +2387,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"serde_with",
|
||||
"sketching",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"tokio",
|
||||
"tokio-openssl",
|
||||
"tokio-util",
|
||||
|
@ -2439,7 +2440,7 @@ dependencies = [
|
|||
"smartstring",
|
||||
"smolset",
|
||||
"sshkeys",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"toml",
|
||||
|
@ -2484,7 +2485,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"sketching",
|
||||
"testkit-macros",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
|
@ -2505,7 +2506,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde-wasm-bindgen 0.5.0",
|
||||
"serde_json",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"url",
|
||||
"uuid",
|
||||
"wasm-bindgen",
|
||||
|
@ -2519,9 +2520,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98"
|
||||
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
|
||||
dependencies = [
|
||||
"kqueue-sys",
|
||||
"libc",
|
||||
|
@ -2529,9 +2530,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "kqueue-sys"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
|
||||
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
|
@ -2750,9 +2751,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
|||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67827e6ea8ee8a7c4a72227ef4fc08957040acffdb5f122733b24fa12daff41b"
|
||||
checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef"
|
||||
|
||||
[[package]]
|
||||
name = "mathru"
|
||||
|
@ -3310,9 +3311,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
|||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.7.1"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5"
|
||||
checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
|
@ -3355,18 +3356,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842"
|
||||
checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
|
||||
checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -3824,9 +3825,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.4"
|
||||
version = "0.38.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
|
||||
checksum = "172891ebdceb05aa0005f533a6cbfca599ddd7d966f6f5d4d9b2e70478e70399"
|
||||
dependencies = [
|
||||
"bitflags 2.3.3",
|
||||
"errno",
|
||||
|
@ -3875,7 +3876,7 @@ dependencies = [
|
|||
"peg",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
|
@ -3939,9 +3940,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "selinux-sys"
|
||||
version = "0.6.5"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2b8dbf5dd0b21d466538786194081b2c4d61878e1427f7e52f99d5845432483"
|
||||
checksum = "d56602385930248c57e45f6174a6a48e12b723d0cc2ae8f467fcbe80c0d06f41"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
|
@ -4085,7 +4086,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4331,9 +4332,9 @@ checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a"
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.7.0"
|
||||
version = "3.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998"
|
||||
checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
|
@ -4415,9 +4416,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b79eabcd964882a646b3584543ccabeae7869e9ac32a46f6f22b7a5bd405308b"
|
||||
checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
|
@ -4719,7 +4720,7 @@ dependencies = [
|
|||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
|
@ -5113,7 +5114,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
"unicode-segmentation",
|
||||
"url",
|
||||
]
|
||||
|
@ -5370,9 +5371,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.2"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7"
|
||||
checksum = "acaaa1190073b2b101e15083c38ee8ec891b5e05cbee516521e94ec008f61e64"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -5401,7 +5402,7 @@ dependencies = [
|
|||
"oid-registry",
|
||||
"rusticata-macros",
|
||||
"thiserror",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5536,5 +5537,5 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"quick-error",
|
||||
"regex",
|
||||
"time 0.3.24",
|
||||
"time 0.3.25",
|
||||
]
|
||||
|
|
|
@ -61,6 +61,7 @@ pub enum ClientError {
|
|||
TotpVerifyFailed(Uuid, TotpSecret),
|
||||
TotpInvalidSha1(Uuid),
|
||||
JsonDecode(reqwest::Error, String),
|
||||
InvalidResponseFormat(String),
|
||||
JsonEncode(SerdeJsonError),
|
||||
SystemError,
|
||||
ConfigParseIssue(String),
|
||||
|
|
|
@ -23,4 +23,40 @@ impl KanidmClient {
|
|||
self.perform_delete_request_with_body("/v1/system/_attr/badlist_password", list)
|
||||
.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_ATTR: &str = "attr";
|
||||
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_CLAIM: &str = "claim";
|
||||
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_PRIMARY_CREDENTIAL: &str = "primary_credential";
|
||||
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_RECYCLED: &str = "recycled";
|
||||
pub const ATTR_REPLICATED: &str = "replicated";
|
||||
|
|
|
@ -1219,6 +1219,24 @@ pub async fn system_delete_attr(
|
|||
.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(
|
||||
State(state): State<ServerState>,
|
||||
Extension(kopid): Extension<KOpId>,
|
||||
|
@ -1661,6 +1679,7 @@ pub fn router(state: ServerState) -> Router<ServerState> {
|
|||
"/v1/system/_attr/:attr",
|
||||
get(system_get_attr)
|
||||
.post(system_post_attr)
|
||||
.put(system_put_attr)
|
||||
.delete(system_delete_attr),
|
||||
)
|
||||
.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_trust_x_forward_for(sconfig.trust_x_forward_for);
|
||||
config.update_admin_bind_path(&sconfig.adminbindpath);
|
||||
|
||||
match &opt.commands {
|
||||
// we aren't going to touch the DB so we can carry on
|
||||
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! {
|
||||
pub static ref E_IDM_ACP_ACCOUNT_UNIX_EXTEND_PRIV_V1: EntryInitNew = entry_init!(
|
||||
(ATTR_CLASS, EntryClass::Object.to_value()),
|
||||
|
|
|
@ -50,6 +50,7 @@ pub enum Attribute {
|
|||
ApiTokenSession,
|
||||
Attr,
|
||||
AttributeName,
|
||||
AuthSessionExpiry,
|
||||
BadlistPassword,
|
||||
Claim,
|
||||
Class,
|
||||
|
@ -115,6 +116,7 @@ pub enum Attribute {
|
|||
Phantom,
|
||||
PrimaryCredential,
|
||||
PrivateCookieKey,
|
||||
PrivilegeExpiry,
|
||||
RadiusSecret,
|
||||
Replicated,
|
||||
Rs256PrivateKeyDer,
|
||||
|
@ -189,6 +191,7 @@ impl TryFrom<String> for Attribute {
|
|||
ATTR_API_TOKEN_SESSION => Attribute::ApiTokenSession,
|
||||
ATTR_ATTR => Attribute::Attr,
|
||||
ATTR_ATTRIBUTENAME => Attribute::AttributeName,
|
||||
ATTR_AUTH_SESSION_EXPIRY => Attribute::AuthSessionExpiry,
|
||||
ATTR_BADLIST_PASSWORD => Attribute::BadlistPassword,
|
||||
ATTR_CLAIM => Attribute::Claim,
|
||||
ATTR_CLASS => Attribute::Class,
|
||||
|
@ -256,6 +259,7 @@ impl TryFrom<String> for Attribute {
|
|||
ATTR_PHANTOM => Attribute::Phantom,
|
||||
ATTR_PRIMARY_CREDENTIAL => Attribute::PrimaryCredential,
|
||||
ATTR_PRIVATE_COOKIE_KEY => Attribute::PrivateCookieKey,
|
||||
ATTR_PRIVILEGE_EXPIRY => Attribute::PrivilegeExpiry,
|
||||
ATTR_RADIUS_SECRET => Attribute::RadiusSecret,
|
||||
ATTR_REPLICATED => Attribute::Replicated,
|
||||
ATTR_RS256_PRIVATE_KEY_DER => Attribute::Rs256PrivateKeyDer,
|
||||
|
@ -416,6 +420,8 @@ impl From<Attribute> for &'static str {
|
|||
Attribute::TestAttr => TEST_ATTR_TEST_ATTR,
|
||||
#[cfg(any(debug_assertions, test))]
|
||||
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;
|
||||
|
||||
// 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.
|
||||
pub const AUTH_PRIVILEGE_EXPIRY: u64 = 600;
|
||||
pub const DEFAULT_AUTH_PRIVILEGE_EXPIRY: u32 = 600;
|
||||
// Default - oauth refresh tokens last for 16 hours.
|
||||
pub const OAUTH_REFRESH_TOKEN_EXPIRY: u64 = 3600 * 8;
|
||||
|
||||
|
|
|
@ -193,6 +193,24 @@ pub static ref SCHEMA_ATTR_BADLIST_PASSWORD: SchemaAttribute = SchemaAttribute {
|
|||
..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 {
|
||||
uuid: UUID_SCHEMA_ATTR_LOGINSHELL,
|
||||
name: Attribute::LoginShell.into(),
|
||||
|
@ -714,6 +732,8 @@ pub static ref SCHEMA_CLASS_SYSTEM_CONFIG: SchemaClass = SchemaClass {
|
|||
systemmay: vec![
|
||||
Attribute::Description.into(),
|
||||
Attribute::BadlistPassword.into(),
|
||||
Attribute::AuthSessionExpiry.into(),
|
||||
Attribute::PrivilegeExpiry.into()
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::constants::uuids::*;
|
|||
|
||||
use crate::entry::{Entry, EntryInit, EntryInitNew, EntryNew};
|
||||
use crate::prelude::{Attribute, EntryClass};
|
||||
use crate::prelude::{DEFAULT_AUTH_PRIVILEGE_EXPIRY, DEFAULT_AUTH_SESSION_EXPIRY};
|
||||
use crate::value::Value;
|
||||
|
||||
// Default entries for system_config
|
||||
|
@ -30,6 +31,14 @@ lazy_static! {
|
|||
"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(),
|
||||
Value::new_iutf8("100preteamare")
|
||||
|
|
|
@ -233,6 +233,10 @@ pub const UUID_SCHEMA_ATTR_SYNC_YIELD_AUTHORITY: Uuid =
|
|||
uuid!("00000000-0000-0000-0000-ffff00000138");
|
||||
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_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
|
||||
// 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 =
|
||||
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_ACP_SYSTEM_CONFIG_SESSION_EXP_PRIV_V1: Uuid =
|
||||
uuid!("00000000-0000-0000-0000-ffffff000047");
|
||||
|
||||
// End of system ranges
|
||||
pub const UUID_DOES_NOT_EXIST: Uuid = uuid!("00000000-0000-0000-0000-fffffffffffe");
|
||||
|
|
|
@ -219,6 +219,7 @@ impl Account {
|
|||
session_id: Uuid,
|
||||
scope: SessionScope,
|
||||
ct: Duration,
|
||||
auth_session_expiry: u32,
|
||||
) -> Option<UserAuthToken> {
|
||||
// TODO: Apply policy to this expiry time.
|
||||
// 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.
|
||||
let ct = ct - Duration::from_nanos(ct.subsec_nanos() as u64);
|
||||
let issued_at = OffsetDateTime::UNIX_EPOCH + ct;
|
||||
|
||||
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 {
|
||||
// Issue an invalid/expired session.
|
||||
|
@ -279,6 +279,7 @@ impl Account {
|
|||
session_expiry: Option<OffsetDateTime>,
|
||||
scope: SessionScope,
|
||||
ct: Duration,
|
||||
auth_privilege_expiry: u32,
|
||||
) -> Option<UserAuthToken> {
|
||||
let issued_at = OffsetDateTime::UNIX_EPOCH + ct;
|
||||
|
||||
|
@ -294,7 +295,9 @@ impl Account {
|
|||
// Return a ReadWrite session with an inner expiry for the privileges
|
||||
{
|
||||
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 },
|
||||
|
@ -327,6 +330,7 @@ impl Account {
|
|||
expire: Option<&OffsetDateTime>,
|
||||
) -> bool {
|
||||
let cot = OffsetDateTime::UNIX_EPOCH + ct;
|
||||
trace!("Checking within valid time: {:?} {:?}", valid_from, expire);
|
||||
|
||||
let vmin = if let Some(vft) = valid_from {
|
||||
// If current time greater than start time window
|
||||
|
@ -539,7 +543,7 @@ impl Account {
|
|||
) -> bool {
|
||||
// Remember, token expiry is checked by validate_and_parse_token_to_token.
|
||||
// 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(
|
||||
ct,
|
||||
|
@ -554,6 +558,7 @@ impl Account {
|
|||
|
||||
// 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.
|
||||
trace!("{}", &uat);
|
||||
|
||||
if uat.uuid == UUID_ANONYMOUS {
|
||||
security_debug!("Anonymous sessions do not have session records, session is valid.");
|
||||
|
@ -845,7 +850,12 @@ mod tests {
|
|||
.expect("account must exist");
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
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");
|
||||
|
||||
// Check the ui hints are as expected.
|
||||
|
@ -871,7 +881,12 @@ mod tests {
|
|||
.expect("account must exist");
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
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");
|
||||
|
||||
assert!(uat.ui_hints.len() == 2);
|
||||
|
@ -902,7 +917,12 @@ mod tests {
|
|||
.expect("account must exist");
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
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");
|
||||
|
||||
assert!(uat.ui_hints.len() == 3);
|
||||
|
|
|
@ -37,6 +37,8 @@ use crate::prelude::*;
|
|||
use crate::value::Session;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use super::server::AccountPolicy;
|
||||
|
||||
// Each CredHandler takes one or more credentials and determines if the
|
||||
// handlers requirements can be 100% fulfilled. This is where MFA or other
|
||||
// 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(
|
||||
cred: &AuthCredential,
|
||||
cred_id: Uuid,
|
||||
|
@ -1005,8 +1007,8 @@ impl AuthSession {
|
|||
async_tx: &Sender<DelayedAction>,
|
||||
audit_tx: &Sender<AuditEvent>,
|
||||
webauthn: &Webauthn,
|
||||
pw_badlist_set: Option<&HashSet<String>>,
|
||||
uat_jwt_signer: &JwsSigner,
|
||||
account_policy: &AccountPolicy,
|
||||
) -> Result<AuthState, OperationError> {
|
||||
let (next_state, response) = match &mut self.state {
|
||||
AuthSessionState::Init(_) | AuthSessionState::Success | AuthSessionState::Denied(_) => {
|
||||
|
@ -1021,11 +1023,18 @@ impl AuthSession {
|
|||
self.account.uuid,
|
||||
async_tx,
|
||||
webauthn,
|
||||
pw_badlist_set,
|
||||
Some(&account_policy.pw_badlist_cache),
|
||||
) {
|
||||
CredState::Success { auth_type, cred_id } => {
|
||||
// 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);
|
||||
|
||||
// Now encrypt and prepare the token for return to the client.
|
||||
|
@ -1096,6 +1105,8 @@ impl AuthSession {
|
|||
time: Duration,
|
||||
async_tx: &Sender<DelayedAction>,
|
||||
cred_id: Uuid,
|
||||
auth_session_expiry: u32,
|
||||
auth_privilege_expiry: u32,
|
||||
) -> Result<UserAuthToken, OperationError> {
|
||||
security_debug!("Successful cred handling");
|
||||
match self.intent {
|
||||
|
@ -1121,7 +1132,7 @@ impl AuthSession {
|
|||
|
||||
let uat = self
|
||||
.account
|
||||
.to_userauthtoken(session_id, scope, time)
|
||||
.to_userauthtoken(session_id, scope, time, auth_session_expiry)
|
||||
.ok_or(OperationError::InvalidState)?;
|
||||
|
||||
// Queue the session info write.
|
||||
|
@ -1181,7 +1192,13 @@ impl AuthSession {
|
|||
|
||||
let uat = self
|
||||
.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(uat)
|
||||
|
@ -1230,6 +1247,7 @@ mod tests {
|
|||
BAD_WEBAUTHN_MSG, PW_BADLIST_MSG,
|
||||
};
|
||||
use crate::idm::delayed::DelayedAction;
|
||||
use crate::idm::server::AccountPolicy;
|
||||
use crate::idm::AuthState;
|
||||
use crate::prelude::*;
|
||||
use crate::utils::{duration_from_epoch_now, readable_password_from_random};
|
||||
|
@ -1353,8 +1371,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(_)) => {}
|
||||
_ => panic!(),
|
||||
|
@ -1377,8 +1395,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||
_ => panic!(),
|
||||
|
@ -1421,8 +1439,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == PW_BADLIST_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -1545,8 +1563,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -1571,8 +1589,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -1594,8 +1612,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -1619,8 +1637,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -1631,8 +1649,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -1656,8 +1674,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -1668,8 +1686,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||
_ => panic!(),
|
||||
|
@ -1733,8 +1751,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -1745,8 +1763,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == PW_BADLIST_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -1890,8 +1908,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
None,
|
||||
&jws_signer,
|
||||
&Default::default(),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -1918,8 +1936,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
None,
|
||||
&jws_signer,
|
||||
&Default::default(),
|
||||
) {
|
||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||
_ => panic!(),
|
||||
|
@ -1953,8 +1971,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
None,
|
||||
&jws_signer,
|
||||
&Default::default(),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2002,8 +2020,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
None,
|
||||
&jws_signer,
|
||||
&Default::default(),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2055,8 +2073,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2079,8 +2097,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2114,8 +2132,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2144,8 +2162,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -2156,8 +2174,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2192,8 +2210,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -2204,8 +2222,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||
_ => panic!(),
|
||||
|
@ -2273,8 +2291,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2297,8 +2315,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_TOTP_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2330,8 +2348,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_WEBAUTHN_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2360,8 +2378,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -2372,8 +2390,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2402,8 +2420,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -2414,8 +2432,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2438,8 +2456,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -2450,8 +2468,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||
_ => panic!(),
|
||||
|
@ -2480,8 +2498,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -2492,8 +2510,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||
_ => panic!(),
|
||||
|
@ -2572,8 +2590,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_AUTH_TYPE_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2595,8 +2613,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_BACKUPCODE_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2619,8 +2637,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -2631,8 +2649,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Denied(msg)) => assert!(msg == BAD_PASSWORD_MSG),
|
||||
_ => panic!(),
|
||||
|
@ -2661,8 +2679,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -2673,8 +2691,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||
_ => panic!(),
|
||||
|
@ -2705,8 +2723,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -2717,8 +2735,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||
_ => panic!(),
|
||||
|
@ -2788,8 +2806,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -2800,8 +2818,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||
_ => panic!(),
|
||||
|
@ -2824,8 +2842,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache.to_owned()),
|
||||
) {
|
||||
Ok(AuthState::Continue(cont)) => assert!(cont == vec![AuthAllowed::Password]),
|
||||
_ => panic!(),
|
||||
|
@ -2836,8 +2854,8 @@ mod tests {
|
|||
&async_tx,
|
||||
&audit_tx,
|
||||
&webauthn,
|
||||
Some(&pw_badlist_cache),
|
||||
&jws_signer,
|
||||
&AccountPolicy::from_pw_badlist_cache(pw_badlist_cache),
|
||||
) {
|
||||
Ok(AuthState::Success(_, AuthIssueSession::Token)) => {}
|
||||
_ => panic!(),
|
||||
|
|
|
@ -1280,7 +1280,7 @@ impl<'a> IdmServerCredUpdateTransaction<'a> {
|
|||
// check a password badlist to eliminate more content
|
||||
// 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)
|
||||
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");
|
||||
Err(PasswordQuality::BadListed)
|
||||
} else {
|
||||
|
|
|
@ -2118,7 +2118,12 @@ mod tests {
|
|||
.target_to_account(UUID_ADMIN)
|
||||
.expect("account must exist");
|
||||
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");
|
||||
|
||||
// Need the uat first for expiry.
|
||||
|
@ -2232,7 +2237,12 @@ mod tests {
|
|||
.target_to_account(UUID_ADMIN)
|
||||
.expect("account must exist");
|
||||
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");
|
||||
|
||||
// Need the uat first for expiry.
|
||||
|
@ -2287,7 +2297,12 @@ mod tests {
|
|||
.expect("account must exist");
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
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");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(&uat, ct)
|
||||
|
@ -2612,7 +2627,12 @@ mod tests {
|
|||
.expect("account must exist");
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
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");
|
||||
let ident2 = idms_prox_write
|
||||
.process_uat_to_identity(&uat2, ct)
|
||||
|
@ -3212,7 +3232,12 @@ mod tests {
|
|||
.expect("account must exist");
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
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");
|
||||
let ident2 = idms_prox_write
|
||||
.process_uat_to_identity(&uat2, ct)
|
||||
|
|
|
@ -69,6 +69,44 @@ pub struct DomainKeys {
|
|||
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 {
|
||||
// There is a good reason to keep this single thread - it
|
||||
// means that limits to sessions can be easily applied and checked to
|
||||
|
@ -87,9 +125,9 @@ pub struct IdmServer {
|
|||
audit_tx: Sender<AuditEvent>,
|
||||
/// [Webauthn] verifier/config
|
||||
webauthn: Webauthn,
|
||||
pw_badlist_cache: Arc<CowCell<HashSet<String>>>,
|
||||
oauth2rs: Arc<Oauth2ResourceServers>,
|
||||
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.
|
||||
|
@ -105,7 +143,7 @@ pub struct IdmServerAuthTransaction<'a> {
|
|||
pub(crate) async_tx: Sender<DelayedAction>,
|
||||
pub(crate) audit_tx: Sender<AuditEvent>,
|
||||
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>,
|
||||
}
|
||||
|
||||
|
@ -113,7 +151,7 @@ pub struct IdmServerCredUpdateTransaction<'a> {
|
|||
pub(crate) _qs_read: QueryServerReadTransaction<'a>,
|
||||
// sid: Sid,
|
||||
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) domain_keys: CowCellReadTxn<DomainKeys>,
|
||||
pub(crate) crypto_policy: &'a CryptoPolicy,
|
||||
|
@ -135,7 +173,7 @@ pub struct IdmServerProxyWriteTransaction<'a> {
|
|||
pub(crate) sid: Sid,
|
||||
crypto_policy: &'a CryptoPolicy,
|
||||
webauthn: &'a Webauthn,
|
||||
pw_badlist_cache: CowCellWriteTxn<'a, HashSet<String>>,
|
||||
account_policy: CowCellWriteTxn<'a, AccountPolicy>,
|
||||
pub(crate) domain_keys: CowCellWriteTxn<'a, DomainKeys>,
|
||||
pub(crate) oauth2rs: Oauth2ResourceServersWriteTransaction<'a>,
|
||||
}
|
||||
|
@ -168,6 +206,8 @@ impl IdmServer {
|
|||
cookie_key,
|
||||
pw_badlist_set,
|
||||
oauth2rs_set,
|
||||
privilege_expiry,
|
||||
authsession_expiry,
|
||||
) = {
|
||||
let mut qs_read = qs.read().await;
|
||||
(
|
||||
|
@ -179,6 +219,8 @@ impl IdmServer {
|
|||
qs_read.get_password_badlist()?,
|
||||
// Add a read/reload of all oauth2 configurations.
|
||||
qs_read.get_oauth2rs_set()?,
|
||||
qs_read.get_privilege_expiry()?,
|
||||
qs_read.get_authsession_expiry()?,
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -253,7 +295,11 @@ impl IdmServer {
|
|||
async_tx,
|
||||
audit_tx,
|
||||
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,
|
||||
oauth2rs: Arc::new(oauth2rs),
|
||||
},
|
||||
|
@ -283,7 +329,7 @@ impl IdmServer {
|
|||
async_tx: self.async_tx.clone(),
|
||||
audit_tx: self.audit_tx.clone(),
|
||||
webauthn: &self.webauthn,
|
||||
pw_badlist_cache: self.pw_badlist_cache.read(),
|
||||
account_policy: self.account_policy.read(),
|
||||
domain_keys: self.domain_keys.read(),
|
||||
}
|
||||
}
|
||||
|
@ -313,7 +359,7 @@ impl IdmServer {
|
|||
sid,
|
||||
crypto_policy: &self.crypto_policy,
|
||||
webauthn: &self.webauthn,
|
||||
pw_badlist_cache: self.pw_badlist_cache.write(),
|
||||
account_policy: self.account_policy.write(),
|
||||
domain_keys: self.domain_keys.write(),
|
||||
oauth2rs: self.oauth2rs.write(),
|
||||
}
|
||||
|
@ -324,7 +370,7 @@ impl IdmServer {
|
|||
_qs_read: self.qs.read().await,
|
||||
// sid: Sid,
|
||||
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(),
|
||||
domain_keys: self.domain_keys.read(),
|
||||
crypto_policy: &self.crypto_policy,
|
||||
|
@ -1120,7 +1166,6 @@ impl<'a> IdmServerAuthTransaction<'a> {
|
|||
// Process the credentials here as required.
|
||||
// Basically throw them at the auth_session and see what
|
||||
// falls out.
|
||||
let pw_badlist_cache = Some(&(*self.pw_badlist_cache));
|
||||
auth_session
|
||||
.validate_creds(
|
||||
&creds.cred,
|
||||
|
@ -1128,8 +1173,8 @@ impl<'a> IdmServerAuthTransaction<'a> {
|
|||
&self.async_tx,
|
||||
&self.audit_tx,
|
||||
self.webauthn,
|
||||
pw_badlist_cache,
|
||||
&self.domain_keys.uat_jwt_signer,
|
||||
&self.account_policy,
|
||||
)
|
||||
.map(|aus| {
|
||||
// Inspect the result:
|
||||
|
@ -1563,7 +1608,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
// check a password badlist to eliminate more content
|
||||
// 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)
|
||||
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");
|
||||
Err(OperationError::PasswordQuality(vec![
|
||||
PasswordFeedback::BadListed,
|
||||
|
@ -1979,7 +2024,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
.get_changed_uuids()
|
||||
.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() {
|
||||
self.qs_write
|
||||
|
@ -2029,21 +2076,18 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
// Commit everything.
|
||||
self.oauth2rs.commit();
|
||||
self.domain_keys.commit();
|
||||
self.pw_badlist_cache.commit();
|
||||
self.account_policy.commit();
|
||||
self.cred_update_sessions.commit();
|
||||
trace!("cred_update_session.commit");
|
||||
self.qs_write.commit()
|
||||
}
|
||||
|
||||
fn reload_password_badlist(&mut self) -> Result<(), OperationError> {
|
||||
match self.qs_write.get_password_badlist() {
|
||||
Ok(badlist_entry) => {
|
||||
*self.pw_badlist_cache = badlist_entry;
|
||||
fn reload_system_account_policy(&mut self) -> Result<(), OperationError> {
|
||||
self.account_policy.pw_badlist_cache = self.qs_write.get_password_badlist()?;
|
||||
self.account_policy.authsession_expiry = self.qs_write.get_authsession_expiry()?;
|
||||
self.account_policy.privilege_expiry = self.qs_write.get_privilege_expiry()?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need tests of the sessions and the auth ...
|
||||
|
@ -2067,7 +2111,7 @@ mod tests {
|
|||
PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
|
||||
UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent,
|
||||
};
|
||||
use crate::idm::server::{IdmServer, IdmServerTransaction};
|
||||
use crate::idm::server::{IdmServer, IdmServerTransaction, Token};
|
||||
use crate::idm::AuthState;
|
||||
use crate::modify::{Modify, ModifyList};
|
||||
use crate::prelude::*;
|
||||
|
@ -3396,7 +3440,7 @@ mod tests {
|
|||
#[idm_test]
|
||||
async fn test_idm_jwt_uat_expiry(idms: &IdmServer, idms_delayed: &mut IdmServerDelayed) {
|
||||
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
|
||||
init_admin_w_password(idms, TEST_PASSWORD)
|
||||
.await
|
||||
|
@ -3431,8 +3475,8 @@ mod tests {
|
|||
_idms_delayed: &mut IdmServerDelayed,
|
||||
) {
|
||||
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
||||
let expiry_a = ct + Duration::from_secs(AUTH_SESSION_EXPIRY + 1);
|
||||
let expiry_b = ct + Duration::from_secs((AUTH_SESSION_EXPIRY + 1) * 2);
|
||||
let expiry_a = ct + Duration::from_secs((DEFAULT_AUTH_SESSION_EXPIRY + 1).into());
|
||||
let expiry_b = ct + Duration::from_secs(((DEFAULT_AUTH_SESSION_EXPIRY + 1) * 2).into());
|
||||
|
||||
let session_a = Uuid::new_v4();
|
||||
let session_b = Uuid::new_v4();
|
||||
|
@ -3533,7 +3577,7 @@ mod tests {
|
|||
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
||||
|
||||
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
|
||||
// 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]
|
||||
async fn test_idm_uat_claim_insertion(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) {
|
||||
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
||||
|
@ -3609,7 +3784,12 @@ mod tests {
|
|||
|
||||
// == anonymous
|
||||
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");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(&uat, ct)
|
||||
|
@ -3623,7 +3803,12 @@ mod tests {
|
|||
|
||||
// == unixpassword
|
||||
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");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(&uat, ct)
|
||||
|
@ -3637,7 +3822,12 @@ mod tests {
|
|||
|
||||
// == password
|
||||
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");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(&uat, ct)
|
||||
|
@ -3651,7 +3841,12 @@ mod tests {
|
|||
|
||||
// == generatedpassword
|
||||
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");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(&uat, ct)
|
||||
|
@ -3665,7 +3860,12 @@ mod tests {
|
|||
|
||||
// == webauthn
|
||||
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");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(&uat, ct)
|
||||
|
@ -3679,7 +3879,12 @@ mod tests {
|
|||
|
||||
// == passwordmfa
|
||||
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");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(&uat, ct)
|
||||
|
|
|
@ -32,6 +32,8 @@ lazy_static! {
|
|||
m.insert("id_verification_eckey");
|
||||
m.insert("badlist_password");
|
||||
m.insert("domain_display_name");
|
||||
m.insert("authsession_expiry");
|
||||
m.insert("privilege_expiry");
|
||||
m
|
||||
};
|
||||
}
|
||||
|
|
|
@ -490,6 +490,8 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
SCHEMA_ATTR_ACCOUNT_EXPIRE.clone().into(),
|
||||
SCHEMA_ATTR_ACCOUNT_VALID_FROM.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_CREDENTIAL_UPDATE_INTENT_TOKEN.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_DOMAIN_ADMIN_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_EXTEND_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> {
|
||||
self.internal_search(filter!(f_eq(
|
||||
Attribute::Class,
|
||||
|
|
|
@ -323,6 +323,8 @@ async fn test_default_entries_rbac_admins_schema_entries(rsclient: KanidmClient)
|
|||
"domain_ssid",
|
||||
"gidnumber",
|
||||
"badlist_password",
|
||||
"authsession_expiry",
|
||||
"privilege_expiry",
|
||||
"loginshell",
|
||||
"unix_password",
|
||||
"nsuniqueid",
|
||||
|
|
|
@ -1460,3 +1460,34 @@ async fn test_server_user_auth_reauthentication(rsclient: KanidmClient) {
|
|||
eprintln!("{:?} {:?}", now, uat.purpose);
|
||||
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 serviceaccount;
|
||||
pub mod session;
|
||||
pub mod session_expiry;
|
||||
pub mod synch;
|
||||
mod webauthn;
|
||||
|
||||
|
@ -72,6 +73,8 @@ impl SystemOpt {
|
|||
SystemOpt::Oauth2 { commands } => commands.debug(),
|
||||
SystemOpt::Domain { 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::Domain { 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)]
|
||||
pub enum SystemOpt {
|
||||
#[clap(name = "pw-badlist")]
|
||||
|
@ -912,6 +942,18 @@ pub enum SystemOpt {
|
|||
#[clap(subcommand)]
|
||||
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")]
|
||||
/// Configure and display oauth2/oidc resource server configuration
|
||||
Oauth2 {
|
||||
|
|
Loading…
Reference in a new issue