mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 11:24:45 +08:00
refactor(user_roles): implement parent group info based role APIs (#8896)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -2,9 +2,10 @@ use common_utils::events::{ApiEventMetric, ApiEventsType};
|
||||
|
||||
use crate::user_role::{
|
||||
role::{
|
||||
CreateRoleRequest, GetRoleRequest, GroupsAndResources, ListRolesAtEntityLevelRequest,
|
||||
ListRolesRequest, RoleInfoResponseNew, RoleInfoWithGroupsResponse, RoleInfoWithParents,
|
||||
UpdateRoleRequest,
|
||||
CreateRoleRequest, CreateRoleV2Request, GetParentGroupsInfoQueryParams, GetRoleRequest,
|
||||
GroupsAndResources, ListRolesAtEntityLevelRequest, ListRolesQueryParams, ListRolesResponse,
|
||||
ParentGroupInfoRequest, RoleInfoResponseNew, RoleInfoResponseWithParentsGroup,
|
||||
RoleInfoWithGroupsResponse, RoleInfoWithParents, UpdateRoleRequest,
|
||||
},
|
||||
AuthorizationInfoResponse, DeleteUserRoleRequest, ListUsersInEntityRequest,
|
||||
UpdateUserRoleRequest,
|
||||
@ -14,17 +15,22 @@ common_utils::impl_api_event_type!(
|
||||
Miscellaneous,
|
||||
(
|
||||
GetRoleRequest,
|
||||
GetParentGroupsInfoQueryParams,
|
||||
AuthorizationInfoResponse,
|
||||
UpdateUserRoleRequest,
|
||||
DeleteUserRoleRequest,
|
||||
CreateRoleRequest,
|
||||
CreateRoleV2Request,
|
||||
UpdateRoleRequest,
|
||||
ListRolesAtEntityLevelRequest,
|
||||
RoleInfoResponseNew,
|
||||
RoleInfoWithGroupsResponse,
|
||||
ListUsersInEntityRequest,
|
||||
ListRolesRequest,
|
||||
ListRolesQueryParams,
|
||||
GroupsAndResources,
|
||||
RoleInfoWithParents
|
||||
RoleInfoWithParents,
|
||||
ParentGroupInfoRequest,
|
||||
RoleInfoResponseWithParentsGroup,
|
||||
ListRolesResponse
|
||||
)
|
||||
);
|
||||
|
||||
@ -10,6 +10,14 @@ pub struct CreateRoleRequest {
|
||||
pub entity_type: Option<EntityType>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct CreateRoleV2Request {
|
||||
pub role_name: String,
|
||||
pub role_scope: RoleScope,
|
||||
pub entity_type: Option<EntityType>,
|
||||
pub parent_groups: Vec<ParentGroupInfoRequest>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct UpdateRoleRequest {
|
||||
pub groups: Option<Vec<PermissionGroup>>,
|
||||
@ -41,8 +49,15 @@ pub struct ParentGroupInfo {
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct ListRolesRequest {
|
||||
pub struct ParentGroupInfoRequest {
|
||||
pub name: ParentGroup,
|
||||
pub scopes: Vec<PermissionScope>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct ListRolesQueryParams {
|
||||
pub entity_type: Option<EntityType>,
|
||||
pub groups: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
@ -54,6 +69,15 @@ pub struct RoleInfoResponseNew {
|
||||
pub scope: RoleScope,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct RoleInfoResponseWithParentsGroup {
|
||||
pub role_id: String,
|
||||
pub role_name: String,
|
||||
pub entity_type: EntityType,
|
||||
pub parent_groups: Vec<ParentGroupInfo>,
|
||||
pub role_scope: RoleScope,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct GetRoleRequest {
|
||||
pub role_id: String,
|
||||
@ -64,6 +88,11 @@ pub struct ListRolesAtEntityLevelRequest {
|
||||
pub entity_type: EntityType,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct GetParentGroupsInfoQueryParams {
|
||||
pub entity_type: Option<EntityType>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub enum RoleCheckType {
|
||||
Invite,
|
||||
@ -81,3 +110,10 @@ pub struct GroupsAndResources {
|
||||
pub groups: Vec<PermissionGroup>,
|
||||
pub resources: Vec<Resource>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ListRolesResponse {
|
||||
WithGroups(Vec<RoleInfoResponseNew>),
|
||||
WithParentGroups(Vec<RoleInfoResponseWithParentsGroup>),
|
||||
}
|
||||
|
||||
@ -7708,7 +7708,9 @@ pub enum PermissionGroup {
|
||||
ThemeManage,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, PartialEq, Eq, Hash, strum::EnumIter)]
|
||||
#[derive(
|
||||
Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, strum::EnumIter,
|
||||
)]
|
||||
pub enum ParentGroup {
|
||||
Operations,
|
||||
Connectors,
|
||||
@ -7722,7 +7724,7 @@ pub enum ParentGroup {
|
||||
Theme,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, serde::Serialize)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Resource {
|
||||
Payment,
|
||||
@ -7753,7 +7755,9 @@ pub enum Resource {
|
||||
Theme,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, serde::Serialize, Hash)]
|
||||
#[derive(
|
||||
Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, serde::Serialize, serde::Deserialize, Hash,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PermissionScope {
|
||||
Read = 0,
|
||||
|
||||
@ -89,6 +89,7 @@ pub async fn get_authorization_info_with_group_tag(
|
||||
pub async fn get_parent_group_info(
|
||||
state: SessionState,
|
||||
user_from_token: auth::UserFromToken,
|
||||
request: role_api::GetParentGroupsInfoQueryParams,
|
||||
) -> UserResponse<Vec<role_api::ParentGroupInfo>> {
|
||||
let role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
|
||||
&state,
|
||||
@ -102,24 +103,34 @@ pub async fn get_parent_group_info(
|
||||
.await
|
||||
.to_not_found_response(UserErrors::InvalidRoleId)?;
|
||||
|
||||
let parent_groups = ParentGroup::get_descriptions_for_groups(
|
||||
role_info.get_entity_type(),
|
||||
PermissionGroup::iter().collect(),
|
||||
)
|
||||
.unwrap_or_default()
|
||||
.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<_>>()
|
||||
let entity_type = request
|
||||
.entity_type
|
||||
.unwrap_or_else(|| role_info.get_entity_type());
|
||||
|
||||
if role_info.get_entity_type() < entity_type {
|
||||
return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
|
||||
"Invalid operation, requestor entity type = {} cannot access entity type = {}",
|
||||
role_info.get_entity_type(),
|
||||
entity_type
|
||||
));
|
||||
}
|
||||
|
||||
let parent_groups =
|
||||
ParentGroup::get_descriptions_for_groups(entity_type, PermissionGroup::iter().collect())
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.collect(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.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 merchant access
|
||||
// and organization access groups are removed
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
.collect(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(ApplicationResponse::Json(parent_groups))
|
||||
}
|
||||
|
||||
@ -157,6 +157,111 @@ pub async fn create_role(
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn create_role_v2(
|
||||
state: SessionState,
|
||||
user_from_token: UserFromToken,
|
||||
req: role_api::CreateRoleV2Request,
|
||||
_req_state: ReqState,
|
||||
) -> UserResponse<role_api::RoleInfoResponseWithParentsGroup> {
|
||||
let now = common_utils::date_time::now();
|
||||
|
||||
let user_entity_type = user_from_token
|
||||
.get_role_info_from_db(&state)
|
||||
.await
|
||||
.attach_printable("Invalid role_id in JWT")?
|
||||
.get_entity_type();
|
||||
|
||||
let role_entity_type = req.entity_type.unwrap_or(EntityType::Merchant);
|
||||
|
||||
if matches!(role_entity_type, EntityType::Organization) {
|
||||
return Err(report!(UserErrors::InvalidRoleOperation))
|
||||
.attach_printable("User trying to create org level custom role");
|
||||
}
|
||||
|
||||
let requestor_entity_from_role_scope = EntityType::from(req.role_scope);
|
||||
|
||||
if requestor_entity_from_role_scope < role_entity_type {
|
||||
return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
|
||||
"User is trying to create role of type {role_entity_type} and scope {requestor_entity_from_role_scope}",
|
||||
));
|
||||
}
|
||||
|
||||
let max_from_scope_and_entity = cmp::max(requestor_entity_from_role_scope, role_entity_type);
|
||||
|
||||
if user_entity_type < max_from_scope_and_entity {
|
||||
return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
|
||||
"{user_entity_type} is trying to create of scope {requestor_entity_from_role_scope} and of type {role_entity_type}",
|
||||
));
|
||||
}
|
||||
|
||||
let role_name = RoleName::new(req.role_name.clone())?;
|
||||
|
||||
let permission_groups =
|
||||
utils::user_role::parent_group_info_request_to_permission_groups(&req.parent_groups)?;
|
||||
|
||||
utils::user_role::validate_role_groups(&permission_groups)?;
|
||||
utils::user_role::validate_role_name(
|
||||
&state,
|
||||
&role_name,
|
||||
&user_from_token.merchant_id,
|
||||
&user_from_token.org_id,
|
||||
user_from_token
|
||||
.tenant_id
|
||||
.as_ref()
|
||||
.unwrap_or(&state.tenant.tenant_id),
|
||||
&user_from_token.profile_id,
|
||||
&role_entity_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let (org_id, merchant_id, profile_id) = match role_entity_type {
|
||||
EntityType::Organization | EntityType::Tenant => (user_from_token.org_id, None, None),
|
||||
EntityType::Merchant => (
|
||||
user_from_token.org_id,
|
||||
Some(user_from_token.merchant_id),
|
||||
None,
|
||||
),
|
||||
EntityType::Profile => (
|
||||
user_from_token.org_id,
|
||||
Some(user_from_token.merchant_id),
|
||||
Some(user_from_token.profile_id),
|
||||
),
|
||||
};
|
||||
|
||||
let role = state
|
||||
.global_store
|
||||
.insert_role(RoleNew {
|
||||
role_id: generate_id_with_default_len("role"),
|
||||
role_name: role_name.get_role_name(),
|
||||
merchant_id,
|
||||
org_id,
|
||||
groups: permission_groups,
|
||||
scope: req.role_scope,
|
||||
entity_type: role_entity_type,
|
||||
created_by: user_from_token.user_id.clone(),
|
||||
last_modified_by: user_from_token.user_id,
|
||||
created_at: now,
|
||||
last_modified_at: now,
|
||||
profile_id,
|
||||
tenant_id: user_from_token.tenant_id.unwrap_or(state.tenant.tenant_id),
|
||||
})
|
||||
.await
|
||||
.to_duplicate_response(UserErrors::RoleNameAlreadyExists)?;
|
||||
|
||||
let response_parent_groups =
|
||||
utils::user_role::permission_groups_to_parent_group_info(&role.groups, role.entity_type);
|
||||
|
||||
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,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn get_role_with_groups(
|
||||
state: SessionState,
|
||||
user_from_token: UserFromToken,
|
||||
@ -227,7 +332,7 @@ pub async fn get_parent_info_for_role(
|
||||
.get_permission_groups()
|
||||
.iter()
|
||||
.filter_map(|group| (group.parent() == parent_group).then_some(group.scope()))
|
||||
// TODO: Remove this hashset conversion when merhant access
|
||||
// TODO: Remove this hashset conversion when merchant access
|
||||
// and organization access groups are removed
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
@ -331,8 +436,8 @@ pub async fn update_role(
|
||||
pub async fn list_roles_with_info(
|
||||
state: SessionState,
|
||||
user_from_token: UserFromToken,
|
||||
request: role_api::ListRolesRequest,
|
||||
) -> UserResponse<Vec<role_api::RoleInfoResponseNew>> {
|
||||
request: role_api::ListRolesQueryParams,
|
||||
) -> UserResponse<role_api::ListRolesResponse> {
|
||||
let user_role_info = user_from_token
|
||||
.get_role_info_from_db(&state)
|
||||
.await
|
||||
@ -397,25 +502,61 @@ pub async fn list_roles_with_info(
|
||||
|
||||
role_info_vec.extend(custom_roles.into_iter().map(roles::RoleInfo::from));
|
||||
|
||||
let list_role_info_response = role_info_vec
|
||||
.into_iter()
|
||||
.filter_map(|role_info| {
|
||||
let is_lower_entity = user_role_entity >= role_info.get_entity_type();
|
||||
let request_filter = request.entity_type.map_or(true, |entity_type| {
|
||||
entity_type == role_info.get_entity_type()
|
||||
});
|
||||
if request.groups == Some(true) {
|
||||
let list_role_info_response = role_info_vec
|
||||
.into_iter()
|
||||
.filter_map(|role_info| {
|
||||
let is_lower_entity = user_role_entity >= role_info.get_entity_type();
|
||||
let request_filter = request.entity_type.map_or(true, |entity_type| {
|
||||
entity_type == role_info.get_entity_type()
|
||||
});
|
||||
|
||||
(is_lower_entity && request_filter).then_some(role_api::RoleInfoResponseNew {
|
||||
role_id: role_info.get_role_id().to_string(),
|
||||
role_name: role_info.get_role_name().to_string(),
|
||||
groups: role_info.get_permission_groups().to_vec(),
|
||||
entity_type: role_info.get_entity_type(),
|
||||
scope: role_info.get_scope(),
|
||||
(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(
|
||||
&permission_groups,
|
||||
role_info.get_entity_type(),
|
||||
);
|
||||
|
||||
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,
|
||||
role_scope: role_info.get_scope(),
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(ApplicationResponse::Json(list_role_info_response))
|
||||
Ok(ApplicationResponse::Json(
|
||||
role_api::ListRolesResponse::WithParentGroups(list_role_info_response),
|
||||
))
|
||||
}
|
||||
// TODO: To be deprecated
|
||||
else {
|
||||
let list_role_info_response = role_info_vec
|
||||
.into_iter()
|
||||
.filter_map(|role_info| {
|
||||
let is_lower_entity = user_role_entity >= role_info.get_entity_type();
|
||||
let request_filter = request.entity_type.map_or(true, |entity_type| {
|
||||
entity_type == role_info.get_entity_type()
|
||||
});
|
||||
|
||||
(is_lower_entity && request_filter).then_some(role_api::RoleInfoResponseNew {
|
||||
role_id: role_info.get_role_id().to_string(),
|
||||
role_name: role_info.get_role_name().to_string(),
|
||||
groups: role_info.get_permission_groups().to_vec(),
|
||||
entity_type: role_info.get_entity_type(),
|
||||
scope: role_info.get_scope(),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(ApplicationResponse::Json(
|
||||
role_api::ListRolesResponse::WithGroups(list_role_info_response),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_roles_at_entity_level(
|
||||
|
||||
@ -2722,45 +2722,51 @@ impl User {
|
||||
}
|
||||
|
||||
// Role information
|
||||
route =
|
||||
route.service(
|
||||
web::scope("/role")
|
||||
.service(
|
||||
web::resource("")
|
||||
.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")
|
||||
.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)),
|
||||
)
|
||||
.service(web::resource("/invite").route(
|
||||
route = route.service(
|
||||
web::scope("/role")
|
||||
.service(
|
||||
web::resource("")
|
||||
.route(web::get().to(user_role::get_role_from_token))
|
||||
// TODO: To be deprecated
|
||||
.route(web::post().to(user_role::create_role)),
|
||||
)
|
||||
.service(
|
||||
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),
|
||||
),
|
||||
)
|
||||
// TODO: To be deprecated
|
||||
.service(
|
||||
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)),
|
||||
)
|
||||
.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}")
|
||||
.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)),
|
||||
),
|
||||
);
|
||||
),
|
||||
),
|
||||
)
|
||||
.service(
|
||||
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)),
|
||||
),
|
||||
);
|
||||
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
{
|
||||
|
||||
@ -331,6 +331,7 @@ impl From<Flow> for ApiIdentifier {
|
||||
| Flow::AcceptInvitationsPreAuth
|
||||
| Flow::DeleteUserRole
|
||||
| Flow::CreateRole
|
||||
| Flow::CreateRoleV2
|
||||
| Flow::UpdateRole
|
||||
| Flow::UserFromEmail
|
||||
| Flow::ListUsersInLineage => Self::UserRole,
|
||||
|
||||
@ -74,7 +74,7 @@ pub async fn get_groups_and_resources_for_role_from_token(
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
// TODO: To be deprecated
|
||||
pub async fn create_role(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
@ -95,6 +95,26 @@ pub async fn create_role(
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn create_role_v2(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
json_payload: web::Json<role_api::CreateRoleV2Request>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::CreateRoleV2;
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state.clone(),
|
||||
&req,
|
||||
json_payload.into_inner(),
|
||||
role_core::create_role_v2,
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::MerchantUserWrite,
|
||||
},
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_role(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
@ -274,6 +294,7 @@ pub async fn get_role_information(
|
||||
pub async fn get_parent_group_info(
|
||||
state: web::Data<AppState>,
|
||||
http_req: HttpRequest,
|
||||
query: web::Query<role_api::GetParentGroupsInfoQueryParams>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::GetParentGroupInfo;
|
||||
|
||||
@ -281,9 +302,9 @@ pub async fn get_parent_group_info(
|
||||
flow,
|
||||
state.clone(),
|
||||
&http_req,
|
||||
(),
|
||||
|state, user_from_token, _, _| async move {
|
||||
user_role_core::get_parent_group_info(state, user_from_token).await
|
||||
query.into_inner(),
|
||||
|state, user_from_token, request, _| async move {
|
||||
user_role_core::get_parent_group_info(state, user_from_token, request).await
|
||||
},
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::ProfileUserRead,
|
||||
@ -317,7 +338,7 @@ pub async fn list_users_in_lineage(
|
||||
pub async fn list_roles_with_info(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
query: web::Query<role_api::ListRolesRequest>,
|
||||
query: web::Query<role_api::ListRolesQueryParams>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::ListRolesV2;
|
||||
|
||||
|
||||
@ -119,6 +119,7 @@ pub trait ParentGroupExt {
|
||||
entity_type: EntityType,
|
||||
groups: Vec<PermissionGroup>,
|
||||
) -> Option<HashMap<ParentGroup, String>>;
|
||||
fn get_available_scopes(&self) -> Vec<PermissionScope>;
|
||||
}
|
||||
|
||||
impl ParentGroupExt for ParentGroup {
|
||||
@ -143,24 +144,26 @@ impl ParentGroupExt for ParentGroup {
|
||||
) -> Option<HashMap<Self, String>> {
|
||||
let descriptions_map = Self::iter()
|
||||
.filter_map(|parent| {
|
||||
let scopes = groups
|
||||
.iter()
|
||||
.filter(|group| group.parent() == parent)
|
||||
.map(|group| group.scope())
|
||||
.max()?;
|
||||
|
||||
let resources = parent
|
||||
if !groups.iter().any(|group| group.parent() == parent) {
|
||||
return None;
|
||||
}
|
||||
let filtered_resources: Vec<_> = parent
|
||||
.resources()
|
||||
.iter()
|
||||
.into_iter()
|
||||
.filter(|res| res.entities().iter().any(|entity| entity <= &entity_type))
|
||||
.collect();
|
||||
|
||||
if filtered_resources.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let description = filtered_resources
|
||||
.iter()
|
||||
.map(|res| permissions::get_resource_name(*res, entity_type))
|
||||
.collect::<Option<Vec<_>>>()?
|
||||
.join(", ");
|
||||
|
||||
Some((
|
||||
parent,
|
||||
format!("{} {}", permissions::get_scope_name(scopes), resources),
|
||||
))
|
||||
Some((parent, description))
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
@ -169,6 +172,13 @@ impl ParentGroupExt for ParentGroup {
|
||||
.not()
|
||||
.then_some(descriptions_map)
|
||||
}
|
||||
|
||||
fn get_available_scopes(&self) -> Vec<PermissionScope> {
|
||||
PermissionGroup::iter()
|
||||
.filter(|group| group.parent() == *self)
|
||||
.map(|group| group.scope())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub static OPERATIONS: [Resource; 8] = [
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
use std::{cmp, collections::HashSet};
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{HashMap, HashSet},
|
||||
};
|
||||
|
||||
use common_enums::{EntityType, PermissionGroup};
|
||||
use api_models::user_role::role as role_api;
|
||||
use common_enums::{EntityType, ParentGroup, PermissionGroup};
|
||||
use common_utils::id_type;
|
||||
use diesel_models::{
|
||||
enums::{UserRoleVersion, UserStatus},
|
||||
@ -10,6 +14,7 @@ use diesel_models::{
|
||||
use error_stack::{report, Report, ResultExt};
|
||||
use router_env::logger;
|
||||
use storage_impl::errors::StorageError;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::{
|
||||
consts,
|
||||
@ -19,7 +24,11 @@ use crate::{
|
||||
user_role::{ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload},
|
||||
},
|
||||
routes::SessionState,
|
||||
services::authorization::{self as authz, roles},
|
||||
services::authorization::{
|
||||
self as authz,
|
||||
permission_groups::{ParentGroupExt, PermissionGroupExt},
|
||||
roles,
|
||||
},
|
||||
types::domain,
|
||||
};
|
||||
|
||||
@ -508,3 +517,68 @@ pub fn get_min_entity(
|
||||
|
||||
Ok(cmp::min(user_entity, filter_entity))
|
||||
}
|
||||
|
||||
pub fn parent_group_info_request_to_permission_groups(
|
||||
parent_groups: &[role_api::ParentGroupInfoRequest],
|
||||
) -> Result<Vec<PermissionGroup>, UserErrors> {
|
||||
parent_groups
|
||||
.iter()
|
||||
.try_fold(Vec::new(), |mut permission_groups, parent_group| {
|
||||
let scopes = &parent_group.scopes;
|
||||
|
||||
if scopes.is_empty() {
|
||||
return Err(UserErrors::InvalidRoleOperation);
|
||||
}
|
||||
|
||||
let available_scopes = parent_group.name.get_available_scopes();
|
||||
|
||||
if !scopes.iter().all(|scope| available_scopes.contains(scope)) {
|
||||
return Err(UserErrors::InvalidRoleOperation);
|
||||
}
|
||||
|
||||
let groups = PermissionGroup::iter()
|
||||
.filter(|group| {
|
||||
group.parent() == parent_group.name && scopes.contains(&group.scope())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
permission_groups.extend(groups);
|
||||
|
||||
Ok(permission_groups)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn permission_groups_to_parent_group_info(
|
||||
permission_groups: &[PermissionGroup],
|
||||
entity_type: EntityType,
|
||||
) -> Vec<role_api::ParentGroupInfo> {
|
||||
let parent_groups_map: HashMap<ParentGroup, Vec<common_enums::PermissionScope>> =
|
||||
permission_groups
|
||||
.iter()
|
||||
.fold(HashMap::new(), |mut acc, group| {
|
||||
let parent = group.parent();
|
||||
let scope = group.scope();
|
||||
acc.entry(parent).or_default().push(scope);
|
||||
acc
|
||||
});
|
||||
|
||||
parent_groups_map
|
||||
.into_iter()
|
||||
.filter_map(|(name, scopes)| {
|
||||
let unique_scopes = scopes
|
||||
.into_iter()
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let description =
|
||||
ParentGroup::get_descriptions_for_groups(entity_type, permission_groups.to_vec())
|
||||
.and_then(|descriptions| descriptions.get(&name).cloned())?;
|
||||
|
||||
Some(role_api::ParentGroupInfo {
|
||||
name,
|
||||
description,
|
||||
scopes: unique_scopes,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -473,6 +473,8 @@ pub enum Flow {
|
||||
PaymentsAuthorize,
|
||||
/// Create Role
|
||||
CreateRole,
|
||||
/// Create Role V2
|
||||
CreateRoleV2,
|
||||
/// Update Role
|
||||
UpdateRole,
|
||||
/// User email flow start
|
||||
|
||||
Reference in New Issue
Block a user