Private
Public Access
1
0

style: Apply rustfmt with stable-only config
Some checks failed
CI Pipeline / Clippy Lints (push) Failing after 0s
CI Pipeline / Rust Unit Tests (push) Failing after 0s
CI Pipeline / Rust Format Check (push) Successful in 4s
CI Pipeline / Frontend Lint & Type Check (push) Failing after 0s
CI Pipeline / Security Audit (push) Failing after 3s
CI Pipeline / Build .deb & Release (push) Has been skipped

- Fixed rustfmt.toml to only use stable options (removed nightly-only)
- Applied cargo fmt --all to fix formatting violations
- Stable options: edition=2021, max_width=100, reorder_imports/modules, match_block_trailing_comma
This commit is contained in:
2026-04-24 15:32:50 +00:00
parent f0fe5f5fd1
commit 5a4d4d583e
44 changed files with 1498 additions and 1040 deletions

View File

@ -91,10 +91,7 @@ pub fn issue_access_token(
}
/// Validate and decode an access token using the Ed25519 public key PEM.
pub fn validate_access_token(
token: &str,
verify_key_pem: &str,
) -> Result<AccessClaims, JwtError> {
pub fn validate_access_token(token: &str, verify_key_pem: &str) -> Result<AccessClaims, JwtError> {
let key = DecodingKey::from_ed_pem(verify_key_pem.as_bytes())
.map_err(|e| JwtError::KeyLoad(e.to_string()))?;
@ -115,14 +112,12 @@ pub fn validate_access_token(
/// Load the Ed25519 signing key from a PEM file path.
pub fn load_signing_key(path: &str) -> Result<String, JwtError> {
std::fs::read_to_string(path)
.map_err(|e| JwtError::KeyLoad(format!("Cannot read {path}: {e}")))
std::fs::read_to_string(path).map_err(|e| JwtError::KeyLoad(format!("Cannot read {path}: {e}")))
}
/// Load the Ed25519 verification (public) key from a PEM file path.
pub fn load_verify_key(path: &str) -> Result<String, JwtError> {
std::fs::read_to_string(path)
.map_err(|e| JwtError::KeyLoad(format!("Cannot read {path}: {e}")))
std::fs::read_to_string(path).map_err(|e| JwtError::KeyLoad(format!("Cannot read {path}: {e}")))
}
#[cfg(test)]

View File

@ -66,9 +66,9 @@ fn build_totp(username: &str, secret_base32: &str) -> Result<TOTP, TotpError> {
// new(issuer, account_name, algorithm, digits, skew, step, secret)
TOTP::new(
Algorithm::SHA1,
6, // digits
1, // skew
30, // step (seconds)
6, // digits
1, // skew
30, // step (seconds)
secret_bytes,
Some(ISSUER.to_string()),
username.to_string(),

View File

@ -7,17 +7,15 @@
//! - Parallelism: 1
use argon2::{
password_hash::{
rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString,
},
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2, Params, Version,
};
use thiserror::Error;
/// Argon2id parameters per spec.
const M_COST: u32 = 65536; // 64 MiB
const T_COST: u32 = 3; // 3 iterations
const P_COST: u32 = 1; // 1 thread
const T_COST: u32 = 3; // 3 iterations
const P_COST: u32 = 1; // 1 thread
#[derive(Debug, Error)]
pub enum PasswordError {
@ -33,7 +31,11 @@ pub enum PasswordError {
fn argon2() -> Result<Argon2<'static>, PasswordError> {
let params = Params::new(M_COST, T_COST, P_COST, None)
.map_err(|e| PasswordError::HashError(e.to_string()))?;
Ok(Argon2::new(argon2::Algorithm::Argon2id, Version::V0x13, params))
Ok(Argon2::new(
argon2::Algorithm::Argon2id,
Version::V0x13,
params,
))
}
/// Hash a plaintext password using Argon2id with a random salt.
@ -54,8 +56,7 @@ pub fn hash_password(password: &str) -> Result<String, PasswordError> {
///
/// Returns `Ok(true)` if the password matches, `Ok(false)` if not.
pub fn verify_password(password: &str, hash: &str) -> Result<bool, PasswordError> {
let parsed_hash =
PasswordHash::new(hash).map_err(|_| PasswordError::InvalidHash)?;
let parsed_hash = PasswordHash::new(hash).map_err(|_| PasswordError::InvalidHash)?;
let argon2 = argon2()?;

View File

@ -143,11 +143,7 @@ fn forbidden(message: &str) -> Response {
///
/// Inserts `AuthUser` into request extensions on success.
/// Rejects with 401 if token is missing/invalid, 403 if IP is blocked.
pub async fn require_auth(
auth_config: Arc<AuthConfig>,
mut req: Request,
next: Next,
) -> Response {
pub async fn require_auth(auth_config: Arc<AuthConfig>, mut req: Request, next: Next) -> Response {
// IP whitelist check
if let Some(ip) = extract_remote_ip(req.headers()) {
if !auth_config.is_ip_allowed(&ip) {
@ -168,7 +164,7 @@ pub async fn require_auth(
Err(e) => {
tracing::debug!(error = %e, "JWT validation failed");
return unauthorized("Invalid token");
}
},
};
let role = match UserRole::from_str(&claims.role) {

View File

@ -123,12 +123,10 @@ pub async fn rotate(
}
// Revoke old token
sqlx::query(
"UPDATE refresh_tokens SET revoked = TRUE, revoked_at = NOW() WHERE id = $1",
)
.bind(stored.id)
.execute(pool)
.await?;
sqlx::query("UPDATE refresh_tokens SET revoked = TRUE, revoked_at = NOW() WHERE id = $1")
.bind(stored.id)
.execute(pool)
.await?;
// Issue new token
let new_token = issue(pool, stored.user_id, user_agent, ip_address).await?;
@ -138,10 +136,7 @@ pub async fn rotate(
}
/// Revoke all refresh tokens for a user (force logout).
pub async fn revoke_all_for_user(
pool: &PgPool,
user_id: Uuid,
) -> Result<u64, RefreshError> {
pub async fn revoke_all_for_user(pool: &PgPool, user_id: Uuid) -> Result<u64, RefreshError> {
let result = sqlx::query(
"UPDATE refresh_tokens SET revoked = TRUE, revoked_at = NOW() WHERE user_id = $1 AND revoked = FALSE",
)
@ -154,10 +149,7 @@ pub async fn revoke_all_for_user(
}
/// Revoke a single refresh token by its raw value.
pub async fn revoke(
pool: &PgPool,
raw_token: &str,
) -> Result<(), RefreshError> {
pub async fn revoke(pool: &PgPool, raw_token: &str) -> Result<(), RefreshError> {
let hash = hex::encode(Sha256::digest(raw_token.as_bytes()));
sqlx::query(

View File

@ -122,13 +122,12 @@ pub async fn login(
// Prevent timing-based username enumeration
let _ = password::hash_password("dummy-timing-fill");
return Err(SessionError::InvalidCredentials);
}
},
};
// 2. Verify password
let hash = user.password_hash.as_deref().unwrap_or("");
let valid = password::verify_password(&req.password, hash)
.unwrap_or(false);
let valid = password::verify_password(&req.password, hash).unwrap_or(false);
if !valid {
tracing::warn!(username = %req.username, "Login failed: invalid password");
@ -146,8 +145,7 @@ pub async fn login(
let code = req.totp_code.as_deref().ok_or(SessionError::MfaRequired)?;
let secret = user.totp_secret.as_deref().unwrap_or("");
let mfa_ok = mfa_totp::verify_code(&user.username, secret, code)
.unwrap_or(false);
let mfa_ok = mfa_totp::verify_code(&user.username, secret, code).unwrap_or(false);
if !mfa_ok {
tracing::warn!(username = %req.username, "Login failed: invalid MFA code");
@ -246,19 +244,13 @@ pub async fn refresh_session(
}
/// Logout: revoke the current refresh token.
pub async fn logout(
pool: &PgPool,
raw_refresh_token: &str,
) -> Result<(), SessionError> {
pub async fn logout(pool: &PgPool, raw_refresh_token: &str) -> Result<(), SessionError> {
refresh::revoke(pool, raw_refresh_token).await?;
Ok(())
}
/// Force-logout: revoke all refresh tokens for a user.
pub async fn force_logout(
pool: &PgPool,
user_id: Uuid,
) -> Result<u64, SessionError> {
pub async fn force_logout(pool: &PgPool, user_id: Uuid) -> Result<u64, SessionError> {
let count = refresh::revoke_all_for_user(pool, user_id).await?;
Ok(count)
}