Add communication with user actor
This commit is contained in:
		@@ -1,7 +1,21 @@
 | 
			
		||||
use actix::{Actor, Context};
 | 
			
		||||
use actix::{Actor, Context, Handler, Message, MessageResult};
 | 
			
		||||
 | 
			
		||||
use crate::data::entity_manager::EntityManager;
 | 
			
		||||
use crate::data::user::User;
 | 
			
		||||
use crate::data::user::{User, verify_password};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum LoginResult {
 | 
			
		||||
    AccountNotFound,
 | 
			
		||||
    InvalidPassword,
 | 
			
		||||
    Success(User),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Message)]
 | 
			
		||||
#[rtype(LoginResult)]
 | 
			
		||||
pub struct LoginRequest {
 | 
			
		||||
    pub login: String,
 | 
			
		||||
    pub password: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct UsersActor {
 | 
			
		||||
    manager: EntityManager<User>,
 | 
			
		||||
@@ -16,3 +30,20 @@ impl UsersActor {
 | 
			
		||||
impl Actor for UsersActor {
 | 
			
		||||
    type Context = Context<Self>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Handler<LoginRequest> for UsersActor {
 | 
			
		||||
    type Result = MessageResult<LoginRequest>;
 | 
			
		||||
 | 
			
		||||
    fn handle(&mut self, msg: LoginRequest, _ctx: &mut Self::Context) -> Self::Result {
 | 
			
		||||
        match self.manager.find_by_username_or_email(&msg.login) {
 | 
			
		||||
            None => MessageResult(LoginResult::AccountNotFound),
 | 
			
		||||
            Some(user) => {
 | 
			
		||||
                if !verify_password(msg.password, &user.password) {
 | 
			
		||||
                    return MessageResult(LoginResult::InvalidPassword);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                MessageResult(LoginResult::Success(user))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,9 @@
 | 
			
		||||
use actix_web::{HttpResponse, Responder};
 | 
			
		||||
use actix::Addr;
 | 
			
		||||
use actix_web::{HttpResponse, Responder, web};
 | 
			
		||||
use askama::Template;
 | 
			
		||||
 | 
			
		||||
use crate::actors::users_actor::{LoginResult, UsersActor};
 | 
			
		||||
use crate::actors::users_actor;
 | 
			
		||||
use crate::constants::APP_NAME;
 | 
			
		||||
 | 
			
		||||
#[derive(Template)]
 | 
			
		||||
@@ -16,20 +19,42 @@ struct BaseLoginPage {
 | 
			
		||||
#[template(path = "login.html")]
 | 
			
		||||
struct LoginTemplate {
 | 
			
		||||
    _parent: BaseLoginPage,
 | 
			
		||||
    mail: String,
 | 
			
		||||
    login: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct LoginRequest {
 | 
			
		||||
    login: String,
 | 
			
		||||
    password: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Authenticate user
 | 
			
		||||
pub async fn login_route(users: web::Data<Addr<UsersActor>>,
 | 
			
		||||
                         req: Option<web::Form<LoginRequest>>) -> impl Responder {
 | 
			
		||||
    let mut danger = String::new();
 | 
			
		||||
    let mut login = String::new();
 | 
			
		||||
 | 
			
		||||
    // Try to authenticate user
 | 
			
		||||
    if let Some(req) = &req {
 | 
			
		||||
        login = req.login.clone();
 | 
			
		||||
        let response: LoginResult = users.send(users_actor::LoginRequest {
 | 
			
		||||
            login: login.clone(),
 | 
			
		||||
            password: req.password.clone(),
 | 
			
		||||
        }).await.unwrap();
 | 
			
		||||
 | 
			
		||||
        danger = format!("{:?}", response)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pub async fn login_route() -> impl Responder {
 | 
			
		||||
    HttpResponse::Ok()
 | 
			
		||||
        .content_type("text/html")
 | 
			
		||||
        .body(LoginTemplate {
 | 
			
		||||
            _parent: BaseLoginPage {
 | 
			
		||||
                page_title: "Login",
 | 
			
		||||
                danger: "".to_string(),
 | 
			
		||||
                danger,
 | 
			
		||||
                success: "".to_string(),
 | 
			
		||||
                app_name: APP_NAME,
 | 
			
		||||
            },
 | 
			
		||||
            mail: "".to_string()
 | 
			
		||||
            login,
 | 
			
		||||
        }.render().unwrap())
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
use std::path::{Path, PathBuf};
 | 
			
		||||
use std::slice::Iter;
 | 
			
		||||
 | 
			
		||||
use crate::utils::err::Res;
 | 
			
		||||
 | 
			
		||||
@@ -44,4 +45,9 @@ impl<E> EntityManager<E> where E: serde::Serialize + serde::de::DeserializeOwned
 | 
			
		||||
        self.list.push(el);
 | 
			
		||||
        self.save()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Iterate over the entries of this entity manager
 | 
			
		||||
    pub fn iter(&self) -> Iter<'_, E> {
 | 
			
		||||
        self.list.iter()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
use crate::data::entity_manager::EntityManager;
 | 
			
		||||
use crate::data::service::ServiceID;
 | 
			
		||||
use crate::utils::err::Res;
 | 
			
		||||
 | 
			
		||||
@@ -38,7 +39,7 @@ impl Default for User {
 | 
			
		||||
            need_reset_password: false,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
            admin: false,
 | 
			
		||||
            authorized_services: None
 | 
			
		||||
            authorized_services: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -46,3 +47,24 @@ impl Default for User {
 | 
			
		||||
pub fn hash_password<P: AsRef<[u8]>>(pwd: P) -> Res<String> {
 | 
			
		||||
    Ok(bcrypt::hash(pwd, bcrypt::DEFAULT_COST)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn verify_password<P: AsRef<[u8]>>(pwd: P, hash: &str) -> bool {
 | 
			
		||||
    match bcrypt::verify(pwd, hash) {
 | 
			
		||||
        Ok(r) => r,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            log::warn!("Failed to verify password! {:?}", e);
 | 
			
		||||
            false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EntityManager<User> {
 | 
			
		||||
    pub fn find_by_username_or_email(&self, u: &str) -> Option<User> {
 | 
			
		||||
        for entry in self.iter() {
 | 
			
		||||
            if entry.username.eq(u) || entry.email.eq(u) {
 | 
			
		||||
                return Some(entry.clone());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,8 +3,8 @@
 | 
			
		||||
<form action="/login" method="post">
 | 
			
		||||
    <div>
 | 
			
		||||
        <div class="form-floating">
 | 
			
		||||
            <input name="mail" type="text" required class="form-control" id="floatingName" placeholder="unsername"
 | 
			
		||||
                   value="{{ mail }}">
 | 
			
		||||
            <input name="login" type="text" required class="form-control" id="floatingName" placeholder="unsername"
 | 
			
		||||
                   value="{{ login }}">
 | 
			
		||||
            <label for="floatingName">Email address or username</label>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user