diff --git a/src/cli/basic/cli.rs b/src/cli/basic/cli.rs index f452afd1dc..260b99afc4 100644 --- a/src/cli/basic/cli.rs +++ b/src/cli/basic/cli.rs @@ -170,15 +170,16 @@ pub async fn cli() -> Result { // init infra, create data dir & tables let cfg = config::get_config(); infra::init().await.expect("infra init failed"); + db::org_users::cache().await?; match name.as_str() { "reset" => { let component = command.get_one::("component").unwrap(); match component.as_str() { "root" => { - let _ = users::update_user( + let ret = users::update_user( meta::organization::DEFAULT_ORG, cfg.auth.root_user_email.as_str(), - false, + meta::user::UserUpdateMode::CliUpdate, cfg.auth.root_user_email.as_str(), meta::user::UpdateUser { change_password: true, @@ -198,6 +199,12 @@ pub async fn cli() -> Result { }, ) .await?; + if !ret.status().is_success() { + return Err(anyhow::anyhow!( + "reset root user failed, error: {:?}", + ret.body() + )); + } } "user" => { db::user::reset().await?; diff --git a/src/common/meta/user.rs b/src/common/meta/user.rs index 43cce656d3..9797e54972 100644 --- a/src/common/meta/user.rs +++ b/src/common/meta/user.rs @@ -127,6 +127,23 @@ pub struct UpdateUser { pub token: Option, } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +pub enum UserUpdateMode { + OtherUpdate, + SelfUpdate, + CliUpdate, +} + +impl UserUpdateMode { + pub fn is_self_update(&self) -> bool { + self == &UserUpdateMode::SelfUpdate + } + + pub fn is_cli_update(&self) -> bool { + self == &UserUpdateMode::CliUpdate + } +} + pub fn get_default_user_org() -> UserOrg { UserOrg { name: "".to_string(), diff --git a/src/handler/http/request/service_accounts/mod.rs b/src/handler/http/request/service_accounts/mod.rs index 71ecbf1006..67f0ccfb06 100644 --- a/src/handler/http/request/service_accounts/mod.rs +++ b/src/handler/http/request/service_accounts/mod.rs @@ -29,7 +29,7 @@ use crate::{ self, http::HttpResponse as MetaHttpResponse, service_account::{APIToken, ServiceAccountRequest, UpdateServiceAccountRequest}, - user::{UpdateUser, UserRequest}, + user::{UpdateUser, UserRequest, UserUpdateMode}, }, utils::auth::UserEmail, }, @@ -226,7 +226,14 @@ pub async fn update( }; let initiator_id = &user_email.user_id; - users::update_user(&org_id, &email_id, false, initiator_id, user).await + users::update_user( + &org_id, + &email_id, + UserUpdateMode::OtherUpdate, + initiator_id, + user, + ) + .await } /// RemoveServiceAccount diff --git a/src/handler/http/request/users/mod.rs b/src/handler/http/request/users/mod.rs index 9f5a30287b..4a12382ce8 100644 --- a/src/handler/http/request/users/mod.rs +++ b/src/handler/http/request/users/mod.rs @@ -45,7 +45,7 @@ use crate::{ self, user::{ AuthTokens, PostUserRequest, RolesResponse, SignInResponse, SignInUser, UpdateUser, - UserOrgRole, UserRequest, UserRoleRequest, get_roles, + UserOrgRole, UserRequest, UserRoleRequest, UserUpdateMode, get_roles, }, }, utils::auth::{UserEmail, generate_presigned_url, is_valid_email}, @@ -245,8 +245,12 @@ pub async fn update( }); } let initiator_id = &user_email.user_id; - let self_update = user_email.user_id.eq(&email_id); - users::update_user(&org_id, &email_id, self_update, initiator_id, user).await + let update_mode = if user_email.user_id.eq(&email_id) { + UserUpdateMode::SelfUpdate + } else { + UserUpdateMode::OtherUpdate + }; + users::update_user(&org_id, &email_id, update_mode, initiator_id, user).await } /// AddUserToOrganization diff --git a/src/service/users.rs b/src/service/users.rs index 7849899ed2..cfa5a817a5 100644 --- a/src/service/users.rs +++ b/src/service/users.rs @@ -44,7 +44,8 @@ use crate::{ http::HttpResponse as MetaHttpResponse, organization::{DEFAULT_ORG, OrgRoleMapping}, user::{ - UpdateUser, UserList, UserOrgRole, UserRequest, UserResponse, get_default_user_org, + UpdateUser, UserList, UserOrgRole, UserRequest, UserResponse, UserUpdateMode, + get_default_user_org, }, }, utils::auth::{get_hash, get_role, is_root_user, is_valid_email}, @@ -246,9 +247,9 @@ pub async fn create_new_user(mut db_user: DBUser) -> Result<(), anyhow::Error> { pub async fn update_user( org_id: &str, email: &str, - self_update: bool, + update_mode: UserUpdateMode, initiator_id: &str, - user: UpdateUser, + mut user: UpdateUser, ) -> Result { let mut allow_password_update = false; if !is_valid_email(email) { @@ -258,9 +259,16 @@ pub async fn update_user( ))); } let is_email_root = is_root_user(email); + #[cfg(not(feature = "enterprise"))] + if is_email_root { + user.role = Some(crate::common::meta::user::UserRoleRequest { + role: UserRole::Root.to_string(), + custom: None, + }); + } // Only root user can update root user - if is_email_root && !self_update { + if is_email_root && !update_mode.is_self_update() && !update_mode.is_cli_update() { return Ok(HttpResponse::BadRequest().json(MetaHttpResponse::message( http::StatusCode::BAD_REQUEST, "Root user cannot be updated", @@ -307,7 +315,7 @@ pub async fn update_user( "Updates not allowed with external users, please update with source system", ))); } - if !self_update { + if !update_mode.is_self_update() { if is_root_user(initiator_id) { allow_password_update = true } else { @@ -325,14 +333,20 @@ pub async fn update_user( } } } - if !self_update && local_user.role.eq(&UserRole::Root) { + if local_user.role.eq(&UserRole::Root) + && !update_mode.is_self_update() + && !update_mode.is_cli_update() + { return Ok(HttpResponse::BadRequest().json(MetaHttpResponse::message( http::StatusCode::BAD_REQUEST, "Only root user can update its details", ))); } new_user = local_user.clone(); - if self_update && user.old_password.is_some() && user.new_password.is_some() { + if update_mode.is_self_update() + && user.old_password.is_some() + && user.new_password.is_some() + { if local_user.password.eq(&get_hash( &user.clone().old_password.unwrap(), &local_user.salt, @@ -350,10 +364,12 @@ pub async fn update_user( message, ))); } - } else if self_update && user.new_password.is_some() && user.old_password.is_none() + } else if update_mode.is_self_update() + && user.new_password.is_some() + && user.old_password.is_none() { message = "Please provide existing password"; - } else if !self_update + } else if !update_mode.is_self_update() && allow_password_update && user.new_password.is_some() && !local_user.is_external @@ -378,7 +394,7 @@ pub async fn update_user( } if user.role.is_some() && !local_user.is_external - && (!self_update + && (!update_mode.is_self_update() || (local_user.role.eq(&UserRole::Admin) // Editor can update other's roles, but viewer can update only self || local_user.role.eq(&UserRole::Editor) @@ -393,7 +409,7 @@ pub async fn update_user( new_role = Some(new_user.role.clone()); if local_user.role.eq(&UserRole::Root) && new_user.role.ne(&UserRole::Root) { message = "Root user role cannot be changed"; - } else if self_update && local_user.role < new_user.role { + } else if update_mode.is_self_update() && local_user.role < new_user.role { message = "Self role cannot be upgraded"; } else if local_user.role.ne(&new_user.role) { #[cfg(feature = "enterprise")] @@ -1380,7 +1396,7 @@ mod tests { let resp = update_user( "dummy", "user2@example.com", - true, + UserUpdateMode::SelfUpdate, "user2@example.com", UpdateUser { token: Some("new_token".to_string()), @@ -1402,7 +1418,7 @@ mod tests { let resp = update_user( "dummy", "user2@example.com", - false, + UserUpdateMode::OtherUpdate, "admin@zo.dev", UpdateUser { token: Some("new_token".to_string()), @@ -1635,7 +1651,7 @@ mod tests { let resp = update_user( "dummy", "invalid-email", - false, + UserUpdateMode::OtherUpdate, "admin@zo.dev", UpdateUser { token: None, @@ -1655,7 +1671,7 @@ mod tests { let resp = update_user( "dummy", "nonexistent@example.com", - false, + UserUpdateMode::OtherUpdate, "admin@zo.dev", UpdateUser { token: None,