Bug: refactor(users): Populate optional fields in the user APIs



Currently
- list users in lineage
- get user role details
- list invitations
APIs send `null` for role_name, entity_name etc...

These fields should be populated with proper data.
diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs
index 7bb8409993d..7b22387b3c3 100644
--- a/crates/api_models/src/user.rs
+++ b/crates/api_models/src/user.rs
@@ -188,6 +188,7 @@ pub struct GetUserRoleDetailsResponseV2 {
     pub profile: Option<NameIdUnit<String, id_type::ProfileId>>,
     pub status: UserStatus,
     pub entity_type: EntityType,
+    pub role_name: String,
 }
 
 #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
diff --git a/crates/api_models/src/user_role/role.rs b/crates/api_models/src/user_role/role.rs
index 22aa80a459a..828dfeb20f8 100644
--- a/crates/api_models/src/user_role/role.rs
+++ b/crates/api_models/src/user_role/role.rs
@@ -63,5 +63,5 @@ pub enum RoleCheckType {
 #[derive(Debug, serde::Serialize, Clone)]
 pub struct MinimalRoleInfo {
     pub role_id: String,
-    pub role_name: Option<String>,
+    pub role_name: String,
 }
diff --git a/crates/diesel_models/src/query/role.rs b/crates/diesel_models/src/query/role.rs
index 0a3c0e6d42f..065a5b6e114 100644
--- a/crates/diesel_models/src/query/role.rs
+++ b/crates/diesel_models/src/query/role.rs
@@ -43,6 +43,20 @@ impl Role {
         .await
     }
 
+    pub async fn find_by_role_id_in_org_scope(
+        conn: &PgPooledConn,
+        role_id: &str,
+        org_id: &id_type::OrganizationId,
+    ) -> StorageResult<Self> {
+        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
+            conn,
+            dsl::role_id
+                .eq(role_id.to_owned())
+                .and(dsl::org_id.eq(org_id.to_owned())),
+        )
+        .await
+    }
+
     pub async fn update_by_role_id(
         conn: &PgPooledConn,
         role_id: &str,
diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs
index c9b0a956dbc..a0b6606c227 100644
--- a/crates/router/src/analytics.rs
+++ b/crates/router/src/analytics.rs
@@ -1829,11 +1829,15 @@ pub mod routes {
             json_payload.into_inner(),
             |state, auth: UserFromToken, req, _| async move {
                 let role_id = auth.role_id;
-                let role_info =
-                    RoleInfo::from_role_id(&state, &role_id, &auth.merchant_id, &auth.org_id)
-                        .await
-                        .change_context(UserErrors::InternalServerError)
-                        .change_context(OpenSearchError::UnknownError)?;
+                let role_info = RoleInfo::from_role_id_in_merchant_scope(
+                    &state,
+                    &role_id,
+                    &auth.merchant_id,
+                    &auth.org_id,
+                )
+                .await
+                .change_context(UserErrors::InternalServerError)
+                .change_context(OpenSearchError::UnknownError)?;
                 let permissions = role_info.get_permissions_set();
                 let accessible_indexes: Vec<_> = OPENSEARCH_INDEX_PERMISSIONS
                     .iter()
@@ -1885,11 +1889,15 @@ pub mod routes {
             indexed_req,
             |state, auth: UserFromToken, req, _| async move {
                 let role_id = auth.role_id;
-                let role_info =
-                    RoleInfo::from_role_id(&state, &role_id, &auth.merchant_id, &auth.org_id)
-                        .await
-                        .change_context(UserErrors::InternalServerError)
-                        .change_context(OpenSearchError::UnknownError)?;
+                let role_info = RoleInfo::from_role_id_in_merchant_scope(
+                    &state,
+                    &role_id,
+                    &auth.merchant_id,
+                    &auth.org_id,
+                )
+                .await
+                .change_context(UserErrors::InternalServerError)
+                .change_context(OpenSearchError::UnknownError)?;
                 let permissions = role_info.get_permissions_set();
                 let _ = OPENSEARCH_INDEX_PERMISSIONS
                     .iter()
diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs
index 31ab0e8ff22..45e9065d263 100644
--- a/crates/router/src/core/user.rs
+++ b/crates/router/src/core/user.rs
@@ -100,7 +100,7 @@ pub async fn get_user_details(
 ) -> UserResponse<user_api::GetUserDetailsResponse> {
     let user = user_from_token.get_user_from_db(&state).await?;
     let verification_days_left = utils::user::get_verification_days_left(&state, &user)?;
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
@@ -539,7 +539,7 @@ async fn handle_invitation(
         .into());
     }
 
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         state,
         &request.role_id,
         &user_from_token.merchant_id,
@@ -1154,7 +1154,7 @@ pub async fn switch_merchant_id(
 
     let user = user_from_token.get_user_from_db(&state).await?;
     let key_manager_state = &(&state).into();
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
@@ -1328,10 +1328,15 @@ pub async fn list_merchants_for_user(
             return Err(report!(UserErrors::InternalServerError)
                 .attach_printable("org_id not found in user_role"));
         };
-        roles::RoleInfo::from_role_id(&state, &user_role.role_id, merchant_id, org_id)
-            .await
-            .change_context(UserErrors::InternalServerError)
-            .attach_printable("Unable to find role info for user role")
+        roles::RoleInfo::from_role_id_in_merchant_scope(
+            &state,
+            &user_role.role_id,
+            merchant_id,
+            org_id,
+        )
+        .await
+        .change_context(UserErrors::InternalServerError)
+        .attach_printable("Unable to find role info for user role")
     }))
     .await?
     .into_iter()
@@ -1394,7 +1399,7 @@ pub async fn get_user_details_in_merchant_account(
         .to_not_found_response(UserErrors::InvalidRoleOperation)
         .attach_printable("User not found in the merchant account")?;
 
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &required_user_role.role_id,
         &user_from_token.merchant_id,
@@ -1428,7 +1433,7 @@ pub async fn list_user_roles_details(
         .await
         .to_not_found_response(UserErrors::InvalidRoleOperation)?;
 
-    let requestor_role_info = roles::RoleInfo::from_role_id(
+    let requestor_role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
@@ -1438,7 +1443,7 @@ pub async fn list_user_roles_details(
     .to_not_found_response(UserErrors::InternalServerError)
     .attach_printable("Failed to fetch role info")?;
 
-    let user_roles_set: HashSet<_> = state
+    let user_roles_set = state
         .store
         .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
             user_id: required_user.get_user_id(),
@@ -1459,7 +1464,7 @@ pub async fn list_user_roles_details(
         .change_context(UserErrors::InternalServerError)
         .attach_printable("Failed to fetch user roles")?
         .into_iter()
-        .collect();
+        .collect::<HashSet<_>>();
 
     let org_name = state
         .store
@@ -1477,11 +1482,11 @@ pub async fn list_user_roles_details(
     let (merchant_ids, merchant_profile_ids) = user_roles_set.iter().try_fold(
         (Vec::new(), Vec::new()),
         |(mut merchant, mut merchant_profile), user_role| {
-            let entity_type = user_role
+            let (_, entity_type) = user_role
                 .get_entity_id_and_type()
                 .ok_or(UserErrors::InternalServerError)
-                .attach_printable("Failed to fetch entity id and type")?
-                .1;
+                .attach_printable("Failed to compute entity id and type")?;
+
             match entity_type {
                 EntityType::Merchant => {
                     let merchant_id = user_role
@@ -1525,7 +1530,7 @@ pub async fn list_user_roles_details(
         },
     )?;
 
-    let merchant_map: HashMap<_, _> = state
+    let merchant_map = state
         .store
         .list_multiple_merchant_accounts(&(&state).into(), merchant_ids)
         .await
@@ -1538,50 +1543,71 @@ pub async fn list_user_roles_details(
                 merchant_account.merchant_name.clone(),
             )
         })
-        .collect();
+        .collect::<HashMap<_, _>>();
 
     let key_manager_state = &(&state).into();
 
-    let profile_map: HashMap<_, _> =
-        futures::future::try_join_all(merchant_profile_ids.iter().map(
-            |merchant_profile_id| async {
-                let merchant_key_store = state
-                    .store
-                    .get_merchant_key_store_by_merchant_id(
-                        key_manager_state,
-                        &merchant_profile_id.0,
-                        &state.store.get_master_key().to_vec().into(),
-                    )
-                    .await
-                    .change_context(UserErrors::InternalServerError)
-                    .attach_printable("Failed to retrieve merchant key store by merchant_id")?;
+    let profile_map = futures::future::try_join_all(merchant_profile_ids.iter().map(
+        |merchant_profile_id| async {
+            let merchant_key_store = state
+                .store
+                .get_merchant_key_store_by_merchant_id(
+                    key_manager_state,
+                    &merchant_profile_id.0,
+                    &state.store.get_master_key().to_vec().into(),
+                )
+                .await
+                .change_context(UserErrors::InternalServerError)
+                .attach_printable("Failed to retrieve merchant key store by merchant_id")?;
 
-                state
-                    .store
-                    .find_business_profile_by_profile_id(
-                        key_manager_state,
-                        &merchant_key_store,
-                        &merchant_profile_id.1,
-                    )
-                    .await
-                    .change_context(UserErrors::InternalServerError)
-                    .attach_printable("Failed to retrieve business profile")
-            },
-        ))
-        .await
-        .change_context(UserErrors::InternalServerError)
-        .attach_printable("Failed to construct proifle map")?
-        .into_iter()
-        .map(|profile| (profile.get_id().to_owned(), profile.profile_name))
-        .collect();
+            state
+                .store
+                .find_business_profile_by_profile_id(
+                    key_manager_state,
+                    &merchant_key_store,
+                    &merchant_profile_id.1,
+                )
+                .await
+                .change_context(UserErrors::InternalServerError)
+                .attach_printable("Failed to retrieve business profile")
+        },
+    ))
+    .await
+    .change_context(UserErrors::InternalServerError)
+    .attach_printable("Failed to construct proifle map")?
+    .into_iter()
+    .map(|profile| (profile.get_id().to_owned(), profile.profile_name))
+    .collect::<HashMap<_, _>>();
+
+    let role_name_map = futures::future::try_join_all(
+        user_roles_set
+            .iter()
+            .map(|user_role| user_role.role_id.clone())
+            .collect::<HashSet<_>>()
+            .into_iter()
+            .map(|role_id| async {
+                let role_info = roles::RoleInfo::from_role_id_in_org_scope(
+                    &state,
+                    &role_id,
+                    &user_from_token.org_id,
+                )
+                .await
+                .change_context(UserErrors::InternalServerError)?;
+
+                Ok::<_, error_stack::Report<_>>((role_id, role_info.get_role_name().to_string()))
+            }),
+    )
+    .await?
+    .into_iter()
+    .collect::<HashMap<_, _>>();
 
     let role_details_list: Vec<_> = user_roles_set
         .iter()
         .map(|user_role| {
-            let entity_type = user_role
+            let (_, entity_type) = user_role
                 .get_entity_id_and_type()
-                .ok_or(UserErrors::InternalServerError)?
-                .1;
+                .ok_or(UserErrors::InternalServerError)?;
+
             let (merchant, profile) = match entity_type {
                 EntityType::Internal => {
                     return Err(UserErrors::InvalidRoleOperationWithMessage(
@@ -1642,6 +1668,10 @@ pub async fn list_user_roles_details(
                 profile,
                 status: user_role.status.foreign_into(),
                 entity_type,
+                role_name: role_name_map
+                    .get(&user_role.role_id)
+                    .ok_or(UserErrors::InternalServerError)
+                    .cloned()?,
             })
         })
         .collect::<Result<Vec<_>, UserErrors>>()?;
@@ -1684,7 +1714,7 @@ pub async fn list_users_for_merchant_account(
     let users_user_roles_and_roles =
         futures::future::try_join_all(users_and_user_roles.into_iter().map(
             |(user, user_role)| async {
-                roles::RoleInfo::from_role_id(
+                roles::RoleInfo::from_role_id_in_merchant_scope(
                     &state,
                     &user_role.role_id.clone(),
                     user_role
@@ -2586,7 +2616,7 @@ pub async fn list_orgs_for_user(
     state: SessionState,
     user_from_token: auth::UserFromToken,
 ) -> UserResponse<Vec<user_api::ListOrgsForUserResponse>> {
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
@@ -2640,7 +2670,7 @@ pub async fn list_merchants_for_user_in_org(
     state: SessionState,
     user_from_token: auth::UserFromToken,
 ) -> UserResponse<Vec<user_api::ListMerchantsForUserInOrgResponse>> {
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
@@ -2707,7 +2737,7 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account(
     state: SessionState,
     user_from_token: auth::UserFromToken,
 ) -> UserResponse<Vec<user_api::ListProfilesForUserInOrgAndMerchantAccountResponse>> {
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
@@ -2796,7 +2826,7 @@ pub async fn switch_org_for_user(
         .into());
     }
 
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
@@ -2875,7 +2905,7 @@ pub async fn switch_merchant_for_user_in_org(
     }
 
     let key_manager_state = &(&state).into();
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
@@ -3084,7 +3114,7 @@ pub async fn switch_profile_for_user_in_org_and_merchant(
     }
 
     let key_manager_state = &(&state).into();
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs
index 369ee97a02a..868d2e65a24 100644
--- a/crates/router/src/core/user_role.rs
+++ b/crates/router/src/core/user_role.rs
@@ -3,9 +3,11 @@ use std::collections::{HashMap, HashSet};
 use api_models::{user as user_api, user_role as user_role_api};
 use diesel_models::{
     enums::{UserRoleVersion, UserStatus},
+    organization::OrganizationBridge,
     user_role::UserRoleUpdate,
 };
 use error_stack::{report, ResultExt};
+use masking::Secret;
 use once_cell::sync::Lazy;
 
 use crate::{
@@ -75,7 +77,7 @@ pub async fn update_user_role(
     req: user_role_api::UpdateUserRoleRequest,
     _req_state: ReqState,
 ) -> UserResponse<()> {
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &req.role_id,
         &user_from_token.merchant_id,
@@ -100,7 +102,7 @@ pub async fn update_user_role(
             .attach_printable("User Changing their own role");
     }
 
-    let updator_role = roles::RoleInfo::from_role_id(
+    let updator_role = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
@@ -133,7 +135,7 @@ pub async fn update_user_role(
     };
 
     if let Some(user_role) = v2_user_role_to_be_updated {
-        let role_to_be_updated = roles::RoleInfo::from_role_id(
+        let role_to_be_updated = roles::RoleInfo::from_role_id_in_merchant_scope(
             &state,
             &user_role.role_id,
             &user_from_token.merchant_id,
@@ -198,7 +200,7 @@ pub async fn update_user_role(
     };
 
     if let Some(user_role) = v1_user_role_to_be_updated {
-        let role_to_be_updated = roles::RoleInfo::from_role_id(
+        let role_to_be_updated = roles::RoleInfo::from_role_id_in_merchant_scope(
             &state,
             &user_role.role_id,
             &user_from_token.merchant_id,
@@ -501,7 +503,7 @@ pub async fn delete_user_role(
             .attach_printable("User deleting himself");
     }
 
-    let deletion_requestor_role_info = roles::RoleInfo::from_role_id(
+    let deletion_requestor_role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
@@ -535,7 +537,7 @@ pub async fn delete_user_role(
     };
 
     if let Some(role_to_be_deleted) = user_role_v2 {
-        let target_role_info = roles::RoleInfo::from_role_id(
+        let target_role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
             &state,
             &role_to_be_deleted.role_id,
             &user_from_token.merchant_id,
@@ -597,7 +599,7 @@ pub async fn delete_user_role(
     };
 
     if let Some(role_to_be_deleted) = user_role_v1 {
-        let target_role_info = roles::RoleInfo::from_role_id(
+        let target_role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
             &state,
             &role_to_be_deleted.role_id,
             &user_from_token.merchant_id,
@@ -672,7 +674,7 @@ pub async fn list_users_in_lineage(
     state: SessionState,
     user_from_token: auth::UserFromToken,
 ) -> UserResponse<Vec<user_role_api::ListUsersInEntityResponse>> {
-    let requestor_role_info = roles::RoleInfo::from_role_id(
+    let requestor_role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &user_from_token.role_id,
         &user_from_token.merchant_id,
@@ -744,6 +746,29 @@ pub async fn list_users_in_lineage(
         .map(|user| (user.user_id.clone(), user.email))
         .collect::<HashMap<_, _>>();
 
+    let role_info_map =
+        futures::future::try_join_all(user_roles_set.iter().map(|user_role| async {
+            roles::RoleInfo::from_role_id_in_org_scope(
+                &state,
+                &user_role.role_id,
+                &user_from_token.org_id,
+            )
+            .await
+            .map(|role_info| {
+                (
+                    user_role.role_id.clone(),
+                    user_role_api::role::MinimalRoleInfo {
+                        role_id: user_role.role_id.clone(),
+                        role_name: role_info.get_role_name().to_string(),
+                    },
+                )
+            })
+        }))
+        .await
+        .change_context(UserErrors::InternalServerError)?
+        .into_iter()
+        .collect::<HashMap<_, _>>();
+
     let user_role_map = user_roles_set
         .into_iter()
         .fold(HashMap::new(), |mut map, user_role| {
@@ -763,11 +788,13 @@ pub async fn list_users_in_lineage(
                         .ok_or(UserErrors::InternalServerError)?,
                     roles: role_id_vec
                         .into_iter()
-                        .map(|role_id| user_role_api::role::MinimalRoleInfo {
-                            role_id,
-                            role_name: None,
+                        .map(|role_id| {
+                            role_info_map
+                                .get(&role_id)
+                                .cloned()
+                                .ok_or(UserErrors::InternalServerError)
                         })
-                        .collect(),
+                        .collect::<Result<Vec<_>, _>>()?,
                 })
             })
             .collect::<Result<Vec<_>, _>>()?,
@@ -778,7 +805,7 @@ pub async fn list_invitations_for_user(
     state: SessionState,
     user_from_token: auth::UserIdFromAuth,
 ) -> UserResponse<Vec<user_role_api::ListInvitationForUserResponse>> {
-    let invitations = state
+    let user_roles = state
         .store
         .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
             user_id: &user_from_token.user_id,
@@ -794,18 +821,143 @@ pub async fn list_invitations_for_user(
         .change_context(UserErrors::InternalServerError)
         .attach_printable("Failed to list user roles by user id and invitation sent")?
         .into_iter()
-        .collect::<HashSet<_>>()
+        .collect::<HashSet<_>>();
+
+    let (org_ids, merchant_ids, profile_ids_with_merchant_ids) = user_roles.iter().try_fold(
+        (Vec::new(), Vec::new(), Vec::new()),
+        |(mut org_ids, mut merchant_ids, mut profile_ids_with_merchant_ids), user_role| {
+            let (_, entity_type) = user_role
+                .get_entity_id_and_type()
+                .ok_or(UserErrors::InternalServerError)
+                .attach_printable("Failed to compute entity id and type")?;
+
+            match entity_type {
+                EntityType::Organization => org_ids.push(
+                    user_role
+                        .org_id
+                        .clone()
+                        .ok_or(UserErrors::InternalServerError)?,
+                ),
+                EntityType::Merchant => merchant_ids.push(
+                    user_role
+                        .merchant_id
+                        .clone()
+                        .ok_or(UserErrors::InternalServerError)?,
+                ),
+                EntityType::Profile => profile_ids_with_merchant_ids.push((
+                    user_role
+                        .profile_id
+                        .clone()
+                        .ok_or(UserErrors::InternalServerError)?,
+                    user_role
+                        .merchant_id
+                        .clone()
+                        .ok_or(UserErrors::InternalServerError)?,
+                )),
+                EntityType::Internal => return Err(report!(UserErrors::InternalServerError)),
+            }
+
+            Ok((org_ids, merchant_ids, profile_ids_with_merchant_ids))
+        },
+    )?;
+
+    let org_name_map = futures::future::try_join_all(org_ids.into_iter().map(|org_id| async {
+        let org_name = state
+            .store
+            .find_organization_by_org_id(&org_id)
+            .await
+            .change_context(UserErrors::InternalServerError)?
+            .get_organization_name()
+            .map(Secret::new);
+
+        Ok::<_, error_stack::Report<UserErrors>>((org_id, org_name))
+    }))
+    .await?
+    .into_iter()
+    .collect::<HashMap<_, _>>();
+
+    let key_manager_state = &(&state).into();
+
+    let merchant_name_map = state
+        .store
+        .list_multiple_merchant_accounts(key_manager_state, merchant_ids)
+        .await
+        .change_context(UserErrors::InternalServerError)?
+        .into_iter()
+        .map(|merchant| {
+            (
+                merchant.get_id().clone(),
+                merchant
+                    .merchant_name
+                    .map(|encryptable_name| encryptable_name.into_inner()),
+            )
+        })
+        .collect::<HashMap<_, _>>();
+
+    let master_key = &state.store.get_master_key().to_vec().into();
+
+    let profile_name_map = futures::future::try_join_all(profile_ids_with_merchant_ids.iter().map(
+        |(profile_id, merchant_id)| async {
+            let merchant_key_store = state
+                .store
+                .get_merchant_key_store_by_merchant_id(key_manager_state, merchant_id, master_key)
+                .await
+                .change_context(UserErrors::InternalServerError)?;
+
+            let business_profile = state
+                .store
+                .find_business_profile_by_profile_id(
+                    key_manager_state,
+                    &merchant_key_store,
+                    profile_id,
+                )
+                .await
+                .change_context(UserErrors::InternalServerError)?;
+
+            Ok::<_, error_stack::Report<UserErrors>>((
+                profile_id.clone(),
+                Secret::new(business_profile.profile_name),
+            ))
+        },
+    ))
+    .await?
+    .into_iter()
+    .collect::<HashMap<_, _>>();
+
+    user_roles
         .into_iter()
-        .filter_map(|user_role| {
-            let (entity_id, entity_type) = user_role.get_entity_id_and_type()?;
-            Some(user_role_api::ListInvitationForUserResponse {
+        .map(|user_role| {
+            let (entity_id, entity_type) = user_role
+                .get_entity_id_and_type()
+                .ok_or(UserErrors::InternalServerError)
+                .attach_printable("Failed to compute entity id and type")?;
+
+            let entity_name = match entity_type {
+                EntityType::Organization => user_role
+                    .org_id
+                    .as_ref()
+                    .and_then(|org_id| org_name_map.get(org_id).cloned())
+                    .ok_or(UserErrors::InternalServerError)?,
+                EntityType::Merchant => user_role
+                    .merchant_id
+                    .as_ref()
+                    .and_then(|merchant_id| merchant_name_map.get(merchant_id).cloned())
+                    .ok_or(UserErrors::InternalServerError)?,
+                EntityType::Profile => user_role
+                    .profile_id
+                    .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 {
                 entity_id,
                 entity_type,
-                entity_name: None,
+                entity_name,
                 role_id: user_role.role_id,
             })
         })
-        .collect();
-
-    Ok(ApplicationResponse::Json(invitations))
+        .collect::<Result<Vec<_>, _>>()
+        .map(ApplicationResponse::Json)
 }
diff --git a/crates/router/src/core/user_role/role.rs b/crates/router/src/core/user_role/role.rs
index 8c9cd1da0ab..571ae7f995d 100644
--- a/crates/router/src/core/user_role/role.rs
+++ b/crates/router/src/core/user_role/role.rs
@@ -127,7 +127,7 @@ pub async fn get_role_with_groups(
     user_from_token: UserFromToken,
     role: role_api::GetRoleRequest,
 ) -> UserResponse<role_api::RoleInfoWithGroupsResponse> {
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         &role.role_id,
         &user_from_token.merchant_id,
@@ -172,7 +172,7 @@ pub async fn update_role(
         utils::user_role::validate_role_groups(groups)?;
     }
 
-    let role_info = roles::RoleInfo::from_role_id(
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
         &state,
         role_id,
         &user_from_token.merchant_id,
@@ -348,7 +348,7 @@ pub async fn list_roles_at_entity_level(
             if check_type && role_info.get_entity_type() == req.entity_type {
                 Some(role_api::MinimalRoleInfo {
                     role_id: role_info.get_role_id().to_string(),
-                    role_name: Some(role_info.get_role_name().to_string()),
+                    role_name: role_info.get_role_name().to_string(),
                 })
             } else {
                 None
diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs
index 76111afc6bd..42cd71c280d 100644
--- a/crates/router/src/db/kafka_store.rs
+++ b/crates/router/src/db/kafka_store.rs
@@ -3341,6 +3341,16 @@ impl RoleInterface for KafkaStore {
             .await
     }
 
+    async fn find_role_by_role_id_in_org_scope(
+        &self,
+        role_id: &str,
+        org_id: &id_type::OrganizationId,
+    ) -> CustomResult<storage::Role, errors::StorageError> {
+        self.diesel_store
+            .find_role_by_role_id_in_org_scope(role_id, org_id)
+            .await
+    }
+
     async fn update_role_by_role_id(
         &self,
         role_id: &str,
diff --git a/crates/router/src/db/role.rs b/crates/router/src/db/role.rs
index ddcb24dc254..20a4922d165 100644
--- a/crates/router/src/db/role.rs
+++ b/crates/router/src/db/role.rs
@@ -30,6 +30,12 @@ pub trait RoleInterface {
         org_id: &id_type::OrganizationId,
     ) -> CustomResult<storage::Role, errors::StorageError>;
 
+    async fn find_role_by_role_id_in_org_scope(
+        &self,
+        role_id: &str,
+        org_id: &id_type::OrganizationId,
+    ) -> CustomResult<storage::Role, errors::StorageError>;
+
     async fn update_role_by_role_id(
         &self,
         role_id: &str,
@@ -93,6 +99,18 @@ impl RoleInterface for Store {
             .map_err(|error| report!(errors::StorageError::from(error)))
     }
 
+    #[instrument(skip_all)]
+    async fn find_role_by_role_id_in_org_scope(
+        &self,
+        role_id: &str,
+        org_id: &id_type::OrganizationId,
+    ) -> CustomResult<storage::Role, errors::StorageError> {
+        let conn = connection::pg_connection_write(self).await?;
+        storage::Role::find_by_role_id_in_org_scope(&conn, role_id, org_id)
+            .await
+            .map_err(|error| report!(errors::StorageError::from(error)))
+    }
+
     #[instrument(skip_all)]
     async fn update_role_by_role_id(
         &self,
@@ -223,6 +241,24 @@ impl RoleInterface for MockDb {
             )
     }
 
+    async fn find_role_by_role_id_in_org_scope(
+        &self,
+        role_id: &str,
+        org_id: &id_type::OrganizationId,
+    ) -> CustomResult<storage::Role, errors::StorageError> {
+        let roles = self.roles.lock().await;
+        roles
+            .iter()
+            .find(|role| role.role_id == role_id && role.org_id == *org_id)
+            .cloned()
+            .ok_or(
+                errors::StorageError::ValueNotFound(format!(
+                    "No role available in org scope for role_id = {role_id} and org_id = {org_id:?}"
+                ))
+                .into(),
+            )
+    }
+
     async fn update_role_by_role_id(
         &self,
         role_id: &str,
diff --git a/crates/router/src/services/authorization/roles.rs b/crates/router/src/services/authorization/roles.rs
index c498a33614e..19383f010f2 100644
--- a/crates/router/src/services/authorization/roles.rs
+++ b/crates/router/src/services/authorization/roles.rs
@@ -71,7 +71,7 @@ impl RoleInfo {
             .any(|group| get_permissions_vec(group).contains(required_permission))
     }
 
-    pub async fn from_role_id(
+    pub async fn from_role_id_in_merchant_scope(
         state: &SessionState,
         role_id: &str,
         merchant_id: &id_type::MerchantId,
@@ -87,6 +87,22 @@ impl RoleInfo {
                 .map(Self::from)
         }
     }
+
+    pub async fn from_role_id_in_org_scope(
+        state: &SessionState,
+        role_id: &str,
+        org_id: &id_type::OrganizationId,
+    ) -> CustomResult<Self, errors::StorageError> {
+        if let Some(role) = predefined_roles::PREDEFINED_ROLES.get(role_id) {
+            Ok(role.clone())
+        } else {
+            state
+                .store
+                .find_role_by_role_id_in_org_scope(role_id, org_id)
+                .await
+                .map(Self::from)
+        }
+    }
 }
 
 impl From<diesel_models::role::Role> for RoleInfo {
diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs
index 3da24f2be7d..22edf6768c8 100644
--- a/crates/router/src/utils/user.rs
+++ b/crates/router/src/utils/user.rs
@@ -75,9 +75,14 @@ impl UserFromToken {
     }
 
     pub async fn get_role_info_from_db(&self, state: &SessionState) -> UserResult<RoleInfo> {
-        RoleInfo::from_role_id(state, &self.role_id, &self.merchant_id, &self.org_id)
-            .await
-            .change_context(UserErrors::InternalServerError)
+        RoleInfo::from_role_id_in_merchant_scope(
+            state,
+            &self.role_id,
+            &self.merchant_id,
+            &self.org_id,
+        )
+        .await
+        .change_context(UserErrors::InternalServerError)
     }
 }
 
diff --git a/crates/router/src/utils/user_role.rs b/crates/router/src/utils/user_role.rs
index 68bee0f022d..2d700ffe505 100644
--- a/crates/router/src/utils/user_role.rs
+++ b/crates/router/src/utils/user_role.rs
@@ -152,10 +152,11 @@ pub async fn set_role_permissions_in_cache_if_required(
         return Ok(());
     }
 
-    let role_info = roles::RoleInfo::from_role_id(state, role_id, merchant_id, org_id)
-        .await
-        .change_context(UserErrors::InternalServerError)
-        .attach_printable("Error getting role_info from role_id")?;
+    let role_info =
+        roles::RoleInfo::from_role_id_in_merchant_scope(state, role_id, merchant_id, org_id)
+            .await
+            .change_context(UserErrors::InternalServerError)
+            .attach_printable("Error getting role_info from role_id")?;
 
     authz::set_role_info_in_cache(
         state,

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [ ] New feature
- [ ] Enhancement
- [x] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
- Populate entity name in list invitations API.
- Add role name in the response of get user role details API.
- Populate role name in list users in lineage API.

### Additional Changes

- [x] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->
Closes #5860.

## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
1. List invitations
    ```
    curl 'http://localhost:8080/user/list/invitation' \
      -H 'authorization: Bearer SPT with accept invite purpose' \
    ```
    ```json
    [
        {
            "entity_id": "merchant_1726053750",
            "entity_type": "merchant",
            "entity_name": "First Merchant",
            "role_id": "role_66NmyEPLz3vHvN67Wdz1"
        },
        {
            "entity_id": "merchant_1726053866",
            "entity_type": "merchant",
            "entity_name": "Second merchant",
            "role_id": "role_pit0nT2xUJsZPCPUqOJD"
        }
    ]
    ```
    Entity name should be populated.

2. List lineage
    ```
    curl 'http://localhost:8080/user/user/v2/list' \
      -H 'authorization: Bearer JWT' \
    ```
    ```json
    [
        {
            "email": "mani.dchandra+secmer@juspay.in",
            "roles": [
                {
                    "role_id": "role_pit0nT2xUJsZPCPUqOJD",
                    "role_name": "role_in_second_merchant"
                },
                {
                    "role_id": "role_66NmyEPLz3vHvN67Wdz1",
                    "role_name": "role_in_first_merchant"
                }
            ]
        },
        {
            "email": "mani.dchandra+firmer@juspay.in",
            "roles": [
                {
                    "role_id": "role_66NmyEPLz3vHvN67Wdz1",
                    "role_name": "role_in_first_merchant"
                }
            ]
        },
        {
            "email": "mani.dchandra@juspay.in",
            "roles": [
                {
                    "role_id": "org_admin",
                    "role_name": "organization_admin"
                }
            ]
        }
    ]
    ```
    Role name should be populated.
3. Get user role details
    ```
    curl 'http://localhost:8080/user/user/v2' \
      -H 'authorization: Bearer JWT' \
      --data-raw '{"email":"email"}'
    ```
    ```
    [
        {
            "role_id": "role_pit0nT2xUJsZPCPUqOJD",
            "org": {
                "name": null,
                "id": "org_UQ9eMA5oT6GjJMONxPtZ"
            },
            "merchant": {
                "name": "Second merchant",
                "id": "merchant_1726053866"
            },
            "profile": null,
            "status": "Active",
            "entity_type": "merchant",
            "role_name": "role_in_second_merchant"
        },
        {
            "role_id": "role_66NmyEPLz3vHvN67Wdz1",
            "org": {
                "name": null,
                "id": "org_UQ9eMA5oT6GjJMONxPtZ"
            },
            "merchant": {
                "name": "First Profile",
                "id": "merchant_1726053750"
            },
            "profile": null,
            "status": "Active",
            "entity_type": "merchant",
            "role_name": "role_in_first_merchant"
        }
    ]
    ```
    `role_name` should be present and populated.

## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
