fixes and new options
This commit is contained in:
+15
-3
@@ -1,5 +1,5 @@
|
||||
use chrono::Utc;
|
||||
use tracing::{debug, info};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
use crate::{queries, state::SharedState};
|
||||
|
||||
@@ -172,12 +172,20 @@ pub async fn authenticate(
|
||||
}
|
||||
|
||||
fn directory_nickname(display_name: &str, email: &str, username: &str) -> String {
|
||||
let words: Vec<&str> = display_name.split_whitespace().filter(|v| !v.is_empty()).collect();
|
||||
let words: Vec<&str> = display_name
|
||||
.split_whitespace()
|
||||
.filter(|v| !v.is_empty())
|
||||
.collect();
|
||||
let candidate = if words.len() >= 2 {
|
||||
let first = words[0].chars().next().unwrap_or('u');
|
||||
format!("{}.{}", first, words[words.len() - 1])
|
||||
} else {
|
||||
email.split('@').next().filter(|v| !v.is_empty()).unwrap_or(username).to_owned()
|
||||
email
|
||||
.split('@')
|
||||
.next()
|
||||
.filter(|v| !v.is_empty())
|
||||
.unwrap_or(username)
|
||||
.to_owned()
|
||||
};
|
||||
candidate.to_lowercase()
|
||||
}
|
||||
@@ -241,6 +249,10 @@ pub(super) async fn login(
|
||||
})?
|
||||
.ok_or_else(|| AuthError::unauthorized("Invalid organization login or password."))?;
|
||||
let user = provision_ldap_user(state, identity).await?;
|
||||
if user.is_active == 0 {
|
||||
warn!(user_id = user.id, "LDAP login rejected: inactive account");
|
||||
return Err(AuthError::forbidden("This account is inactive."));
|
||||
}
|
||||
let session = create_session(state, &user).await?;
|
||||
info!(user_id = user.id, nickname = %user.nickname, "LDAP login successful");
|
||||
Ok(session)
|
||||
|
||||
@@ -17,6 +17,10 @@ pub(super) async fn login(
|
||||
let user = find_user_by_email(state, &email)
|
||||
.await?
|
||||
.ok_or_else(|| AuthError::unauthorized("Invalid e-mail address or password."))?;
|
||||
if user.is_active == 0 {
|
||||
warn!(user_id = user.id, "login rejected: inactive account");
|
||||
return Err(AuthError::forbidden("This account is inactive."));
|
||||
}
|
||||
if !verify_password(&user.password_hash, password) {
|
||||
warn!(user_id = user.id, "login rejected: invalid password");
|
||||
return Err(AuthError::unauthorized(
|
||||
|
||||
+44
-3
@@ -38,6 +38,7 @@ pub struct User {
|
||||
pub email: String,
|
||||
pub password_hash: String,
|
||||
pub confirmed_at: Option<String>,
|
||||
pub is_active: i64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -123,6 +124,7 @@ impl<'r> sqlx::FromRow<'r, AnyRow> for User {
|
||||
email: crate::row_decode::text(row, "email")?,
|
||||
password_hash: crate::row_decode::text(row, "password_hash")?,
|
||||
confirmed_at: crate::row_decode::optional_text(row, "confirmed_at")?,
|
||||
is_active: row.try_get("is_active")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -827,12 +829,51 @@ pub async fn confirm_account_action(
|
||||
.map_err(AuthError::database)?;
|
||||
"E-mail address changed."
|
||||
} else if action == "delete" {
|
||||
sqlx::query(queries::get(state.db.kind(), queries::AUTH_DELETE_USER))
|
||||
.bind(user_id)
|
||||
let original_nickname: Option<String> = sqlx::query_scalar(queries::get(
|
||||
state.db.kind(),
|
||||
queries::AUTH_NICKNAME_BY_ID,
|
||||
))
|
||||
.bind(user_id)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await
|
||||
.map_err(AuthError::database)?;
|
||||
let deleted_nickname = format!("Deleted user #{user_id}");
|
||||
if let Some(original_nickname) = original_nickname {
|
||||
sqlx::query(queries::get(
|
||||
state.db.kind(),
|
||||
queries::AUTH_ANONYMIZE_NOTE_CREATORS,
|
||||
))
|
||||
.bind(&deleted_nickname)
|
||||
.bind(original_nickname)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(AuthError::database)?;
|
||||
"Account deleted."
|
||||
}
|
||||
let deleted_email = format!("deleted-user-{user_id}@invalid.local");
|
||||
let deleted_password = hash_password(&random_token())?;
|
||||
sqlx::query(queries::get(
|
||||
state.db.kind(),
|
||||
queries::AUTH_DELETE_SESSIONS_BY_USER,
|
||||
))
|
||||
.bind(user_id)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(AuthError::database)?;
|
||||
sqlx::query(queries::get(
|
||||
state.db.kind(),
|
||||
queries::AUTH_ANONYMIZE_USER,
|
||||
))
|
||||
.bind(&deleted_nickname)
|
||||
.bind(normalize(&deleted_nickname))
|
||||
.bind(&deleted_email)
|
||||
.bind(normalize(&deleted_email))
|
||||
.bind(deleted_password)
|
||||
.bind(now.to_rfc3339())
|
||||
.bind(user_id)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(AuthError::database)?;
|
||||
"Account deleted. Content has been preserved under an anonymized owner."
|
||||
} else {
|
||||
return Err(AuthError::bad_request("Unknown account action."));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user