mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +08:00
feat(user_role): Add update by lineage DB function (#5651)
This commit is contained in:
@ -41,47 +41,6 @@ impl UserRole {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_by_user_id_merchant_id(
|
|
||||||
conn: &PgPooledConn,
|
|
||||||
user_id: String,
|
|
||||||
merchant_id: id_type::MerchantId,
|
|
||||||
update: UserRoleUpdate,
|
|
||||||
version: UserRoleVersion,
|
|
||||||
) -> StorageResult<Self> {
|
|
||||||
generics::generic_update_with_unique_predicate_get_result::<
|
|
||||||
<Self as HasTable>::Table,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
>(
|
|
||||||
conn,
|
|
||||||
dsl::user_id
|
|
||||||
.eq(user_id)
|
|
||||||
.and(dsl::merchant_id.eq(merchant_id))
|
|
||||||
.and(dsl::version.eq(version)),
|
|
||||||
UserRoleUpdateInternal::from(update),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update_by_user_id_org_id(
|
|
||||||
conn: &PgPooledConn,
|
|
||||||
user_id: String,
|
|
||||||
org_id: id_type::OrganizationId,
|
|
||||||
update: UserRoleUpdate,
|
|
||||||
version: UserRoleVersion,
|
|
||||||
) -> StorageResult<Vec<Self>> {
|
|
||||||
generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
|
|
||||||
conn,
|
|
||||||
dsl::user_id
|
|
||||||
.eq(user_id)
|
|
||||||
.and(dsl::org_id.eq(org_id))
|
|
||||||
.and(dsl::version.eq(version)),
|
|
||||||
UserRoleUpdateInternal::from(update),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn list_by_user_id(
|
pub async fn list_by_user_id(
|
||||||
conn: &PgPooledConn,
|
conn: &PgPooledConn,
|
||||||
user_id: String,
|
user_id: String,
|
||||||
@ -147,6 +106,46 @@ impl UserRole {
|
|||||||
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(conn, predicate).await
|
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(conn, predicate).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn update_by_user_id_org_id_merchant_id_profile_id(
|
||||||
|
conn: &PgPooledConn,
|
||||||
|
user_id: String,
|
||||||
|
org_id: id_type::OrganizationId,
|
||||||
|
merchant_id: id_type::MerchantId,
|
||||||
|
profile_id: Option<String>,
|
||||||
|
update: UserRoleUpdate,
|
||||||
|
version: UserRoleVersion,
|
||||||
|
) -> StorageResult<Self> {
|
||||||
|
// Checking in user roles, for a user in token hierarchy, only one of the relation will be true, either org level, merchant level or profile level
|
||||||
|
// (org_id = ? && merchant_id = null && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = ?)
|
||||||
|
let check_lineage = dsl::org_id
|
||||||
|
.eq(org_id.clone())
|
||||||
|
.and(dsl::merchant_id.is_null().and(dsl::profile_id.is_null()))
|
||||||
|
.or(dsl::org_id.eq(org_id.clone()).and(
|
||||||
|
dsl::merchant_id
|
||||||
|
.eq(merchant_id.clone())
|
||||||
|
.and(dsl::profile_id.is_null()),
|
||||||
|
))
|
||||||
|
.or(dsl::org_id.eq(org_id).and(
|
||||||
|
dsl::merchant_id
|
||||||
|
.eq(merchant_id)
|
||||||
|
//TODO: In case of None, profile_id = NULL its unexpected behaviour, after V1 profile id will not be option
|
||||||
|
.and(dsl::profile_id.eq(profile_id)),
|
||||||
|
));
|
||||||
|
|
||||||
|
let predicate = dsl::user_id
|
||||||
|
.eq(user_id)
|
||||||
|
.and(check_lineage)
|
||||||
|
.and(dsl::version.eq(version));
|
||||||
|
|
||||||
|
generics::generic_update_with_unique_predicate_get_result::<
|
||||||
|
<Self as HasTable>::Table,
|
||||||
|
UserRoleUpdateInternal,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
>(conn, predicate, update.into())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn delete_by_user_id_org_id_merchant_id_profile_id(
|
pub async fn delete_by_user_id_org_id_merchant_id_profile_id(
|
||||||
conn: &PgPooledConn,
|
conn: &PgPooledConn,
|
||||||
user_id: String,
|
user_id: String,
|
||||||
|
|||||||
@ -51,6 +51,7 @@ pub struct UserRoleUpdateInternal {
|
|||||||
last_modified: PrimitiveDateTime,
|
last_modified: PrimitiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum UserRoleUpdate {
|
pub enum UserRoleUpdate {
|
||||||
UpdateStatus {
|
UpdateStatus {
|
||||||
status: enums::UserStatus,
|
status: enums::UserStatus,
|
||||||
|
|||||||
@ -594,19 +594,58 @@ pub async fn reset_password(
|
|||||||
.change_context(UserErrors::InternalServerError)?;
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
if let Some(inviter_merchant_id) = email_token.get_merchant_id() {
|
if let Some(inviter_merchant_id) = email_token.get_merchant_id() {
|
||||||
let update_status_result = state
|
let key_manager_state = &(&state).into();
|
||||||
|
|
||||||
|
let key_store = state
|
||||||
.store
|
.store
|
||||||
.update_user_role_by_user_id_merchant_id(
|
.get_merchant_key_store_by_merchant_id(
|
||||||
user.user_id.clone().as_str(),
|
key_manager_state,
|
||||||
inviter_merchant_id,
|
inviter_merchant_id,
|
||||||
|
&state.store.get_master_key().to_vec().into(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)
|
||||||
|
.attach_printable("merchant_key_store not found")?;
|
||||||
|
|
||||||
|
let merchant_account = state
|
||||||
|
.store
|
||||||
|
.find_merchant_account_by_merchant_id(
|
||||||
|
key_manager_state,
|
||||||
|
inviter_merchant_id,
|
||||||
|
&key_store,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)
|
||||||
|
.attach_printable("merchant_account not found")?;
|
||||||
|
|
||||||
|
let (update_v1_result, update_v2_result) =
|
||||||
|
utils::user_role::update_v1_and_v2_user_roles_in_db(
|
||||||
|
&state,
|
||||||
|
user.user_id.clone().as_str(),
|
||||||
|
&merchant_account.organization_id,
|
||||||
|
inviter_merchant_id,
|
||||||
|
None,
|
||||||
UserRoleUpdate::UpdateStatus {
|
UserRoleUpdate::UpdateStatus {
|
||||||
status: UserStatus::Active,
|
status: UserStatus::Active,
|
||||||
modified_by: user.user_id.clone(),
|
modified_by: user.user_id.clone(),
|
||||||
},
|
},
|
||||||
UserRoleVersion::V1,
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
logger::info!(?update_status_result);
|
|
||||||
|
if update_v1_result
|
||||||
|
.as_ref()
|
||||||
|
.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
|
|| update_v2_result
|
||||||
|
.as_ref()
|
||||||
|
.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
|
{
|
||||||
|
return Err(report!(UserErrors::InternalServerError));
|
||||||
|
}
|
||||||
|
|
||||||
|
if update_v1_result.is_err() && update_v2_result.is_err() {
|
||||||
|
return Err(report!(UserErrors::InvalidRoleOperation))
|
||||||
|
.attach_printable("User not found in the organization")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = auth::blacklist::insert_email_token_in_blacklist(&state, &token)
|
let _ = auth::blacklist::insert_email_token_in_blacklist(&state, &token)
|
||||||
@ -1014,19 +1053,53 @@ pub async fn accept_invite_from_email(
|
|||||||
.get_merchant_id()
|
.get_merchant_id()
|
||||||
.ok_or(UserErrors::InternalServerError)?;
|
.ok_or(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
let update_status_result = state
|
let key_manager_state = &(&state).into();
|
||||||
|
|
||||||
|
let key_store = state
|
||||||
.store
|
.store
|
||||||
.update_user_role_by_user_id_merchant_id(
|
.get_merchant_key_store_by_merchant_id(
|
||||||
user.get_user_id(),
|
key_manager_state,
|
||||||
merchant_id,
|
merchant_id,
|
||||||
UserRoleUpdate::UpdateStatus {
|
&state.store.get_master_key().to_vec().into(),
|
||||||
status: UserStatus::Active,
|
|
||||||
modified_by: user.get_user_id().to_string(),
|
|
||||||
},
|
|
||||||
UserRoleVersion::V1,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)?;
|
.change_context(UserErrors::InternalServerError)
|
||||||
|
.attach_printable("merchant_key_store not found")?;
|
||||||
|
|
||||||
|
let merchant_account = state
|
||||||
|
.store
|
||||||
|
.find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store)
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)
|
||||||
|
.attach_printable("merchant_account not found")?;
|
||||||
|
|
||||||
|
let (update_v1_result, update_v2_result) = utils::user_role::update_v1_and_v2_user_roles_in_db(
|
||||||
|
&state,
|
||||||
|
user.get_user_id(),
|
||||||
|
&merchant_account.organization_id,
|
||||||
|
merchant_id,
|
||||||
|
None,
|
||||||
|
UserRoleUpdate::UpdateStatus {
|
||||||
|
status: UserStatus::Active,
|
||||||
|
modified_by: user.get_user_id().to_string(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if update_v1_result
|
||||||
|
.as_ref()
|
||||||
|
.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
|
|| update_v2_result
|
||||||
|
.as_ref()
|
||||||
|
.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
|
{
|
||||||
|
return Err(report!(UserErrors::InternalServerError));
|
||||||
|
}
|
||||||
|
|
||||||
|
if update_v1_result.is_err() && update_v2_result.is_err() {
|
||||||
|
return Err(report!(UserErrors::InvalidRoleOperation))
|
||||||
|
.attach_printable("User not found in the organization")?;
|
||||||
|
}
|
||||||
|
|
||||||
let _ = auth::blacklist::insert_email_token_in_blacklist(&state, &token)
|
let _ = auth::blacklist::insert_email_token_in_blacklist(&state, &token)
|
||||||
.await
|
.await
|
||||||
@ -1039,21 +1112,18 @@ pub async fn accept_invite_from_email(
|
|||||||
.change_context(UserErrors::InternalServerError)?
|
.change_context(UserErrors::InternalServerError)?
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let token = utils::user::generate_jwt_auth_token_without_profile(
|
let user_role = user_from_db
|
||||||
&state,
|
.get_preferred_or_active_user_role_from_db(&state)
|
||||||
&user_from_db,
|
.await
|
||||||
&update_status_result,
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &update_status_result)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let response = utils::user::get_dashboard_entry_response(
|
let token =
|
||||||
&state,
|
utils::user::generate_jwt_auth_token_without_profile(&state, &user_from_db, &user_role)
|
||||||
user_from_db,
|
.await?;
|
||||||
update_status_result,
|
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
||||||
token.clone(),
|
|
||||||
)?;
|
let response =
|
||||||
|
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token.clone())?;
|
||||||
|
|
||||||
auth::cookies::set_cookie_response(response, token)
|
auth::cookies::set_cookie_response(response, token)
|
||||||
}
|
}
|
||||||
@ -1091,19 +1161,53 @@ pub async fn accept_invite_from_email_token_only_flow(
|
|||||||
.get_merchant_id()
|
.get_merchant_id()
|
||||||
.ok_or(UserErrors::LinkInvalid)?;
|
.ok_or(UserErrors::LinkInvalid)?;
|
||||||
|
|
||||||
let user_role = state
|
let key_manager_state = &(&state).into();
|
||||||
|
|
||||||
|
let key_store = state
|
||||||
.store
|
.store
|
||||||
.update_user_role_by_user_id_merchant_id(
|
.get_merchant_key_store_by_merchant_id(
|
||||||
user_from_db.get_user_id(),
|
key_manager_state,
|
||||||
merchant_id,
|
merchant_id,
|
||||||
UserRoleUpdate::UpdateStatus {
|
&state.store.get_master_key().to_vec().into(),
|
||||||
status: UserStatus::Active,
|
|
||||||
modified_by: user_from_db.get_user_id().to_string(),
|
|
||||||
},
|
|
||||||
UserRoleVersion::V1,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)?;
|
.change_context(UserErrors::InternalServerError)
|
||||||
|
.attach_printable("merchant_key_store not found")?;
|
||||||
|
|
||||||
|
let merchant_account = state
|
||||||
|
.store
|
||||||
|
.find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store)
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)
|
||||||
|
.attach_printable("merchant_account not found")?;
|
||||||
|
|
||||||
|
let (update_v1_result, update_v2_result) = utils::user_role::update_v1_and_v2_user_roles_in_db(
|
||||||
|
&state,
|
||||||
|
user_from_db.get_user_id(),
|
||||||
|
&merchant_account.organization_id,
|
||||||
|
merchant_id,
|
||||||
|
None,
|
||||||
|
UserRoleUpdate::UpdateStatus {
|
||||||
|
status: UserStatus::Active,
|
||||||
|
modified_by: user_from_db.get_user_id().to_owned(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if update_v1_result
|
||||||
|
.as_ref()
|
||||||
|
.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
|
|| update_v2_result
|
||||||
|
.as_ref()
|
||||||
|
.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
|
{
|
||||||
|
return Err(report!(UserErrors::InternalServerError));
|
||||||
|
}
|
||||||
|
|
||||||
|
if update_v1_result.is_err() && update_v2_result.is_err() {
|
||||||
|
return Err(report!(UserErrors::InvalidRoleOperation))
|
||||||
|
.attach_printable("User not found in the organization")?;
|
||||||
|
}
|
||||||
|
|
||||||
if !user_from_db.is_verified() {
|
if !user_from_db.is_verified() {
|
||||||
let _ = state
|
let _ = state
|
||||||
@ -1126,6 +1230,11 @@ pub async fn accept_invite_from_email_token_only_flow(
|
|||||||
)?;
|
)?;
|
||||||
let next_flow = current_flow.next(user_from_db.clone(), &state).await?;
|
let next_flow = current_flow.next(user_from_db.clone(), &state).await?;
|
||||||
|
|
||||||
|
let user_role = user_from_db
|
||||||
|
.get_preferred_or_active_user_role_from_db(&state)
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
let token = next_flow
|
let token = next_flow
|
||||||
.get_token_with_user_role(&state, &user_role)
|
.get_token_with_user_role(&state, &user_role)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@ -7,10 +7,8 @@ use diesel_models::{
|
|||||||
};
|
};
|
||||||
use error_stack::{report, ResultExt};
|
use error_stack::{report, ResultExt};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use router_env::logger;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consts,
|
|
||||||
core::errors::{StorageErrorExt, UserErrors, UserResponse},
|
core::errors::{StorageErrorExt, UserErrors, UserResponse},
|
||||||
routes::{app::ReqState, SessionState},
|
routes::{app::ReqState, SessionState},
|
||||||
services::{
|
services::{
|
||||||
@ -115,103 +113,155 @@ pub async fn update_user_role(
|
|||||||
.attach_printable("User Changing their own role");
|
.attach_printable("User Changing their own role");
|
||||||
}
|
}
|
||||||
|
|
||||||
let user_role_to_be_updated = user_to_be_updated
|
let updator_role = roles::RoleInfo::from_role_id(
|
||||||
.get_role_from_db_by_merchant_id(&state, &user_from_token.merchant_id)
|
|
||||||
.await
|
|
||||||
.to_not_found_response(UserErrors::InvalidRoleOperation)?;
|
|
||||||
|
|
||||||
let role_to_be_updated = roles::RoleInfo::from_role_id(
|
|
||||||
&state,
|
&state,
|
||||||
&user_role_to_be_updated.role_id,
|
&user_from_token.role_id,
|
||||||
&user_from_token.merchant_id,
|
&user_from_token.merchant_id,
|
||||||
&user_from_token.org_id,
|
&user_from_token.org_id,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)?;
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
if !role_to_be_updated.is_updatable() {
|
let mut is_updated = false;
|
||||||
return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
|
|
||||||
"User role cannot be updated from {}",
|
|
||||||
role_to_be_updated.get_role_id()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
state
|
let v2_user_role_to_be_updated = match state
|
||||||
.store
|
.store
|
||||||
.update_user_role_by_user_id_merchant_id(
|
.find_user_role_by_user_id_and_lineage(
|
||||||
user_to_be_updated.get_user_id(),
|
|
||||||
&user_role_to_be_updated
|
|
||||||
.merchant_id
|
|
||||||
.ok_or(UserErrors::InternalServerError)
|
|
||||||
.attach_printable("merchant_id not found in user_role")?,
|
|
||||||
UserRoleUpdate::UpdateRole {
|
|
||||||
role_id: req.role_id.clone(),
|
|
||||||
modified_by: user_from_token.user_id,
|
|
||||||
},
|
|
||||||
UserRoleVersion::V1,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.to_not_found_response(UserErrors::InvalidRoleOperation)
|
|
||||||
.attach_printable("User with given email is not found in the organization")?;
|
|
||||||
|
|
||||||
auth::blacklist::insert_user_in_blacklist(&state, user_to_be_updated.get_user_id()).await?;
|
|
||||||
|
|
||||||
Ok(ApplicationResponse::StatusOk)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn transfer_org_ownership(
|
|
||||||
state: SessionState,
|
|
||||||
user_from_token: auth::UserFromToken,
|
|
||||||
req: user_role_api::TransferOrgOwnershipRequest,
|
|
||||||
_req_state: ReqState,
|
|
||||||
) -> UserResponse<user_api::DashboardEntryResponse> {
|
|
||||||
if user_from_token.role_id != consts::user_role::ROLE_ID_ORGANIZATION_ADMIN {
|
|
||||||
return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
|
|
||||||
"role_id = {} is not org_admin",
|
|
||||||
user_from_token.role_id
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let user_to_be_updated =
|
|
||||||
utils::user::get_user_from_db_by_email(&state, domain::UserEmail::try_from(req.email)?)
|
|
||||||
.await
|
|
||||||
.to_not_found_response(UserErrors::InvalidRoleOperation)
|
|
||||||
.attach_printable("User not found in our records".to_string())?;
|
|
||||||
|
|
||||||
if user_from_token.user_id == user_to_be_updated.get_user_id() {
|
|
||||||
return Err(report!(UserErrors::InvalidRoleOperation))
|
|
||||||
.attach_printable("User transferring ownership to themselves".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
state
|
|
||||||
.store
|
|
||||||
.transfer_org_ownership_between_users(
|
|
||||||
&user_from_token.user_id,
|
|
||||||
user_to_be_updated.get_user_id(),
|
user_to_be_updated.get_user_id(),
|
||||||
&user_from_token.org_id,
|
&user_from_token.org_id,
|
||||||
UserRoleVersion::V1,
|
&user_from_token.merchant_id,
|
||||||
|
None,
|
||||||
|
UserRoleVersion::V2,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(user_role) => Some(user_role),
|
||||||
|
Err(e) => {
|
||||||
|
if e.current_context().is_db_not_found() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
return Err(UserErrors::InternalServerError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(user_role) = v2_user_role_to_be_updated {
|
||||||
|
let role_to_be_updated = roles::RoleInfo::from_role_id(
|
||||||
|
&state,
|
||||||
|
&user_role.role_id,
|
||||||
|
&user_from_token.merchant_id,
|
||||||
|
&user_from_token.org_id,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)?;
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
auth::blacklist::insert_user_in_blacklist(&state, user_to_be_updated.get_user_id()).await?;
|
if !role_to_be_updated.is_updatable() {
|
||||||
auth::blacklist::insert_user_in_blacklist(&state, &user_from_token.user_id).await?;
|
return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
|
||||||
|
"User role cannot be updated from {}",
|
||||||
|
role_to_be_updated.get_role_id()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let user_from_db = user_from_token.get_user_from_db(&state).await?;
|
if updator_role.get_entity_type() < role_to_be_updated.get_entity_type() {
|
||||||
let user_role = user_from_db
|
return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
|
||||||
.get_role_from_db_by_merchant_id(&state, &user_from_token.merchant_id)
|
"Invalid operation, update requestor = {} cannot update target = {}",
|
||||||
|
updator_role.get_entity_type(),
|
||||||
|
role_to_be_updated.get_entity_type()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
state
|
||||||
|
.store
|
||||||
|
.update_user_role_by_user_id_and_lineage(
|
||||||
|
user_to_be_updated.get_user_id(),
|
||||||
|
&user_from_token.org_id,
|
||||||
|
&user_from_token.merchant_id,
|
||||||
|
None,
|
||||||
|
UserRoleUpdate::UpdateRole {
|
||||||
|
role_id: req.role_id.clone(),
|
||||||
|
modified_by: user_from_token.user_id.clone(),
|
||||||
|
},
|
||||||
|
UserRoleVersion::V2,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
|
is_updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let v1_user_role_to_be_updated = match state
|
||||||
|
.store
|
||||||
|
.find_user_role_by_user_id_and_lineage(
|
||||||
|
user_to_be_updated.get_user_id(),
|
||||||
|
&user_from_token.org_id,
|
||||||
|
&user_from_token.merchant_id,
|
||||||
|
None,
|
||||||
|
UserRoleVersion::V1,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.to_not_found_response(UserErrors::InvalidRoleOperation)?;
|
{
|
||||||
|
Ok(user_role) => Some(user_role),
|
||||||
|
Err(e) => {
|
||||||
|
if e.current_context().is_db_not_found() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
return Err(UserErrors::InternalServerError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
if let Some(user_role) = v1_user_role_to_be_updated {
|
||||||
|
let role_to_be_updated = roles::RoleInfo::from_role_id(
|
||||||
|
&state,
|
||||||
|
&user_role.role_id,
|
||||||
|
&user_from_token.merchant_id,
|
||||||
|
&user_from_token.org_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
let token =
|
if !role_to_be_updated.is_updatable() {
|
||||||
utils::user::generate_jwt_auth_token_without_profile(&state, &user_from_db, &user_role)
|
return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
|
||||||
.await?;
|
"User role cannot be updated from {}",
|
||||||
let response =
|
role_to_be_updated.get_role_id()
|
||||||
utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token.clone())?;
|
));
|
||||||
|
}
|
||||||
|
|
||||||
auth::cookies::set_cookie_response(response, token)
|
if updator_role.get_entity_type() < role_to_be_updated.get_entity_type() {
|
||||||
|
return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
|
||||||
|
"Invalid operation, update requestor = {} cannot update target = {}",
|
||||||
|
updator_role.get_entity_type(),
|
||||||
|
role_to_be_updated.get_entity_type()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
state
|
||||||
|
.store
|
||||||
|
.update_user_role_by_user_id_and_lineage(
|
||||||
|
user_to_be_updated.get_user_id(),
|
||||||
|
&user_from_token.org_id,
|
||||||
|
&user_from_token.merchant_id,
|
||||||
|
None,
|
||||||
|
UserRoleUpdate::UpdateRole {
|
||||||
|
role_id: req.role_id.clone(),
|
||||||
|
modified_by: user_from_token.user_id,
|
||||||
|
},
|
||||||
|
UserRoleVersion::V1,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
|
is_updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_updated {
|
||||||
|
return Err(report!(UserErrors::InvalidRoleOperation))
|
||||||
|
.attach_printable("User with given email is not found in the organization")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
auth::blacklist::insert_user_in_blacklist(&state, user_to_be_updated.get_user_id()).await?;
|
||||||
|
|
||||||
|
Ok(ApplicationResponse::StatusOk)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn accept_invitation(
|
pub async fn accept_invitation(
|
||||||
@ -219,30 +269,43 @@ pub async fn accept_invitation(
|
|||||||
user_token: auth::UserFromToken,
|
user_token: auth::UserFromToken,
|
||||||
req: user_role_api::AcceptInvitationRequest,
|
req: user_role_api::AcceptInvitationRequest,
|
||||||
) -> UserResponse<()> {
|
) -> UserResponse<()> {
|
||||||
futures::future::join_all(req.merchant_ids.iter().map(|merchant_id| async {
|
let merchant_accounts = state
|
||||||
state
|
.store
|
||||||
.store
|
.list_multiple_merchant_accounts(&(&state).into(), req.merchant_ids)
|
||||||
.update_user_role_by_user_id_merchant_id(
|
.await
|
||||||
user_token.user_id.as_str(),
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
merchant_id,
|
|
||||||
UserRoleUpdate::UpdateStatus {
|
let update_result =
|
||||||
status: UserStatus::Active,
|
futures::future::join_all(merchant_accounts.iter().map(|merchant_account| async {
|
||||||
modified_by: user_token.user_id.clone(),
|
let (update_v1_result, update_v2_result) =
|
||||||
},
|
utils::user_role::update_v1_and_v2_user_roles_in_db(
|
||||||
UserRoleVersion::V1,
|
&state,
|
||||||
)
|
user_token.user_id.as_str(),
|
||||||
.await
|
&merchant_account.organization_id,
|
||||||
.map_err(|e| {
|
merchant_account.get_id(),
|
||||||
logger::error!("Error while accepting invitation {e:?}");
|
None,
|
||||||
})
|
UserRoleUpdate::UpdateStatus {
|
||||||
.ok()
|
status: UserStatus::Active,
|
||||||
}))
|
modified_by: user_token.user_id.clone(),
|
||||||
.await
|
},
|
||||||
.into_iter()
|
)
|
||||||
.reduce(Option::or)
|
.await;
|
||||||
.flatten()
|
|
||||||
.ok_or(UserErrors::MerchantIdNotFound.into())
|
if update_v1_result.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
.map(|_| ApplicationResponse::StatusOk)
|
|| update_v2_result.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
|
{
|
||||||
|
Err(report!(UserErrors::InternalServerError))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if update_result.iter().all(Result::is_err) {
|
||||||
|
return Err(UserErrors::MerchantIdNotFound.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ApplicationResponse::StatusOk)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn merchant_select(
|
pub async fn merchant_select(
|
||||||
@ -250,38 +313,55 @@ pub async fn merchant_select(
|
|||||||
user_token: auth::UserFromSinglePurposeToken,
|
user_token: auth::UserFromSinglePurposeToken,
|
||||||
req: user_role_api::MerchantSelectRequest,
|
req: user_role_api::MerchantSelectRequest,
|
||||||
) -> UserResponse<user_api::TokenOrPayloadResponse<user_api::DashboardEntryResponse>> {
|
) -> UserResponse<user_api::TokenOrPayloadResponse<user_api::DashboardEntryResponse>> {
|
||||||
let user_role = futures::future::join_all(req.merchant_ids.iter().map(|merchant_id| async {
|
let merchant_accounts = state
|
||||||
state
|
.store
|
||||||
.store
|
.list_multiple_merchant_accounts(&(&state).into(), req.merchant_ids)
|
||||||
.update_user_role_by_user_id_merchant_id(
|
.await
|
||||||
user_token.user_id.as_str(),
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
merchant_id,
|
|
||||||
UserRoleUpdate::UpdateStatus {
|
let update_result =
|
||||||
status: UserStatus::Active,
|
futures::future::join_all(merchant_accounts.iter().map(|merchant_account| async {
|
||||||
modified_by: user_token.user_id.clone(),
|
let (update_v1_result, update_v2_result) =
|
||||||
},
|
utils::user_role::update_v1_and_v2_user_roles_in_db(
|
||||||
UserRoleVersion::V1,
|
&state,
|
||||||
)
|
user_token.user_id.as_str(),
|
||||||
.await
|
&merchant_account.organization_id,
|
||||||
.map_err(|e| {
|
merchant_account.get_id(),
|
||||||
logger::error!("Error while accepting invitation {e:?}");
|
None,
|
||||||
})
|
UserRoleUpdate::UpdateStatus {
|
||||||
.ok()
|
status: UserStatus::Active,
|
||||||
}))
|
modified_by: user_token.user_id.clone(),
|
||||||
.await
|
},
|
||||||
.into_iter()
|
)
|
||||||
.reduce(Option::or)
|
.await;
|
||||||
.flatten()
|
|
||||||
.ok_or(UserErrors::MerchantIdNotFound)?;
|
if update_v1_result.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
|
|| update_v2_result.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
|
{
|
||||||
|
Err(report!(UserErrors::InternalServerError))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if update_result.iter().all(Result::is_err) {
|
||||||
|
return Err(UserErrors::MerchantIdNotFound.into());
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(true) = req.need_dashboard_entry_response {
|
if let Some(true) = req.need_dashboard_entry_response {
|
||||||
let user_from_db = state
|
let user_from_db: domain::UserFromStorage = state
|
||||||
.global_store
|
.global_store
|
||||||
.find_user_by_id(user_token.user_id.as_str())
|
.find_user_by_id(user_token.user_id.as_str())
|
||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)?
|
.change_context(UserErrors::InternalServerError)?
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
let user_role = user_from_db
|
||||||
|
.get_preferred_or_active_user_role_from_db(&state)
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
|
||||||
|
|
||||||
let token =
|
let token =
|
||||||
@ -307,29 +387,41 @@ pub async fn merchant_select_token_only_flow(
|
|||||||
user_token: auth::UserFromSinglePurposeToken,
|
user_token: auth::UserFromSinglePurposeToken,
|
||||||
req: user_role_api::MerchantSelectRequest,
|
req: user_role_api::MerchantSelectRequest,
|
||||||
) -> UserResponse<user_api::TokenOrPayloadResponse<user_api::DashboardEntryResponse>> {
|
) -> UserResponse<user_api::TokenOrPayloadResponse<user_api::DashboardEntryResponse>> {
|
||||||
let user_role = futures::future::join_all(req.merchant_ids.iter().map(|merchant_id| async {
|
let merchant_accounts = state
|
||||||
state
|
.store
|
||||||
.store
|
.list_multiple_merchant_accounts(&(&state).into(), req.merchant_ids)
|
||||||
.update_user_role_by_user_id_merchant_id(
|
.await
|
||||||
user_token.user_id.as_str(),
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
merchant_id,
|
|
||||||
UserRoleUpdate::UpdateStatus {
|
let update_result =
|
||||||
status: UserStatus::Active,
|
futures::future::join_all(merchant_accounts.iter().map(|merchant_account| async {
|
||||||
modified_by: user_token.user_id.clone(),
|
let (update_v1_result, update_v2_result) =
|
||||||
},
|
utils::user_role::update_v1_and_v2_user_roles_in_db(
|
||||||
UserRoleVersion::V1,
|
&state,
|
||||||
)
|
user_token.user_id.as_str(),
|
||||||
.await
|
&merchant_account.organization_id,
|
||||||
.map_err(|e| {
|
merchant_account.get_id(),
|
||||||
logger::error!("Error while accepting invitation {e:?}");
|
None,
|
||||||
})
|
UserRoleUpdate::UpdateStatus {
|
||||||
.ok()
|
status: UserStatus::Active,
|
||||||
}))
|
modified_by: user_token.user_id.clone(),
|
||||||
.await
|
},
|
||||||
.into_iter()
|
)
|
||||||
.reduce(Option::or)
|
.await;
|
||||||
.flatten()
|
|
||||||
.ok_or(UserErrors::MerchantIdNotFound)?;
|
if update_v1_result.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
|
|| update_v2_result.is_err_and(|err| !err.current_context().is_db_not_found())
|
||||||
|
{
|
||||||
|
Err(report!(UserErrors::InternalServerError))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if update_result.iter().all(Result::is_err) {
|
||||||
|
return Err(UserErrors::MerchantIdNotFound.into());
|
||||||
|
}
|
||||||
|
|
||||||
let user_from_db: domain::UserFromStorage = state
|
let user_from_db: domain::UserFromStorage = state
|
||||||
.global_store
|
.global_store
|
||||||
@ -338,6 +430,11 @@ pub async fn merchant_select_token_only_flow(
|
|||||||
.change_context(UserErrors::InternalServerError)?
|
.change_context(UserErrors::InternalServerError)?
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
let user_role = user_from_db
|
||||||
|
.get_preferred_or_active_user_role_from_db(&state)
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)?;
|
||||||
|
|
||||||
let current_flow =
|
let current_flow =
|
||||||
domain::CurrentFlow::new(user_token, domain::SPTFlow::MerchantSelect.into())?;
|
domain::CurrentFlow::new(user_token, domain::SPTFlow::MerchantSelect.into())?;
|
||||||
let next_flow = current_flow.next(user_from_db.clone(), &state).await?;
|
let next_flow = current_flow.next(user_from_db.clone(), &state).await?;
|
||||||
|
|||||||
@ -2793,30 +2793,6 @@ impl UserRoleInterface for KafkaStore {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_user_role_by_user_id_merchant_id(
|
|
||||||
&self,
|
|
||||||
user_id: &str,
|
|
||||||
merchant_id: &id_type::MerchantId,
|
|
||||||
update: user_storage::UserRoleUpdate,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<user_storage::UserRole, errors::StorageError> {
|
|
||||||
self.diesel_store
|
|
||||||
.update_user_role_by_user_id_merchant_id(user_id, merchant_id, update, version)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update_user_roles_by_user_id_org_id(
|
|
||||||
&self,
|
|
||||||
user_id: &str,
|
|
||||||
org_id: &id_type::OrganizationId,
|
|
||||||
update: user_storage::UserRoleUpdate,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<Vec<user_storage::UserRole>, errors::StorageError> {
|
|
||||||
self.diesel_store
|
|
||||||
.update_user_roles_by_user_id_org_id(user_id, org_id, update, version)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn list_user_roles_by_user_id(
|
async fn list_user_roles_by_user_id(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
@ -2846,6 +2822,27 @@ impl UserRoleInterface for KafkaStore {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_user_role_by_user_id_and_lineage(
|
||||||
|
&self,
|
||||||
|
user_id: &str,
|
||||||
|
org_id: &id_type::OrganizationId,
|
||||||
|
merchant_id: &id_type::MerchantId,
|
||||||
|
profile_id: Option<&String>,
|
||||||
|
update: user_storage::UserRoleUpdate,
|
||||||
|
version: enums::UserRoleVersion,
|
||||||
|
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||||
|
self.diesel_store
|
||||||
|
.update_user_role_by_user_id_and_lineage(
|
||||||
|
user_id,
|
||||||
|
org_id,
|
||||||
|
merchant_id,
|
||||||
|
profile_id,
|
||||||
|
update,
|
||||||
|
version,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn delete_user_role_by_user_id_and_lineage(
|
async fn delete_user_role_by_user_id_and_lineage(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
@ -2865,18 +2862,6 @@ impl UserRoleInterface for KafkaStore {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn transfer_org_ownership_between_users(
|
|
||||||
&self,
|
|
||||||
from_user_id: &str,
|
|
||||||
to_user_id: &str,
|
|
||||||
org_id: &id_type::OrganizationId,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<(), errors::StorageError> {
|
|
||||||
self.diesel_store
|
|
||||||
.transfer_org_ownership_between_users(from_user_id, to_user_id, org_id, version)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn list_user_roles_by_merchant_id(
|
async fn list_user_roles_by_merchant_id(
|
||||||
&self,
|
&self,
|
||||||
merchant_id: &id_type::MerchantId,
|
merchant_id: &id_type::MerchantId,
|
||||||
|
|||||||
@ -1,6 +1,3 @@
|
|||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use async_bb8_diesel::AsyncConnection;
|
|
||||||
use common_utils::id_type;
|
use common_utils::id_type;
|
||||||
use diesel_models::{enums, user_role as storage};
|
use diesel_models::{enums, user_role as storage};
|
||||||
use error_stack::{report, ResultExt};
|
use error_stack::{report, ResultExt};
|
||||||
@ -8,7 +5,7 @@ use router_env::{instrument, tracing};
|
|||||||
|
|
||||||
use super::MockDb;
|
use super::MockDb;
|
||||||
use crate::{
|
use crate::{
|
||||||
connection, consts,
|
connection,
|
||||||
core::errors::{self, CustomResult},
|
core::errors::{self, CustomResult},
|
||||||
services::Store,
|
services::Store,
|
||||||
};
|
};
|
||||||
@ -33,22 +30,6 @@ pub trait UserRoleInterface {
|
|||||||
version: enums::UserRoleVersion,
|
version: enums::UserRoleVersion,
|
||||||
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
||||||
|
|
||||||
async fn update_user_role_by_user_id_merchant_id(
|
|
||||||
&self,
|
|
||||||
user_id: &str,
|
|
||||||
merchant_id: &id_type::MerchantId,
|
|
||||||
update: storage::UserRoleUpdate,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
|
||||||
|
|
||||||
async fn update_user_roles_by_user_id_org_id(
|
|
||||||
&self,
|
|
||||||
user_id: &str,
|
|
||||||
org_id: &id_type::OrganizationId,
|
|
||||||
update: storage::UserRoleUpdate,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError>;
|
|
||||||
|
|
||||||
async fn list_user_roles_by_user_id(
|
async fn list_user_roles_by_user_id(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
@ -70,6 +51,16 @@ pub trait UserRoleInterface {
|
|||||||
version: enums::UserRoleVersion,
|
version: enums::UserRoleVersion,
|
||||||
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
||||||
|
|
||||||
|
async fn update_user_role_by_user_id_and_lineage(
|
||||||
|
&self,
|
||||||
|
user_id: &str,
|
||||||
|
org_id: &id_type::OrganizationId,
|
||||||
|
merchant_id: &id_type::MerchantId,
|
||||||
|
profile_id: Option<&String>,
|
||||||
|
update: storage::UserRoleUpdate,
|
||||||
|
version: enums::UserRoleVersion,
|
||||||
|
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
||||||
|
|
||||||
async fn delete_user_role_by_user_id_and_lineage(
|
async fn delete_user_role_by_user_id_and_lineage(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
@ -78,14 +69,6 @@ pub trait UserRoleInterface {
|
|||||||
profile_id: Option<&String>,
|
profile_id: Option<&String>,
|
||||||
version: enums::UserRoleVersion,
|
version: enums::UserRoleVersion,
|
||||||
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
||||||
|
|
||||||
async fn transfer_org_ownership_between_users(
|
|
||||||
&self,
|
|
||||||
from_user_id: &str,
|
|
||||||
to_user_id: &str,
|
|
||||||
org_id: &id_type::OrganizationId,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<(), errors::StorageError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@ -132,46 +115,6 @@ impl UserRoleInterface for Store {
|
|||||||
.map_err(|error| report!(errors::StorageError::from(error)))
|
.map_err(|error| report!(errors::StorageError::from(error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
|
||||||
async fn update_user_role_by_user_id_merchant_id(
|
|
||||||
&self,
|
|
||||||
user_id: &str,
|
|
||||||
merchant_id: &id_type::MerchantId,
|
|
||||||
update: storage::UserRoleUpdate,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
|
||||||
let conn = connection::pg_connection_write(self).await?;
|
|
||||||
storage::UserRole::update_by_user_id_merchant_id(
|
|
||||||
&conn,
|
|
||||||
user_id.to_owned(),
|
|
||||||
merchant_id.to_owned(),
|
|
||||||
update,
|
|
||||||
version,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|error| report!(errors::StorageError::from(error)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
|
||||||
async fn update_user_roles_by_user_id_org_id(
|
|
||||||
&self,
|
|
||||||
user_id: &str,
|
|
||||||
org_id: &id_type::OrganizationId,
|
|
||||||
update: storage::UserRoleUpdate,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
|
||||||
let conn = connection::pg_connection_write(self).await?;
|
|
||||||
storage::UserRole::update_by_user_id_org_id(
|
|
||||||
&conn,
|
|
||||||
user_id.to_owned(),
|
|
||||||
org_id.to_owned(),
|
|
||||||
update,
|
|
||||||
version,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|error| report!(errors::StorageError::from(error)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
async fn list_user_roles_by_user_id(
|
async fn list_user_roles_by_user_id(
|
||||||
&self,
|
&self,
|
||||||
@ -218,6 +161,30 @@ impl UserRoleInterface for Store {
|
|||||||
.map_err(|error| report!(errors::StorageError::from(error)))
|
.map_err(|error| report!(errors::StorageError::from(error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
async fn update_user_role_by_user_id_and_lineage(
|
||||||
|
&self,
|
||||||
|
user_id: &str,
|
||||||
|
org_id: &id_type::OrganizationId,
|
||||||
|
merchant_id: &id_type::MerchantId,
|
||||||
|
profile_id: Option<&String>,
|
||||||
|
update: storage::UserRoleUpdate,
|
||||||
|
version: enums::UserRoleVersion,
|
||||||
|
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||||
|
let conn = connection::pg_connection_write(self).await?;
|
||||||
|
storage::UserRole::update_by_user_id_org_id_merchant_id_profile_id(
|
||||||
|
&conn,
|
||||||
|
user_id.to_owned(),
|
||||||
|
org_id.to_owned(),
|
||||||
|
merchant_id.to_owned(),
|
||||||
|
profile_id.cloned(),
|
||||||
|
update,
|
||||||
|
version,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|error| report!(errors::StorageError::from(error)))
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
async fn delete_user_role_by_user_id_and_lineage(
|
async fn delete_user_role_by_user_id_and_lineage(
|
||||||
&self,
|
&self,
|
||||||
@ -239,100 +206,6 @@ impl UserRoleInterface for Store {
|
|||||||
.await
|
.await
|
||||||
.map_err(|error| report!(errors::StorageError::from(error)))
|
.map_err(|error| report!(errors::StorageError::from(error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
|
||||||
async fn transfer_org_ownership_between_users(
|
|
||||||
&self,
|
|
||||||
from_user_id: &str,
|
|
||||||
to_user_id: &str,
|
|
||||||
org_id: &id_type::OrganizationId,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<(), errors::StorageError> {
|
|
||||||
let conn = connection::pg_connection_write(self)
|
|
||||||
.await
|
|
||||||
.change_context(errors::StorageError::DatabaseConnectionError)?;
|
|
||||||
|
|
||||||
conn.transaction_async(|conn| async move {
|
|
||||||
let old_org_admin_user_roles = storage::UserRole::update_by_user_id_org_id(
|
|
||||||
&conn,
|
|
||||||
from_user_id.to_owned(),
|
|
||||||
org_id.to_owned(),
|
|
||||||
storage::UserRoleUpdate::UpdateRole {
|
|
||||||
role_id: consts::user_role::ROLE_ID_MERCHANT_ADMIN.to_string(),
|
|
||||||
modified_by: from_user_id.to_owned(),
|
|
||||||
},
|
|
||||||
version,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|e| *e.current_context())?;
|
|
||||||
|
|
||||||
let new_org_admin_user_roles = storage::UserRole::update_by_user_id_org_id(
|
|
||||||
&conn,
|
|
||||||
to_user_id.to_owned(),
|
|
||||||
org_id.to_owned(),
|
|
||||||
storage::UserRoleUpdate::UpdateRole {
|
|
||||||
role_id: consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
|
|
||||||
modified_by: from_user_id.to_owned(),
|
|
||||||
},
|
|
||||||
version,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|e| *e.current_context())?;
|
|
||||||
|
|
||||||
let new_org_admin_merchant_ids = new_org_admin_user_roles
|
|
||||||
.iter()
|
|
||||||
.map(|user_role| {
|
|
||||||
user_role
|
|
||||||
.merchant_id
|
|
||||||
.to_owned()
|
|
||||||
.ok_or(errors::DatabaseError::Others)
|
|
||||||
})
|
|
||||||
.collect::<Result<HashSet<_>, _>>()?;
|
|
||||||
|
|
||||||
let now = common_utils::date_time::now();
|
|
||||||
|
|
||||||
let mut missing_new_user_roles = Vec::new();
|
|
||||||
|
|
||||||
for old_role in old_org_admin_user_roles {
|
|
||||||
let Some(old_role_merchant_id) = &old_role.merchant_id else {
|
|
||||||
return Err(errors::DatabaseError::Others);
|
|
||||||
};
|
|
||||||
if !new_org_admin_merchant_ids.contains(old_role_merchant_id) {
|
|
||||||
missing_new_user_roles.push(storage::UserRoleNew {
|
|
||||||
user_id: to_user_id.to_string(),
|
|
||||||
merchant_id: Some(old_role_merchant_id.to_owned()),
|
|
||||||
role_id: consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
|
|
||||||
org_id: Some(org_id.to_owned()),
|
|
||||||
status: enums::UserStatus::Active,
|
|
||||||
created_by: from_user_id.to_string(),
|
|
||||||
last_modified_by: from_user_id.to_string(),
|
|
||||||
created_at: now,
|
|
||||||
last_modified: now,
|
|
||||||
profile_id: None,
|
|
||||||
entity_id: None,
|
|
||||||
entity_type: None,
|
|
||||||
version: enums::UserRoleVersion::V1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
futures::future::try_join_all(missing_new_user_roles.into_iter().map(
|
|
||||||
|user_role| async {
|
|
||||||
user_role
|
|
||||||
.insert(&conn)
|
|
||||||
.await
|
|
||||||
.map_err(|e| *e.current_context())
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok::<_, errors::DatabaseError>(())
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.map_err(|error| report!(errors::StorageError::from(report!(error))))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@ -422,185 +295,6 @@ impl UserRoleInterface for MockDb {
|
|||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_user_role_by_user_id_merchant_id(
|
|
||||||
&self,
|
|
||||||
user_id: &str,
|
|
||||||
merchant_id: &id_type::MerchantId,
|
|
||||||
update: storage::UserRoleUpdate,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
|
||||||
let mut user_roles = self.user_roles.lock().await;
|
|
||||||
|
|
||||||
for user_role in user_roles.iter_mut() {
|
|
||||||
let Some(user_role_merchant_id) = &user_role.merchant_id else {
|
|
||||||
return Err(errors::StorageError::DatabaseError(
|
|
||||||
report!(errors::DatabaseError::Others)
|
|
||||||
.attach_printable("merchant_id not found for user_role"),
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
};
|
|
||||||
if user_role.user_id == user_id
|
|
||||||
&& user_role_merchant_id == merchant_id
|
|
||||||
&& user_role.version == version
|
|
||||||
{
|
|
||||||
match &update {
|
|
||||||
storage::UserRoleUpdate::UpdateRole {
|
|
||||||
role_id,
|
|
||||||
modified_by,
|
|
||||||
} => {
|
|
||||||
user_role.role_id = role_id.to_string();
|
|
||||||
user_role.last_modified_by = modified_by.to_string();
|
|
||||||
}
|
|
||||||
storage::UserRoleUpdate::UpdateStatus {
|
|
||||||
status,
|
|
||||||
modified_by,
|
|
||||||
} => {
|
|
||||||
user_role.status = *status;
|
|
||||||
user_role.last_modified_by = modified_by.to_string();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return Ok(user_role.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(errors::StorageError::ValueNotFound(format!(
|
|
||||||
"No user role available for user_id = {user_id} and merchant_id = {merchant_id:?}"
|
|
||||||
))
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update_user_roles_by_user_id_org_id(
|
|
||||||
&self,
|
|
||||||
user_id: &str,
|
|
||||||
org_id: &id_type::OrganizationId,
|
|
||||||
update: storage::UserRoleUpdate,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
|
||||||
let mut user_roles = self.user_roles.lock().await;
|
|
||||||
let mut updated_user_roles = Vec::new();
|
|
||||||
for user_role in user_roles.iter_mut() {
|
|
||||||
let Some(user_role_org_id) = &user_role.org_id else {
|
|
||||||
return Err(errors::StorageError::DatabaseError(
|
|
||||||
report!(errors::DatabaseError::Others)
|
|
||||||
.attach_printable("org_id not found for user_role"),
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
};
|
|
||||||
if user_role.user_id == user_id
|
|
||||||
&& user_role_org_id == org_id
|
|
||||||
&& user_role.version == version
|
|
||||||
{
|
|
||||||
match &update {
|
|
||||||
storage::UserRoleUpdate::UpdateRole {
|
|
||||||
role_id,
|
|
||||||
modified_by,
|
|
||||||
} => {
|
|
||||||
user_role.role_id = role_id.to_string();
|
|
||||||
user_role.last_modified_by = modified_by.to_string();
|
|
||||||
}
|
|
||||||
storage::UserRoleUpdate::UpdateStatus {
|
|
||||||
status,
|
|
||||||
modified_by,
|
|
||||||
} => {
|
|
||||||
status.clone_into(&mut user_role.status);
|
|
||||||
modified_by.clone_into(&mut user_role.last_modified_by);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updated_user_roles.push(user_role.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if updated_user_roles.is_empty() {
|
|
||||||
Err(errors::StorageError::ValueNotFound(format!(
|
|
||||||
"No user role available for user_id = {user_id} and org_id = {org_id:?}"
|
|
||||||
))
|
|
||||||
.into())
|
|
||||||
} else {
|
|
||||||
Ok(updated_user_roles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn transfer_org_ownership_between_users(
|
|
||||||
&self,
|
|
||||||
from_user_id: &str,
|
|
||||||
to_user_id: &str,
|
|
||||||
org_id: &id_type::OrganizationId,
|
|
||||||
version: enums::UserRoleVersion,
|
|
||||||
) -> CustomResult<(), errors::StorageError> {
|
|
||||||
let old_org_admin_user_roles = self
|
|
||||||
.update_user_roles_by_user_id_org_id(
|
|
||||||
from_user_id,
|
|
||||||
org_id,
|
|
||||||
storage::UserRoleUpdate::UpdateRole {
|
|
||||||
role_id: consts::user_role::ROLE_ID_MERCHANT_ADMIN.to_string(),
|
|
||||||
modified_by: from_user_id.to_string(),
|
|
||||||
},
|
|
||||||
version,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let new_org_admin_user_roles = self
|
|
||||||
.update_user_roles_by_user_id_org_id(
|
|
||||||
to_user_id,
|
|
||||||
org_id,
|
|
||||||
storage::UserRoleUpdate::UpdateRole {
|
|
||||||
role_id: consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
|
|
||||||
modified_by: from_user_id.to_string(),
|
|
||||||
},
|
|
||||||
version,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let new_org_admin_merchant_ids = new_org_admin_user_roles
|
|
||||||
.iter()
|
|
||||||
.map(|user_role| {
|
|
||||||
user_role.merchant_id.to_owned().ok_or(report!(
|
|
||||||
errors::StorageError::DatabaseError(
|
|
||||||
report!(errors::DatabaseError::Others)
|
|
||||||
.attach_printable("merchant_id not found for user_role"),
|
|
||||||
)
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.collect::<Result<HashSet<_>, _>>()?;
|
|
||||||
|
|
||||||
let now = common_utils::date_time::now();
|
|
||||||
let mut missing_new_user_roles = Vec::new();
|
|
||||||
|
|
||||||
for old_roles in old_org_admin_user_roles {
|
|
||||||
let Some(merchant_id) = &old_roles.merchant_id else {
|
|
||||||
return Err(errors::StorageError::DatabaseError(
|
|
||||||
report!(errors::DatabaseError::Others)
|
|
||||||
.attach_printable("merchant id not found for user role"),
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
};
|
|
||||||
if !new_org_admin_merchant_ids.contains(merchant_id) {
|
|
||||||
let new_user_role = storage::UserRoleNew {
|
|
||||||
user_id: to_user_id.to_string(),
|
|
||||||
merchant_id: Some(merchant_id.to_owned()),
|
|
||||||
role_id: consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
|
|
||||||
org_id: Some(org_id.to_owned()),
|
|
||||||
status: enums::UserStatus::Active,
|
|
||||||
created_by: from_user_id.to_string(),
|
|
||||||
last_modified_by: from_user_id.to_string(),
|
|
||||||
created_at: now,
|
|
||||||
last_modified: now,
|
|
||||||
profile_id: None,
|
|
||||||
entity_id: None,
|
|
||||||
entity_type: None,
|
|
||||||
version: enums::UserRoleVersion::V1,
|
|
||||||
};
|
|
||||||
|
|
||||||
missing_new_user_roles.push(new_user_role);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for user_role in missing_new_user_roles {
|
|
||||||
self.insert_user_role(user_role).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn list_user_roles_by_user_id(
|
async fn list_user_roles_by_user_id(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
@ -684,6 +378,60 @@ impl UserRoleInterface for MockDb {
|
|||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_user_role_by_user_id_and_lineage(
|
||||||
|
&self,
|
||||||
|
user_id: &str,
|
||||||
|
org_id: &id_type::OrganizationId,
|
||||||
|
merchant_id: &id_type::MerchantId,
|
||||||
|
profile_id: Option<&String>,
|
||||||
|
update: storage::UserRoleUpdate,
|
||||||
|
version: enums::UserRoleVersion,
|
||||||
|
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||||
|
let mut user_roles = self.user_roles.lock().await;
|
||||||
|
|
||||||
|
for user_role in user_roles.iter_mut() {
|
||||||
|
let org_level_check = user_role.org_id.as_ref() == Some(org_id)
|
||||||
|
&& user_role.merchant_id.is_none()
|
||||||
|
&& user_role.profile_id.is_none();
|
||||||
|
|
||||||
|
let merchant_level_check = user_role.org_id.as_ref() == Some(org_id)
|
||||||
|
&& user_role.merchant_id.as_ref() == Some(merchant_id)
|
||||||
|
&& user_role.profile_id.is_none();
|
||||||
|
|
||||||
|
let profile_level_check = user_role.org_id.as_ref() == Some(org_id)
|
||||||
|
&& user_role.merchant_id.as_ref() == Some(merchant_id)
|
||||||
|
&& user_role.profile_id.as_ref() == profile_id;
|
||||||
|
|
||||||
|
// Check if the user role matches the conditions and the version matches
|
||||||
|
if user_role.user_id == user_id
|
||||||
|
&& (org_level_check || merchant_level_check || profile_level_check)
|
||||||
|
&& user_role.version == version
|
||||||
|
{
|
||||||
|
match &update {
|
||||||
|
storage::UserRoleUpdate::UpdateRole {
|
||||||
|
role_id,
|
||||||
|
modified_by,
|
||||||
|
} => {
|
||||||
|
user_role.role_id = role_id.to_string();
|
||||||
|
user_role.last_modified_by = modified_by.to_string();
|
||||||
|
}
|
||||||
|
storage::UserRoleUpdate::UpdateStatus {
|
||||||
|
status,
|
||||||
|
modified_by,
|
||||||
|
} => {
|
||||||
|
user_role.status = *status;
|
||||||
|
user_role.last_modified_by = modified_by.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(user_role.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(
|
||||||
|
errors::StorageError::ValueNotFound("Cannot find user role to update".to_string())
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async fn delete_user_role_by_user_id_and_lineage(
|
async fn delete_user_role_by_user_id_and_lineage(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
|
|||||||
@ -1720,10 +1720,6 @@ impl User {
|
|||||||
.route(web::put().to(accept_invitation)),
|
.route(web::put().to(accept_invitation)),
|
||||||
)
|
)
|
||||||
.service(web::resource("/update_role").route(web::post().to(update_user_role)))
|
.service(web::resource("/update_role").route(web::post().to(update_user_role)))
|
||||||
.service(
|
|
||||||
web::resource("/transfer_ownership")
|
|
||||||
.route(web::post().to(transfer_org_ownership)),
|
|
||||||
)
|
|
||||||
.service(web::resource("/delete").route(web::delete().to(delete_user_role))),
|
.service(web::resource("/delete").route(web::delete().to(delete_user_role))),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -186,25 +186,6 @@ pub async fn update_user_role(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn transfer_org_ownership(
|
|
||||||
state: web::Data<AppState>,
|
|
||||||
req: HttpRequest,
|
|
||||||
json_payload: web::Json<user_role_api::TransferOrgOwnershipRequest>,
|
|
||||||
) -> HttpResponse {
|
|
||||||
let flow = Flow::TransferOrgOwnership;
|
|
||||||
let payload = json_payload.into_inner();
|
|
||||||
Box::pin(api::server_wrap(
|
|
||||||
flow,
|
|
||||||
state.clone(),
|
|
||||||
&req,
|
|
||||||
payload,
|
|
||||||
user_role_core::transfer_org_ownership,
|
|
||||||
&auth::JWTAuth(Permission::UsersWrite),
|
|
||||||
api_locking::LockAction::NotApplicable,
|
|
||||||
))
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn accept_invitation(
|
pub async fn accept_invitation(
|
||||||
state: web::Data<AppState>,
|
state: web::Data<AppState>,
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
|
|||||||
@ -3,9 +3,13 @@ use std::collections::HashSet;
|
|||||||
use api_models::user_role as user_role_api;
|
use api_models::user_role as user_role_api;
|
||||||
use common_enums::PermissionGroup;
|
use common_enums::PermissionGroup;
|
||||||
use common_utils::id_type;
|
use common_utils::id_type;
|
||||||
use diesel_models::user_role::UserRole;
|
use diesel_models::{
|
||||||
use error_stack::{report, ResultExt};
|
enums::UserRoleVersion,
|
||||||
|
user_role::{UserRole, UserRoleUpdate},
|
||||||
|
};
|
||||||
|
use error_stack::{report, Report, ResultExt};
|
||||||
use router_env::logger;
|
use router_env::logger;
|
||||||
|
use storage_impl::errors::StorageError;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consts,
|
consts,
|
||||||
@ -168,7 +172,53 @@ pub async fn get_multiple_role_info_for_user_roles(
|
|||||||
.await
|
.await
|
||||||
.to_not_found_response(UserErrors::InternalServerError)
|
.to_not_found_response(UserErrors::InternalServerError)
|
||||||
.attach_printable("Role for user role doesn't exist")?;
|
.attach_printable("Role for user role doesn't exist")?;
|
||||||
Ok::<_, error_stack::Report<UserErrors>>(role)
|
Ok::<_, Report<UserErrors>>(role)
|
||||||
}))
|
}))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn update_v1_and_v2_user_roles_in_db(
|
||||||
|
state: &SessionState,
|
||||||
|
user_id: &str,
|
||||||
|
org_id: &id_type::OrganizationId,
|
||||||
|
merchant_id: &id_type::MerchantId,
|
||||||
|
profile_id: Option<&String>,
|
||||||
|
update: UserRoleUpdate,
|
||||||
|
) -> (
|
||||||
|
Result<UserRole, Report<StorageError>>,
|
||||||
|
Result<UserRole, Report<StorageError>>,
|
||||||
|
) {
|
||||||
|
let updated_v1_role = state
|
||||||
|
.store
|
||||||
|
.update_user_role_by_user_id_and_lineage(
|
||||||
|
user_id,
|
||||||
|
org_id,
|
||||||
|
merchant_id,
|
||||||
|
profile_id,
|
||||||
|
update.clone(),
|
||||||
|
UserRoleVersion::V1,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
logger::error!("Error updating user_role {e:?}");
|
||||||
|
e
|
||||||
|
});
|
||||||
|
|
||||||
|
let updated_v2_role = state
|
||||||
|
.store
|
||||||
|
.update_user_role_by_user_id_and_lineage(
|
||||||
|
user_id,
|
||||||
|
org_id,
|
||||||
|
merchant_id,
|
||||||
|
profile_id,
|
||||||
|
update,
|
||||||
|
UserRoleVersion::V2,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
logger::error!("Error updating user_role {e:?}");
|
||||||
|
e
|
||||||
|
});
|
||||||
|
|
||||||
|
(updated_v1_role, updated_v2_role)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user