Skip to main content

actix_web_admin/handlers/
auth.rs

1use actix_session::Session;
2use actix_web::{web, HttpResponse, Responder};
3use serde::Deserialize;
4use std::sync::Arc;
5use tera::{Context, Tera};
6use crate::auth::{UserStore, verify_password};
7use crate::resource::{AdminTitle, AdminPrefix};
8
9#[derive(Clone)]
10#[deprecated(note = "use UserStore trait with a concrete implementation instead")]
11pub struct SimpleAuth {
12    pub username: String,
13    pub password: String,
14}
15
16#[allow(deprecated)]
17impl SimpleAuth {
18    pub fn check(&self, username: &str, password: &str) -> bool {
19        self.username == username && self.password == password
20    }
21}
22
23#[derive(Deserialize)]
24pub struct LoginRequest {
25    pub username: String,
26    pub password: String,
27}
28
29pub async fn login_page(tmpl: web::Data<Tera>, title: web::Data<AdminTitle>, prefix: web::Data<AdminPrefix>) -> impl Responder {
30    let mut ctx = Context::new();
31    ctx.insert("title", &title.0);
32    ctx.insert("page_title", "Login");
33    ctx.insert("path_dashboard", &format!("/{}/", prefix.0));
34    ctx.insert("path_logout", &format!("/{}/logout", prefix.0));
35    ctx.insert("resources", &Vec::<crate::resource::ResourceInfo>::new());
36
37    match tmpl.render("login.html", &ctx) {
38        Ok(rendered) => HttpResponse::Ok().content_type("text/html").body(rendered),
39        Err(_) => HttpResponse::InternalServerError().finish(),
40    }
41}
42
43pub async fn login(
44    session: Session,
45    store: web::Data<Arc<dyn UserStore>>,
46    form: web::Form<LoginRequest>,
47    tmpl: web::Data<Tera>,
48    title: web::Data<AdminTitle>,
49    prefix: web::Data<AdminPrefix>,
50) -> impl Responder {
51    let user = if form.username.contains('@') {
52        store.find_by_email(&form.username).await
53    } else {
54        store.find_by_username(&form.username).await
55    };
56
57    match user {
58        Ok(Some(u)) if verify_password(&form.password, &u.password_hash).unwrap_or(false) => {
59            if session.insert("admin_user", &u.username).is_err() {
60                return HttpResponse::InternalServerError().body("Session error");
61            }
62            return HttpResponse::Found()
63                .insert_header(("Location", format!("/{}/", prefix.0)))
64                .finish();
65        }
66        Ok(_) | Err(_) => {}
67    }
68
69    let mut ctx = Context::new();
70    ctx.insert("title", &title.0);
71    ctx.insert("page_title", "Login");
72    ctx.insert("path_dashboard", &format!("/{}/", prefix.0));
73    ctx.insert("path_logout", &format!("/{}/logout", prefix.0));
74    ctx.insert("resources", &Vec::<crate::resource::ResourceInfo>::new());
75    ctx.insert("error", "Invalid username or password");
76
77    match tmpl.render("login.html", &ctx) {
78        Ok(rendered) => HttpResponse::Unauthorized().content_type("text/html").body(rendered),
79        Err(_) => HttpResponse::InternalServerError().finish(),
80    }
81}
82
83pub async fn logout(session: Session, prefix: web::Data<AdminPrefix>) -> impl Responder {
84    session.purge();
85    HttpResponse::Found()
86        .insert_header(("Location", format!("/{}/login", prefix.0)))
87        .finish()
88}