use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::sync::Arc;
use parking_lot::RwLock;
use uuid::Uuid;
use chrono::{DateTime, Duration, Utc};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Role {
Admin,
Developer,
Viewer,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: String,
pub username: String,
#[serde(skip_serializing)]
pub password_hash: String,
pub email: Option<String>,
pub role: Role,
pub created_at: DateTime<Utc>,
pub last_login: Option<DateTime<Utc>>,
}
impl User {
pub fn new(username: String, password: &str, role: Role) -> Self {
Self {
id: Uuid::new_v4().to_string(),
username,
password_hash: hash_password(password),
email: None,
role,
created_at: Utc::now(),
last_login: None,
}
}
pub fn verify_password(&self, password: &str) -> bool {
self.password_hash == hash_password(password)
}
pub fn has_permission(&self, required_role: &Role) -> bool {
match (&self.role, required_role) {
(Role::Admin, _) => true,
(Role::Developer, Role::Developer | Role::Viewer) => true,
(Role::Viewer, Role::Viewer) => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
pub token: String,
pub user_id: String,
pub username: String,
pub role: Role,
pub created_at: DateTime<Utc>,
pub expires_at: DateTime<Utc>,
}
impl Session {
pub fn new(user: &User, duration_hours: i64) -> Self {
let now = Utc::now();
Self {
token: Uuid::new_v4().to_string(),
user_id: user.id.clone(),
username: user.username.clone(),
role: user.role.clone(),
created_at: now,
expires_at: now + Duration::hours(duration_hours),
}
}
pub fn is_expired(&self) -> bool {
Utc::now() > self.expires_at
}
}
pub struct AuthManager {
users: Arc<RwLock<HashMap<String, User>>>,
sessions: Arc<RwLock<HashMap<String, Session>>>,
}
impl AuthManager {
pub fn new() -> Self {
let mut users = HashMap::new();
let admin = User::new("admin".to_string(), "admin123", Role::Admin);
users.insert(admin.username.clone(), admin);
Self {
users: Arc::new(RwLock::new(users)),
sessions: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn register(&self, username: String, password: &str, role: Role) -> Result<User> {
let mut users = self.users.write();
if users.contains_key(&username) {
return Err(anyhow!("Username already exists"));
}
let user = User::new(username.clone(), password, role);
users.insert(username, user.clone());
Ok(user)
}
pub fn login(&self, username: &str, password: &str) -> Result<Session> {
let mut users = self.users.write();
let user = users
.get_mut(username)
.ok_or_else(|| anyhow!("Invalid username or password"))?;
if !user.verify_password(password) {
return Err(anyhow!("Invalid username or password"));
}
user.last_login = Some(Utc::now());
let session = Session::new(user, 24);
let mut sessions = self.sessions.write();
sessions.insert(session.token.clone(), session.clone());
Ok(session)
}
pub fn validate_token(&self, token: &str) -> Result<Session> {
let mut sessions = self.sessions.write();
let session = sessions
.get(token)
.ok_or_else(|| anyhow!("Invalid or expired session"))?;
if session.is_expired() {
sessions.remove(token);
return Err(anyhow!("Session expired"));
}
Ok(session.clone())
}
pub fn logout(&self, token: &str) -> Result<()> {
let mut sessions = self.sessions.write();
sessions.remove(token);
Ok(())
}
pub fn get_user(&self, username: &str) -> Option<User> {
let users = self.users.read();
users.get(username).cloned()
}
pub fn list_users(&self) -> Vec<User> {
let users = self.users.read();
users.values().cloned().collect()
}
pub fn update_password(&self, username: &str, old_password: &str, new_password: &str) -> Result<()> {
let mut users = self.users.write();
let user = users
.get_mut(username)
.ok_or_else(|| anyhow!("User not found"))?;
if !user.verify_password(old_password) {
return Err(anyhow!("Invalid current password"));
}
user.password_hash = hash_password(new_password);
Ok(())
}
pub fn delete_user(&self, username: &str) -> Result<()> {
let mut users = self.users.write();
users.remove(username).ok_or_else(|| anyhow!("User not found"))?;
Ok(())
}
pub fn clean_expired_sessions(&self) {
let mut sessions = self.sessions.write();
sessions.retain(|_, session| !session.is_expired());
}
}
impl Default for AuthManager {
fn default() -> Self {
Self::new()
}
}
fn hash_password(password: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(password.as_bytes());
format!("{:x}", hasher.finalize())
}
#[derive(Debug, Deserialize)]
pub struct LoginRequest {
pub username: String,
pub password: String,
}
#[derive(Debug, Serialize)]
pub struct LoginResponse {
pub token: String,
pub username: String,
pub role: Role,
pub expires_at: DateTime<Utc>,
}
#[derive(Debug, Deserialize)]
pub struct CreateUserRequest {
pub username: String,
pub password: String,
pub role: Role,
pub email: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct ChangePasswordRequest {
pub old_password: String,
pub new_password: String,
}