diff options
Diffstat (limited to 'src/auth/token.rs')
| -rw-r--r-- | src/auth/token.rs | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/src/auth/token.rs b/src/auth/token.rs new file mode 100644 index 0000000..17f75e4 --- /dev/null +++ b/src/auth/token.rs @@ -0,0 +1,99 @@ +// Token/RFID authentication module +use crate::config::SecurityConfig; +use crate::models::{Role, User}; +use anyhow::{Context, Result}; +use sqlx::MySqlPool; + +pub async fn authenticate_token( + pool: &MySqlPool, + login_string: &str, + security_config: &SecurityConfig, +) -> Result<Option<(User, Role)>> { + // If hashing is enabled, we can't use WHERE login_string = ? directly + // Need to fetch all users and verify hashes + if security_config.hash_tokens { + // Fetch all active users with login_string set + let users: Vec<User> = sqlx::query_as::<_, User>( + r#" + SELECT id, name, username, password, pin_code, login_string, role_id, + email, phone, notes, active, last_login_date, created_date, + password_reset_token, password_reset_expiry + FROM users + WHERE login_string IS NOT NULL AND active = TRUE + "#, + ) + .fetch_all(pool) + .await + .context("Failed to fetch users from database")?; + + // Find matching user by verifying bcrypt hash + for user in users { + if let Some(ref stored_hash) = user.login_string { + if bcrypt::verify(login_string, stored_hash).unwrap_or(false) { + // Found matching user + return authenticate_user_by_id(pool, user.id).await; + } + } + } + Ok(None) + } else { + // Plaintext comparison - direct database query + let user: Option<User> = sqlx::query_as::<_, User>( + r#" + SELECT id, name, username, password, pin_code, login_string, role_id, + email, phone, notes, active, last_login_date, created_date, + password_reset_token, password_reset_expiry + FROM users + WHERE login_string = ? AND active = TRUE + "#, + ) + .bind(login_string) + .fetch_optional(pool) + .await + .context("Failed to fetch user from database")?; + + if let Some(user) = user { + authenticate_user_by_id(pool, user.id).await + } else { + Ok(None) + } + } +} + +async fn authenticate_user_by_id(pool: &MySqlPool, user_id: i32) -> Result<Option<(User, Role)>> { + // Fetch user + let user: User = sqlx::query_as::<_, User>( + r#" + SELECT id, name, username, password, pin_code, login_string, role_id, + email, phone, notes, active, last_login_date, created_date, + password_reset_token, password_reset_expiry + FROM users + WHERE id = ? + "#, + ) + .bind(user_id) + .fetch_one(pool) + .await + .context("Failed to fetch user")?; + + if user.active { + // Fetch user's role + let role: Role = + sqlx::query_as::<_, Role>("SELECT id, name, power, created_at FROM roles WHERE id = ?") + .bind(user.role_id) + .fetch_one(pool) + .await + .context("Failed to fetch user role")?; + + // Update last login date + sqlx::query("UPDATE users SET last_login_date = NOW() WHERE id = ?") + .bind(user.id) + .execute(pool) + .await + .context("Failed to update last login date")?; + + Ok(Some((user, role))) + } else { + Ok(None) + } +} |
