aboutsummaryrefslogtreecommitdiff
path: root/src/auth/token.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/auth/token.rs')
-rw-r--r--src/auth/token.rs99
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)
+ }
+}