From f5ac7bf278743680b821e1ec4e8c05bfac964f18 Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Sat, 26 Nov 2022 14:51:08 +0100 Subject: [PATCH] Refactor users management (#6) * Use asynchronous interface to set authorized clients list --- src/actors/users_actor.rs | 14 ++++++++- src/controllers/admin_controller.rs | 45 ++++++++++++++++++++--------- src/data/action_logger.rs | 8 ++++- src/data/user.rs | 32 ++++++++++++++++++-- src/data/users_file_entity.rs | 9 +++++- templates/settings/edit_user.html | 4 +-- 6 files changed, 90 insertions(+), 22 deletions(-) diff --git a/src/actors/users_actor.rs b/src/actors/users_actor.rs index 2852a52..3ad3d6d 100644 --- a/src/actors/users_actor.rs +++ b/src/actors/users_actor.rs @@ -1,7 +1,7 @@ use actix::{Actor, Context, Handler, Message, MessageResult}; use std::net::IpAddr; -use crate::data::user::{FactorID, TwoFactor, User, UserID}; +use crate::data::user::{FactorID, GrantedClients, TwoFactor, User, UserID}; use crate::utils::err::Res; /// User storage interface @@ -16,6 +16,7 @@ pub trait UsersBackend { 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; + fn set_granted_2fa_clients(&mut self, id: &UserID, clients: GrantedClients) -> bool; // FIXME : remove this fn update_or_insert_user(&mut self, user: User) -> Res; @@ -85,6 +86,10 @@ pub struct AddSuccessful2FALogin(pub UserID, pub IpAddr); #[rtype(result = "bool")] pub struct Clear2FALoginHistory(pub UserID); +#[derive(Message)] +#[rtype(result = "bool")] +pub struct SetGrantedClients(pub UserID, pub GrantedClients); + #[derive(Message)] #[rtype(result = "bool")] pub struct UpdateUserRequest(pub User); @@ -174,6 +179,13 @@ impl Handler for UsersActor { } } +impl Handler for UsersActor { + type Result = ::Result; + fn handle(&mut self, msg: SetGrantedClients, _ctx: &mut Self::Context) -> Self::Result { + self.manager.set_granted_2fa_clients(&msg.0, msg.1) + } +} + impl Handler for UsersActor { type Result = MessageResult; diff --git a/src/controllers/admin_controller.rs b/src/controllers/admin_controller.rs index 5100cae..27f4df5 100644 --- a/src/controllers/admin_controller.rs +++ b/src/controllers/admin_controller.rs @@ -11,7 +11,7 @@ use crate::controllers::settings_controller::BaseSettingsPage; use crate::data::action_logger::{Action, ActionLogger}; use crate::data::client::{Client, ClientID, ClientManager}; use crate::data::current_user::CurrentUser; -use crate::data::user::{User, UserID}; +use crate::data::user::{GrantedClients, User, UserID}; use crate::utils::string_utils::rand_str; #[derive(Template)] @@ -98,24 +98,41 @@ pub async fn users_route( user.two_factor .retain(|f| factors_to_keep.contains(&f.id.0.as_str())); - user.authorized_clients = match update.0.grant_type.as_str() { - "all_clients" => None, - "custom_clients" => Some( - update - .0 - .granted_clients - .split(',') - .map(|c| ClientID(c.to_string())) - .collect::>(), - ), - _ => Some(Vec::new()), - }; - let res = users .send(users_actor::UpdateUserRequest(user.clone())) .await .unwrap(); + // Update list of granted clients + let granted_clients = match update.0.grant_type.as_str() { + "all_clients" => GrantedClients::AllClients, + "custom_clients" if !update.0.granted_clients.is_empty() => { + GrantedClients::SomeClients( + update + .0 + .granted_clients + .split(',') + .map(|c| ClientID(c.to_string())) + .collect::>(), + ) + } + _ => GrantedClients::NoClient, + }; + + if user.granted_clients() != granted_clients { + logger.log(Action::AdminSetNewGrantedClientsList( + &user, + &granted_clients, + )); + users + .send(users_actor::SetGrantedClients( + user.uid.clone(), + granted_clients, + )) + .await + .unwrap(); + } + // Clear user 2FA history if requested if update.0.clear_2fa_history.is_some() { logger.log(Action::AdminClear2FAHistory(&user)); diff --git a/src/data/action_logger.rs b/src/data/action_logger.rs index cf752e0..de7e1d6 100644 --- a/src/data/action_logger.rs +++ b/src/data/action_logger.rs @@ -12,13 +12,14 @@ use crate::actors::users_actor::UsersActor; use crate::data::client::Client; use crate::data::remote_ip::RemoteIP; use crate::data::session_identity::SessionIdentity; -use crate::data::user::{FactorID, TwoFactor, User, UserID}; +use crate::data::user::{FactorID, GrantedClients, TwoFactor, User, UserID}; pub enum Action<'a> { AdminCreateUser(&'a User), AdminUpdateUser(&'a User), AdminDeleteUser(&'a User), AdminResetUserPassword(&'a User), + AdminSetNewGrantedClientsList(&'a User, &'a GrantedClients), AdminClear2FAHistory(&'a User), LoginWebauthnAttempt { success: bool, user_id: UserID }, Signout, @@ -57,6 +58,11 @@ impl<'a> Action<'a> { Action::AdminClear2FAHistory(user) => { format!("cleared 2FA history of {}", user.quick_identity()) } + Action::AdminSetNewGrantedClientsList(user, clients) => format!( + "set new granted clients list ({:?}) for user ({})", + clients, + user.quick_identity() + ), Action::LoginWebauthnAttempt { success, user_id } => match success { true => format!( "successfully performed webauthn attempt for user {:?}", diff --git a/src/data/user.rs b/src/data/user.rs index 9fec0bb..3bb6240 100644 --- a/src/data/user.rs +++ b/src/data/user.rs @@ -11,6 +11,23 @@ use crate::utils::time::{fmt_time, time}; #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct UserID(pub String); +#[derive(Eq, PartialEq, Clone, Debug)] +pub enum GrantedClients { + AllClients, + SomeClients(Vec), + NoClient, +} + +impl GrantedClients { + pub fn to_user(self) -> Option> { + match self { + GrantedClients::AllClients => None, + GrantedClients::SomeClients(users) => Some(users), + GrantedClients::NoClient => Some(vec![]), + } + } +} + #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct FactorID(pub String); @@ -124,10 +141,19 @@ impl User { ) } + pub fn granted_clients(&self) -> GrantedClients { + match self.authorized_clients.as_deref() { + None => GrantedClients::AllClients, + Some(&[]) => GrantedClients::NoClient, + Some(clients) => GrantedClients::SomeClients(clients.to_vec()), + } + } + pub fn can_access_app(&self, id: &ClientID) -> bool { - match &self.authorized_clients { - None => true, - Some(c) => c.contains(id), + match self.granted_clients() { + GrantedClients::AllClients => true, + GrantedClients::SomeClients(c) => c.contains(id), + GrantedClients::NoClient => false, } } diff --git a/src/data/users_file_entity.rs b/src/data/users_file_entity.rs index 7318bfa..6d26826 100644 --- a/src/data/users_file_entity.rs +++ b/src/data/users_file_entity.rs @@ -1,6 +1,6 @@ use crate::actors::users_actor::UsersBackend; use crate::data::entity_manager::EntityManager; -use crate::data::user::{FactorID, TwoFactor, User, UserID}; +use crate::data::user::{FactorID, GrantedClients, TwoFactor, User, UserID}; use crate::utils::err::Res; use crate::utils::time::time; use std::net::IpAddr; @@ -138,6 +138,13 @@ impl UsersBackend for EntityManager { } } + fn set_granted_2fa_clients(&mut self, id: &UserID, clients: GrantedClients) -> bool { + self.update_user(id, |mut user| { + user.authorized_clients = clients.to_user(); + user + }) + } + fn update_or_insert_user(&mut self, user: User) -> Res { self.update_or_push(user) } diff --git a/templates/settings/edit_user.html b/templates/settings/edit_user.html index 2ded57e..92da877 100644 --- a/templates/settings/edit_user.html +++ b/templates/settings/edit_user.html @@ -126,14 +126,14 @@