Skip to content

Commit

Permalink
Merge pull request #145 from a981008/master
Browse files Browse the repository at this point in the history
feat(user): prevent deletion of the last admin user
  • Loading branch information
heqingpan authored Oct 13, 2024
2 parents a38ab5b + 67e1287 commit dc8b895
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 16 deletions.
25 changes: 22 additions & 3 deletions src/console/user_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)))
}
}
}
58 changes: 48 additions & 10 deletions src/user/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -140,9 +141,9 @@ impl Inject for UserManager {
}
};
}
.into_actor(act)
.map(|_, _, _| {})
.spawn(ctx);
.into_actor(act)
.map(|_, _, _| {})
.spawn(ctx);
});
}
}
Expand Down Expand Up @@ -327,7 +328,7 @@ impl Handler<UserManagerReq> 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!(
Expand All @@ -347,13 +348,50 @@ impl Handler<UserManagerReq> 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 } => {
Expand Down
20 changes: 17 additions & 3 deletions src/user/permission.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()],
Expand Down

0 comments on commit dc8b895

Please sign in to comment.