use std::str::FromStr; use actix::Addr; use actix_http::error::ResponseError; use actix_identity::Identity; use actix_web::{http::header, web, HttpResponse}; use askama::Template; use serde::{Serialize, Deserialize}; use crate::error::ServiceError; use crate::model::{DbExecutor, NewUser}; use crate::user::LoginData; use crate::util::UserRequest; use crate::Secret; pub mod user_creation; use user_creation::{ChangePasswordRequest, PassHashExecutor, PassHashRequest, SaveUserRequest}; #[derive(Template)] #[template(path = "admin.htm")] struct AdminTemplate { success: OperationResult, } #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(rename_all = "kebab-case")] pub enum OperationResult { ChangePassword, CreateUser, Empty, } pub async fn handle_index(ident: Identity, db: web::Data>) -> HttpResponse { if let Some(name) = ident.identity() { ident.remember(name.clone()); let user = match db.send(UserRequest(name.clone())).await.unwrap() { Ok(v) => v, Err(_) => { ident.forget(); return HttpResponse::Found() .header(header::LOCATION, "/login") .finish(); } }; if user.power_level != 9001 { return ServiceError::Unauthorized.error_response(); } let page = AdminTemplate { success: OperationResult::Empty, } .render() .unwrap(); HttpResponse::Ok().content_type("text/html").body(page) } else { HttpResponse::Found() .header(header::LOCATION, "/user/login") .finish() } } #[derive(Deserialize, Debug)] #[serde(rename_all = "kebab-case")] pub struct UserData { pub user: String, pub balance: String, pub password: String, } pub async fn handle_create_user( ident: Identity, params: web::Form, db: web::Data>, hasher: web::Data>, sec: web::Data, ) -> HttpResponse { if let Some(name) = ident.identity() { ident.remember(name.clone()); let user = match db.send(UserRequest(name.clone())).await.unwrap() { Ok(v) => v, Err(_) => { ident.forget(); return HttpResponse::Found() .header(header::LOCATION, "/login") .finish(); } }; if user.power_level != 9001 { return ServiceError::Unauthorized.error_response(); } let new_login = params.into_inner(); let balance = match u64::from_str(&new_login.balance) { Ok(v) => v, Err(e) => { return HttpResponse::Ok() .content_type("text/html") .body(e.to_string()) } }; let hash = hasher .send(PassHashRequest::new(new_login.password, sec.0.clone())) .await .unwrap() .unwrap(); let new_user = NewUser { name: new_login.user, // TODO Let the admin pick power_level: 0, balance, }; db.send(SaveUserRequest::new(new_user, hash)) .await .unwrap() .unwrap(); HttpResponse::Found() .header(header::LOCATION, "/admin") .finish() } else { HttpResponse::Found() .header(header::LOCATION, "/login") .finish() } } pub async fn handle_change_password( ident: Identity, params: web::Form, db: web::Data>, hasher: web::Data>, sec: web::Data, ) -> HttpResponse { if let Some(name) = ident.identity() { ident.remember(name.clone()); let user = db.send(UserRequest(name.clone())).await.unwrap().unwrap(); if user.power_level != 9001 { return ServiceError::Unauthorized.error_response(); } change_password(params.into_inner(), &db, &hasher, sec.0.clone()).await; let page = AdminTemplate { success: OperationResult::ChangePassword, } .render() .unwrap(); HttpResponse::Ok().content_type("text/html").body(page) } else { HttpResponse::Found() .header(header::LOCATION, "/user/login") .finish() } } pub async fn change_password( login: LoginData, db: &Addr, hasher: &Addr, secret: String, ) { let hash = hasher .send(PassHashRequest::new(login.password, secret)) .await .unwrap() .unwrap(); db.send(ChangePasswordRequest(login.user, hash)) .await .unwrap() .unwrap(); }