use std::net::IpAddr; use crate::data::provider::{Provider, ProviderID}; use crate::data::user::{FactorID, GeneralSettings, GrantedClients, TwoFactor, User, UserID}; use crate::utils::err::Res; use crate::utils::string_utils::is_acceptable_login; use actix::{Actor, Context, Handler, Message, MessageResult}; use light_openid::primitives::OpenIDUserInfo; /// User storage interface pub trait UsersSyncBackend { fn find_by_username_or_email(&self, u: &str) -> Res>; fn find_by_email(&self, u: &str) -> Res>; fn find_by_user_id(&self, id: &UserID) -> Res>; fn get_entire_users_list(&self) -> Res>; fn create_user_account(&mut self, settings: GeneralSettings) -> Res; fn set_general_user_settings(&mut self, settings: GeneralSettings) -> Res; fn change_user_password(&mut self, id: &UserID, password: &str, temporary: bool) -> Res; fn verify_user_password(&self, user: &UserID, password: &str) -> Res; fn add_2fa_factor(&mut self, user: &UserID, factor: TwoFactor) -> Res; fn remove_2fa_factor(&mut self, user: &UserID, factor: FactorID) -> Res; fn save_new_successful_2fa_authentication(&mut self, id: &UserID, ip: IpAddr) -> Res; fn clear_2fa_login_history(&mut self, id: &UserID) -> Res; fn delete_account(&mut self, id: &UserID) -> Res; fn set_authorized_authentication_sources( &mut self, id: &UserID, sources: AuthorizedAuthenticationSources, ) -> Res; fn set_granted_2fa_clients(&mut self, id: &UserID, clients: GrantedClients) -> Res; } #[derive(Debug)] pub enum LoginResult { Error, AccountNotFound, InvalidPassword, AccountDisabled, LocalAuthForbidden, AuthFromProviderForbidden, Success(Box), AccountAutoCreated(Box), CannotAutoCreateAccount(String), } #[derive(Message)] #[rtype(LoginResult)] pub struct LocalLoginRequest { pub login: String, pub password: String, } #[derive(Message)] #[rtype(LoginResult)] pub struct ProviderLoginRequest { pub email: String, pub user_info: OpenIDUserInfo, pub provider: Provider, } #[derive(Message)] #[rtype(GetUserResult)] pub struct GetUserRequest(pub UserID); #[derive(Debug)] pub struct GetUserResult(pub Option); #[derive(Message)] #[rtype(result = "bool")] pub struct VerifyUserPasswordRequest(pub UserID, pub String); #[derive(Message)] #[rtype(FindUserByUsernameResult)] pub struct FindUserByUsername(pub String); #[derive(Debug)] pub struct FindUserByUsernameResult(pub Option); #[derive(Message)] #[rtype(result = "Vec")] pub struct GetAllUsers; #[derive(Message)] #[rtype(result = "Option")] pub struct CreateAccount(pub GeneralSettings); #[derive(Message)] #[rtype(result = "bool")] pub struct ChangePasswordRequest { pub user_id: UserID, pub new_password: String, pub temporary: bool, } #[derive(Message)] #[rtype(result = "bool")] pub struct Add2FAFactor(pub UserID, pub TwoFactor); #[derive(Message)] #[rtype(result = "bool")] pub struct Remove2FAFactor(pub UserID, pub FactorID); #[derive(Message)] #[rtype(result = "bool")] pub struct AddSuccessful2FALogin(pub UserID, pub IpAddr); #[derive(Message)] #[rtype(result = "bool")] pub struct Clear2FALoginHistory(pub UserID); #[derive(Eq, PartialEq, Debug, Clone, serde::Serialize)] pub struct AuthorizedAuthenticationSources { pub local: bool, pub upstream: Vec, } #[derive(Message)] #[rtype(result = "bool")] pub struct SetAuthorizedAuthenticationSources(pub UserID, pub AuthorizedAuthenticationSources); #[derive(Message)] #[rtype(result = "bool")] pub struct SetGrantedClients(pub UserID, pub GrantedClients); #[derive(Message)] #[rtype(result = "bool")] pub struct UpdateUserSettings(pub GeneralSettings); #[derive(Message)] #[rtype(result = "bool")] pub struct DeleteUserRequest(pub UserID); pub struct UsersActor { manager: Box, } impl UsersActor { pub fn new(manager: E) -> Self where E: UsersSyncBackend + 'static, { Self { manager: Box::new(manager), } } } impl Actor for UsersActor { type Context = Context; } impl Handler for UsersActor { type Result = MessageResult; fn handle(&mut self, msg: LocalLoginRequest, _ctx: &mut Self::Context) -> Self::Result { match self.manager.find_by_username_or_email(&msg.login) { Err(e) => { log::error!("Failed to find user! {e}"); MessageResult(LoginResult::Error) } Ok(None) => MessageResult(LoginResult::AccountNotFound), Ok(Some(user)) => { if !self .manager .verify_user_password(&user.uid, &msg.password) .unwrap_or(false) { return MessageResult(LoginResult::InvalidPassword); } if !user.enabled { return MessageResult(LoginResult::AccountDisabled); } if !user.allow_local_login { return MessageResult(LoginResult::LocalAuthForbidden); } MessageResult(LoginResult::Success(Box::new(user))) } } } } impl Handler for UsersActor { type Result = MessageResult; fn handle(&mut self, msg: ProviderLoginRequest, _ctx: &mut Self::Context) -> Self::Result { match self.manager.find_by_email(&msg.email) { Err(e) => { log::error!("Failed to find user! {e}"); MessageResult(LoginResult::Error) } Ok(None) => { // Check if automatic account creation is enabled for this provider if !msg.provider.allow_auto_account_creation { return MessageResult(LoginResult::AccountNotFound); } // Extract username for account creation let mut username = msg .user_info .preferred_username .unwrap_or(msg.email.to_string()); // Determine username from email, if necessary if !is_acceptable_login(&username) || matches!( self.manager.find_by_username_or_email(&username), Ok(Some(_)) ) { username = msg.email.clone(); } // Check if username is already taken if matches!( self.manager.find_by_username_or_email(&username), Ok(Some(_)) ) { return MessageResult(LoginResult::CannotAutoCreateAccount(format!( "username {username} is already taken!" ))); } if !is_acceptable_login(&username) { return MessageResult(LoginResult::CannotAutoCreateAccount( "could not determine acceptable login for user!".to_string(), )); } // Automatic account creation let user_id = match self.manager.create_user_account(GeneralSettings { uid: UserID::random(), username, first_name: msg.user_info.given_name.unwrap_or_default(), last_name: msg.user_info.family_name.unwrap_or_default(), email: msg.email.to_string(), enabled: true, two_factor_exemption_after_successful_login: false, is_admin: false, }) { Ok(u) => u, Err(e) => { log::error!("Failed to create user account! {e}"); return MessageResult(LoginResult::CannotAutoCreateAccount( "missing some user information".to_string(), )); } }; // Mark the provider as the only authorized source if let Err(e) = self.manager.set_authorized_authentication_sources( &user_id, AuthorizedAuthenticationSources { local: false, upstream: vec![msg.provider.id], }, ) { log::error!( "Failed to set authorized authentication sources for newly created account! {e}" ); } // Extract user information to return them let Ok(Some(user)) = self.manager.find_by_user_id(&user_id) else { return MessageResult(LoginResult::CannotAutoCreateAccount( "failed to get created user information".to_string(), )); }; MessageResult(LoginResult::AccountAutoCreated(Box::new(user))) } Ok(Some(user)) => { if !user.can_login_from_provider(&msg.provider) { return MessageResult(LoginResult::AuthFromProviderForbidden); } if !user.enabled { return MessageResult(LoginResult::AccountDisabled); } MessageResult(LoginResult::Success(Box::new(user))) } } } } impl Handler for UsersActor { type Result = ::Result; fn handle(&mut self, msg: CreateAccount, _ctx: &mut Self::Context) -> Self::Result { match self.manager.create_user_account(msg.0) { Ok(id) => Some(id), Err(e) => { log::error!("Failed to create user account! {e}"); None } } } } impl Handler for UsersActor { type Result = ::Result; fn handle(&mut self, msg: ChangePasswordRequest, _ctx: &mut Self::Context) -> Self::Result { match self .manager .change_user_password(&msg.user_id, &msg.new_password, msg.temporary) { Ok(_) => true, Err(e) => { log::error!("Failed to change user password! {e:?}"); false } } } } impl Handler for UsersActor { type Result = ::Result; fn handle(&mut self, msg: Add2FAFactor, _ctx: &mut Self::Context) -> Self::Result { match self.manager.add_2fa_factor(&msg.0, msg.1) { Ok(_) => true, Err(e) => { log::error!("Failed to add 2FA factor! {e}"); false } } } } impl Handler for UsersActor { type Result = ::Result; fn handle(&mut self, msg: Remove2FAFactor, _ctx: &mut Self::Context) -> Self::Result { match self.manager.remove_2fa_factor(&msg.0, msg.1) { Ok(_) => true, Err(e) => { log::error!("Failed to remove 2FA factor! {e}"); false } } } } impl Handler for UsersActor { type Result = ::Result; fn handle(&mut self, msg: AddSuccessful2FALogin, _ctx: &mut Self::Context) -> Self::Result { match self .manager .save_new_successful_2fa_authentication(&msg.0, msg.1) { Ok(_) => true, Err(e) => { log::error!("Failed to save successful 2FA authentication! {e}"); false } } } } impl Handler for UsersActor { type Result = ::Result; fn handle(&mut self, msg: Clear2FALoginHistory, _ctx: &mut Self::Context) -> Self::Result { match self.manager.clear_2fa_login_history(&msg.0) { Ok(_) => true, Err(e) => { log::error!( "Failed to clear 2FA login history of user {:?} ! {}", msg.0, e ); false } } } } impl Handler for UsersActor { type Result = ::Result; fn handle( &mut self, msg: SetAuthorizedAuthenticationSources, _ctx: &mut Self::Context, ) -> Self::Result { match self .manager .set_authorized_authentication_sources(&msg.0, msg.1) { Ok(_) => true, Err(e) => { log::error!("Failed to set authorized authentication sources for user! {e}"); false } } } } impl Handler for UsersActor { type Result = ::Result; fn handle(&mut self, msg: SetGrantedClients, _ctx: &mut Self::Context) -> Self::Result { match self.manager.set_granted_2fa_clients(&msg.0, msg.1) { Ok(_) => true, Err(e) => { log::error!("Failed to set granted 2FA clients! {e}"); false } } } } impl Handler for UsersActor { type Result = MessageResult; fn handle(&mut self, msg: GetUserRequest, _ctx: &mut Self::Context) -> Self::Result { MessageResult(GetUserResult(match self.manager.find_by_user_id(&msg.0) { Ok(r) => r, Err(e) => { log::error!("Failed to find user by id! {e}"); None } })) } } impl Handler for UsersActor { type Result = ::Result; fn handle(&mut self, msg: VerifyUserPasswordRequest, _ctx: &mut Self::Context) -> Self::Result { self.manager .verify_user_password(&msg.0, &msg.1) .unwrap_or_else(|e| { log::error!("Failed to verify user password! {e}"); false }) } } impl Handler for UsersActor { type Result = MessageResult; fn handle(&mut self, msg: FindUserByUsername, _ctx: &mut Self::Context) -> Self::Result { MessageResult(FindUserByUsernameResult( self.manager .find_by_username_or_email(&msg.0) .unwrap_or_else(|e| { log::error!("Failed to find user by username or email! {e}"); None }), )) } } impl Handler for UsersActor { type Result = ::Result; fn handle(&mut self, _msg: GetAllUsers, _ctx: &mut Self::Context) -> Self::Result { match self.manager.get_entire_users_list() { Ok(r) => r, Err(e) => { log::error!("Failed to get entire users list! {e}"); vec![] } } } } impl Handler for UsersActor { type Result = ::Result; fn handle(&mut self, msg: UpdateUserSettings, _ctx: &mut Self::Context) -> Self::Result { match self.manager.set_general_user_settings(msg.0) { Ok(_) => true, Err(e) => { log::error!("Failed to update general user information! {e:?}"); false } } } } impl Handler for UsersActor { type Result = ::Result; fn handle(&mut self, msg: DeleteUserRequest, _ctx: &mut Self::Context) -> Self::Result { match self.manager.delete_account(&msg.0) { Ok(_) => true, Err(e) => { log::error!("Failed to delete user account! {e}"); false } } } }