mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(user_roles): add parent group info based API to fetch permissions for user role (#9487)
This commit is contained in:
@ -36,13 +36,13 @@ pub struct RoleInfoWithGroupsResponse {
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct RoleInfoWithParents {
|
||||
pub role_id: String,
|
||||
pub parent_groups: Vec<ParentGroupInfo>,
|
||||
pub parent_groups: Vec<ParentGroupDescription>,
|
||||
pub role_name: String,
|
||||
pub role_scope: RoleScope,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct ParentGroupInfo {
|
||||
pub struct ParentGroupDescription {
|
||||
pub name: ParentGroup,
|
||||
pub description: String,
|
||||
pub scopes: Vec<PermissionScope>,
|
||||
@ -74,7 +74,7 @@ pub struct RoleInfoResponseWithParentsGroup {
|
||||
pub role_id: String,
|
||||
pub role_name: String,
|
||||
pub entity_type: EntityType,
|
||||
pub parent_groups: Vec<ParentGroupInfo>,
|
||||
pub parent_groups: Vec<ParentGroupDescription>,
|
||||
pub role_scope: RoleScope,
|
||||
}
|
||||
|
||||
@ -117,3 +117,10 @@ pub enum ListRolesResponse {
|
||||
WithGroups(Vec<RoleInfoResponseNew>),
|
||||
WithParentGroups(Vec<RoleInfoResponseWithParentsGroup>),
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct ParentGroupInfo {
|
||||
pub name: ParentGroup,
|
||||
pub resources: Vec<Resource>,
|
||||
pub scopes: Vec<PermissionScope>,
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ pub async fn get_parent_group_info(
|
||||
state: SessionState,
|
||||
user_from_token: auth::UserFromToken,
|
||||
request: role_api::GetParentGroupsInfoQueryParams,
|
||||
) -> UserResponse<Vec<role_api::ParentGroupInfo>> {
|
||||
) -> UserResponse<Vec<role_api::ParentGroupDescription>> {
|
||||
let role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
|
||||
&state,
|
||||
&user_from_token.role_id,
|
||||
@ -119,17 +119,21 @@ pub async fn get_parent_group_info(
|
||||
ParentGroup::get_descriptions_for_groups(entity_type, PermissionGroup::iter().collect())
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|(parent_group, description)| role_api::ParentGroupInfo {
|
||||
.map(
|
||||
|(parent_group, description)| role_api::ParentGroupDescription {
|
||||
name: parent_group.clone(),
|
||||
description,
|
||||
scopes: PermissionGroup::iter()
|
||||
.filter_map(|group| (group.parent() == parent_group).then_some(group.scope()))
|
||||
.filter_map(|group| {
|
||||
(group.parent() == parent_group).then_some(group.scope())
|
||||
})
|
||||
// TODO: Remove this hashset conversion when merchant access
|
||||
// and organization access groups are removed
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
.collect(),
|
||||
})
|
||||
},
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(ApplicationResponse::Json(parent_groups))
|
||||
|
||||
@ -58,6 +58,25 @@ pub async fn get_groups_and_resources_for_role_from_token(
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn get_parent_groups_info_for_role_from_token(
|
||||
state: SessionState,
|
||||
user_from_token: UserFromToken,
|
||||
) -> UserResponse<Vec<role_api::ParentGroupInfo>> {
|
||||
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 parent_groups = utils::user_role::permission_groups_to_parent_group_info(
|
||||
&groups,
|
||||
role_info.get_entity_type(),
|
||||
);
|
||||
|
||||
Ok(ApplicationResponse::Json(parent_groups))
|
||||
}
|
||||
|
||||
pub async fn create_role(
|
||||
state: SessionState,
|
||||
user_from_token: UserFromToken,
|
||||
@ -248,16 +267,31 @@ pub async fn create_role_v2(
|
||||
.await
|
||||
.to_duplicate_response(UserErrors::RoleNameAlreadyExists)?;
|
||||
|
||||
let response_parent_groups =
|
||||
let parent_group_details =
|
||||
utils::user_role::permission_groups_to_parent_group_info(&role.groups, role.entity_type);
|
||||
|
||||
let parent_group_descriptions: Vec<role_api::ParentGroupDescription> = parent_group_details
|
||||
.into_iter()
|
||||
.filter_map(|group_details| {
|
||||
let description = utils::user_role::resources_to_description(
|
||||
group_details.resources,
|
||||
role.entity_type,
|
||||
)?;
|
||||
Some(role_api::ParentGroupDescription {
|
||||
name: group_details.name,
|
||||
description,
|
||||
scopes: group_details.scopes,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(ApplicationResponse::Json(
|
||||
role_api::RoleInfoResponseWithParentsGroup {
|
||||
role_id: role.role_id,
|
||||
role_name: role.role_name,
|
||||
role_scope: role.scope,
|
||||
entity_type: role.entity_type,
|
||||
parent_groups: response_parent_groups,
|
||||
parent_groups: parent_group_descriptions,
|
||||
},
|
||||
))
|
||||
}
|
||||
@ -325,7 +359,8 @@ pub async fn get_parent_info_for_role(
|
||||
role.role_id
|
||||
))?
|
||||
.into_iter()
|
||||
.map(|(parent_group, description)| role_api::ParentGroupInfo {
|
||||
.map(
|
||||
|(parent_group, description)| role_api::ParentGroupDescription {
|
||||
name: parent_group.clone(),
|
||||
description,
|
||||
scopes: role_info
|
||||
@ -337,7 +372,8 @@ pub async fn get_parent_info_for_role(
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
.collect(),
|
||||
})
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
|
||||
Ok(ApplicationResponse::Json(role_api::RoleInfoWithParents {
|
||||
@ -517,16 +553,33 @@ pub async fn list_roles_with_info(
|
||||
|
||||
(is_lower_entity && request_filter).then_some({
|
||||
let permission_groups = role_info.get_permission_groups();
|
||||
let parent_groups = utils::user_role::permission_groups_to_parent_group_info(
|
||||
let parent_group_details =
|
||||
utils::user_role::permission_groups_to_parent_group_info(
|
||||
&permission_groups,
|
||||
role_info.get_entity_type(),
|
||||
);
|
||||
|
||||
let parent_group_descriptions: Vec<role_api::ParentGroupDescription> =
|
||||
parent_group_details
|
||||
.into_iter()
|
||||
.filter_map(|group_details| {
|
||||
let description = utils::user_role::resources_to_description(
|
||||
group_details.resources,
|
||||
role_info.get_entity_type(),
|
||||
)?;
|
||||
Some(role_api::ParentGroupDescription {
|
||||
name: group_details.name,
|
||||
description,
|
||||
scopes: group_details.scopes,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
role_api::RoleInfoResponseWithParentsGroup {
|
||||
role_id: role_info.get_role_id().to_string(),
|
||||
role_name: role_info.get_role_name().to_string(),
|
||||
entity_type: role_info.get_entity_type(),
|
||||
parent_groups,
|
||||
parent_groups: parent_group_descriptions,
|
||||
role_scope: role_info.get_scope(),
|
||||
}
|
||||
})
|
||||
|
||||
@ -2779,7 +2779,8 @@ impl User {
|
||||
}
|
||||
|
||||
// Role information
|
||||
route = route.service(
|
||||
route =
|
||||
route.service(
|
||||
web::scope("/role")
|
||||
.service(
|
||||
web::resource("")
|
||||
@ -2791,28 +2792,30 @@ impl User {
|
||||
web::resource("/v2")
|
||||
.route(web::post().to(user_role::create_role_v2))
|
||||
.route(
|
||||
web::get().to(user_role::get_groups_and_resources_for_role_from_token),
|
||||
web::get()
|
||||
.to(user_role::get_groups_and_resources_for_role_from_token),
|
||||
),
|
||||
)
|
||||
.service(web::resource("/v3").route(
|
||||
web::get().to(user_role::get_parent_groups_info_for_role_from_token),
|
||||
))
|
||||
// TODO: To be deprecated
|
||||
.service(
|
||||
web::resource("/v2/list").route(web::get().to(user_role::list_roles_with_info)),
|
||||
web::resource("/v2/list")
|
||||
.route(web::get().to(user_role::list_roles_with_info)),
|
||||
)
|
||||
.service(
|
||||
web::scope("/list")
|
||||
.service(
|
||||
web::resource("").route(web::get().to(user_role::list_roles_with_info)),
|
||||
web::resource("")
|
||||
.route(web::get().to(user_role::list_roles_with_info)),
|
||||
)
|
||||
.service(
|
||||
web::resource("/invite").route(
|
||||
.service(web::resource("/invite").route(
|
||||
web::get().to(user_role::list_invitable_roles_at_entity_level),
|
||||
),
|
||||
)
|
||||
.service(
|
||||
web::resource("/update").route(
|
||||
))
|
||||
.service(web::resource("/update").route(
|
||||
web::get().to(user_role::list_updatable_roles_at_entity_level),
|
||||
),
|
||||
),
|
||||
)),
|
||||
)
|
||||
.service(
|
||||
web::resource("/{role_id}")
|
||||
|
||||
@ -306,6 +306,7 @@ impl From<Flow> for ApiIdentifier {
|
||||
| Flow::GetRoleV2
|
||||
| Flow::GetRoleFromToken
|
||||
| Flow::GetRoleFromTokenV2
|
||||
| Flow::GetParentGroupsInfoForRoleFromToken
|
||||
| Flow::UpdateUserRole
|
||||
| Flow::GetAuthorizationInfo
|
||||
| Flow::GetRolesInfo
|
||||
|
||||
@ -74,6 +74,27 @@ pub async fn get_groups_and_resources_for_role_from_token(
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_parent_groups_info_for_role_from_token(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::GetParentGroupsInfoForRoleFromToken;
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state.clone(),
|
||||
&req,
|
||||
(),
|
||||
|state, user, _, _| async move {
|
||||
role_core::get_parent_groups_info_for_role_from_token(state, user).await
|
||||
},
|
||||
&auth::DashboardNoPermissionAuth,
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
// TODO: To be deprecated
|
||||
pub async fn create_role(
|
||||
state: web::Data<AppState>,
|
||||
|
||||
@ -3,7 +3,7 @@ use std::{collections::HashMap, ops::Not};
|
||||
use common_enums::{EntityType, ParentGroup, PermissionGroup, PermissionScope, Resource};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use super::permissions::{self, ResourceExt};
|
||||
use super::permissions;
|
||||
|
||||
pub trait PermissionGroupExt {
|
||||
fn scope(&self) -> PermissionScope;
|
||||
@ -147,15 +147,8 @@ impl ParentGroupExt for ParentGroup {
|
||||
if !groups.iter().any(|group| group.parent() == parent) {
|
||||
return None;
|
||||
}
|
||||
let filtered_resources: Vec<_> = parent
|
||||
.resources()
|
||||
.into_iter()
|
||||
.filter(|res| res.entities().iter().any(|entity| entity <= &entity_type))
|
||||
.collect();
|
||||
|
||||
if filtered_resources.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let filtered_resources =
|
||||
permissions::filter_resources_by_entity_type(parent.resources(), entity_type)?;
|
||||
|
||||
let description = filtered_resources
|
||||
.iter()
|
||||
|
||||
@ -157,3 +157,15 @@ pub fn get_scope_name(scope: PermissionScope) -> &'static str {
|
||||
PermissionScope::Write => "View and Manage",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filter_resources_by_entity_type(
|
||||
resources: Vec<Resource>,
|
||||
entity_type: EntityType,
|
||||
) -> Option<Vec<Resource>> {
|
||||
let filtered: Vec<Resource> = resources
|
||||
.into_iter()
|
||||
.filter(|res| res.entities().iter().any(|entity| entity <= &entity_type))
|
||||
.collect();
|
||||
|
||||
(!filtered.is_empty()).then_some(filtered)
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ use crate::{
|
||||
services::authorization::{
|
||||
self as authz,
|
||||
permission_groups::{ParentGroupExt, PermissionGroupExt},
|
||||
roles,
|
||||
permissions, roles,
|
||||
},
|
||||
types::domain,
|
||||
};
|
||||
@ -570,15 +570,33 @@ pub fn permission_groups_to_parent_group_info(
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let description =
|
||||
ParentGroup::get_descriptions_for_groups(entity_type, permission_groups.to_vec())
|
||||
.and_then(|descriptions| descriptions.get(&name).cloned())?;
|
||||
let filtered_resources =
|
||||
permissions::filter_resources_by_entity_type(name.resources(), entity_type)?;
|
||||
|
||||
Some(role_api::ParentGroupInfo {
|
||||
name,
|
||||
description,
|
||||
resources: filtered_resources,
|
||||
scopes: unique_scopes,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn resources_to_description(
|
||||
resources: Vec<common_enums::Resource>,
|
||||
entity_type: EntityType,
|
||||
) -> Option<String> {
|
||||
if resources.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let filtered_resources = permissions::filter_resources_by_entity_type(resources, entity_type)?;
|
||||
|
||||
let description = filtered_resources
|
||||
.iter()
|
||||
.map(|res| permissions::get_resource_name(*res, entity_type))
|
||||
.collect::<Option<Vec<_>>>()?
|
||||
.join(", ");
|
||||
|
||||
Some(description)
|
||||
}
|
||||
|
||||
@ -429,6 +429,8 @@ pub enum Flow {
|
||||
GetRoleFromToken,
|
||||
/// Get resources and groups for role from token
|
||||
GetRoleFromTokenV2,
|
||||
/// Get parent groups info for role from token
|
||||
GetParentGroupsInfoForRoleFromToken,
|
||||
/// Update user role
|
||||
UpdateUserRole,
|
||||
/// Create merchant account for user in a org
|
||||
|
||||
Reference in New Issue
Block a user