mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	feat(users): custom role at profile read (#6875)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Gnanasundari24 <118818938+Gnanasundari24@users.noreply.github.com>
This commit is contained in:
		| @ -637,6 +637,7 @@ async fn handle_invitation( | ||||
|         &request.role_id, | ||||
|         &user_from_token.merchant_id, | ||||
|         &user_from_token.org_id, | ||||
|         &user_from_token.profile_id, | ||||
|         user_from_token | ||||
|             .tenant_id | ||||
|             .as_ref() | ||||
|  | ||||
| @ -127,6 +127,7 @@ pub async fn update_user_role( | ||||
|         &req.role_id, | ||||
|         &user_from_token.merchant_id, | ||||
|         &user_from_token.org_id, | ||||
|         &user_from_token.profile_id, | ||||
|         user_from_token | ||||
|             .tenant_id | ||||
|             .as_ref() | ||||
| @ -551,6 +552,7 @@ pub async fn delete_user_role( | ||||
|             &role_to_be_deleted.role_id, | ||||
|             &user_from_token.merchant_id, | ||||
|             &user_from_token.org_id, | ||||
|             &user_from_token.profile_id, | ||||
|             user_from_token | ||||
|                 .tenant_id | ||||
|                 .as_ref() | ||||
| @ -625,6 +627,7 @@ pub async fn delete_user_role( | ||||
|             &role_to_be_deleted.role_id, | ||||
|             &user_from_token.merchant_id, | ||||
|             &user_from_token.org_id, | ||||
|             &user_from_token.profile_id, | ||||
|             user_from_token | ||||
|                 .tenant_id | ||||
|                 .as_ref() | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| use std::collections::HashSet; | ||||
| use std::{cmp, collections::HashSet}; | ||||
|  | ||||
| use api_models::user_role::role as role_api; | ||||
| use common_enums::{EntityType, ParentGroup, PermissionGroup, RoleScope}; | ||||
| use common_enums::{EntityType, ParentGroup, PermissionGroup}; | ||||
| use common_utils::generate_id_with_default_len; | ||||
| use diesel_models::role::{RoleNew, RoleUpdate}; | ||||
| use diesel_models::role::{ListRolesByEntityPayload, RoleNew, RoleUpdate}; | ||||
| use error_stack::{report, ResultExt}; | ||||
|  | ||||
| use crate::{ | ||||
| @ -65,6 +65,43 @@ pub async fn create_role( | ||||
|     _req_state: ReqState, | ||||
| ) -> UserResponse<role_api::RoleInfoWithGroupsResponse> { | ||||
|     let now = common_utils::date_time::now(); | ||||
|  | ||||
|     let user_entity_type = user_from_token | ||||
|         .get_role_info_from_db(&state) | ||||
|         .await | ||||
|         .attach_printable("Invalid role_id in JWT")? | ||||
|         .get_entity_type(); | ||||
|  | ||||
|     let role_entity_type = req.entity_type.unwrap_or(EntityType::Merchant); | ||||
|  | ||||
|     if matches!(role_entity_type, EntityType::Organization) { | ||||
|         return Err(report!(UserErrors::InvalidRoleOperation)) | ||||
|             .attach_printable("User trying to create org level custom role"); | ||||
|     } | ||||
|  | ||||
|     // TODO: Remove in PR custom-role-write-pr | ||||
|     if matches!(role_entity_type, EntityType::Profile) { | ||||
|         return Err(report!(UserErrors::InvalidRoleOperation)) | ||||
|             .attach_printable("User trying to create profile level custom role"); | ||||
|     } | ||||
|  | ||||
|     let requestor_entity_from_role_scope = EntityType::from(req.role_scope); | ||||
|  | ||||
|     if requestor_entity_from_role_scope < role_entity_type { | ||||
|         return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!( | ||||
|             "User is trying to create role of type {} and scope {}", | ||||
|             role_entity_type, requestor_entity_from_role_scope | ||||
|         )); | ||||
|     } | ||||
|     let max_from_scope_and_entity = cmp::max(requestor_entity_from_role_scope, role_entity_type); | ||||
|  | ||||
|     if user_entity_type < max_from_scope_and_entity { | ||||
|         return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!( | ||||
|             "{} is trying to create of scope {} and of type {}", | ||||
|             user_entity_type, requestor_entity_from_role_scope, role_entity_type | ||||
|         )); | ||||
|     } | ||||
|  | ||||
|     let role_name = RoleName::new(req.role_name)?; | ||||
|  | ||||
|     utils::user_role::validate_role_groups(&req.groups)?; | ||||
| @ -77,33 +114,38 @@ pub async fn create_role( | ||||
|             .tenant_id | ||||
|             .as_ref() | ||||
|             .unwrap_or(&state.tenant.tenant_id), | ||||
|         &user_from_token.profile_id, | ||||
|         &role_entity_type, | ||||
|     ) | ||||
|     .await?; | ||||
|  | ||||
|     let user_role_info = user_from_token.get_role_info_from_db(&state).await?; | ||||
|  | ||||
|     if matches!(req.role_scope, RoleScope::Organization) | ||||
|         && user_role_info.get_entity_type() < EntityType::Organization | ||||
|     { | ||||
|         return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable( | ||||
|             "User does not have sufficient privileges to perform organization-level role operation", | ||||
|         ); | ||||
|     } | ||||
|     let (org_id, merchant_id, profile_id) = match role_entity_type { | ||||
|         EntityType::Organization | EntityType::Tenant => { | ||||
|             (user_from_token.org_id, user_from_token.merchant_id, None) | ||||
|         } | ||||
|         EntityType::Merchant => (user_from_token.org_id, user_from_token.merchant_id, None), | ||||
|         EntityType::Profile => ( | ||||
|             user_from_token.org_id, | ||||
|             user_from_token.merchant_id, | ||||
|             Some(user_from_token.profile_id), | ||||
|         ), | ||||
|     }; | ||||
|  | ||||
|     let role = state | ||||
|         .global_store | ||||
|         .insert_role(RoleNew { | ||||
|             role_id: generate_id_with_default_len("role"), | ||||
|             role_name: role_name.get_role_name(), | ||||
|             merchant_id: user_from_token.merchant_id, | ||||
|             org_id: user_from_token.org_id, | ||||
|             merchant_id, | ||||
|             org_id, | ||||
|             groups: req.groups, | ||||
|             scope: req.role_scope, | ||||
|             entity_type: EntityType::Merchant, | ||||
|             entity_type: role_entity_type, | ||||
|             created_by: user_from_token.user_id.clone(), | ||||
|             last_modified_by: user_from_token.user_id, | ||||
|             created_at: now, | ||||
|             last_modified_at: now, | ||||
|             profile_id, | ||||
|             tenant_id: user_from_token.tenant_id.unwrap_or(state.tenant.tenant_id), | ||||
|         }) | ||||
|         .await | ||||
| @ -115,6 +157,7 @@ pub async fn create_role( | ||||
|             role_id: role.role_id, | ||||
|             role_name: role.role_name, | ||||
|             role_scope: role.scope, | ||||
|             entity_type: role.entity_type, | ||||
|         }, | ||||
|     )) | ||||
| } | ||||
| @ -146,6 +189,7 @@ pub async fn get_role_with_groups( | ||||
|             role_id: role.role_id, | ||||
|             role_name: role_info.get_role_name().to_string(), | ||||
|             role_scope: role_info.get_scope(), | ||||
|             entity_type: role_info.get_entity_type(), | ||||
|         }, | ||||
|     )) | ||||
| } | ||||
| @ -207,29 +251,12 @@ pub async fn update_role( | ||||
| ) -> UserResponse<role_api::RoleInfoWithGroupsResponse> { | ||||
|     let role_name = req.role_name.map(RoleName::new).transpose()?; | ||||
|  | ||||
|     if let Some(ref role_name) = role_name { | ||||
|         utils::user_role::validate_role_name( | ||||
|             &state, | ||||
|             role_name, | ||||
|             &user_from_token.merchant_id, | ||||
|             &user_from_token.org_id, | ||||
|             user_from_token | ||||
|                 .tenant_id | ||||
|                 .as_ref() | ||||
|                 .unwrap_or(&state.tenant.tenant_id), | ||||
|         ) | ||||
|         .await?; | ||||
|     } | ||||
|  | ||||
|     if let Some(ref groups) = req.groups { | ||||
|         utils::user_role::validate_role_groups(groups)?; | ||||
|     } | ||||
|  | ||||
|     let role_info = roles::RoleInfo::from_role_id_in_lineage( | ||||
|         &state, | ||||
|         role_id, | ||||
|         &user_from_token.merchant_id, | ||||
|         &user_from_token.org_id, | ||||
|         &user_from_token.profile_id, | ||||
|         user_from_token | ||||
|             .tenant_id | ||||
|             .as_ref() | ||||
| @ -240,11 +267,38 @@ pub async fn update_role( | ||||
|  | ||||
|     let user_role_info = user_from_token.get_role_info_from_db(&state).await?; | ||||
|  | ||||
|     if matches!(role_info.get_scope(), RoleScope::Organization) | ||||
|         && user_role_info.get_entity_type() != EntityType::Organization | ||||
|     { | ||||
|         return Err(report!(UserErrors::InvalidRoleOperation)) | ||||
|             .attach_printable("Non org admin user changing org level role"); | ||||
|     let requested_entity_from_role_scope = EntityType::from(role_info.get_scope()); | ||||
|     let requested_role_entity_type = role_info.get_entity_type(); | ||||
|     let max_from_scope_and_entity = | ||||
|         cmp::max(requested_entity_from_role_scope, requested_role_entity_type); | ||||
|  | ||||
|     if user_role_info.get_entity_type() < max_from_scope_and_entity { | ||||
|         return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!( | ||||
|             "{} is trying to update of scope {} and of type {}", | ||||
|             user_role_info.get_entity_type(), | ||||
|             requested_entity_from_role_scope, | ||||
|             requested_role_entity_type | ||||
|         )); | ||||
|     } | ||||
|  | ||||
|     if let Some(ref role_name) = role_name { | ||||
|         utils::user_role::validate_role_name( | ||||
|             &state, | ||||
|             role_name, | ||||
|             &user_from_token.merchant_id, | ||||
|             &user_from_token.org_id, | ||||
|             user_from_token | ||||
|                 .tenant_id | ||||
|                 .as_ref() | ||||
|                 .unwrap_or(&state.tenant.tenant_id), | ||||
|             &user_from_token.profile_id, | ||||
|             &role_info.get_entity_type(), | ||||
|         ) | ||||
|         .await?; | ||||
|     } | ||||
|  | ||||
|     if let Some(ref groups) = req.groups { | ||||
|         utils::user_role::validate_role_groups(groups)?; | ||||
|     } | ||||
|  | ||||
|     let updated_role = state | ||||
| @ -269,6 +323,7 @@ pub async fn update_role( | ||||
|             role_id: updated_role.role_id, | ||||
|             role_name: updated_role.role_name, | ||||
|             role_scope: updated_role.scope, | ||||
|             entity_type: updated_role.entity_type, | ||||
|         }, | ||||
|     )) | ||||
| } | ||||
| @ -296,40 +351,51 @@ pub async fn list_roles_with_info( | ||||
|         .collect::<Vec<_>>(); | ||||
|  | ||||
|     let user_role_entity = user_role_info.get_entity_type(); | ||||
|     let is_lineage_data_required = request.entity_type.is_none(); | ||||
|     let tenant_id = user_from_token | ||||
|         .tenant_id | ||||
|         .as_ref() | ||||
|         .unwrap_or(&state.tenant.tenant_id) | ||||
|         .to_owned(); | ||||
|     let custom_roles = | ||||
|         match utils::user_role::get_min_entity(user_role_entity, request.entity_type)? { | ||||
|             EntityType::Tenant | EntityType::Organization => state | ||||
|                 .global_store | ||||
|                 .list_roles_for_org_by_parameters( | ||||
|                     user_from_token | ||||
|                         .tenant_id | ||||
|                         .as_ref() | ||||
|                         .unwrap_or(&state.tenant.tenant_id), | ||||
|                     &user_from_token.org_id, | ||||
|                     None, | ||||
|                     request.entity_type, | ||||
|                     None, | ||||
|                 .generic_list_roles_by_entity_type( | ||||
|                     ListRolesByEntityPayload::Organization, | ||||
|                     is_lineage_data_required, | ||||
|                     tenant_id, | ||||
|                     user_from_token.org_id, | ||||
|                 ) | ||||
|                 .await | ||||
|                 .change_context(UserErrors::InternalServerError) | ||||
|                 .attach_printable("Failed to get roles")?, | ||||
|             EntityType::Merchant => state | ||||
|                 .global_store | ||||
|                 .list_roles_for_org_by_parameters( | ||||
|                     user_from_token | ||||
|                         .tenant_id | ||||
|                         .as_ref() | ||||
|                         .unwrap_or(&state.tenant.tenant_id), | ||||
|                     &user_from_token.org_id, | ||||
|                     Some(&user_from_token.merchant_id), | ||||
|                     request.entity_type, | ||||
|                     None, | ||||
|                 .generic_list_roles_by_entity_type( | ||||
|                     ListRolesByEntityPayload::Merchant(user_from_token.merchant_id), | ||||
|                     is_lineage_data_required, | ||||
|                     tenant_id, | ||||
|                     user_from_token.org_id, | ||||
|                 ) | ||||
|                 .await | ||||
|                 .change_context(UserErrors::InternalServerError) | ||||
|                 .attach_printable("Failed to get roles")?, | ||||
|  | ||||
|             EntityType::Profile => state | ||||
|                 .global_store | ||||
|                 .generic_list_roles_by_entity_type( | ||||
|                     ListRolesByEntityPayload::Profile( | ||||
|                         user_from_token.merchant_id, | ||||
|                         user_from_token.profile_id, | ||||
|                     ), | ||||
|                     is_lineage_data_required, | ||||
|                     tenant_id, | ||||
|                     user_from_token.org_id, | ||||
|                 ) | ||||
|                 .await | ||||
|                 .change_context(UserErrors::InternalServerError) | ||||
|                 .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(), | ||||
|         }; | ||||
|  | ||||
|     role_info_vec.extend(custom_roles.into_iter().map(roles::RoleInfo::from)); | ||||
| @ -378,18 +444,21 @@ pub async fn list_roles_at_entity_level( | ||||
|         .map(|(_, role_info)| role_info.clone()) | ||||
|         .collect::<Vec<_>>(); | ||||
|  | ||||
|     let tenant_id = user_from_token | ||||
|         .tenant_id | ||||
|         .as_ref() | ||||
|         .unwrap_or(&state.tenant.tenant_id) | ||||
|         .to_owned(); | ||||
|  | ||||
|     let is_lineage_data_required = false; | ||||
|     let custom_roles = match req.entity_type { | ||||
|         EntityType::Tenant | EntityType::Organization => state | ||||
|             .global_store | ||||
|             .list_roles_for_org_by_parameters( | ||||
|                 user_from_token | ||||
|                     .tenant_id | ||||
|                     .as_ref() | ||||
|                     .unwrap_or(&state.tenant.tenant_id), | ||||
|                 &user_from_token.org_id, | ||||
|                 None, | ||||
|                 Some(req.entity_type), | ||||
|                 None, | ||||
|             .generic_list_roles_by_entity_type( | ||||
|                 ListRolesByEntityPayload::Organization, | ||||
|                 is_lineage_data_required, | ||||
|                 tenant_id, | ||||
|                 user_from_token.org_id, | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(UserErrors::InternalServerError) | ||||
| @ -397,21 +466,30 @@ pub async fn list_roles_at_entity_level( | ||||
|  | ||||
|         EntityType::Merchant => state | ||||
|             .global_store | ||||
|             .list_roles_for_org_by_parameters( | ||||
|                 user_from_token | ||||
|                     .tenant_id | ||||
|                     .as_ref() | ||||
|                     .unwrap_or(&state.tenant.tenant_id), | ||||
|                 &user_from_token.org_id, | ||||
|                 Some(&user_from_token.merchant_id), | ||||
|                 Some(req.entity_type), | ||||
|                 None, | ||||
|             .generic_list_roles_by_entity_type( | ||||
|                 ListRolesByEntityPayload::Merchant(user_from_token.merchant_id), | ||||
|                 is_lineage_data_required, | ||||
|                 tenant_id, | ||||
|                 user_from_token.org_id, | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(UserErrors::InternalServerError) | ||||
|             .attach_printable("Failed to get roles")?, | ||||
|  | ||||
|         EntityType::Profile => state | ||||
|             .global_store | ||||
|             .generic_list_roles_by_entity_type( | ||||
|                 ListRolesByEntityPayload::Profile( | ||||
|                     user_from_token.merchant_id, | ||||
|                     user_from_token.profile_id, | ||||
|                 ), | ||||
|                 is_lineage_data_required, | ||||
|                 tenant_id, | ||||
|                 user_from_token.org_id, | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(UserErrors::InternalServerError) | ||||
|             .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(), | ||||
|     }; | ||||
|  | ||||
|     role_info_vec.extend(custom_roles.into_iter().map(roles::RoleInfo::from)); | ||||
|  | ||||
| @ -3611,28 +3611,16 @@ impl RoleInterface for KafkaStore { | ||||
|         self.diesel_store.find_role_by_role_id(role_id).await | ||||
|     } | ||||
|  | ||||
|     //TODO:Remove once find_by_role_id_in_lineage is stable | ||||
|     async fn find_role_by_role_id_in_merchant_scope( | ||||
|         &self, | ||||
|         role_id: &str, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<storage::Role, errors::StorageError> { | ||||
|         self.diesel_store | ||||
|             .find_role_by_role_id_in_merchant_scope(role_id, merchant_id, org_id, tenant_id) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     async fn find_role_by_role_id_in_lineage( | ||||
|         &self, | ||||
|         role_id: &str, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         profile_id: &id_type::ProfileId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<storage::Role, errors::StorageError> { | ||||
|         self.diesel_store | ||||
|             .find_role_by_role_id_in_lineage(role_id, merchant_id, org_id, tenant_id) | ||||
|             .find_role_by_role_id_in_lineage(role_id, merchant_id, org_id, profile_id, tenant_id) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
| @ -3664,17 +3652,7 @@ impl RoleInterface for KafkaStore { | ||||
|         self.diesel_store.delete_role_by_role_id(role_id).await | ||||
|     } | ||||
|  | ||||
|     async fn list_all_roles( | ||||
|         &self, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<Vec<storage::Role>, errors::StorageError> { | ||||
|         self.diesel_store | ||||
|             .list_all_roles(merchant_id, org_id, tenant_id) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     //TODO: Remove once generic_list_roles_by_entity_type is stable | ||||
|     async fn list_roles_for_org_by_parameters( | ||||
|         &self, | ||||
|         tenant_id: &id_type::TenantId, | ||||
| @ -3687,6 +3665,18 @@ impl RoleInterface for KafkaStore { | ||||
|             .list_roles_for_org_by_parameters(tenant_id, org_id, merchant_id, entity_type, limit) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     async fn generic_list_roles_by_entity_type( | ||||
|         &self, | ||||
|         payload: diesel_models::role::ListRolesByEntityPayload, | ||||
|         is_lineage_data_required: bool, | ||||
|         tenant_id: id_type::TenantId, | ||||
|         org_id: id_type::OrganizationId, | ||||
|     ) -> CustomResult<Vec<storage::Role>, errors::StorageError> { | ||||
|         self.diesel_store | ||||
|             .generic_list_roles_by_entity_type(payload, is_lineage_data_required, tenant_id, org_id) | ||||
|             .await | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| use common_enums::enums; | ||||
| use common_utils::id_type; | ||||
| use diesel_models::role as storage; | ||||
| use diesel_models::{ | ||||
|     enums::{EntityType, RoleScope}, | ||||
|     role as storage, | ||||
| }; | ||||
| use error_stack::report; | ||||
| use router_env::{instrument, tracing}; | ||||
|  | ||||
| @ -23,20 +25,12 @@ pub trait RoleInterface { | ||||
|         role_id: &str, | ||||
|     ) -> CustomResult<storage::Role, errors::StorageError>; | ||||
|  | ||||
|     //TODO:Remove once find_by_role_id_in_lineage is stable | ||||
|     async fn find_role_by_role_id_in_merchant_scope( | ||||
|         &self, | ||||
|         role_id: &str, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<storage::Role, errors::StorageError>; | ||||
|  | ||||
|     async fn find_role_by_role_id_in_lineage( | ||||
|         &self, | ||||
|         role_id: &str, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         profile_id: &id_type::ProfileId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<storage::Role, errors::StorageError>; | ||||
|  | ||||
| @ -58,21 +52,23 @@ pub trait RoleInterface { | ||||
|         role_id: &str, | ||||
|     ) -> CustomResult<storage::Role, errors::StorageError>; | ||||
|  | ||||
|     async fn list_all_roles( | ||||
|         &self, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<Vec<storage::Role>, errors::StorageError>; | ||||
|  | ||||
|     //TODO: Remove once generic_list_roles_by_entity_type is stable | ||||
|     async fn list_roles_for_org_by_parameters( | ||||
|         &self, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         merchant_id: Option<&id_type::MerchantId>, | ||||
|         entity_type: Option<enums::EntityType>, | ||||
|         entity_type: Option<EntityType>, | ||||
|         limit: Option<u32>, | ||||
|     ) -> CustomResult<Vec<storage::Role>, errors::StorageError>; | ||||
|  | ||||
|     async fn generic_list_roles_by_entity_type( | ||||
|         &self, | ||||
|         payload: storage::ListRolesByEntityPayload, | ||||
|         is_lineage_data_required: bool, | ||||
|         tenant_id: id_type::TenantId, | ||||
|         org_id: id_type::OrganizationId, | ||||
|     ) -> CustomResult<Vec<storage::Role>, errors::StorageError>; | ||||
| } | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
| @ -99,39 +95,26 @@ impl RoleInterface for Store { | ||||
|             .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
|  | ||||
|     //TODO:Remove once find_by_role_id_in_lineage is stable | ||||
|     #[instrument(skip_all)] | ||||
|     async fn find_role_by_role_id_in_merchant_scope( | ||||
|         &self, | ||||
|         role_id: &str, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<storage::Role, errors::StorageError> { | ||||
|         let conn = connection::pg_connection_read(self).await?; | ||||
|         storage::Role::find_by_role_id_in_merchant_scope( | ||||
|             &conn, | ||||
|             role_id, | ||||
|             merchant_id, | ||||
|             org_id, | ||||
|             tenant_id, | ||||
|         ) | ||||
|         .await | ||||
|         .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
|  | ||||
|     #[instrument(skip_all)] | ||||
|     async fn find_role_by_role_id_in_lineage( | ||||
|         &self, | ||||
|         role_id: &str, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         profile_id: &id_type::ProfileId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<storage::Role, errors::StorageError> { | ||||
|         let conn = connection::pg_connection_read(self).await?; | ||||
|         storage::Role::find_by_role_id_in_lineage(&conn, role_id, merchant_id, org_id, tenant_id) | ||||
|             .await | ||||
|             .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|         storage::Role::find_by_role_id_in_lineage( | ||||
|             &conn, | ||||
|             role_id, | ||||
|             merchant_id, | ||||
|             org_id, | ||||
|             profile_id, | ||||
|             tenant_id, | ||||
|         ) | ||||
|         .await | ||||
|         .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
|  | ||||
|     #[instrument(skip_all)] | ||||
| @ -170,26 +153,14 @@ impl RoleInterface for Store { | ||||
|             .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
|  | ||||
|     #[instrument(skip_all)] | ||||
|     async fn list_all_roles( | ||||
|         &self, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<Vec<storage::Role>, errors::StorageError> { | ||||
|         let conn = connection::pg_connection_read(self).await?; | ||||
|         storage::Role::list_roles(&conn, merchant_id, org_id, tenant_id) | ||||
|             .await | ||||
|             .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
|  | ||||
|     //TODO: Remove once generic_list_roles_by_entity_type is stable | ||||
|     #[instrument(skip_all)] | ||||
|     async fn list_roles_for_org_by_parameters( | ||||
|         &self, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         merchant_id: Option<&id_type::MerchantId>, | ||||
|         entity_type: Option<enums::EntityType>, | ||||
|         entity_type: Option<EntityType>, | ||||
|         limit: Option<u32>, | ||||
|     ) -> CustomResult<Vec<storage::Role>, errors::StorageError> { | ||||
|         let conn = connection::pg_connection_read(self).await?; | ||||
| @ -204,6 +175,26 @@ impl RoleInterface for Store { | ||||
|         .await | ||||
|         .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
|  | ||||
|     #[instrument(skip_all)] | ||||
|     async fn generic_list_roles_by_entity_type( | ||||
|         &self, | ||||
|         payload: storage::ListRolesByEntityPayload, | ||||
|         is_lineage_data_required: bool, | ||||
|         tenant_id: id_type::TenantId, | ||||
|         org_id: id_type::OrganizationId, | ||||
|     ) -> CustomResult<Vec<storage::Role>, errors::StorageError> { | ||||
|         let conn = connection::pg_connection_read(self).await?; | ||||
|         storage::Role::generic_list_roles_by_entity_type( | ||||
|             &conn, | ||||
|             payload, | ||||
|             is_lineage_data_required, | ||||
|             tenant_id, | ||||
|             org_id, | ||||
|         ) | ||||
|         .await | ||||
|         .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
| @ -234,6 +225,7 @@ impl RoleInterface for MockDb { | ||||
|             created_at: role.created_at, | ||||
|             last_modified_at: role.last_modified_at, | ||||
|             last_modified_by: role.last_modified_by, | ||||
|             profile_id: role.profile_id, | ||||
|             tenant_id: role.tenant_id, | ||||
|         }; | ||||
|         roles.push(role.clone()); | ||||
| @ -257,38 +249,12 @@ impl RoleInterface for MockDb { | ||||
|             ) | ||||
|     } | ||||
|  | ||||
|     // TODO: Remove once find_by_role_id_in_lineage is stable | ||||
|     async fn find_role_by_role_id_in_merchant_scope( | ||||
|         &self, | ||||
|         role_id: &str, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<storage::Role, errors::StorageError> { | ||||
|         let roles = self.roles.lock().await; | ||||
|         roles | ||||
|             .iter() | ||||
|             .find(|role| { | ||||
|                 role.role_id == role_id | ||||
|                     && (role.tenant_id == *tenant_id) | ||||
|                     && (role.merchant_id == *merchant_id | ||||
|                         || (role.org_id == *org_id && role.scope == enums::RoleScope::Organization)) | ||||
|             }) | ||||
|             .cloned() | ||||
|             .ok_or( | ||||
|                 errors::StorageError::ValueNotFound(format!( | ||||
|                     "No role available in merchant scope for role_id = {role_id}, \ | ||||
|                     merchant_id = {merchant_id:?} and org_id = {org_id:?}" | ||||
|                 )) | ||||
|                 .into(), | ||||
|             ) | ||||
|     } | ||||
|  | ||||
|     async fn find_role_by_role_id_in_lineage( | ||||
|         &self, | ||||
|         role_id: &str, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         profile_id: &id_type::ProfileId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<storage::Role, errors::StorageError> { | ||||
|         let roles = self.roles.lock().await; | ||||
| @ -298,9 +264,15 @@ impl RoleInterface for MockDb { | ||||
|                 role.role_id == role_id | ||||
|                     && (role.tenant_id == *tenant_id) | ||||
|                     && role.org_id == *org_id | ||||
|                     && ((role.scope == enums::RoleScope::Organization) | ||||
|                         || (role.merchant_id == *merchant_id | ||||
|                             && role.scope == enums::RoleScope::Merchant)) | ||||
|                     && ((role.scope == RoleScope::Organization) | ||||
|                         || (role.merchant_id == *merchant_id && role.scope == RoleScope::Merchant) | ||||
|                         || (role | ||||
|                             .profile_id | ||||
|                             .as_ref() | ||||
|                             .is_some_and(|profile_id_from_role| { | ||||
|                                 profile_id_from_role == profile_id | ||||
|                                     && role.scope == RoleScope::Profile | ||||
|                             }))) | ||||
|             }) | ||||
|             .cloned() | ||||
|             .ok_or( | ||||
| @ -382,43 +354,14 @@ impl RoleInterface for MockDb { | ||||
|         Ok(roles.remove(role_index)) | ||||
|     } | ||||
|  | ||||
|     async fn list_all_roles( | ||||
|         &self, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<Vec<storage::Role>, errors::StorageError> { | ||||
|         let roles = self.roles.lock().await; | ||||
|  | ||||
|         let roles_list: Vec<_> = roles | ||||
|             .iter() | ||||
|             .filter(|role| { | ||||
|                 role.tenant_id == *tenant_id | ||||
|                     && (role.merchant_id == *merchant_id | ||||
|                         || (role.org_id == *org_id | ||||
|                             && role.scope == diesel_models::enums::RoleScope::Organization)) | ||||
|             }) | ||||
|             .cloned() | ||||
|             .collect(); | ||||
|  | ||||
|         if roles_list.is_empty() { | ||||
|             return Err(errors::StorageError::ValueNotFound(format!( | ||||
|                 "No role found for merchant id = {:?} and org_id = {:?}", | ||||
|                 merchant_id, org_id | ||||
|             )) | ||||
|             .into()); | ||||
|         } | ||||
|  | ||||
|         Ok(roles_list) | ||||
|     } | ||||
|  | ||||
|     //TODO: Remove once generic_list_roles_by_entity_type is stable | ||||
|     #[instrument(skip_all)] | ||||
|     async fn list_roles_for_org_by_parameters( | ||||
|         &self, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         merchant_id: Option<&id_type::MerchantId>, | ||||
|         entity_type: Option<enums::EntityType>, | ||||
|         entity_type: Option<EntityType>, | ||||
|         limit: Option<u32>, | ||||
|     ) -> CustomResult<Vec<storage::Role>, errors::StorageError> { | ||||
|         let roles = self.roles.lock().await; | ||||
| @ -442,4 +385,72 @@ impl RoleInterface for MockDb { | ||||
|  | ||||
|         Ok(roles_list) | ||||
|     } | ||||
|  | ||||
|     #[instrument(skip_all)] | ||||
|     async fn generic_list_roles_by_entity_type( | ||||
|         &self, | ||||
|         payload: storage::ListRolesByEntityPayload, | ||||
|         is_lineage_data_required: bool, | ||||
|         tenant_id: id_type::TenantId, | ||||
|         org_id: id_type::OrganizationId, | ||||
|     ) -> CustomResult<Vec<storage::Role>, errors::StorageError> { | ||||
|         let roles = self.roles.lock().await; | ||||
|         let roles_list: Vec<_> = roles | ||||
|             .iter() | ||||
|             .filter(|role| match &payload { | ||||
|                 storage::ListRolesByEntityPayload::Organization => { | ||||
|                     let entity_in_vec = if is_lineage_data_required { | ||||
|                         vec![ | ||||
|                             EntityType::Organization, | ||||
|                             EntityType::Merchant, | ||||
|                             EntityType::Profile, | ||||
|                         ] | ||||
|                     } else { | ||||
|                         vec![EntityType::Organization] | ||||
|                     }; | ||||
|  | ||||
|                     role.tenant_id == tenant_id | ||||
|                         && role.org_id == org_id | ||||
|                         && entity_in_vec.contains(&role.entity_type) | ||||
|                 } | ||||
|                 storage::ListRolesByEntityPayload::Merchant(merchant_id) => { | ||||
|                     let entity_in_vec = if is_lineage_data_required { | ||||
|                         vec![EntityType::Merchant, EntityType::Profile] | ||||
|                     } else { | ||||
|                         vec![EntityType::Merchant] | ||||
|                     }; | ||||
|  | ||||
|                     role.tenant_id == tenant_id | ||||
|                         && role.org_id == org_id | ||||
|                         && (role.scope == RoleScope::Organization | ||||
|                             || role.merchant_id == *merchant_id) | ||||
|                         && entity_in_vec.contains(&role.entity_type) | ||||
|                 } | ||||
|                 storage::ListRolesByEntityPayload::Profile(merchant_id, profile_id) => { | ||||
|                     let entity_in_vec = [EntityType::Profile]; | ||||
|  | ||||
|                     let matches_merchant = | ||||
|                         role.merchant_id == *merchant_id && role.scope == RoleScope::Merchant; | ||||
|  | ||||
|                     let matches_profile = | ||||
|                         role.profile_id | ||||
|                             .as_ref() | ||||
|                             .is_some_and(|profile_id_from_role| { | ||||
|                                 profile_id_from_role == profile_id | ||||
|                                     && role.scope == RoleScope::Profile | ||||
|                             }); | ||||
|  | ||||
|                     role.tenant_id == tenant_id | ||||
|                         && role.org_id == org_id | ||||
|                         && (role.scope == RoleScope::Organization | ||||
|                             || matches_merchant | ||||
|                             || matches_profile) | ||||
|                         && entity_in_vec.contains(&role.entity_type) | ||||
|                 } | ||||
|             }) | ||||
|             .cloned() | ||||
|             .collect(); | ||||
|  | ||||
|         Ok(roles_list) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -121,6 +121,7 @@ impl RoleInfo { | ||||
|         role_id: &str, | ||||
|         merchant_id: &id_type::MerchantId, | ||||
|         org_id: &id_type::OrganizationId, | ||||
|         profile_id: &id_type::ProfileId, | ||||
|         tenant_id: &id_type::TenantId, | ||||
|     ) -> CustomResult<Self, errors::StorageError> { | ||||
|         if let Some(role) = predefined_roles::PREDEFINED_ROLES.get(role_id) { | ||||
| @ -128,7 +129,13 @@ impl RoleInfo { | ||||
|         } else { | ||||
|             state | ||||
|                 .global_store | ||||
|                 .find_role_by_role_id_in_lineage(role_id, merchant_id, org_id, tenant_id) | ||||
|                 .find_role_by_role_id_in_lineage( | ||||
|                     role_id, | ||||
|                     merchant_id, | ||||
|                     org_id, | ||||
|                     profile_id, | ||||
|                     tenant_id, | ||||
|                 ) | ||||
|                 .await | ||||
|                 .map(Self::from) | ||||
|         } | ||||
|  | ||||
| @ -4,6 +4,7 @@ use common_enums::{EntityType, PermissionGroup}; | ||||
| use common_utils::id_type; | ||||
| use diesel_models::{ | ||||
|     enums::{UserRoleVersion, UserStatus}, | ||||
|     role::ListRolesByEntityPayload, | ||||
|     user_role::{UserRole, UserRoleUpdate}, | ||||
| }; | ||||
| use error_stack::{report, Report, ResultExt}; | ||||
| @ -49,6 +50,8 @@ pub async fn validate_role_name( | ||||
|     merchant_id: &id_type::MerchantId, | ||||
|     org_id: &id_type::OrganizationId, | ||||
|     tenant_id: &id_type::TenantId, | ||||
|     profile_id: &id_type::ProfileId, | ||||
|     entity_type: &EntityType, | ||||
| ) -> UserResult<()> { | ||||
|     let role_name_str = role_name.clone().get_role_name(); | ||||
|  | ||||
| @ -56,16 +59,37 @@ pub async fn validate_role_name( | ||||
|         .iter() | ||||
|         .any(|(_, role_info)| role_info.get_role_name() == role_name_str); | ||||
|  | ||||
|     // TODO: Create and use find_by_role_name to make this efficient | ||||
|     let is_present_in_custom_roles = state | ||||
|         .global_store | ||||
|         .list_all_roles(merchant_id, org_id, tenant_id) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)? | ||||
|         .iter() | ||||
|         .any(|role| role.role_name == role_name_str); | ||||
|     let entity_type_for_role = match entity_type { | ||||
|         EntityType::Tenant | EntityType::Organization => ListRolesByEntityPayload::Organization, | ||||
|         EntityType::Merchant => ListRolesByEntityPayload::Merchant(merchant_id.to_owned()), | ||||
|         EntityType::Profile => { | ||||
|             ListRolesByEntityPayload::Profile(merchant_id.to_owned(), profile_id.to_owned()) | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     if is_present_in_predefined_roles || is_present_in_custom_roles { | ||||
|     let is_present_in_custom_role = match state | ||||
|         .global_store | ||||
|         .generic_list_roles_by_entity_type( | ||||
|             entity_type_for_role, | ||||
|             false, | ||||
|             tenant_id.to_owned(), | ||||
|             org_id.to_owned(), | ||||
|         ) | ||||
|         .await | ||||
|     { | ||||
|         Ok(roles_list) => roles_list | ||||
|             .iter() | ||||
|             .any(|role| role.role_name == role_name_str), | ||||
|         Err(e) => { | ||||
|             if e.current_context().is_db_not_found() { | ||||
|                 false | ||||
|             } else { | ||||
|                 return Err(UserErrors::InternalServerError.into()); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     if is_present_in_predefined_roles || is_present_in_custom_role { | ||||
|         return Err(UserErrors::RoleNameAlreadyExists.into()); | ||||
|     } | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Riddhiagrawal001
					Riddhiagrawal001