actix_web_admin/handlers/
auth.rs1use 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}