Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(roles): Change list roles, get role and authorization info api to respond with groups #3837

Merged
merged 9 commits into from
Feb 27, 2024
10 changes: 6 additions & 4 deletions crates/api_models/src/events/user_role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@ use common_utils::events::{ApiEventMetric, ApiEventsType};

use crate::user_role::{
role::{
CreateRoleRequest, GetRoleRequest, ListRolesResponse, RoleInfoResponse, UpdateRoleRequest,
CreateRoleRequest, GetRoleRequest, ListRolesResponse, RoleInfoResponse,
RoleInfoWithPermissionsResponse, UpdateRoleRequest,
},
AcceptInvitationRequest, AuthorizationInfoResponse, DeleteUserRoleRequest,
TransferOrgOwnershipRequest, UpdateUserRoleRequest,
};

common_utils::impl_misc_api_event_type!(
ListRolesResponse,
RoleInfoResponse,
RoleInfoWithPermissionsResponse,
GetRoleRequest,
AuthorizationInfoResponse,
UpdateUserRoleRequest,
AcceptInvitationRequest,
DeleteUserRoleRequest,
TransferOrgOwnershipRequest,
CreateRoleRequest,
UpdateRoleRequest
UpdateRoleRequest,
ListRolesResponse,
RoleInfoResponse
);
17 changes: 16 additions & 1 deletion crates/api_models/src/user_role.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use common_enums::PermissionGroup;
use common_utils::pii;

use crate::user::DashboardEntryResponse;
Expand Down Expand Up @@ -51,7 +52,14 @@ pub enum PermissionModule {
}

#[derive(Debug, serde::Serialize)]
pub struct AuthorizationInfoResponse(pub Vec<ModuleInfo>);
pub struct AuthorizationInfoResponse(pub Vec<AuthorizationInfo>);

#[derive(Debug, serde::Serialize)]
#[serde(untagged)]
pub enum AuthorizationInfo {
Module(ModuleInfo),
Group(GroupInfo),
}

