From 5e13b1a7cbd9e2bffc7c69aa00ca74fe027957bc Mon Sep 17 00:00:00 2001 From: Jeremy Lin Date: Thu, 30 Jun 2022 20:46:17 -0700 Subject: [PATCH] Add `password_hints_allowed` config option Disabling password hints is mainly useful for admins who are concerned that their users might provide password hints that are too revealing. --- .env.template | 3 +++ src/api/core/accounts.rs | 36 +++++++++++++++++++++++++++--------- src/config.rs | 2 ++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/.env.template b/.env.template index 015046f7..66a04343 100644 --- a/.env.template +++ b/.env.template @@ -270,6 +270,9 @@ ## The change only applies when the password is changed # PASSWORD_ITERATIONS=100000 +## Controls whether users can set password hints. This setting applies globally to all users. +# PASSWORD_HINTS_ALLOWED=true + ## Controls whether a password hint should be shown directly in the web page if ## SMTP service is not configured. Not recommended for publicly-accessible instances ## as this provides unauthenticated access to potentially sensitive data. diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 61299de6..5054427c 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -62,6 +62,24 @@ struct KeysData { PublicKey: String, } +/// Trims whitespace from password hints, and converts blank password hints to `None`. +fn clean_password_hint(password_hint: &Option) -> Option { + match password_hint { + None => None, + Some(h) => match h.trim() { + "" => None, + ht => Some(ht.to_string()), + }, + } +} + +fn enforce_password_hint_setting(password_hint: &Option) -> EmptyResult { + if password_hint.is_some() && !CONFIG.password_hints_allowed() { + err!("Password hints have been disabled by the administrator. Remove the hint and try again."); + } + Ok(()) +} + #[post("/accounts/register", data = "")] async fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { let data: RegisterData = data.into_inner().data; @@ -75,6 +93,11 @@ async fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { } } + // Check against the password hint setting here so if it fails, the user + // can retry without losing their invitation below. + let password_hint = clean_password_hint(&data.MasterPasswordHint); + enforce_password_hint_setting(&password_hint)?; + let mut user = match User::find_by_mail(&email, &conn).await { Some(user) => { if !user.password_hash.is_empty() { @@ -131,16 +154,13 @@ async fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { user.set_password(&data.MasterPasswordHash, None); user.akey = data.Key; + user.password_hint = password_hint; // Add extra fields if present if let Some(name) = data.Name { user.name = name; } - if let Some(hint) = data.MasterPasswordHint { - user.password_hint = Some(hint); - } - if let Some(keys) = data.Keys { user.private_key = Some(keys.EncryptedPrivateKey); user.public_key = Some(keys.PublicKey); @@ -191,12 +211,10 @@ async fn post_profile(data: JsonUpcase, headers: Headers, conn: DbC } let mut user = headers.user; - user.name = data.Name; - user.password_hint = match data.MasterPasswordHint { - Some(ref h) if h.is_empty() => None, - _ => data.MasterPasswordHint, - }; + user.password_hint = clean_password_hint(&data.MasterPasswordHint); + enforce_password_hint_setting(&user.password_hint)?; + user.save(&conn).await?; Ok(Json(user.to_json(&conn).await)) } diff --git a/src/config.rs b/src/config.rs index a0fad9d9..09240c36 100644 --- a/src/config.rs +++ b/src/config.rs @@ -436,6 +436,8 @@ make_config! { /// Password iterations |> Number of server-side passwords hashing iterations. /// The changes only apply when a user changes their password. Not recommended to lower the value password_iterations: i32, true, def, 100_000; + /// Allow password hints |> Controls whether users can set password hints. This setting applies globally to all users. + password_hints_allowed: bool, true, def, true; /// Show password hint |> Controls whether a password hint should be shown directly in the web page /// if SMTP service is not configured. Not recommended for publicly-accessible instances as this /// provides unauthenticated access to potentially sensitive data.