Bug: fix(authz): Proper descriptions for parents



Currently descriptions for parent groups in invite page of control center are not entity specific. That means, customers in description will be visible for profile level users when it shouldn't.
diff --git a/crates/api_models/src/events/user_role.rs b/crates/api_models/src/events/user_role.rs
index a2c76ecc394..e0df36a3349 100644
--- a/crates/api_models/src/events/user_role.rs
+++ b/crates/api_models/src/events/user_role.rs
@@ -2,8 +2,9 @@ use common_utils::events::{ApiEventMetric, ApiEventsType};
 
 use crate::user_role::{
     role::{
-        CreateRoleRequest, GetRoleRequest, ListRolesAtEntityLevelRequest, ListRolesRequest,
-        RoleInfoResponseNew, RoleInfoWithGroupsResponse, UpdateRoleRequest,
+        CreateRoleRequest, GetRoleRequest, GroupsAndResources, ListRolesAtEntityLevelRequest,
+        ListRolesRequest, RoleInfoResponseNew, RoleInfoWithGroupsResponse, RoleInfoWithParents,
+        UpdateRoleRequest,
     },
     AuthorizationInfoResponse, DeleteUserRoleRequest, ListUsersInEntityRequest,
     UpdateUserRoleRequest,
@@ -22,6 +23,8 @@ common_utils::impl_api_event_type!(
         RoleInfoResponseNew,
         RoleInfoWithGroupsResponse,
         ListUsersInEntityRequest,
-        ListRolesRequest
+        ListRolesRequest,
+        GroupsAndResources,
+        RoleInfoWithParents
     )
 );
