use std::ops::Deref; use actix::Addr; use actix_web::{HttpResponse, Responder, web}; use askama::Template; use crate::actors::users_actor; use crate::actors::users_actor::UsersActor; use crate::constants::TEMPORARY_PASSWORDS_LEN; use crate::controllers::settings_controller::BaseSettingsPage; use crate::data::client::{Client, ClientID, ClientManager}; use crate::data::current_user::CurrentUser; use crate::data::user::{hash_password, User, UserID}; use crate::utils::string_utils::rand_str; #[derive(Template)] #[template(path = "settings/clients_list.html")] struct ClientsListTemplate { _p: BaseSettingsPage, clients: Vec, } #[derive(Template)] #[template(path = "settings/users_list.html")] struct UsersListTemplate { _p: BaseSettingsPage, users: Vec, } #[derive(Template)] #[template(path = "settings/edit_user.html")] struct EditUserTemplate { _p: BaseSettingsPage, u: User, clients: Vec, } pub async fn clients_route(user: CurrentUser, clients: web::Data) -> impl Responder { HttpResponse::Ok().body(ClientsListTemplate { _p: BaseSettingsPage::get( "Clients list", &user, None, None, ), clients: clients.cloned(), }.render().unwrap()) } #[derive(serde::Deserialize, Debug)] pub struct UpdateUserQuery { uid: UserID, username: String, first_name: String, last_name: String, email: String, gen_new_password: Option, enabled: Option, admin: Option, grant_type: String, granted_clients: String, two_factor: String, } pub async fn users_route(user: CurrentUser, users: web::Data>, update_query: Option>) -> impl Responder { let mut danger = None; let mut success = None; if let Some(update) = update_query { let current_user: Option = users.send(users_actor::FindUserByUsername(update.username.to_string())) .await.unwrap().0; let is_creating = current_user.is_none(); let mut user = current_user.unwrap_or_default(); user.uid = update.0.uid; user.username = update.0.username; user.first_name = update.0.first_name; user.last_name = update.0.last_name; user.email = update.0.email; user.enabled = update.0.enabled.is_some(); user.admin = update.0.admin.is_some(); let factors_to_keep = update.0.two_factor.split(';').collect::>(); 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 new_password = match update.0.gen_new_password.is_some() { false => None, true => { let temp_pass = rand_str(TEMPORARY_PASSWORDS_LEN); user.password = hash_password(&temp_pass) .expect("Failed to hash password"); user.need_reset_password = true; Some(temp_pass) } }; let res = users.send(users_actor::UpdateUserRequest(user.clone())).await.unwrap().0; if !res { danger = Some(match is_creating { true => "Failed to create user!", false => "Failed to update user!" }.to_string()) } else { success = Some(match is_creating { true => format!("User {} was successfully created!", user.full_name()), false => format!("User {} was successfully updated!", user.full_name()) }); if let Some(pass) = new_password { danger = Some(format!("{}'s temporary password is {}", user.full_name(), pass)); } } } let users = users.send(users_actor::GetAllUsersRequest).await.unwrap().0; HttpResponse::Ok().body(UsersListTemplate { _p: BaseSettingsPage::get( "Users list", &user, danger, success, ), users, }.render().unwrap()) } pub async fn create_user(user: CurrentUser, clients: web::Data) -> impl Responder { HttpResponse::Ok().body(EditUserTemplate { _p: BaseSettingsPage::get("Create a new user", user.deref(), None, None), u: Default::default(), clients: clients.cloned(), }.render().unwrap()) } #[derive(serde::Deserialize)] pub struct EditUserQuery { id: UserID, } pub async fn edit_user(user: CurrentUser, clients: web::Data, users: web::Data>, query: web::Query, ) -> impl Responder { let edited_account = users.send(users_actor::GetUserRequest(query.0.id)) .await.unwrap().0; HttpResponse::Ok().body(EditUserTemplate { _p: BaseSettingsPage::get( "Edit user account", user.deref(), match edited_account.is_none() { true => Some("Could not find requested user!".to_string()), false => None }, None, ), u: edited_account.unwrap_or_default(), clients: clients.cloned(), }.render().unwrap()) }