Refactor users management (#2)
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			* Create UserBackend trait
This commit is contained in:
		@@ -1,8 +1,23 @@
 | 
			
		||||
use actix::{Actor, Context, Handler, Message, MessageResult};
 | 
			
		||||
use std::net::IpAddr;
 | 
			
		||||
 | 
			
		||||
use crate::data::entity_manager::EntityManager;
 | 
			
		||||
use crate::data::user::{User, UserID};
 | 
			
		||||
use crate::utils::err::Res;
 | 
			
		||||
 | 
			
		||||
/// User storage interface
 | 
			
		||||
pub trait UsersBackend {
 | 
			
		||||
    fn find_by_username_or_email(&self, u: &str) -> Option<User>;
 | 
			
		||||
    fn find_by_user_id(&self, id: &UserID) -> Option<User>;
 | 
			
		||||
    fn get_entire_users_list(&self) -> Vec<User>;
 | 
			
		||||
    fn change_user_password(&mut self, id: &UserID, password: &str, temporary: bool) -> bool;
 | 
			
		||||
    fn verify_user_password(&self, user: &UserID, password: &str) -> bool;
 | 
			
		||||
    fn save_new_successful_2fa_authentication(&mut self, id: &UserID, ip: IpAddr) -> bool;
 | 
			
		||||
    fn clear_2fa_login_history(&mut self, id: &UserID) -> bool;
 | 
			
		||||
    fn delete_account(&mut self, id: &UserID) -> bool;
 | 
			
		||||
 | 
			
		||||
    // FIXME : remove this
 | 
			
		||||
    fn update_or_insert_user(&mut self, user: User) -> Res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum LoginResult {
 | 
			
		||||
@@ -69,12 +84,17 @@ pub struct UpdateUserRequest(pub User);
 | 
			
		||||
pub struct DeleteUserRequest(pub UserID);
 | 
			
		||||
 | 
			
		||||
pub struct UsersActor {
 | 
			
		||||
    manager: EntityManager<User>,
 | 
			
		||||
    manager: Box<dyn UsersBackend>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl UsersActor {
 | 
			
		||||
    pub fn new(manager: EntityManager<User>) -> Self {
 | 
			
		||||
        Self { manager }
 | 
			
		||||
    pub fn new<E>(manager: E) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        E: UsersBackend + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        Self {
 | 
			
		||||
            manager: Box::new(manager),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -158,7 +178,7 @@ impl Handler<GetAllUsersRequest> for UsersActor {
 | 
			
		||||
    type Result = MessageResult<GetAllUsersRequest>;
 | 
			
		||||
 | 
			
		||||
    fn handle(&mut self, _msg: GetAllUsersRequest, _ctx: &mut Self::Context) -> Self::Result {
 | 
			
		||||
        MessageResult(GetAllUsersResult(self.manager.cloned()))
 | 
			
		||||
        MessageResult(GetAllUsersResult(self.manager.get_entire_users_list()))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -166,7 +186,7 @@ impl Handler<UpdateUserRequest> for UsersActor {
 | 
			
		||||
    type Result = <UpdateUserRequest as actix::Message>::Result;
 | 
			
		||||
 | 
			
		||||
    fn handle(&mut self, msg: UpdateUserRequest, _ctx: &mut Self::Context) -> Self::Result {
 | 
			
		||||
        match self.manager.update_or_push(msg.0) {
 | 
			
		||||
        match self.manager.update_or_insert_user(msg.0) {
 | 
			
		||||
            Ok(_) => true,
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                log::error!("Failed to update user information! {:?}", e);
 | 
			
		||||
@@ -180,23 +200,6 @@ impl Handler<DeleteUserRequest> for UsersActor {
 | 
			
		||||
    type Result = <DeleteUserRequest as actix::Message>::Result;
 | 
			
		||||
 | 
			
		||||
    fn handle(&mut self, msg: DeleteUserRequest, _ctx: &mut Self::Context) -> Self::Result {
 | 
			
		||||
        let user = match self.manager.find_by_user_id(&msg.0) {
 | 
			
		||||
            None => {
 | 
			
		||||
                log::warn!(
 | 
			
		||||
                    "Could not delete account {:?} because it was not found!",
 | 
			
		||||
                    msg.0
 | 
			
		||||
                );
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            Some(s) => s,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        match self.manager.remove(&user) {
 | 
			
		||||
            Ok(_) => true,
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                log::error!("Failed to update delete account! {:?}", e);
 | 
			
		||||
                false
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        self.manager.delete_account(&msg.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,42 +1,11 @@
 | 
			
		||||
use crate::actors::users_actor::UsersBackend;
 | 
			
		||||
use crate::data::entity_manager::EntityManager;
 | 
			
		||||
use crate::data::user::{User, UserID};
 | 
			
		||||
use crate::utils::err::Res;
 | 
			
		||||
use crate::utils::time::time;
 | 
			
		||||
use std::net::IpAddr;
 | 
			
		||||
 | 
			
		||||
fn hash_password<P: AsRef<[u8]>>(pwd: P) -> Res<String> {
 | 
			
		||||
    Ok(bcrypt::hash(pwd, bcrypt::DEFAULT_COST)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn find_by_user_id(&self, id: &UserID) -> Option<User> {
 | 
			
		||||
        for entry in self.iter() {
 | 
			
		||||
            if entry.uid.eq(id) {
 | 
			
		||||
                return Some(entry.clone());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Update user information
 | 
			
		||||
    fn update_user<F>(&mut self, id: &UserID, update: F) -> bool
 | 
			
		||||
    where
 | 
			
		||||
@@ -54,8 +23,46 @@ impl EntityManager<User> {
 | 
			
		||||
 | 
			
		||||
        true
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    pub fn change_user_password(&mut self, id: &UserID, password: &str, temporary: bool) -> bool {
 | 
			
		||||
fn hash_password<P: AsRef<[u8]>>(pwd: P) -> Res<String> {
 | 
			
		||||
    Ok(bcrypt::hash(pwd, bcrypt::DEFAULT_COST)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 UsersBackend for EntityManager<User> {
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn find_by_user_id(&self, id: &UserID) -> Option<User> {
 | 
			
		||||
        for entry in self.iter() {
 | 
			
		||||
            if entry.uid.eq(id) {
 | 
			
		||||
                return Some(entry.clone());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_entire_users_list(&self) -> Vec<User> {
 | 
			
		||||
        self.cloned()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn change_user_password(&mut self, id: &UserID, password: &str, temporary: bool) -> bool {
 | 
			
		||||
        let new_hash = match hash_password(password) {
 | 
			
		||||
            Ok(h) => h,
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
@@ -72,13 +79,13 @@ impl EntityManager<User> {
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn verify_user_password(&self, user: &UserID, password: &str) -> bool {
 | 
			
		||||
    fn verify_user_password(&self, user: &UserID, password: &str) -> bool {
 | 
			
		||||
        self.find_by_user_id(user)
 | 
			
		||||
            .map(|u| verify_password(password, &u.password))
 | 
			
		||||
            .unwrap_or(false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn save_new_successful_2fa_authentication(&mut self, id: &UserID, ip: IpAddr) -> bool {
 | 
			
		||||
    fn save_new_successful_2fa_authentication(&mut self, id: &UserID, ip: IpAddr) -> bool {
 | 
			
		||||
        self.update_user(id, |mut user| {
 | 
			
		||||
            user.last_successful_2fa.insert(ip, time());
 | 
			
		||||
 | 
			
		||||
@@ -89,10 +96,35 @@ impl EntityManager<User> {
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn clear_2fa_login_history(&mut self, id: &UserID) -> bool {
 | 
			
		||||
    fn clear_2fa_login_history(&mut self, id: &UserID) -> bool {
 | 
			
		||||
        self.update_user(id, |mut user| {
 | 
			
		||||
            user.last_successful_2fa = Default::default();
 | 
			
		||||
            user
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn delete_account(&mut self, id: &UserID) -> bool {
 | 
			
		||||
        let user = match self.find_by_user_id(id) {
 | 
			
		||||
            None => {
 | 
			
		||||
                log::warn!(
 | 
			
		||||
                    "Could not delete account {:?} because it was not found!",
 | 
			
		||||
                    id
 | 
			
		||||
                );
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            Some(s) => s,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        match self.remove(&user) {
 | 
			
		||||
            Ok(_) => true,
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                log::error!("Failed to update delete account! {:?}", e);
 | 
			
		||||
                false
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn update_or_insert_user(&mut self, user: User) -> Res {
 | 
			
		||||
        self.update_or_push(user)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ use actix_web::{get, middleware, web, App, HttpResponse, HttpServer};
 | 
			
		||||
 | 
			
		||||
use basic_oidc::actors::bruteforce_actor::BruteForceActor;
 | 
			
		||||
use basic_oidc::actors::openid_sessions_actor::OpenIDSessionsActor;
 | 
			
		||||
use basic_oidc::actors::users_actor::UsersActor;
 | 
			
		||||
use basic_oidc::actors::users_actor::{UsersActor, UsersBackend};
 | 
			
		||||
use basic_oidc::constants::*;
 | 
			
		||||
use basic_oidc::controllers::assets_controller::assets_route;
 | 
			
		||||
use basic_oidc::controllers::*;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user