Skip to content

Commit

Permalink
feat(user): prevent deletion of the last admin user
Browse files Browse the repository at this point in the history
Added logic to check if the user being deleted is the only admin. If the user
is an admin, the system will further check if they are the last admin. If so,
the deletion is disallowed to ensure at least one admin remains in the system.

BREAKING CHANGE: Attempting to delete the last admin now returns an error
and prevents the operation.

Refs #34
  • Loading branch information
a981008 committed Oct 10, 2024
1 parent 179d614 commit a66a43d
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 a66a43d

Please sign in to comment.