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.
This commit is contained in:
Jeremy Lin 2022-06-30 20:46:17 -07:00
parent 60b339f450
commit 5e13b1a7cb
3 changed files with 32 additions and 9 deletions

View file

@ -270,6 +270,9 @@
## The change only applies when the password is changed ## The change only applies when the password is changed
# PASSWORD_ITERATIONS=100000 # 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 ## 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 ## SMTP service is not configured. Not recommended for publicly-accessible instances
## as this provides unauthenticated access to potentially sensitive data. ## as this provides unauthenticated access to potentially sensitive data.

View file

@ -62,6 +62,24 @@ struct KeysData {
PublicKey: String, PublicKey: String,
} }
/// Trims whitespace from password hints, and converts blank password hints to `None`.
fn clean_password_hint(password_hint: &Option<String>) -> Option<String> {
match password_hint {
None => None,
Some(h) => match h.trim() {
"" => None,
ht => Some(ht.to_string()),
},
}
}
fn enforce_password_hint_setting(password_hint: &Option<String>) -> 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 = "<data>")] #[post("/accounts/register", data = "<data>")]
async fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult { async fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
let data: RegisterData = data.into_inner().data; let data: RegisterData = data.into_inner().data;
@ -75,6 +93,11 @@ async fn register(data: JsonUpcase<RegisterData>, 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 { let mut user = match User::find_by_mail(&email, &conn).await {
Some(user) => { Some(user) => {
if !user.password_hash.is_empty() { if !user.password_hash.is_empty() {
@ -131,16 +154,13 @@ async fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
user.set_password(&data.MasterPasswordHash, None); user.set_password(&data.MasterPasswordHash, None);
user.akey = data.Key; user.akey = data.Key;
user.password_hint = password_hint;
// Add extra fields if present // Add extra fields if present
if let Some(name) = data.Name { if let Some(name) = data.Name {
user.name = name; user.name = name;
} }
if let Some(hint) = data.MasterPasswordHint {
user.password_hint = Some(hint);
}
if let Some(keys) = data.Keys { if let Some(keys) = data.Keys {
user.private_key = Some(keys.EncryptedPrivateKey); user.private_key = Some(keys.EncryptedPrivateKey);
user.public_key = Some(keys.PublicKey); user.public_key = Some(keys.PublicKey);
@ -191,12 +211,10 @@ async fn post_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbC
} }
let mut user = headers.user; let mut user = headers.user;
user.name = data.Name; user.name = data.Name;
user.password_hint = match data.MasterPasswordHint { user.password_hint = clean_password_hint(&data.MasterPasswordHint);
Some(ref h) if h.is_empty() => None, enforce_password_hint_setting(&user.password_hint)?;
_ => data.MasterPasswordHint,
};
user.save(&conn).await?; user.save(&conn).await?;
Ok(Json(user.to_json(&conn).await)) Ok(Json(user.to_json(&conn).await))
} }

View file

@ -436,6 +436,8 @@ make_config! {
/// Password iterations |> Number of server-side passwords hashing iterations. /// 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 /// The changes only apply when a user changes their password. Not recommended to lower the value
password_iterations: i32, true, def, 100_000; 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 /// 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 /// if SMTP service is not configured. Not recommended for publicly-accessible instances as this
/// provides unauthenticated access to potentially sensitive data. /// provides unauthenticated access to potentially sensitive data.