commit 5e13b1a7cbd9e2bffc7c69aa00ca74fe027957bc
parent 60b339f450db10c84c2f92f4c42bac57929be478
Author: Jeremy Lin <jjlin@users.noreply.github.com>
Date: Thu, 30 Jun 2022 20:46:17 -0700
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.
Diffstat:
3 files changed, 32 insertions(+), 9 deletions(-)
diff --git 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
@@ -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<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>")]
async fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
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 {
Some(user) => {
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.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<ProfileData>, 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
@@ -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.