pinakes-server: fix session token distribution bias; propagate auth audit

errors

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I96027de89511f13e9db6d277de2bcf436a6a6964
This commit is contained in:
raf 2026-03-07 16:55:43 +03:00
commit b12ad5d272
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF

View file

@ -64,30 +64,33 @@ pub async fn login(
} }
// Record failed login attempt in audit log // Record failed login attempt in audit log
let _ = pinakes_core::audit::record_action( if let Err(e) = pinakes_core::audit::record_action(
&state.storage, &state.storage,
None, None,
pinakes_core::model::AuditAction::LoginFailed, pinakes_core::model::AuditAction::LoginFailed,
Some(format!("username: {}", req.username)), Some(format!("username: {}", req.username)),
) )
.await; .await
{
tracing::warn!(error = %e, "failed to record failed login audit");
}
return Err(StatusCode::UNAUTHORIZED); return Err(StatusCode::UNAUTHORIZED);
} }
// At this point we know the user exists and password is valid // At this point we know the user exists and password is valid
let user = user.expect("user should exist at this point"); let user = user.ok_or(StatusCode::INTERNAL_SERVER_ERROR)?;
// Generate session token // Generate session token using unbiased uniform distribution
let token: String = (0..48) let token: String = {
.map(|_| { use rand::seq::IndexedRandom;
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ const CHARSET: &[u8] =
abcdefghijklmnopqrstuvwxyz\ b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
0123456789"; let mut rng = rand::rng();
let idx = (rand::random::<u32>() as usize) % CHARSET.len(); (0..48)
CHARSET[idx] as char .map(|_| *CHARSET.choose(&mut rng).expect("non-empty charset") as char)
}) .collect()
.collect(); };
let role = user.role; let role = user.role;
let username = user.username.clone(); let username = user.username.clone();
@ -112,13 +115,16 @@ pub async fn login(
tracing::info!(username = %username, role = %role, "login successful"); tracing::info!(username = %username, role = %role, "login successful");
// Record successful login in audit log // Record successful login in audit log
let _ = pinakes_core::audit::record_action( if let Err(e) = pinakes_core::audit::record_action(
&state.storage, &state.storage,
None, None,
pinakes_core::model::AuditAction::LoginSuccess, pinakes_core::model::AuditAction::LoginSuccess,
Some(format!("username: {}, role: {}", username, role)), Some(format!("username: {}, role: {}", username, role)),
) )
.await; .await
{
tracing::warn!(error = %e, "failed to record login audit");
}
Ok(Json(LoginResponse { Ok(Json(LoginResponse {
token, token,
@ -146,13 +152,16 @@ pub async fn logout(
// Record logout in audit log // Record logout in audit log
if let Some(user) = username { if let Some(user) = username {
let _ = pinakes_core::audit::record_action( if let Err(e) = pinakes_core::audit::record_action(
&state.storage, &state.storage,
None, None,
pinakes_core::model::AuditAction::Logout, pinakes_core::model::AuditAction::Logout,
Some(format!("username: {}", user)), Some(format!("username: {}", user)),
) )
.await; .await
{
tracing::warn!(error = %e, "failed to record logout audit");
}
} }
} }
StatusCode::OK StatusCode::OK
@ -221,13 +230,16 @@ pub async fn revoke_all_sessions(
tracing::info!(username = %username, count = count, "revoked all user sessions"); tracing::info!(username = %username, count = count, "revoked all user sessions");
// Record in audit log // Record in audit log
let _ = pinakes_core::audit::record_action( if let Err(e) = pinakes_core::audit::record_action(
&state.storage, &state.storage,
None, None,
pinakes_core::model::AuditAction::Logout, pinakes_core::model::AuditAction::Logout,
Some(format!("revoked all sessions for username: {}", username)), Some(format!("revoked all sessions for username: {}", username)),
) )
.await; .await
{
tracing::warn!(error = %e, "failed to record session revocation audit");
}
StatusCode::OK StatusCode::OK
}, },