diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 126df9269d..067ef1000a 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -3144,7 +3144,6 @@ pub enum ApiVersion { #[strum(serialize_all = "snake_case")] #[serde(rename_all = "snake_case")] pub enum EntityType { - Internal = 3, Organization = 2, Merchant = 1, Profile = 0, diff --git a/crates/diesel_models/src/user_role.rs b/crates/diesel_models/src/user_role.rs index 29be4d62ae..71caa41dea 100644 --- a/crates/diesel_models/src/user_role.rs +++ b/crates/diesel_models/src/user_role.rs @@ -33,11 +33,6 @@ impl UserRole { let org_id = self.org_id.clone()?.get_string_repr().to_string(); Some((org_id, EntityType::Organization)) } - (enums::UserRoleVersion::V1, consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER) - | (enums::UserRoleVersion::V1, consts::ROLE_ID_INTERNAL_ADMIN) => { - let merchant_id = self.merchant_id.clone()?.get_string_repr().to_string(); - Some((merchant_id, EntityType::Internal)) - } (enums::UserRoleVersion::V1, _) => { let merchant_id = self.merchant_id.clone()?.get_string_repr().to_string(); Some((merchant_id, EntityType::Merchant)) diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 4c79748697..f36ecc1811 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -650,7 +650,6 @@ async fn handle_existing_user_invitation( }; let _user_role = match role_info.get_entity_type() { - EntityType::Internal => return Err(UserErrors::InvalidRoleId.into()), EntityType::Organization => return Err(UserErrors::InvalidRoleId.into()), EntityType::Merchant => { user_role @@ -682,7 +681,6 @@ async fn handle_existing_user_invitation( { let invitee_email = domain::UserEmail::from_pii_email(request.email.clone())?; let entity = match role_info.get_entity_type() { - EntityType::Internal => return Err(UserErrors::InvalidRoleId.into()), EntityType::Organization => return Err(UserErrors::InvalidRoleId.into()), EntityType::Merchant => email_types::Entity { entity_id: user_from_token.merchant_id.get_string_repr().to_owned(), @@ -769,7 +767,6 @@ async fn handle_new_user_invitation( }; let _user_role = match role_info.get_entity_type() { - EntityType::Internal => return Err(UserErrors::InvalidRoleId.into()), EntityType::Organization => return Err(UserErrors::InvalidRoleId.into()), EntityType::Merchant => { user_role @@ -805,7 +802,6 @@ async fn handle_new_user_invitation( let _ = req_state.clone(); let invitee_email = domain::UserEmail::from_pii_email(request.email.clone())?; let entity = match role_info.get_entity_type() { - EntityType::Internal => return Err(UserErrors::InvalidRoleId.into()), EntityType::Organization => return Err(UserErrors::InvalidRoleId.into()), EntityType::Merchant => email_types::Entity { entity_id: user_from_token.merchant_id.get_string_repr().to_owned(), @@ -1089,15 +1085,13 @@ pub async fn create_internal_user( } })?; + let internal_merchant_id = common_utils::id_type::MerchantId::get_internal_user_merchant_id( + consts::user_role::INTERNAL_USER_MERCHANT_ID, + ); + let internal_merchant = state .store - .find_merchant_account_by_merchant_id( - key_manager_state, - &common_utils::id_type::MerchantId::get_internal_user_merchant_id( - consts::user_role::INTERNAL_USER_MERCHANT_ID, - ), - &key_store, - ) + .find_merchant_account_by_merchant_id(key_manager_state, &internal_merchant_id, &key_store) .await .map_err(|e| { if e.current_context().is_db_not_found() { @@ -1130,8 +1124,9 @@ pub async fn create_internal_user( common_utils::consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), UserStatus::Active, ) - .add_entity(domain::InternalLevel { + .add_entity(domain::MerchantLevel { org_id: internal_merchant.organization_id, + merchant_id: internal_merchant_id, }) .insert_in_v1_and_v2(&state) .await @@ -1443,6 +1438,13 @@ pub async fn list_user_roles_details( .to_not_found_response(UserErrors::InternalServerError) .attach_printable("Failed to fetch role info")?; + if requestor_role_info.is_internal() { + return Err(UserErrors::InvalidRoleOperationWithMessage( + "Internal roles are not allowed for this operation".to_string(), + ) + .into()); + } + let user_roles_set = state .store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { @@ -1517,12 +1519,6 @@ pub async fn list_user_roles_details( merchant.push(merchant_id.clone()); merchant_profile.push((merchant_id, profile_id)) } - EntityType::Internal => { - return Err(UserErrors::InvalidRoleOperationWithMessage( - "Internal roles are not allowed for this operation".to_string(), - ) - .into()); - } EntityType::Organization => (), }; @@ -1609,11 +1605,6 @@ pub async fn list_user_roles_details( .ok_or(UserErrors::InternalServerError)?; let (merchant, profile) = match entity_type { - EntityType::Internal => { - return Err(UserErrors::InvalidRoleOperationWithMessage( - "Internal roles are not allowed for this operation".to_string(), - )); - } EntityType::Organization => (None, None), EntityType::Merchant => { let merchant_id = &user_role @@ -2623,26 +2614,30 @@ pub async fn list_orgs_for_user( .await .change_context(UserErrors::InternalServerError)?; - let orgs = match role_info.get_entity_type() { - EntityType::Internal => return Err(UserErrors::InvalidRoleOperation.into()), - EntityType::Organization | EntityType::Merchant | EntityType::Profile => state - .store - .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { - user_id: user_from_token.user_id.as_str(), - org_id: None, - merchant_id: None, - profile_id: None, - entity_id: None, - version: None, - status: Some(UserStatus::Active), - limit: None, - }) - .await - .change_context(UserErrors::InternalServerError)? - .into_iter() - .filter_map(|user_role| user_role.org_id) - .collect::>(), - }; + if role_info.is_internal() { + return Err(UserErrors::InvalidRoleOperationWithMessage( + "Internal roles are not allowed for this operation".to_string(), + ) + .into()); + } + + let orgs = state + .store + .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { + user_id: user_from_token.user_id.as_str(), + org_id: None, + merchant_id: None, + profile_id: None, + entity_id: None, + version: None, + status: Some(UserStatus::Active), + limit: None, + }) + .await + .change_context(UserErrors::InternalServerError)? + .into_iter() + .filter_map(|user_role| user_role.org_id) + .collect::>(); let resp = futures::future::try_join_all( orgs.iter() @@ -2676,8 +2671,16 @@ pub async fn list_merchants_for_user_in_org( ) .await .change_context(UserErrors::InternalServerError)?; + + if role_info.is_internal() { + return Err(UserErrors::InvalidRoleOperationWithMessage( + "Internal roles are not allowed for this operation".to_string(), + ) + .into()); + } + let merchant_accounts = match role_info.get_entity_type() { - EntityType::Organization | EntityType::Internal => state + EntityType::Organization => state .store .list_merchant_accounts_by_organization_id(&(&state).into(), &user_from_token.org_id) .await @@ -2752,7 +2755,7 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account( .await .change_context(UserErrors::InternalServerError)?; let profiles = match role_info.get_entity_type() { - EntityType::Organization | EntityType::Merchant | EntityType::Internal => state + EntityType::Organization | EntityType::Merchant => state .store .list_profile_by_merchant_id( key_manager_state, @@ -2831,7 +2834,7 @@ pub async fn switch_org_for_user( .change_context(UserErrors::InternalServerError) .attach_printable("Failed to retrieve role information")?; - if role_info.get_entity_type() == EntityType::Internal { + if role_info.is_internal() { return Err(UserErrors::InvalidRoleOperationWithMessage( "Org switching not allowed for Internal role".to_string(), ) @@ -2910,126 +2913,54 @@ pub async fn switch_merchant_for_user_in_org( .change_context(UserErrors::InternalServerError) .attach_printable("Failed to retrieve role information")?; - let (org_id, merchant_id, profile_id, role_id) = match role_info.get_entity_type() { - EntityType::Internal => { - let merchant_key_store = state - .store - .get_merchant_key_store_by_merchant_id( - key_manager_state, - &request.merchant_id, - &state.store.get_master_key().to_vec().into(), - ) - .await - .to_not_found_response(UserErrors::MerchantIdNotFound)?; - - let merchant_account = state - .store - .find_merchant_account_by_merchant_id( - key_manager_state, - &request.merchant_id, - &merchant_key_store, - ) - .await - .to_not_found_response(UserErrors::MerchantIdNotFound)?; - - let profile_id = state - .store - .list_profile_by_merchant_id( - key_manager_state, - &merchant_key_store, - &request.merchant_id, - ) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to list business profiles by merchant_id")? - .pop() - .ok_or(UserErrors::InternalServerError) - .attach_printable("No business profile found for the given merchant_id")? - .get_id() - .to_owned(); - - ( - merchant_account.organization_id, - request.merchant_id, - profile_id, - user_from_token.role_id.clone(), + // Check if the role is internal and handle separately + let (org_id, merchant_id, profile_id, role_id) = if role_info.is_internal() { + let merchant_key_store = state + .store + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &request.merchant_id, + &state.store.get_master_key().to_vec().into(), ) - } + .await + .to_not_found_response(UserErrors::MerchantIdNotFound)?; - EntityType::Organization => { - let merchant_key_store = state - .store - .get_merchant_key_store_by_merchant_id( - key_manager_state, - &request.merchant_id, - &state.store.get_master_key().to_vec().into(), - ) - .await - .to_not_found_response(UserErrors::MerchantIdNotFound)?; - - let merchant_id = state - .store - .find_merchant_account_by_merchant_id( - key_manager_state, - &request.merchant_id, - &merchant_key_store, - ) - .await - .change_context(UserErrors::MerchantIdNotFound)? - .organization_id - .eq(&user_from_token.org_id) - .then(|| request.merchant_id.clone()) - .ok_or_else(|| { - UserErrors::InvalidRoleOperationWithMessage( - "No such merchant_id found for the user in the org".to_string(), - ) - })?; - - let profile_id = state - .store - .list_profile_by_merchant_id(key_manager_state, &merchant_key_store, &merchant_id) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to list business profiles by merchant_id")? - .pop() - .ok_or(UserErrors::InternalServerError) - .attach_printable("No business profile found for the merchant_id")? - .get_id() - .to_owned(); - ( - user_from_token.org_id.clone(), - merchant_id, - profile_id, - user_from_token.role_id.clone(), + let merchant_account = state + .store + .find_merchant_account_by_merchant_id( + key_manager_state, + &request.merchant_id, + &merchant_key_store, ) - } + .await + .to_not_found_response(UserErrors::MerchantIdNotFound)?; - EntityType::Merchant | EntityType::Profile => { - let user_role = state - .store - .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { - user_id: &user_from_token.user_id, - org_id: Some(&user_from_token.org_id), - merchant_id: Some(&request.merchant_id), - profile_id: None, - entity_id: None, - version: None, - status: Some(UserStatus::Active), - limit: Some(1), - }) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable( - "Failed to list user roles for the given user_id, org_id and merchant_id", - )? - .pop() - .ok_or(UserErrors::InvalidRoleOperationWithMessage( - "No user role associated with the requested merchant_id".to_string(), - ))?; + let profile_id = state + .store + .list_profile_by_merchant_id( + key_manager_state, + &merchant_key_store, + &request.merchant_id, + ) + .await + .change_context(UserErrors::InternalServerError) + .attach_printable("Failed to list business profiles by merchant_id")? + .pop() + .ok_or(UserErrors::InternalServerError) + .attach_printable("No business profile found for the given merchant_id")? + .get_id() + .to_owned(); - let profile_id = if let Some(profile_id) = &user_role.profile_id { - profile_id.clone() - } else { + ( + merchant_account.organization_id, + request.merchant_id, + profile_id, + user_from_token.role_id.clone(), + ) + } else { + // Match based on the other entity types + match role_info.get_entity_type() { + EntityType::Organization => { let merchant_key_store = state .store .get_merchant_key_store_by_merchant_id( @@ -3038,31 +2969,82 @@ pub async fn switch_merchant_for_user_in_org( &state.store.get_master_key().to_vec().into(), ) .await - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to retrieve merchant key store by merchant_id")?; + .to_not_found_response(UserErrors::MerchantIdNotFound)?; - state + let merchant_id = state + .store + .find_merchant_account_by_merchant_id( + key_manager_state, + &request.merchant_id, + &merchant_key_store, + ) + .await + .change_context(UserErrors::MerchantIdNotFound)? + .organization_id + .eq(&user_from_token.org_id) + .then(|| request.merchant_id.clone()) + .ok_or_else(|| { + UserErrors::InvalidRoleOperationWithMessage( + "No such merchant_id found for the user in the org".to_string(), + ) + })?; + + let profile_id = state .store .list_profile_by_merchant_id( key_manager_state, &merchant_key_store, - &request.merchant_id, + &merchant_id, ) .await .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to list business profiles for the given merchant_id")? + .attach_printable("Failed to list business profiles by merchant_id")? .pop() .ok_or(UserErrors::InternalServerError) - .attach_printable("No business profile found for the given merchant_id")? + .attach_printable("No business profile found for the merchant_id")? .get_id() - .to_owned() - }; - ( - user_from_token.org_id, - request.merchant_id, - profile_id, - user_role.role_id, - ) + .to_owned(); + ( + user_from_token.org_id.clone(), + merchant_id, + profile_id, + user_from_token.role_id.clone(), + ) + } + + EntityType::Merchant | EntityType::Profile => { + let user_role = state + .store + .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { + user_id: &user_from_token.user_id, + org_id: Some(&user_from_token.org_id), + merchant_id: Some(&request.merchant_id), + profile_id: None, + entity_id: None, + version: None, + status: Some(UserStatus::Active), + limit: Some(1), + }) + .await + .change_context(UserErrors::InternalServerError) + .attach_printable( + "Failed to list user roles for the given user_id, org_id and merchant_id", + )? + .pop() + .ok_or(UserErrors::InvalidRoleOperationWithMessage( + "No user role associated with the requested merchant_id".to_string(), + ))?; + + let (merchant_id, profile_id) = + utils::user_role::get_single_merchant_id_and_profile_id(&state, &user_role) + .await?; + ( + user_from_token.org_id, + merchant_id, + profile_id, + user_role.role_id, + ) + } } }; @@ -3116,7 +3098,7 @@ pub async fn switch_profile_for_user_in_org_and_merchant( .attach_printable("Failed to retrieve role information")?; let (profile_id, role_id) = match role_info.get_entity_type() { - EntityType::Internal | EntityType::Organization | EntityType::Merchant => { + EntityType::Organization | EntityType::Merchant => { let merchant_key_store = state .store .get_merchant_key_store_by_merchant_id( diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs index fd9c8c8903..b841f88eca 100644 --- a/crates/router/src/core/user_role.rs +++ b/crates/router/src/core/user_role.rs @@ -734,7 +734,6 @@ pub async fn list_users_in_lineage( ) .await? } - EntityType::Internal => HashSet::new(), }; let mut email_map = state @@ -859,10 +858,13 @@ pub async fn list_invitations_for_user( .clone() .ok_or(UserErrors::InternalServerError)?, )), - EntityType::Internal => return Err(report!(UserErrors::InternalServerError)), } - Ok((org_ids, merchant_ids, profile_ids_with_merchant_ids)) + Ok::<_, error_stack::Report>(( + org_ids, + merchant_ids, + profile_ids_with_merchant_ids, + )) }, )?; @@ -953,7 +955,6 @@ pub async fn list_invitations_for_user( .as_ref() .map(|profile_id| profile_name_map.get(profile_id).cloned()) .ok_or(UserErrors::InternalServerError)?, - EntityType::Internal => return Err(report!(UserErrors::InternalServerError)), }; Ok(user_role_api::ListInvitationForUserResponse { diff --git a/crates/router/src/core/user_role/role.rs b/crates/router/src/core/user_role/role.rs index ffc18214c2..d5d655b4a8 100644 --- a/crates/router/src/core/user_role/role.rs +++ b/crates/router/src/core/user_role/role.rs @@ -224,6 +224,13 @@ pub async fn list_roles_with_info( .await .attach_printable("Invalid role_id in JWT")?; + if user_role_info.is_internal() { + return Err(UserErrors::InvalidRoleOperationWithMessage( + "Internal roles are not allowed for this operation".to_string(), + ) + .into()); + } + let mut role_info_vec = PREDEFINED_ROLES .iter() .map(|(_, role_info)| role_info.clone()) @@ -256,12 +263,6 @@ pub async fn list_roles_with_info( .attach_printable("Failed to get roles")?, // TODO: Populate this from Db function when support for profile id and profile level custom roles is added EntityType::Profile => Vec::new(), - EntityType::Internal => { - return Err(UserErrors::InvalidRoleOperationWithMessage( - "Internal roles are not allowed for this operation".to_string(), - ) - .into()); - } }; role_info_vec.extend(custom_roles.into_iter().map(roles::RoleInfo::from)); @@ -336,13 +337,6 @@ pub async fn list_roles_at_entity_level( .attach_printable("Failed to get roles")?, // TODO: Populate this from Db function when support for profile id and profile level custom roles is added EntityType::Profile => Vec::new(), - - EntityType::Internal => { - return Err(UserErrors::InvalidRoleOperationWithMessage( - "Internal roles are not allowed for this operation".to_string(), - ) - .into()); - } }; role_info_vec.extend(custom_roles.into_iter().map(roles::RoleInfo::from)); diff --git a/crates/router/src/services/authorization/roles/predefined_roles.rs b/crates/router/src/services/authorization/roles/predefined_roles.rs index df42d41298..b271abd1ef 100644 --- a/crates/router/src/services/authorization/roles/predefined_roles.rs +++ b/crates/router/src/services/authorization/roles/predefined_roles.rs @@ -31,7 +31,7 @@ pub static PREDEFINED_ROLES: Lazy> = Lazy::new(| role_id: common_utils::consts::ROLE_ID_INTERNAL_ADMIN.to_string(), role_name: "internal_admin".to_string(), scope: RoleScope::Organization, - entity_type: EntityType::Internal, + entity_type: EntityType::Merchant, is_invitable: false, is_deletable: false, is_updatable: false, @@ -52,7 +52,7 @@ pub static PREDEFINED_ROLES: Lazy> = Lazy::new(| role_id: common_utils::consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), role_name: "internal_view_only".to_string(), scope: RoleScope::Organization, - entity_type: EntityType::Internal, + entity_type: EntityType::Merchant, is_invitable: false, is_deletable: false, is_updatable: false, diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 725cefb8c4..19d682aeef 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -1136,11 +1136,6 @@ pub struct ProfileLevel { pub profile_id: id_type::ProfileId, } -#[derive(Clone)] -pub struct InternalLevel { - pub org_id: id_type::OrganizationId, -} - #[derive(Clone)] pub struct NewUserRole { pub user_id: String, @@ -1316,29 +1311,6 @@ impl NewUserRole { } } -impl NewUserRole { - pub async fn insert_in_v1_and_v2(self, state: &SessionState) -> UserResult { - let entity = self.entity.clone(); - let internal_merchant_id = id_type::MerchantId::get_internal_user_merchant_id( - consts::user_role::INTERNAL_USER_MERCHANT_ID, - ); - - let new_v1_role = self - .clone() - .convert_to_new_v1_role(entity.org_id.clone(), internal_merchant_id.clone()); - - let new_v2_role = self.convert_to_new_v2_role(EntityInfo { - org_id: entity.org_id.clone(), - merchant_id: Some(internal_merchant_id.clone()), - profile_id: None, - entity_id: internal_merchant_id.get_string_repr().to_owned(), - entity_type: EntityType::Internal, - }); - - Self::insert_v1_and_v2_in_db_and_get_v2(state, new_v1_role, new_v2_role).await - } -} - impl NewUserRole { pub async fn insert_in_v2(self, state: &SessionState) -> UserResult { let entity = self.entity.clone(); diff --git a/crates/router/src/utils/user_role.rs b/crates/router/src/utils/user_role.rs index 9141b91bdd..f2aae68fcb 100644 --- a/crates/router/src/utils/user_role.rs +++ b/crates/router/src/utils/user_role.rs @@ -239,10 +239,7 @@ pub async fn get_single_merchant_id( .attach_printable("No merchants found for org_id")? .get_id() .clone()), - Some(EntityType::Merchant) - | Some(EntityType::Internal) - | Some(EntityType::Profile) - | None => user_role + Some(EntityType::Merchant) | Some(EntityType::Profile) | None => user_role .merchant_id .clone() .ok_or(UserErrors::InternalServerError) @@ -263,9 +260,7 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite( )>, > { match entity_type { - EntityType::Internal | EntityType::Organization => { - Err(UserErrors::InvalidRoleOperation.into()) - } + EntityType::Organization => Err(UserErrors::InvalidRoleOperation.into()), EntityType::Merchant => { let Ok(merchant_id) = id_type::MerchantId::wrap(entity_id) else { return Ok(None); @@ -369,7 +364,7 @@ pub async fn get_single_merchant_id_and_profile_id( .get_entity_id_and_type() .ok_or(UserErrors::InternalServerError)?; let profile_id = match entity_type { - EntityType::Organization | EntityType::Merchant | EntityType::Internal => { + EntityType::Organization | EntityType::Merchant => { let key_store = state .store .get_merchant_key_store_by_merchant_id( @@ -438,14 +433,6 @@ pub fn get_min_entity( | (EntityType::Merchant, Some(EntityType::Profile)) | (EntityType::Profile, Some(EntityType::Profile)) => Ok(EntityType::Profile), - (EntityType::Internal, _) => Ok(EntityType::Internal), - - (EntityType::Organization, Some(EntityType::Internal)) - | (EntityType::Merchant, Some(EntityType::Internal)) - | (EntityType::Profile, Some(EntityType::Internal)) => { - Err(UserErrors::InvalidRoleOperation.into()) - } - (EntityType::Merchant, Some(EntityType::Organization)) | (EntityType::Profile, Some(EntityType::Organization)) | (EntityType::Profile, Some(EntityType::Merchant)) => { diff --git a/migrations/2024-09-24-105659_alter_entity_type_internal_to_merchant/down.sql b/migrations/2024-09-24-105659_alter_entity_type_internal_to_merchant/down.sql new file mode 100644 index 0000000000..7d206b424e --- /dev/null +++ b/migrations/2024-09-24-105659_alter_entity_type_internal_to_merchant/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +UPDATE user_roles SET entity_type = 'internal' where role_id like 'internal%' and version = 'v2'; diff --git a/migrations/2024-09-24-105659_alter_entity_type_internal_to_merchant/up.sql b/migrations/2024-09-24-105659_alter_entity_type_internal_to_merchant/up.sql new file mode 100644 index 0000000000..4fc5ea7d31 --- /dev/null +++ b/migrations/2024-09-24-105659_alter_entity_type_internal_to_merchant/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +UPDATE user_roles SET entity_type = 'merchant' WHERE entity_type = 'internal'; \ No newline at end of file