diff --git a/crates/api_models/src/user_role/role.rs b/crates/api_models/src/user_role/role.rs
index 885ec455e4c..7c877cd7477 100644
--- a/crates/api_models/src/user_role/role.rs
+++ b/crates/api_models/src/user_role/role.rs
@@ -1,5 +1,6 @@
-pub use common_enums::PermissionGroup;
-use common_enums::{EntityType, RoleScope};
+use common_enums::{
+    EntityType, ParentGroup, PermissionGroup, PermissionScope, Resource, RoleScope,
+};
 
 #[derive(Debug, serde::Deserialize, serde::Serialize)]
 pub struct CreateRoleRequest {
@@ -22,6 +23,21 @@ pub struct RoleInfoWithGroupsResponse {
     pub role_scope: RoleScope,
 }
 
+#[derive(Debug, serde::Serialize)]
+pub struct RoleInfoWithParents {
+    pub role_id: String,
+    pub parent_groups: Vec<ParentGroupInfo>,
+    pub role_name: String,
+    pub role_scope: RoleScope,
+}
+
+#[derive(Debug, serde::Serialize)]
+pub struct ParentGroupInfo {
+    pub name: ParentGroup,
+    pub description: String,
+    pub scopes: Vec<PermissionScope>,
+}
+
 #[derive(Debug, serde::Deserialize, serde::Serialize)]
 pub struct ListRolesRequest {
     pub entity_type: Option<EntityType>,
@@ -57,3 +73,9 @@ pub struct MinimalRoleInfo {
     pub role_id: String,
     pub role_name: String,
 }
+
+#[derive(Debug, serde::Serialize)]
+pub struct GroupsAndResources {
+    pub groups: Vec<PermissionGroup>,
+    pub resources: Vec<Resource>,
+}
diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs
index c103153eec8..faae71eda22 100644
--- a/crates/common_enums/src/enums.rs
+++ b/crates/common_enums/src/enums.rs
@@ -2884,10 +2884,15 @@ pub enum PermissionGroup {
     AnalyticsView,
     UsersView,
     UsersManage,
+    // TODO: To be deprecated, make sure DB is migrated before removing
     MerchantDetailsView,
+    // TODO: To be deprecated, make sure DB is migrated before removing
     MerchantDetailsManage,
+    // TODO: To be deprecated, make sure DB is migrated before removing
     OrganizationManage,
     ReconOps,
+    AccountView,
+    AccountManage,
 }
 
 #[derive(Clone, Debug, serde::Serialize, PartialEq, Eq, Hash, strum::EnumIter)]
@@ -2897,14 +2902,12 @@ pub enum ParentGroup {
     Workflows,
     Analytics,
     Users,
-    #[serde(rename = "MerchantAccess")]
-    Merchant,
-    #[serde(rename = "OrganizationAccess")]
-    Organization,
     Recon,
+    Account,
 }
 
-#[derive(Clone, Copy, Eq, PartialEq, Hash)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, serde::Serialize)]
+#[serde(rename_all = "snake_case")]
 pub enum Resource {
     Payment,
     Refund,
@@ -2925,10 +2928,11 @@ pub enum Resource {
     Recon,
 }
 
-#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, serde::Serialize, Hash)]
+#[serde(rename_all = "snake_case")]
 pub enum PermissionScope {
-    Read,
-    Write,
+    Read = 0,
+    Write = 1,
 }
 
 /// Name of banks supported by Hyperswitch
diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs
index 284be4ab4ba..27c1e2ae494 100644
--- a/crates/router/src/core/user_role.rs
+++ b/crates/router/src/core/user_role.rs
@@ -1,6 +1,9 @@
 use std::collections::{HashMap, HashSet};
 
-use api_models::{user as user_api, user_role as user_role_api};
+use api_models::{
+    user as user_api,
+    user_role::{self as user_role_api, role as role_api},
+};
 use diesel_models::{
     enums::{UserRoleVersion, UserStatus},
     organization::OrganizationBridge,
@@ -16,7 +19,11 @@ use crate::{
     routes::{app::ReqState, SessionState},
     services::{
         authentication as auth,
-        authorization::{info, permission_groups::PermissionGroupExt, roles},
+        authorization::{
+            info,
+            permission_groups::{ParentGroupExt, PermissionGroupExt},
+            roles,
+        },
         ApplicationResponse,
     },
     types::domain,
@@ -72,6 +79,40 @@ pub async fn get_authorization_info_with_group_tag(
     ))
 }
 
+pub async fn get_parent_group_info(
+    state: SessionState,
+    user_from_token: auth::UserFromToken,
+) -> UserResponse<Vec<role_api::ParentGroupInfo>> {
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
+        &state,
+        &user_from_token.role_id,
+        &user_from_token.merchant_id,
+        &user_from_token.org_id,
+    )
+    .await
+    .to_not_found_response(UserErrors::InvalidRoleId)?;
+
+    let parent_groups = ParentGroup::get_descriptions_for_groups(
+        role_info.get_entity_type(),
+        PermissionGroup::iter().collect(),
+    )
+    .into_iter()
+    .map(|(parent_group, description)| role_api::ParentGroupInfo {
+        name: parent_group.clone(),
+        description,
+        scopes: PermissionGroup::iter()
+            .filter_map(|group| (group.parent() == parent_group).then_some(group.scope()))
+            // TODO: Remove this hashset conversion when merhant access
+            // and organization access groups are removed
+            .collect::<HashSet<_>>()
+            .into_iter()
+            .collect(),
+    })
+    .collect::<Vec<_>>();
+
+    Ok(ApplicationResponse::Json(parent_groups))
+}
+
 pub async fn update_user_role(
     state: SessionState,
     user_from_token: auth::UserFromToken,
diff --git a/crates/router/src/core/user_role/role.rs b/crates/router/src/core/user_role/role.rs
index b5b5cab421f..1d37f7ac8d1 100644
--- a/crates/router/src/core/user_role/role.rs
+++ b/crates/router/src/core/user_role/role.rs
@@ -1,5 +1,7 @@
+use std::collections::HashSet;
+
 use api_models::user_role::role::{self as role_api};
-use common_enums::{EntityType, RoleScope};
+use common_enums::{EntityType, ParentGroup, PermissionGroup, RoleScope};
 use common_utils::generate_id_with_default_len;
 use diesel_models::role::{RoleNew, RoleUpdate};
 use error_stack::{report, ResultExt};
@@ -9,7 +11,10 @@ use crate::{
     routes::{app::ReqState, SessionState},
     services::{
         authentication::{blacklist, UserFromToken},
-        authorization::roles::{self, predefined_roles::PREDEFINED_ROLES},
+        authorization::{
+            permission_groups::{ParentGroupExt, PermissionGroupExt},
+            roles::{self, predefined_roles::PREDEFINED_ROLES},
+        },
         ApplicationResponse,
     },
     types::domain::user::RoleName,
@@ -19,7 +24,7 @@ use crate::{
 pub async fn get_role_from_token_with_groups(
     state: SessionState,
     user_from_token: UserFromToken,
-) -> UserResponse<Vec<role_api::PermissionGroup>> {
+) -> UserResponse<Vec<PermissionGroup>> {
     let role_info = user_from_token
         .get_role_info_from_db(&state)
         .await
@@ -30,6 +35,29 @@ pub async fn get_role_from_token_with_groups(
     Ok(ApplicationResponse::Json(permissions))
 }
 
+pub async fn get_groups_and_resources_for_role_from_token(
+    state: SessionState,
+    user_from_token: UserFromToken,
+) -> UserResponse<role_api::GroupsAndResources> {
+    let role_info = user_from_token.get_role_info_from_db(&state).await?;
+
+    let groups = role_info
+        .get_permission_groups()
+        .into_iter()
+        .collect::<Vec<_>>();
+    let resources = groups
+        .iter()
+        .flat_map(|group| group.resources())
+        .collect::<HashSet<_>>()
+        .into_iter()
+        .collect();
+
+    Ok(ApplicationResponse::Json(role_api::GroupsAndResources {
+        groups,
+        resources,
+    }))
+}
+
 pub async fn create_role(
     state: SessionState,
     user_from_token: UserFromToken,
@@ -111,6 +139,52 @@ pub async fn get_role_with_groups(
     ))
 }
 
+pub async fn get_parent_info_for_role(
+    state: SessionState,
+    user_from_token: UserFromToken,
+    role: role_api::GetRoleRequest,
+) -> UserResponse<role_api::RoleInfoWithParents> {
+    let role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
+        &state,
+        &role.role_id,
+        &user_from_token.merchant_id,
+        &user_from_token.org_id,
+    )
+    .await
+    .to_not_found_response(UserErrors::InvalidRoleId)?;
+
+    if role_info.is_internal() {
+        return Err(UserErrors::InvalidRoleId.into());
+    }
+
+    let parent_groups = ParentGroup::get_descriptions_for_groups(
+        role_info.get_entity_type(),
+        role_info.get_permission_groups().to_vec(),
+    )
+    .into_iter()
+    .map(|(parent_group, description)| role_api::ParentGroupInfo {
+        name: parent_group.clone(),
+        description,
+        scopes: role_info
+            .get_permission_groups()
+            .iter()
+            .filter_map(|group| (group.parent() == parent_group).then_some(group.scope()))
+            // TODO: Remove this hashset conversion when merhant access
+            // and organization access groups are removed
+            .collect::<HashSet<_>>()
+            .into_iter()
+            .collect(),
+    })
+    .collect();
+
+    Ok(ApplicationResponse::Json(role_api::RoleInfoWithParents {
+        role_id: role.role_id,
+        parent_groups,
+        role_name: role_info.get_role_name().to_string(),
+        role_scope: role_info.get_scope(),
+    }))
+}
+
 pub async fn update_role(
     state: SessionState,
     user_from_token: UserFromToken,
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index f11840edf63..f54895ce7da 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -1819,9 +1819,14 @@ impl User {
                 web::resource("/permission_info")
                     .route(web::get().to(user_role::get_authorization_info)),
             )
+            // TODO: To be deprecated
             .service(
                 web::resource("/module/list").route(web::get().to(user_role::get_role_information)),
             )
+            .service(
+                web::resource("/parent/list")
+                    .route(web::get().to(user_role::get_parent_group_info)),
+            )
             .service(
                 web::resource("/update").route(web::post().to(user::update_user_account_details)),
             )
@@ -2017,6 +2022,9 @@ impl User {
                             .route(web::get().to(user_role::get_role_from_token))
                             .route(web::post().to(user_role::create_role)),
                     )
+                    .service(web::resource("/v2").route(
+                        web::get().to(user_role::get_groups_and_resources_for_role_from_token),
+                    ))
                     // TODO: To be deprecated
                     .service(
                         web::resource("/v2/list")
@@ -2039,6 +2047,10 @@ impl User {
                         web::resource("/{role_id}")
                             .route(web::get().to(user_role::get_role))
                             .route(web::put().to(user_role::update_role)),
+                    )
+                    .service(
+                        web::resource("/{role_id}/v2")
+                            .route(web::get().to(user_role::get_parent_info_for_role)),
                     ),
             );
 
diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs
index a0c5c0f9902..ac17cf80210 100644
--- a/crates/router/src/routes/lock_utils.rs
+++ b/crates/router/src/routes/lock_utils.rs
@@ -263,10 +263,13 @@ impl From<Flow> for ApiIdentifier {
             | Flow::ListInvitableRolesAtEntityLevel
             | Flow::ListUpdatableRolesAtEntityLevel
             | Flow::GetRole
+            | Flow::GetRoleV2
             | Flow::GetRoleFromToken
+            | Flow::GetRoleFromTokenV2
             | Flow::UpdateUserRole
             | Flow::GetAuthorizationInfo
             | Flow::GetRolesInfo
+            | Flow::GetParentGroupInfo
             | Flow::AcceptInvitationsV2
             | Flow::AcceptInvitationsPreAuth
             | Flow::DeleteUserRole
diff --git a/crates/router/src/routes/user_role.rs b/crates/router/src/routes/user_role.rs
index 74847f06474..9910cef3950 100644
--- a/crates/router/src/routes/user_role.rs
+++ b/crates/router/src/routes/user_role.rs
@@ -55,6 +55,26 @@ pub async fn get_role_from_token(state: web::Data<AppState>, req: HttpRequest) -
     .await
 }
 
+pub async fn get_groups_and_resources_for_role_from_token(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+) -> HttpResponse {
+    let flow = Flow::GetRoleFromTokenV2;
+
+    Box::pin(api::server_wrap(
+        flow,
+        state.clone(),
+        &req,
+        (),
+        |state, user, _, _| async move {
+            role_core::get_groups_and_resources_for_role_from_token(state, user).await
+        },
+        &auth::DashboardNoPermissionAuth,
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
 pub async fn create_role(
     state: web::Data<AppState>,
     req: HttpRequest,
@@ -100,6 +120,31 @@ pub async fn get_role(
     .await
 }
 
+pub async fn get_parent_info_for_role(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    path: web::Path<String>,
+) -> HttpResponse {
+    let flow = Flow::GetRoleV2;
+    let request_payload = user_role_api::role::GetRoleRequest {
+        role_id: path.into_inner(),
+    };
+    Box::pin(api::server_wrap(
+        flow,
+        state.clone(),
+        &req,
+        request_payload,
+        |state, user, payload, _| async move {
+            role_core::get_parent_info_for_role(state, user, payload).await
+        },
+        &auth::JWTAuth {
+            permission: Permission::ProfileUserRead,
+        },
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
 pub async fn update_role(
     state: web::Data<AppState>,
     req: HttpRequest,
@@ -226,6 +271,28 @@ pub async fn get_role_information(
     .await
 }
 
+pub async fn get_parent_group_info(
+    state: web::Data<AppState>,
+    http_req: HttpRequest,
+) -> HttpResponse {
+    let flow = Flow::GetParentGroupInfo;
+
+    Box::pin(api::server_wrap(
+        flow,
+        state.clone(),
+        &http_req,
+        (),
+        |state, user_from_token, _, _| async move {
+            user_role_core::get_parent_group_info(state, user_from_token).await
+        },
+        &auth::JWTAuth {
+            permission: Permission::ProfileUserRead,
+        },
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
 pub async fn list_users_in_lineage(
     state: web::Data<AppState>,
     req: HttpRequest,
diff --git a/crates/router/src/services/authorization/info.rs b/crates/router/src/services/authorization/info.rs
index dba96dac188..bd987e2fe9a 100644
--- a/crates/router/src/services/authorization/info.rs
+++ b/crates/router/src/services/authorization/info.rs
@@ -37,32 +37,13 @@ fn get_group_description(group: PermissionGroup) -> &'static str {
         PermissionGroup::AnalyticsView => "View Analytics",
         PermissionGroup::UsersView => "View Users",
         PermissionGroup::UsersManage => "Manage and invite Users to the Team",
-        PermissionGroup::MerchantDetailsView => "View Merchant Details",
-        PermissionGroup::MerchantDetailsManage => "Create, modify and delete Merchant Details like api keys, webhooks, etc",
+        PermissionGroup::MerchantDetailsView | PermissionGroup::AccountView => "View Merchant Details",
+        PermissionGroup::MerchantDetailsManage | PermissionGroup::AccountManage => "Create, modify and delete Merchant Details like api keys, webhooks, etc",
         PermissionGroup::OrganizationManage => "Manage organization level tasks like create new Merchant accounts, Organization level roles, etc",
         PermissionGroup::ReconOps => "View and manage reconciliation reports",
     }
 }
 
-pub fn get_parent_name(group: PermissionGroup) -> ParentGroup {
-    match group {
-        PermissionGroup::OperationsView | PermissionGroup::OperationsManage => {
-            ParentGroup::Operations
-        }
-        PermissionGroup::ConnectorsView | PermissionGroup::ConnectorsManage => {
-            ParentGroup::Connectors
-        }
-        PermissionGroup::WorkflowsView | PermissionGroup::WorkflowsManage => ParentGroup::Workflows,
-        PermissionGroup::AnalyticsView => ParentGroup::Analytics,
-        PermissionGroup::UsersView | PermissionGroup::UsersManage => ParentGroup::Users,
-        PermissionGroup::MerchantDetailsView | PermissionGroup::MerchantDetailsManage => {
-            ParentGroup::Merchant
-        }
-        PermissionGroup::OrganizationManage => ParentGroup::Organization,
-        PermissionGroup::ReconOps => ParentGroup::Recon,
-    }
-}
-
 pub fn get_parent_group_description(group: ParentGroup) -> &'static str {
     match group {
         ParentGroup::Operations => "Payments, Refunds, Payouts, Mandates, Disputes and Customers",
@@ -70,8 +51,7 @@ pub fn get_parent_group_description(group: ParentGroup) -> &'static str {
         ParentGroup::Workflows => "Create, modify and delete Routing, 3DS Decision Manager, Surcharge Decision Manager",
         ParentGroup::Analytics => "View Analytics",
         ParentGroup::Users =>  "Manage and invite Users to the Team",
-        ParentGroup::Merchant => "Create, modify and delete Merchant Details like api keys, webhooks, etc",
-        ParentGroup::Organization =>"Manage organization level tasks like create new Merchant accounts, Organization level roles, etc",
+        ParentGroup::Account => "Create, modify and delete Merchant Details like api keys, webhooks, etc",
         ParentGroup::Recon => "View and manage reconciliation reports",
     }
 }
diff --git a/crates/router/src/services/authorization/permission_groups.rs b/crates/router/src/services/authorization/permission_groups.rs
index 3d1a0c8ea5b..57c385565a8 100644
--- a/crates/router/src/services/authorization/permission_groups.rs
+++ b/crates/router/src/services/authorization/permission_groups.rs
@@ -1,4 +1,9 @@
-use common_enums::{ParentGroup, PermissionGroup, PermissionScope, Resource};
+use std::collections::HashMap;
+
+use common_enums::{EntityType, ParentGroup, PermissionGroup, PermissionScope, Resource};
+use strum::IntoEnumIterator;
+
+use super::permissions::{self, ResourceExt};
 
 pub trait PermissionGroupExt {
     fn scope(&self) -> PermissionScope;
@@ -15,7 +20,8 @@ impl PermissionGroupExt for PermissionGroup {
             | Self::WorkflowsView
             | Self::AnalyticsView
             | Self::UsersView
-            | Self::MerchantDetailsView => PermissionScope::Read,
+            | Self::MerchantDetailsView
+            | Self::AccountView => PermissionScope::Read,
 
             Self::OperationsManage
             | Self::ConnectorsManage
@@ -23,7 +29,8 @@ impl PermissionGroupExt for PermissionGroup {
             | Self::UsersManage
             | Self::MerchantDetailsManage
             | Self::OrganizationManage
-            | Self::ReconOps => PermissionScope::Write,
+            | Self::ReconOps
+            | Self::AccountManage => PermissionScope::Write,
         }
     }
 
@@ -34,9 +41,12 @@ impl PermissionGroupExt for PermissionGroup {
             Self::WorkflowsView | Self::WorkflowsManage => ParentGroup::Workflows,
             Self::AnalyticsView => ParentGroup::Analytics,
             Self::UsersView | Self::UsersManage => ParentGroup::Users,
-            Self::MerchantDetailsView | Self::MerchantDetailsManage => ParentGroup::Merchant,
-            Self::OrganizationManage => ParentGroup::Organization,
             Self::ReconOps => ParentGroup::Recon,
+            Self::MerchantDetailsView
+            | Self::OrganizationManage
+            | Self::MerchantDetailsManage
+            | Self::AccountView
+            | Self::AccountManage => ParentGroup::Account,
         }
     }
 
@@ -52,10 +62,14 @@ impl PermissionGroupExt for PermissionGroup {
             Self::ConnectorsView => vec![Self::ConnectorsView],
             Self::ConnectorsManage => vec![Self::ConnectorsView, Self::ConnectorsManage],
 
-            Self::WorkflowsView => vec![Self::WorkflowsView],
-            Self::WorkflowsManage => vec![Self::WorkflowsView, Self::WorkflowsManage],
+            Self::WorkflowsView => vec![Self::WorkflowsView, Self::ConnectorsView],
+            Self::WorkflowsManage => vec![
+                Self::WorkflowsView,
+                Self::WorkflowsManage,
+                Self::ConnectorsView,
+            ],
 
-            Self::AnalyticsView => vec![Self::AnalyticsView],
+            Self::AnalyticsView => vec![Self::AnalyticsView, Self::OperationsView],
 
             Self::UsersView => vec![Self::UsersView],
             Self::UsersManage => {
@@ -70,12 +84,19 @@ impl PermissionGroupExt for PermissionGroup {
             }
 
             Self::OrganizationManage => vec![Self::OrganizationManage],
+
+            Self::AccountView => vec![Self::AccountView],
+            Self::AccountManage => vec![Self::AccountView, Self::AccountManage],
         }
     }
 }
 
 pub trait ParentGroupExt {
     fn resources(&self) -> Vec<Resource>;
+    fn get_descriptions_for_groups(
+        entity_type: EntityType,
+        groups: Vec<PermissionGroup>,
+    ) -> HashMap<ParentGroup, String>;
 }
 
 impl ParentGroupExt for ParentGroup {
@@ -86,10 +107,38 @@ impl ParentGroupExt for ParentGroup {
             Self::Workflows => WORKFLOWS.to_vec(),
             Self::Analytics => ANALYTICS.to_vec(),
             Self::Users => USERS.to_vec(),
-            Self::Merchant | Self::Organization => ACCOUNT.to_vec(),
+            Self::Account => ACCOUNT.to_vec(),
             Self::Recon => RECON.to_vec(),
         }
     }
+
+    fn get_descriptions_for_groups(
+        entity_type: EntityType,
+        groups: Vec<PermissionGroup>,
+    ) -> HashMap<Self, String> {
+        Self::iter()
+            .filter_map(|parent| {
+                let scopes = groups
+                    .iter()
+                    .filter(|group| group.parent() == parent)
+                    .map(|group| group.scope())
+                    .max()?;
+
+                let resources = parent
+                    .resources()
+                    .iter()
+                    .filter(|res| res.entities().iter().any(|entity| entity <= &entity_type))
+                    .map(|res| permissions::get_resource_name(res, &entity_type))
+                    .collect::<Vec<_>>()
+                    .join(", ");
+
+                Some((
+                    parent,
+                    format!("{} {}", permissions::get_scope_name(&scopes), resources),
+                ))
+            })
+            .collect()
+    }
 }
 
 pub static OPERATIONS: [Resource; 8] = [
@@ -105,11 +154,10 @@ pub static OPERATIONS: [Resource; 8] = [
 
 pub static CONNECTORS: [Resource; 2] = [Resource::Connector, Resource::Account];
 
-pub static WORKFLOWS: [Resource; 5] = [
+pub static WORKFLOWS: [Resource; 4] = [
     Resource::Routing,
     Resource::ThreeDsDecisionManager,
     Resource::SurchargeDecisionManager,
-    Resource::Connector,
     Resource::Account,
 ];
 
diff --git a/crates/router/src/services/authorization/permissions.rs b/crates/router/src/services/authorization/permissions.rs
index 0521db7acc1..6e472d55623 100644
--- a/crates/router/src/services/authorization/permissions.rs
+++ b/crates/router/src/services/authorization/permissions.rs
@@ -73,3 +73,34 @@ generate_permissions! {
         },
     ]
 }
+
+pub fn get_resource_name(resource: &Resource, entity_type: &EntityType) -> &'static str {
+    match (resource, entity_type) {
+        (Resource::Payment, _) => "Payments",
+        (Resource::Refund, _) => "Refunds",
+        (Resource::Dispute, _) => "Disputes",
+        (Resource::Mandate, _) => "Mandates",
+        (Resource::Customer, _) => "Customers",
+        (Resource::Payout, _) => "Payouts",
+        (Resource::ApiKey, _) => "Api Keys",
+        (Resource::Connector, _) => "Payment Processors, Payout Processors, Fraud & Risk Managers",
+        (Resource::Routing, _) => "Routing",
+        (Resource::ThreeDsDecisionManager, _) => "3DS Decision Manager",
+        (Resource::SurchargeDecisionManager, _) => "Surcharge Decision Manager",
+        (Resource::Analytics, _) => "Analytics",
+        (Resource::Report, _) => "Operation Reports",
+        (Resource::User, _) => "Users",
+        (Resource::WebhookEvent, _) => "Webhook Events",
+        (Resource::Recon, _) => "Reconciliation Reports",
+        (Resource::Account, EntityType::Profile) => "Business Profile Account",
+        (Resource::Account, EntityType::Merchant) => "Merchant Account",
+        (Resource::Account, EntityType::Organization) => "Organization Account",
+    }
+}
+
+pub fn get_scope_name(scope: &PermissionScope) -> &'static str {
+    match scope {
+        PermissionScope::Read => "View",
+        PermissionScope::Write => "View and Manage",
+    }
+}
diff --git a/crates/router/src/services/authorization/roles/predefined_roles.rs b/crates/router/src/services/authorization/roles/predefined_roles.rs
index 33a1d5f53ca..39f6d47f824 100644
--- a/crates/router/src/services/authorization/roles/predefined_roles.rs
+++ b/crates/router/src/services/authorization/roles/predefined_roles.rs
@@ -24,7 +24,9 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::UsersView,
                 PermissionGroup::UsersManage,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
                 PermissionGroup::MerchantDetailsManage,
+                PermissionGroup::AccountManage,
                 PermissionGroup::OrganizationManage,
                 PermissionGroup::ReconOps,
             ],
@@ -48,6 +50,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::AnalyticsView,
                 PermissionGroup::UsersView,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
             ],
             role_id: common_utils::consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(),
             role_name: "internal_view_only".to_string(),
@@ -75,7 +78,9 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::UsersView,
                 PermissionGroup::UsersManage,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
                 PermissionGroup::MerchantDetailsManage,
+                PermissionGroup::AccountManage,
                 PermissionGroup::OrganizationManage,
                 PermissionGroup::ReconOps,
             ],
@@ -105,7 +110,9 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::UsersView,
                 PermissionGroup::UsersManage,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
                 PermissionGroup::MerchantDetailsManage,
+                PermissionGroup::AccountManage,
                 PermissionGroup::ReconOps,
             ],
             role_id: consts::user_role::ROLE_ID_MERCHANT_ADMIN.to_string(),
@@ -128,6 +135,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::AnalyticsView,
                 PermissionGroup::UsersView,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
             ],
             role_id: consts::user_role::ROLE_ID_MERCHANT_VIEW_ONLY.to_string(),
             role_name: "merchant_view_only".to_string(),
@@ -148,6 +156,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::UsersView,
                 PermissionGroup::UsersManage,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
             ],
             role_id: consts::user_role::ROLE_ID_MERCHANT_IAM_ADMIN.to_string(),
             role_name: "merchant_iam".to_string(),
@@ -168,7 +177,9 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::AnalyticsView,
                 PermissionGroup::UsersView,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
                 PermissionGroup::MerchantDetailsManage,
+                PermissionGroup::AccountManage,
             ],
             role_id: consts::user_role::ROLE_ID_MERCHANT_DEVELOPER.to_string(),
             role_name: "merchant_developer".to_string(),
@@ -191,6 +202,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::AnalyticsView,
                 PermissionGroup::UsersView,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
             ],
             role_id: consts::user_role::ROLE_ID_MERCHANT_OPERATOR.to_string(),
             role_name: "merchant_operator".to_string(),
@@ -210,6 +222,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::AnalyticsView,
                 PermissionGroup::UsersView,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
             ],
             role_id: consts::user_role::ROLE_ID_MERCHANT_CUSTOMER_SUPPORT.to_string(),
             role_name: "customer_support".to_string(),
@@ -237,7 +250,9 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::UsersView,
                 PermissionGroup::UsersManage,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
                 PermissionGroup::MerchantDetailsManage,
+                PermissionGroup::AccountManage,
             ],
             role_id: consts::user_role::ROLE_ID_PROFILE_ADMIN.to_string(),
             role_name: "profile_admin".to_string(),
@@ -259,6 +274,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::AnalyticsView,
                 PermissionGroup::UsersView,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
             ],
             role_id: consts::user_role::ROLE_ID_PROFILE_VIEW_ONLY.to_string(),
             role_name: "profile_view_only".to_string(),
