206 lines
6.9 KiB
Rust
206 lines
6.9 KiB
Rust
use actix_web::{web, HttpResponse, Responder, Result, http::header, cookie::Cookie};
|
|
use actix_session::Session;
|
|
use tera::Tera;
|
|
use crate::models::user::{User, LoginCredentials, RegistrationData, UserRole};
|
|
use crate::utils::render_template;
|
|
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey};
|
|
use serde::{Deserialize, Serialize};
|
|
use chrono::{Utc, Duration};
|
|
use lazy_static::lazy_static;
|
|
|
|
// JWT Claims structure
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct Claims {
|
|
pub sub: String, // Subject (email)
|
|
pub exp: usize, // Expiration time
|
|
pub iat: usize, // Issued at
|
|
pub role: String, // User role
|
|
}
|
|
|
|
// JWT Secret key
|
|
lazy_static! {
|
|
static ref JWT_SECRET: String = std::env::var("JWT_SECRET").unwrap_or_else(|_| "your_jwt_secret_key".to_string());
|
|
}
|
|
|
|
/// Controller for handling authentication-related routes
|
|
pub struct AuthController;
|
|
|
|
#[allow(dead_code)]
|
|
impl AuthController {
|
|
/// Generate a JWT token for a user
|
|
fn generate_token(email: &str, role: &UserRole) -> Result<String, jsonwebtoken::errors::Error> {
|
|
let role_str = match role {
|
|
UserRole::Admin => "admin",
|
|
UserRole::User => "user",
|
|
};
|
|
|
|
let expiration = Utc::now()
|
|
.checked_add_signed(Duration::hours(24))
|
|
.expect("valid timestamp")
|
|
.timestamp() as usize;
|
|
|
|
let claims = Claims {
|
|
sub: email.to_owned(),
|
|
exp: expiration,
|
|
iat: Utc::now().timestamp() as usize,
|
|
role: role_str.to_string(),
|
|
};
|
|
|
|
encode(
|
|
&Header::default(),
|
|
&claims,
|
|
&EncodingKey::from_secret(JWT_SECRET.as_bytes())
|
|
)
|
|
}
|
|
|
|
/// Validate a JWT token
|
|
pub fn validate_token(token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
|
|
let validation = Validation::new(Algorithm::HS256);
|
|
|
|
let token_data = decode::<Claims>(
|
|
token,
|
|
&DecodingKey::from_secret(JWT_SECRET.as_bytes()),
|
|
&validation
|
|
)?;
|
|
|
|
Ok(token_data.claims)
|
|
}
|
|
|
|
/// Extract token from session
|
|
pub fn extract_token_from_session(session: &Session) -> Option<String> {
|
|
session.get::<String>("auth_token").ok().flatten()
|
|
}
|
|
|
|
/// Extract token from cookie
|
|
pub fn extract_token_from_cookie(req: &actix_web::HttpRequest) -> Option<String> {
|
|
req.cookie("auth_token").map(|c| c.value().to_string())
|
|
}
|
|
|
|
/// Check if user is authenticated from session
|
|
pub async fn is_authenticated(session: &Session) -> Option<Claims> {
|
|
if let Some(token) = Self::extract_token_from_session(session) {
|
|
match Self::validate_token(&token) {
|
|
Ok(claims) => Some(claims),
|
|
Err(_) => None,
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Renders the login page
|
|
pub async fn login_page(tmpl: web::Data<Tera>) -> Result<impl Responder> {
|
|
let mut ctx = tera::Context::new();
|
|
ctx.insert("active_page", "login");
|
|
|
|
render_template(&tmpl, "auth/login.html", &ctx)
|
|
}
|
|
|
|
/// Handles user login
|
|
pub async fn login(
|
|
form: web::Form<LoginCredentials>,
|
|
session: Session,
|
|
_tmpl: web::Data<Tera>
|
|
) -> Result<impl Responder> {
|
|
// For simplicity, always log in the user without checking credentials
|
|
// Create a user object with admin role
|
|
let mut test_user = User::new(
|
|
"Admin User".to_string(),
|
|
form.email.clone()
|
|
);
|
|
|
|
// Set the ID and admin role
|
|
test_user.id = Some(1);
|
|
test_user.role = UserRole::Admin;
|
|
|
|
// Generate JWT token
|
|
let token = Self::generate_token(&test_user.email, &test_user.role)
|
|
.map_err(|_| actix_web::error::ErrorInternalServerError("Failed to generate token"))?;
|
|
|
|
// Store user data in session
|
|
let user_json = serde_json::to_string(&test_user).unwrap();
|
|
session.insert("user", &user_json)?;
|
|
session.insert("auth_token", &token)?;
|
|
|
|
// Create a cookie with the JWT token
|
|
let cookie = Cookie::build("auth_token", token)
|
|
.path("/")
|
|
.http_only(true)
|
|
.secure(false) // Set to true in production with HTTPS
|
|
.max_age(actix_web::cookie::time::Duration::hours(24))
|
|
.finish();
|
|
|
|
// Redirect to the home page with JWT token in cookie
|
|
Ok(HttpResponse::Found()
|
|
.cookie(cookie)
|
|
.append_header((header::LOCATION, "/"))
|
|
.finish())
|
|
}
|
|
|
|
/// Renders the registration page
|
|
pub async fn register_page(tmpl: web::Data<Tera>) -> Result<impl Responder> {
|
|
let mut ctx = tera::Context::new();
|
|
ctx.insert("active_page", "register");
|
|
|
|
render_template(&tmpl, "auth/register.html", &ctx)
|
|
}
|
|
|
|
/// Handles user registration
|
|
pub async fn register(
|
|
form: web::Form<RegistrationData>,
|
|
session: Session,
|
|
_tmpl: web::Data<Tera>
|
|
) -> Result<impl Responder> {
|
|
// Skip validation and always create an admin user
|
|
let mut user = User::new(
|
|
form.name.clone(),
|
|
form.email.clone()
|
|
);
|
|
|
|
// Set the ID and admin role
|
|
user.id = Some(1);
|
|
user.role = UserRole::Admin;
|
|
|
|
// Generate JWT token
|
|
let token = Self::generate_token(&user.email, &user.role)
|
|
.map_err(|_| actix_web::error::ErrorInternalServerError("Failed to generate token"))?;
|
|
|
|
// Store user data in session
|
|
let user_json = serde_json::to_string(&user).unwrap();
|
|
session.insert("user", &user_json)?;
|
|
session.insert("auth_token", &token)?;
|
|
|
|
// Create a cookie with the JWT token
|
|
let cookie = Cookie::build("auth_token", token)
|
|
.path("/")
|
|
.http_only(true)
|
|
.secure(false) // Set to true in production with HTTPS
|
|
.max_age(actix_web::cookie::time::Duration::hours(24))
|
|
.finish();
|
|
|
|
// Redirect to the home page with JWT token in cookie
|
|
Ok(HttpResponse::Found()
|
|
.cookie(cookie)
|
|
.append_header((header::LOCATION, "/"))
|
|
.finish())
|
|
}
|
|
|
|
/// Handles user logout
|
|
pub async fn logout(session: Session) -> Result<impl Responder> {
|
|
// Clear the session
|
|
session.purge();
|
|
|
|
// Create an expired cookie to remove the JWT token
|
|
let cookie = Cookie::build("auth_token", "")
|
|
.path("/")
|
|
.http_only(true)
|
|
.max_age(actix_web::cookie::time::Duration::seconds(0))
|
|
.finish();
|
|
|
|
// Redirect to the home page and clear the auth token cookie
|
|
Ok(HttpResponse::Found()
|
|
.cookie(cookie)
|
|
.append_header((header::LOCATION, "/"))
|
|
.finish())
|
|
}
|
|
} |