Skip to content

Commit

Permalink
feat(authz): Make info APIs support ParentGroup (#6440)
Browse files Browse the repository at this point in the history
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
ThisIsMani and hyperswitch-bot[bot] authored Oct 30, 2024
1 parent 8372389 commit 7dcffcc
Show file tree
Hide file tree
Showing 13 changed files with 364 additions and 52 deletions.
9 changes: 6 additions & 3 deletions crates/api_models/src/events/user_role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -22,6 +23,8 @@ common_utils::impl_api_event_type!(
RoleInfoResponseNew,
RoleInfoWithGroupsResponse,
ListUsersInEntityRequest,
ListRolesRequest
ListRolesRequest,
GroupsAndResources,
RoleInfoWithParents
)
);
26 changes: 24 additions & 2 deletions crates/api_models/src/user_role/role.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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>,
Expand Down Expand Up @@ -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>,
}
20 changes: 12 additions & 8 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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,
Expand All @@ -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
Expand Down
45 changes: 43 additions & 2 deletions crates/router/src/core/user_role.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
80 changes: 77 additions & 3 deletions crates/router/src/core/user_role/role.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 12 additions & 0 deletions crates/router/src/routes/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
)
Expand Down Expand Up @@ -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")
Expand All @@ -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)),
),
);

Expand Down
3 changes: 3 additions & 0 deletions crates/router/src/routes/lock_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 7dcffcc

Please sign in to comment.