@@ -279,6 +295,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::UsersView,
                 PermissionGroup::UsersManage,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
             ],
             role_id: consts::user_role::ROLE_ID_PROFILE_IAM_ADMIN.to_string(),
             role_name: "profile_iam".to_string(),
@@ -299,7 +316,9 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::AnalyticsView,
                 PermissionGroup::UsersView,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
                 PermissionGroup::MerchantDetailsManage,
+                PermissionGroup::AccountManage,
             ],
             role_id: consts::user_role::ROLE_ID_PROFILE_DEVELOPER.to_string(),
             role_name: "profile_developer".to_string(),
@@ -322,6 +341,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::AnalyticsView,
                 PermissionGroup::UsersView,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
             ],
             role_id: consts::user_role::ROLE_ID_PROFILE_OPERATOR.to_string(),
             role_name: "profile_operator".to_string(),
@@ -341,6 +361,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
                 PermissionGroup::AnalyticsView,
                 PermissionGroup::UsersView,
                 PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
             ],
             role_id: consts::user_role::ROLE_ID_PROFILE_CUSTOMER_SUPPORT.to_string(),
             role_name: "profile_customer_support".to_string(),
diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs
index 8d853ded338..652ce2b81db 100644
--- a/crates/router_env/src/logger/types.rs
+++ b/crates/router_env/src/logger/types.rs
@@ -374,6 +374,8 @@ pub enum Flow {
     GetAuthorizationInfo,
     /// Get Roles info
     GetRolesInfo,
+    /// Get Parent Group Info
+    GetParentGroupInfo,
     /// List roles v2
     ListRolesV2,
     /// List invitable roles at entity level
@@ -382,8 +384,12 @@ pub enum Flow {
     ListUpdatableRolesAtEntityLevel,
     /// Get role
     GetRole,
+    /// Get parent info for role
+    GetRoleV2,
     /// Get role from token
     GetRoleFromToken,
+    /// Get resources and groups for role from token
+    GetRoleFromTokenV2,
     /// Update user role
     UpdateUserRole,
     /// Create merchant account for user in a org

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

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

## Description
<!-- Describe your changes in detail -->
Make new APIs to replace
- `/user/role/{{role_id}}` -> `/user/role/{{role_id}}/v2`
- `/user/role` -> `/user/role/v2`
- `/user/module/list` -> `/user/parent/list`

The new APIs will have proper description and parent info which has entity level context unlike the previous APIs.

### 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 #6439.

## 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. Parent List
    ```
    curl --location 'http://localhost:8080/user/parent/list' \
    --header 'Authorization: JWT' \
    ```
    ```json
    [
      {
        "name": "Operations",
        "description": "View and Manage Payments, Refunds, Mandates, Disputes, Customers, Payouts, Operation Reports, Organization Account",
        "scopes": [
          "write",
          "read"
        ]
      },
      {
        "name": "Recon",
        "description": "View and Manage Reconciliation Reports",
        "scopes": [
          "write"
        ]
      },
      {
        "name": "Analytics",
        "description": "View Analytics, Operation Reports, Organization Account",
        "scopes": [
          "read"
        ]
      },
      {
        "name": "Workflows",
        "description": "View and Manage Routing, 3DS Decision Manager, Surcharge Decision Manager, Organization Account",
        "scopes": [
          "write",
          "read"
        ]
      },
      {
        "name": "Users",
        "description": "View and Manage Users, Organization Account",
        "scopes": [
          "read",
          "write"
        ]
      },
      {
        "name": "Account",
        "description": "View and Manage Organization Account, Api Keys, Webhook Events",
        "scopes": [
          "read",
          "write"
        ]
      },
      {
        "name": "Connectors",
        "description": "View and Manage Payment Processors, Payout Processors, Fraud & Risk Managers, Organization Account",
        "scopes": [
          "write",
          "read"
        ]
      }
    ]
    ```
    The descriptions and scopes in the above response will change with respect the role.
2. Get Role V2
    ```
    curl --location 'http://localhost:8080/user/role/merchant_admin/v2' \
    --header 'Authorization: JWT' \
    ```
    
    ```json
    {
        "role_id": "merchant_admin",
        "parent_groups": [
            {
                "name": "Users",
                "description": "View and Manage Users, Merchant Account",
                "scopes": [
                    "read",
                    "write"
                ]
            },
            {
                "name": "Connectors",
                "description": "View and Manage Payment Processors, Payout Processors, Fraud & Risk Managers, Merchant Account",
                "scopes": [
                    "read",
                    "write"
                ]
            },
            {
                "name": "Recon",
                "description": "View and Manage Reconciliation Reports",
                "scopes": [
                    "write"
                ]
            },
            {
                "name": "Account",
                "description": "View and Manage Merchant Account, Api Keys, Webhook Events",
                "scopes": [
                    "read",
                    "write"
                ]
            },
            {
                "name": "Workflows",
                "description": "View and Manage Routing, 3DS Decision Manager, Surcharge Decision Manager, Merchant Account",
                "scopes": [
                    "write",
                    "read"
                ]
            },
            {
                "name": "Analytics",
                "description": "View Analytics, Operation Reports, Merchant Account",
                "scopes": [
                    "read"
                ]
            },
            {
                "name": "Operations",
                "description": "View and Manage Payments, Refunds, Mandates, Disputes, Customers, Payouts, Operation Reports, Merchant Account",
                "scopes": [
                    "read",
                    "write"
                ]
            }
        ],
        "role_name": "merchant_admin",
        "role_scope": "organization"
    }
    ```
3. Get Role from Token V2
    ```
    curl --location 'http://localhost:8080/user/role/v2' \
    --header 'Authorization: JWT' \
    ```
    
    ```
    {
        "groups": [
            "connectors_manage",
            "account_manage",
            "users_manage",
            "merchant_details_manage",
            "merchant_details_view",
            "workflows_manage",
            "analytics_view",
            "operations_view",
            "organization_manage",
            "connectors_view",
            "recon_ops",
            "users_view",
            "workflows_view",
            "account_view",
            "operations_manage"
        ],
        "resources": [
            "routing",
            "customer",
            "payout",
            "api_key",
            "refund",
            "connector",
            "user",
            "mandate",
            "analytics",
            "account",
            "surcharge_decision_manager",
            "payment",
            "webhook_event",
            "recon",
            "three_ds_decision_manager",
            "dispute",
            "report"
        ]
    }
    ```


## 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