#[derive(Debug, serde::Serialize)]
pub struct ModuleInfo {
Expand All @@ -60,6 +68,13 @@ pub struct ModuleInfo {
pub permissions: Vec<PermissionInfo>,
}

#[derive(Debug, serde::Serialize)]
pub struct GroupInfo {
pub group: PermissionGroup,
pub description: &'static str,
pub permissions: Vec<PermissionInfo>,
}

#[derive(Debug, serde::Serialize)]
pub struct PermissionInfo {
pub enum_name: Permission,
Expand Down
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,7 @@
use common_enums::{PermissionGroup, RoleScope};

use super::Permission;

#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct CreateRoleRequest {
pub role_name: String,
Expand All @@ -16,10 +18,30 @@ pub struct UpdateRoleRequest {
#[derive(Debug, serde::Serialize)]
pub struct ListRolesResponse(pub Vec<RoleInfoResponse>);

#[derive(Debug, serde::Deserialize)]
pub struct GetGroupsQueryParam {
pub groups: Option<bool>,
}

#[derive(Debug, serde::Serialize)]
pub struct RoleInfoResponse {
#[serde(untagged)]
pub enum RoleInfoResponse {
Permissions(RoleInfoWithPermissionsResponse),
Groups(RoleInfoWithGroupsResponse),
}

#[derive(Debug, serde::Serialize)]
pub struct RoleInfoWithPermissionsResponse {
pub role_id: String,
pub permissions: Vec<super::Permission>,
pub permissions: Vec<Permission>,
pub role_name: String,
pub role_scope: RoleScope,
}

#[derive(Debug, serde::Serialize)]
pub struct RoleInfoWithGroupsResponse {
pub role_id: String,
pub groups: Vec<PermissionGroup>,
pub role_name: String,
pub role_scope: RoleScope,
}
Expand Down
1 change: 1 addition & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2197,6 +2197,7 @@ pub enum RoleScope {
serde::Deserialize,
strum::Display,
strum::EnumString,
strum::EnumIter,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[serde(rename_all = "snake_case")]
Expand Down
8 changes: 4 additions & 4 deletions crates/router/src/core/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ pub async fn invite_user(
.into());
}

let role_info = roles::get_role_info_from_role_id(
let role_info = roles::RoleInfo::from_role_id(
&state,
&request.role_id,
&user_from_token.merchant_id,
Expand Down Expand Up @@ -659,7 +659,7 @@ async fn handle_invitation(
.into());
}

let role_info = roles::get_role_info_from_role_id(
let role_info = roles::RoleInfo::from_role_id(
state,
&request.role_id,
&user_from_token.merchant_id,
Expand Down Expand Up @@ -1054,7 +1054,7 @@ pub async fn switch_merchant_id(

let user = user_from_token.get_user_from_db(&state).await?;

let role_info = roles::get_role_info_from_role_id(
let role_info = roles::RoleInfo::from_role_id(
&state,
&user_from_token.role_id,
&user_from_token.merchant_id,
Expand Down Expand Up @@ -1207,7 +1207,7 @@ pub async fn get_users_for_merchant_account(
let users_user_roles_and_roles =
futures::future::try_join_all(users_and_user_roles.into_iter().map(
|(user, user_role)| async {
roles::get_role_info_from_role_id(
roles::RoleInfo::from_role_id(
&state,
&user_role.role_id,
&user_role.merchant_id,
Expand Down
26 changes: 20 additions & 6 deletions crates/router/src/core/user_role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,28 @@ use crate::{

pub mod role;

pub async fn get_authorization_info(
// TODO: To be deprecated once groups are stable
pub async fn get_authorization_info_with_modules(
_state: AppState,
) -> UserResponse<user_role_api::AuthorizationInfoResponse> {
Ok(ApplicationResponse::Json(
user_role_api::AuthorizationInfoResponse(
info::get_authorization_info()
info::get_module_authorization_info()
.into_iter()
.map(Into::into)
.map(|module_info| user_role_api::AuthorizationInfo::Module(module_info.into()))
.collect(),
),
))
}

pub async fn get_authorization_info_with_groups(
_state: AppState,
) -> UserResponse<user_role_api::AuthorizationInfoResponse> {
Ok(ApplicationResponse::Json(
user_role_api::AuthorizationInfoResponse(
info::get_group_authorization_info()
.into_iter()
.map(user_role_api::AuthorizationInfo::Group)
.collect(),
),
))
Expand All @@ -37,7 +51,7 @@ pub async fn update_user_role(
user_from_token: auth::UserFromToken,
req: user_role_api::UpdateUserRoleRequest,
) -> UserResponse<()> {
let role_info = roles::get_role_info_from_role_id(
let role_info = roles::RoleInfo::from_role_id(
&state,
&req.role_id,
&user_from_token.merchant_id,
Expand Down Expand Up @@ -67,7 +81,7 @@ pub async fn update_user_role(
.await
.to_not_found_response(UserErrors::InvalidRoleOperation)?;

let role_to_be_updated = roles::get_role_info_from_role_id(
let role_to_be_updated = roles::RoleInfo::from_role_id(
&state,
&user_role_to_be_updated.role_id,
&user_from_token.merchant_id,
Expand Down Expand Up @@ -236,7 +250,7 @@ pub async fn delete_user_role(
.find(|&role| role.merchant_id == user_from_token.merchant_id.as_str())
{
Some(user_role) => {
let role_info = roles::get_role_info_from_role_id(
let role_info = roles::RoleInfo::from_role_id(
&state,
&user_role.role_id,
&user_from_token.merchant_id,
Expand Down
140 changes: 110 additions & 30 deletions crates/router/src/core/user_role/role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,22 +86,25 @@ pub async fn create_role(
Ok(ApplicationResponse::StatusOk)
}

pub async fn list_invitable_roles(
// TODO: To be deprecated once groups are stable
pub async fn list_invitable_roles_with_permissions(
state: AppState,
user_from_token: UserFromToken,
) -> UserResponse<role_api::ListRolesResponse> {
let predefined_roles_map = PREDEFINED_ROLES
.iter()
.filter(|(_, role_info)| role_info.is_invitable())
.map(|(role_id, role_info)| role_api::RoleInfoResponse {
permissions: role_info
.get_permissions_set()
.into_iter()
.map(Into::into)
.collect(),
role_id: role_id.to_string(),
role_name: role_info.get_role_name().to_string(),
role_scope: role_info.get_scope(),
.map(|(role_id, role_info)| {
role_api::RoleInfoResponse::Permissions(role_api::RoleInfoWithPermissionsResponse {
permissions: role_info
.get_permissions_set()
.into_iter()
.map(Into::into)
.collect(),
role_id: role_id.to_string(),
role_name: role_info.get_role_name().to_string(),
role_scope: role_info.get_scope(),
})
});

let custom_roles_map = state
Expand All @@ -110,30 +113,77 @@ pub async fn list_invitable_roles(
.await
.change_context(UserErrors::InternalServerError)?
.into_iter()
.map(roles::RoleInfo::from)
.filter(|role_info| role_info.is_invitable())
.map(|role_info| role_api::RoleInfoResponse {
permissions: role_info
.get_permissions_set()
.into_iter()
.map(Into::into)
.collect(),
role_id: role_info.get_role_id().to_string(),
role_name: role_info.get_role_name().to_string(),
role_scope: role_info.get_scope(),
.filter_map(|role| {
let role_info = roles::RoleInfo::from(role);
role_info
.is_invitable()
.then_some(role_api::RoleInfoResponse::Permissions(
role_api::RoleInfoWithPermissionsResponse {
permissions: role_info
.get_permissions_set()
.into_iter()
.map(Into::into)
.collect(),
role_id: role_info.get_role_id().to_string(),
role_name: role_info.get_role_name().to_string(),
role_scope: role_info.get_scope(),
},
))
});

Ok(ApplicationResponse::Json(role_api::ListRolesResponse(
predefined_roles_map.chain(custom_roles_map).collect(),
)))
}

pub async fn get_role(
pub async fn list_invitable_roles_with_groups(
state: AppState,
user_from_token: UserFromToken,
) -> UserResponse<role_api::ListRolesResponse> {
let predefined_roles_map = PREDEFINED_ROLES
.iter()
.filter(|(_, role_info)| role_info.is_invitable())
.map(|(role_id, role_info)| {
role_api::RoleInfoResponse::Groups(role_api::RoleInfoWithGroupsResponse {
groups: role_info.get_permission_groups().to_vec(),
role_id: role_id.to_string(),
role_name: role_info.get_role_name().to_string(),
role_scope: role_info.get_scope(),
})
});

let custom_roles_map = state
.store
.list_all_roles(&user_from_token.merchant_id, &user_from_token.org_id)
.await
.change_context(UserErrors::InternalServerError)?
.into_iter()
.filter_map(|role| {
let role_info = roles::RoleInfo::from(role);
role_info
.is_invitable()
.then_some(role_api::RoleInfoResponse::Groups(
role_api::RoleInfoWithGroupsResponse {
groups: role_info.get_permission_groups().to_vec(),
role_id: role_info.get_role_id().to_string(),
role_name: role_info.get_role_name().to_string(),
role_scope: role_info.get_scope(),
},
))
});

Ok(ApplicationResponse::Json(role_api::ListRolesResponse(
predefined_roles_map.chain(custom_roles_map).collect(),
)))
}

// TODO: To be deprecated once groups are stable
pub async fn get_role_with_permissions(
state: AppState,
user_from_token: UserFromToken,
role: role_api::GetRoleRequest,
) -> UserResponse<role_api::RoleInfoResponse> {
let role_info = roles::get_role_info_from_role_id(
let role_info = roles::RoleInfo::from_role_id(
&state,
&role.role_id,
&user_from_token.merchant_id,
Expand All @@ -152,12 +202,42 @@ pub async fn get_role(
.map(Into::into)
.collect();

Ok(ApplicationResponse::Json(role_api::RoleInfoResponse {
permissions,
role_id: role.role_id,
role_name: role_info.get_role_name().to_string(),
role_scope: role_info.get_scope(),
}))
Ok(ApplicationResponse::Json(
role_api::RoleInfoResponse::Permissions(role_api::RoleInfoWithPermissionsResponse {
permissions,
role_id: role.role_id,
role_name: role_info.get_role_name().to_string(),
role_scope: role_info.get_scope(),
}),
))
}

pub async fn get_role_with_groups(
state: AppState,
user_from_token: UserFromToken,
role: role_api::GetRoleRequest,
) -> UserResponse<role_api::RoleInfoResponse> {
let role_info = roles::RoleInfo::from_role_id(
&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());
}

Ok(ApplicationResponse::Json(
role_api::RoleInfoResponse::Groups(role_api::RoleInfoWithGroupsResponse {
groups: role_info.get_permission_groups().to_vec(),
role_id: role.role_id,
role_name: role_info.get_role_name().to_string(),
role_scope: role_info.get_scope(),
}),
))
}

pub async fn update_role(
Expand All @@ -182,7 +262,7 @@ pub async fn update_role(
.await?;
}

let role_info = roles::get_role_info_from_role_id(
let role_info = roles::RoleInfo::from_role_id(
&state,
role_id,
&user_from_token.merchant_id,
Expand Down
Loading
Loading