diff --git a/src/console/user_api.rs b/src/console/user_api.rs index dd858a78..553de459 100644 --- a/src/console/user_api.rs +++ b/src/console/user_api.rs @@ -183,6 +183,25 @@ pub async fn remove_user( let msg = UserManagerReq::Remove { username: user.username, }; - app.user_manager.send(msg).await.ok(); - Ok(HttpResponse::Ok().json(ApiResult::success(Some(true)))) -} + match app.user_manager.send(msg).await { + Ok(r) => { + match r { + Ok(_) => { + Ok(HttpResponse::Ok().json(ApiResult::success(Some(true)))) + } + Err(e) => { + Ok(HttpResponse::Ok().json(ApiResult::<()>::error( + e.to_string(), + Some(e.to_string()), + ))) + } + } + } + Err(e) => { + Ok(HttpResponse::Ok().json(ApiResult::<()>::error( + "SYSTEM_ERROR".to_owned(), + None, + ))) + } + } +} \ No newline at end of file diff --git a/src/user/mod.rs b/src/user/mod.rs index 5cba4843..af3819eb 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -11,6 +11,7 @@ use self::{ }; use crate::common::constant::USER_TREE_NAME; use crate::common::string_utils::StringUtils; +use crate::user::permission::UserRole; use crate::{ now_millis, raft::{ @@ -140,9 +141,9 @@ impl Inject for UserManager { } }; } - .into_actor(act) - .map(|_, _, _| {}) - .spawn(ctx); + .into_actor(act) + .map(|_, _, _| {}) + .spawn(ctx); }); } } @@ -327,7 +328,7 @@ impl Handler for UserManager { if !StringUtils::is_option_empty(&last_user.password_hash) { check_success = check_success && verify_password_hash_option(&password, &last_user.password_hash) - .unwrap_or(false); + .unwrap_or(false); //debug info /* println!( @@ -347,13 +348,50 @@ impl Handler for UserManager { )) } UserManagerReq::Remove { username } => { - let req = TableManagerReq::Remove { - table_name: USER_TREE_NAME.clone(), - key: username.as_bytes().to_owned(), - }; - if let Some(raft_table_route) = raft_table_route { - raft_table_route.request(req).await.ok(); + if let Some(table_manager) = &table_manager { + // 查询该用户信息 + let query_req = TableManagerQueryReq::GetByArcKey { + table_name: USER_TREE_NAME.clone(), + key: username.clone(), + }; + + if let TableManagerResult::Value(v) = table_manager.send(query_req).await?? { + let user_do = UserDo::from_bytes(&v)?; + + // 如果该用户是 admin,进行检查 + if user_do.roles.contains(&UserRole::Manager.to_role_value().to_string()) { + let query_req = TableManagerQueryReq::QueryPageList { + table_name: USER_TREE_NAME.clone(), + like_key: None, + offset: None, + limit: None, + is_rev: true, + }; + + if let TableManagerResult::PageListResult(_, list) = table_manager.send(query_req).await?? { + let manager_count = list.iter() + .filter_map(|(_, v)| UserDo::from_bytes(&v).ok()) + .filter(|user_do| user_do.roles.contains(&UserRole::Manager.to_role_value().to_string())) + .count(); + + // 仅剩一个 admin 时,不允许删除 + if manager_count <= 1 { + return Err(anyhow::anyhow!("at least one admin must be reserved!")); + } + } + } + } + + // 移除该用户 + let req = TableManagerReq::Remove { + table_name: USER_TREE_NAME.clone(), + key: username.as_bytes().to_owned(), + }; + if let Some(raft_table_route) = raft_table_route { + raft_table_route.request(req).await.ok(); + } } + Ok(UserManagerInnerCtx::None) } UserManagerReq::Query { name } => { diff --git a/src/user/permission.rs b/src/user/permission.rs index 29a893ae..ae288361 100644 --- a/src/user/permission.rs +++ b/src/user/permission.rs @@ -5,6 +5,7 @@ /// 2)http请求路径,由后端拦截器控制否支持请求; use std::{collections::HashSet, hash::Hash, sync::Arc}; + use crate::common::constant::{EMPTY_STR, HTTP_METHOD_ALL, HTTP_METHOD_GET}; pub enum Resource { @@ -361,17 +362,30 @@ pub enum UserRole { OldConsole, None, } +const MANAGER_VALUE: &'static str = "0"; +const DEVELOPER_VALUE: &'static str = "1"; +const VISITOR_VALUE: &'static str = "2"; +const NONE_VALUE: &'static str = ""; impl UserRole { pub fn new(role_value: &str) -> Self { match role_value { - "0" => Self::Manager, - "1" => Self::Developer, - "2" => Self::Visitor, + MANAGER_VALUE => Self::Manager, + DEVELOPER_VALUE => Self::Developer, + VISITOR_VALUE => Self::Visitor, _ => Self::None, } } + pub fn to_role_value(&self) -> &str { + match self { + Self::Manager => MANAGER_VALUE, + Self::Developer => DEVELOPER_VALUE, + Self::Visitor => VISITOR_VALUE, + _ => NONE_VALUE, + } + } + pub fn get_resources(&self) -> Vec<&GroupResource> { match &self { UserRole::Visitor => vec![R_VISITOR.as_ref()],