handle unique constraint on users.handle properly, misc

This commit is contained in:
2026-03-04 19:26:41 +01:00
parent f253ff1157
commit de6ea0d913

View File

@@ -2,7 +2,7 @@ use axum::{
http::StatusCode, http::StatusCode,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
}; };
use rusqlite::{ErrorCode, OptionalExtension}; use rusqlite::{OptionalExtension, ffi::SQLITE_CONSTRAINT_UNIQUE};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
@@ -35,8 +35,8 @@ pub enum UserError {
NoUserWithId(Uuid), NoUserWithId(Uuid),
#[error("No user found with handle {0}")] #[error("No user found with handle {0}")]
NoUserWithHandle(UserHandle), NoUserWithHandle(UserHandle),
#[error("A user with handle {0} already exists")] #[error("A user with this handle already exists")]
HandleAlreadyExists(UserHandle), HandleAlreadyExists,
#[error("{0}")] #[error("{0}")]
DatabaseError(#[from] DatabaseError), DatabaseError(#[from] DatabaseError),
#[error("Argon2 passhash error: {0}")] #[error("Argon2 passhash error: {0}")]
@@ -77,15 +77,7 @@ impl User {
pub fn set_handle(&mut self, new_handle: UserHandle) -> Result<(), UserError> { pub fn set_handle(&mut self, new_handle: UserHandle) -> Result<(), UserError> {
let conn = database::conn()?; let conn = database::conn()?;
conn.prepare("UPDATE users SET handle = ?1 WHERE id = ?2")? conn.prepare("UPDATE users SET handle = ?1 WHERE id = ?2")?
.execute((&new_handle, self.id)) .execute((&new_handle, self.id))?;
.map_err(|e| {
if let Some(e) = e.sqlite_error()
&& e.code == ErrorCode::ConstraintViolation
{
return UserError::HandleAlreadyExists(new_handle.clone());
}
UserError::from(e)
})?;
self.handle = new_handle; self.handle = new_handle;
Ok(()) Ok(())
} }
@@ -157,7 +149,7 @@ impl User {
log::info!("[USERS] The infradmin account password has been (re)generated."); log::info!("[USERS] The infradmin account password has been (re)generated.");
log::info!("[USERS] Handle: {}", self.handle.as_str()); log::info!("[USERS] Handle: {}", self.handle.as_str());
log::info!("[USERS] Password: {}", passw); log::info!("[USERS] Password: {}", passw);
log::info!("[USERS] The infradmin is urged to change this password to a secure one.\n"); log::info!("[USERS] The infradmin is urged to change this password to a secure one.");
Ok(()) Ok(())
} }
@@ -192,6 +184,12 @@ impl User {
impl From<rusqlite::Error> for UserError { impl From<rusqlite::Error> for UserError {
fn from(error: rusqlite::Error) -> Self { fn from(error: rusqlite::Error) -> Self {
if let rusqlite::Error::SqliteFailure(err, Some(msg)) = &error
&& err.extended_code == SQLITE_CONSTRAINT_UNIQUE
&& msg.contains("handle")
{
return UserError::HandleAlreadyExists;
}
UserError::DatabaseError(DatabaseError::from(error)) UserError::DatabaseError(DatabaseError::from(error))
} }
} }
@@ -213,9 +211,7 @@ impl IntoResponse for UserError {
Self::NoUserWithHandle(_) => { Self::NoUserWithHandle(_) => {
(StatusCode::BAD_REQUEST, self.to_string()).into_response() (StatusCode::BAD_REQUEST, self.to_string()).into_response()
} }
Self::HandleAlreadyExists(_) => { Self::HandleAlreadyExists => (StatusCode::CONFLICT, self.to_string()).into_response(),
(StatusCode::CONFLICT, self.to_string()).into_response()
}
} }
} }
} }