mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +08:00
feat(users): Add entity type filter in list users and list roles API (#5997)
This commit is contained in:
@ -2,12 +2,12 @@ use common_utils::events::{ApiEventMetric, ApiEventsType};
|
||||
|
||||
use crate::user_role::{
|
||||
role::{
|
||||
CreateRoleRequest, GetRoleRequest, ListRolesAtEntityLevelRequest, ListRolesResponse,
|
||||
RoleInfoResponseNew, RoleInfoWithGroupsResponse, RoleInfoWithPermissionsResponse,
|
||||
UpdateRoleRequest,
|
||||
CreateRoleRequest, GetRoleRequest, ListRolesAtEntityLevelRequest, ListRolesRequest,
|
||||
ListRolesResponse, RoleInfoResponseNew, RoleInfoWithGroupsResponse,
|
||||
RoleInfoWithPermissionsResponse, UpdateRoleRequest,
|
||||
},
|
||||
AcceptInvitationRequest, AuthorizationInfoResponse, DeleteUserRoleRequest,
|
||||
MerchantSelectRequest, UpdateUserRoleRequest,
|
||||
ListUsersInEntityRequest, MerchantSelectRequest, UpdateUserRoleRequest,
|
||||
};
|
||||
|
||||
common_utils::impl_api_event_type!(
|
||||
@ -25,6 +25,8 @@ common_utils::impl_api_event_type!(
|
||||
ListRolesResponse,
|
||||
ListRolesAtEntityLevelRequest,
|
||||
RoleInfoResponseNew,
|
||||
RoleInfoWithGroupsResponse
|
||||
RoleInfoWithGroupsResponse,
|
||||
ListUsersInEntityRequest,
|
||||
ListRolesRequest
|
||||
)
|
||||
);
|
||||
|
||||
@ -159,3 +159,8 @@ pub struct Entity {
|
||||
pub entity_id: String,
|
||||
pub entity_type: common_enums::EntityType,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct ListUsersInEntityRequest {
|
||||
pub entity_type: Option<common_enums::EntityType>,
|
||||
}
|
||||
|
||||
@ -35,6 +35,11 @@ pub struct RoleInfoWithGroupsResponse {
|
||||
pub role_scope: RoleScope,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct ListRolesRequest {
|
||||
pub entity_type: Option<EntityType>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct RoleInfoResponseNew {
|
||||
pub role_id: String,
|
||||
|
||||
@ -673,6 +673,7 @@ pub async fn delete_user_role(
|
||||
pub async fn list_users_in_lineage(
|
||||
state: SessionState,
|
||||
user_from_token: auth::UserFromToken,
|
||||
request: user_role_api::ListUsersInEntityRequest,
|
||||
) -> UserResponse<Vec<user_role_api::ListUsersInEntityResponse>> {
|
||||
let requestor_role_info = roles::RoleInfo::from_role_id_in_merchant_scope(
|
||||
&state,
|
||||
@ -683,51 +684,55 @@ pub async fn list_users_in_lineage(
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
let user_roles_set: HashSet<_> = match requestor_role_info.get_entity_type() {
|
||||
EntityType::Organization => state
|
||||
.store
|
||||
.list_user_roles_by_org_id(ListUserRolesByOrgIdPayload {
|
||||
let user_roles_set: HashSet<_> = match utils::user_role::get_min_entity(
|
||||
requestor_role_info.get_entity_type(),
|
||||
request.entity_type,
|
||||
)? {
|
||||
EntityType::Organization => {
|
||||
utils::user_role::fetch_user_roles_by_payload(
|
||||
&state,
|
||||
ListUserRolesByOrgIdPayload {
|
||||
user_id: None,
|
||||
org_id: &user_from_token.org_id,
|
||||
merchant_id: None,
|
||||
profile_id: None,
|
||||
version: None,
|
||||
})
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.into_iter()
|
||||
.collect(),
|
||||
EntityType::Merchant => state
|
||||
.store
|
||||
.list_user_roles_by_org_id(ListUserRolesByOrgIdPayload {
|
||||
},
|
||||
request.entity_type,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
EntityType::Merchant => {
|
||||
utils::user_role::fetch_user_roles_by_payload(
|
||||
&state,
|
||||
ListUserRolesByOrgIdPayload {
|
||||
user_id: None,
|
||||
org_id: &user_from_token.org_id,
|
||||
merchant_id: Some(&user_from_token.merchant_id),
|
||||
profile_id: None,
|
||||
version: None,
|
||||
})
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.into_iter()
|
||||
.collect(),
|
||||
},
|
||||
request.entity_type,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
EntityType::Profile => {
|
||||
let Some(profile_id) = user_from_token.profile_id.as_ref() else {
|
||||
return Err(UserErrors::JwtProfileIdMissing.into());
|
||||
};
|
||||
|
||||
state
|
||||
.store
|
||||
.list_user_roles_by_org_id(ListUserRolesByOrgIdPayload {
|
||||
utils::user_role::fetch_user_roles_by_payload(
|
||||
&state,
|
||||
ListUserRolesByOrgIdPayload {
|
||||
user_id: None,
|
||||
org_id: &user_from_token.org_id,
|
||||
merchant_id: Some(&user_from_token.merchant_id),
|
||||
profile_id: Some(profile_id),
|
||||
version: None,
|
||||
})
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.into_iter()
|
||||
.collect()
|
||||
},
|
||||
request.entity_type,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
EntityType::Internal => HashSet::new(),
|
||||
};
|
||||
|
||||
@ -217,6 +217,7 @@ 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>> {
|
||||
let user_role_info = user_from_token
|
||||
.get_role_info_from_db(&state)
|
||||
@ -229,10 +230,16 @@ pub async fn list_roles_with_info(
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let user_role_entity = user_role_info.get_entity_type();
|
||||
let custom_roles = match user_role_entity {
|
||||
let custom_roles =
|
||||
match utils::user_role::get_min_entity(user_role_entity, request.entity_type)? {
|
||||
EntityType::Organization => state
|
||||
.store
|
||||
.list_roles_for_org_by_parameters(&user_from_token.org_id, None, None, None)
|
||||
.list_roles_for_org_by_parameters(
|
||||
&user_from_token.org_id,
|
||||
None,
|
||||
request.entity_type,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
.attach_printable("Failed to get roles")?,
|
||||
@ -241,7 +248,7 @@ pub async fn list_roles_with_info(
|
||||
.list_roles_for_org_by_parameters(
|
||||
&user_from_token.org_id,
|
||||
Some(&user_from_token.merchant_id),
|
||||
None,
|
||||
request.entity_type,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@ -258,20 +265,22 @@ 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| {
|
||||
if user_role_entity >= role_info.get_entity_type() {
|
||||
Some(role_api::RoleInfoResponseNew {
|
||||
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(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
||||
@ -291,16 +291,20 @@ pub async fn get_role_information(
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_users_in_lineage(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
|
||||
pub async fn list_users_in_lineage(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
query: web::Query<user_role_api::ListUsersInEntityRequest>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::ListUsersInLineage;
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state.clone(),
|
||||
&req,
|
||||
(),
|
||||
|state, user_from_token, _, _| {
|
||||
user_role_core::list_users_in_lineage(state, user_from_token)
|
||||
query.into_inner(),
|
||||
|state, user_from_token, request, _| {
|
||||
user_role_core::list_users_in_lineage(state, user_from_token, request)
|
||||
},
|
||||
&auth::DashboardNoPermissionAuth,
|
||||
api_locking::LockAction::NotApplicable,
|
||||
@ -308,15 +312,21 @@ pub async fn list_users_in_lineage(state: web::Data<AppState>, req: HttpRequest)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_roles_with_info(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
|
||||
pub async fn list_roles_with_info(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
query: web::Query<role_api::ListRolesRequest>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::ListRolesV2;
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state.clone(),
|
||||
&req,
|
||||
(),
|
||||
|state, user_from_token, _, _| role_core::list_roles_with_info(state, user_from_token),
|
||||
query.into_inner(),
|
||||
|state, user_from_token, request, _| {
|
||||
role_core::list_roles_with_info(state, user_from_token, request)
|
||||
},
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::UsersRead,
|
||||
minimum_entity_level: EntityType::Profile,
|
||||
|
||||
@ -14,7 +14,7 @@ use storage_impl::errors::StorageError;
|
||||
use crate::{
|
||||
consts,
|
||||
core::errors::{UserErrors, UserResult},
|
||||
db::user_role::ListUserRolesByUserIdPayload,
|
||||
db::user_role::{ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload},
|
||||
routes::SessionState,
|
||||
services::authorization::{self as authz, permissions::Permission, roles},
|
||||
types::domain,
|
||||
@ -398,3 +398,61 @@ pub async fn get_single_merchant_id_and_profile_id(
|
||||
|
||||
Ok((merchant_id, profile_id))
|
||||
}
|
||||
|
||||
pub async fn fetch_user_roles_by_payload(
|
||||
state: &SessionState,
|
||||
payload: ListUserRolesByOrgIdPayload<'_>,
|
||||
request_entity_type: Option<EntityType>,
|
||||
) -> UserResult<HashSet<UserRole>> {
|
||||
Ok(state
|
||||
.store
|
||||
.list_user_roles_by_org_id(payload)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.into_iter()
|
||||
.filter_map(|user_role| {
|
||||
let (_entity_id, entity_type) = user_role.get_entity_id_and_type()?;
|
||||
request_entity_type
|
||||
.map_or(true, |req_entity_type| entity_type == req_entity_type)
|
||||
.then_some(user_role)
|
||||
})
|
||||
.collect::<HashSet<_>>())
|
||||
}
|
||||
|
||||
pub fn get_min_entity(
|
||||
user_entity: EntityType,
|
||||
filter_entity: Option<EntityType>,
|
||||
) -> UserResult<EntityType> {
|
||||
match (user_entity, filter_entity) {
|
||||
(EntityType::Organization, None)
|
||||
| (EntityType::Organization, Some(EntityType::Organization)) => {
|
||||
Ok(EntityType::Organization)
|
||||
}
|
||||
|
||||
(EntityType::Merchant, None)
|
||||
| (EntityType::Organization, Some(EntityType::Merchant))
|
||||
| (EntityType::Merchant, Some(EntityType::Merchant)) => Ok(EntityType::Merchant),
|
||||
|
||||
(EntityType::Profile, None)
|
||||
| (EntityType::Organization, Some(EntityType::Profile))
|
||||
| (EntityType::Merchant, Some(EntityType::Profile))
|
||||
| (EntityType::Profile, Some(EntityType::Profile)) => Ok(EntityType::Profile),
|
||||
|
||||
(EntityType::Internal, _) => Ok(EntityType::Internal),
|
||||
|
||||
(EntityType::Organization, Some(EntityType::Internal))
|
||||
| (EntityType::Merchant, Some(EntityType::Internal))
|
||||
| (EntityType::Profile, Some(EntityType::Internal)) => {
|
||||
Err(UserErrors::InvalidRoleOperation.into())
|
||||
}
|
||||
|
||||
(EntityType::Merchant, Some(EntityType::Organization))
|
||||
| (EntityType::Profile, Some(EntityType::Organization))
|
||||
| (EntityType::Profile, Some(EntityType::Merchant)) => {
|
||||
Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
|
||||
"{} level user requesting data for {:?} level",
|
||||
user_entity, filter_entity
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user