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