mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	feat(users): Add API to list users in user lineage (#5722)
This commit is contained in:
		| @ -1,8 +1,5 @@ | ||||
| // User Roles | ||||
| pub const ROLE_ID_INTERNAL_VIEW_ONLY_USER: &str = "internal_view_only"; | ||||
| pub const ROLE_ID_INTERNAL_ADMIN: &str = "internal_admin"; | ||||
| pub const ROLE_ID_MERCHANT_ADMIN: &str = "merchant_admin"; | ||||
| pub const ROLE_ID_ORGANIZATION_ADMIN: &str = "org_admin"; | ||||
| pub const ROLE_ID_MERCHANT_VIEW_ONLY: &str = "merchant_view_only"; | ||||
| pub const ROLE_ID_MERCHANT_IAM_ADMIN: &str = "merchant_iam_admin"; | ||||
| pub const ROLE_ID_MERCHANT_DEVELOPER: &str = "merchant_developer"; | ||||
|  | ||||
| @ -88,6 +88,8 @@ pub enum UserErrors { | ||||
|     AuthConfigParsingError, | ||||
|     #[error("Invalid SSO request")] | ||||
|     SSOFailed, | ||||
|     #[error("profile_id missing in JWT")] | ||||
|     JwtProfileIdMissing, | ||||
| } | ||||
|  | ||||
| impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorResponse> for UserErrors { | ||||
| @ -224,6 +226,9 @@ impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorRespon | ||||
|             Self::SSOFailed => { | ||||
|                 AER::BadRequest(ApiError::new(sub_code, 46, self.get_error_message(), None)) | ||||
|             } | ||||
|             Self::JwtProfileIdMissing => { | ||||
|                 AER::Unauthorized(ApiError::new(sub_code, 47, self.get_error_message(), None)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -271,6 +276,7 @@ impl UserErrors { | ||||
|             Self::InvalidUserAuthMethodOperation => "Invalid user auth method operation", | ||||
|             Self::AuthConfigParsingError => "Auth config parsing error", | ||||
|             Self::SSOFailed => "Invalid SSO request", | ||||
|             Self::JwtProfileIdMissing => "profile_id missing in JWT", | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -62,7 +62,7 @@ pub async fn signup_with_merchant_id( | ||||
|     let user_role = new_user | ||||
|         .insert_user_role_in_db( | ||||
|             state.clone(), | ||||
|             consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|             common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|             UserStatus::Active, | ||||
|         ) | ||||
|         .await?; | ||||
| @ -134,7 +134,7 @@ pub async fn signup( | ||||
|     let user_role = new_user | ||||
|         .insert_user_role_in_db( | ||||
|             state.clone(), | ||||
|             consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|             common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|             UserStatus::Active, | ||||
|         ) | ||||
|         .await?; | ||||
| @ -165,7 +165,7 @@ pub async fn signup_token_only_flow( | ||||
|     let user_role = new_user | ||||
|         .insert_user_role_in_db( | ||||
|             state.clone(), | ||||
|             consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|             common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|             UserStatus::Active, | ||||
|         ) | ||||
|         .await?; | ||||
| @ -316,7 +316,7 @@ pub async fn connect_account( | ||||
|         let user_role = new_user | ||||
|             .insert_user_role_in_db( | ||||
|                 state.clone(), | ||||
|                 consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|                 common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|                 UserStatus::Active, | ||||
|             ) | ||||
|             .await?; | ||||
| @ -1310,7 +1310,7 @@ pub async fn create_internal_user( | ||||
|     new_user | ||||
|         .insert_user_role_in_db( | ||||
|             state, | ||||
|             consts::user_role::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), | ||||
|             common_utils::consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), | ||||
|             UserStatus::Active, | ||||
|         ) | ||||
|         .await?; | ||||
| @ -1389,7 +1389,7 @@ pub async fn switch_merchant_id( | ||||
|     } else { | ||||
|         let user_roles = state | ||||
|             .store | ||||
|             .list_user_roles_by_user_id(&user_from_token.user_id, UserRoleVersion::V1) | ||||
|             .list_user_roles_by_user_id_and_version(&user_from_token.user_id, UserRoleVersion::V1) | ||||
|             .await | ||||
|             .change_context(UserErrors::InternalServerError)?; | ||||
|  | ||||
| @ -1450,7 +1450,7 @@ pub async fn create_merchant_account( | ||||
|     let role_insertion_res = new_user | ||||
|         .insert_user_role_in_db( | ||||
|             state.clone(), | ||||
|             consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|             common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|             UserStatus::Active, | ||||
|         ) | ||||
|         .await; | ||||
| @ -1471,7 +1471,10 @@ pub async fn list_merchants_for_user( | ||||
| ) -> UserResponse<Vec<user_api::UserMerchantAccount>> { | ||||
|     let user_roles = state | ||||
|         .store | ||||
|         .list_user_roles_by_user_id(user_from_token.user_id.as_str(), UserRoleVersion::V1) | ||||
|         .list_user_roles_by_user_id_and_version( | ||||
|             user_from_token.user_id.as_str(), | ||||
|             UserRoleVersion::V1, | ||||
|         ) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)?; | ||||
|  | ||||
| @ -2572,7 +2575,7 @@ pub async fn list_orgs_for_user( | ||||
| ) -> UserResponse<Vec<user_api::ListOrgsForUserResponse>> { | ||||
|     let orgs = state | ||||
|         .store | ||||
|         .list_user_roles( | ||||
|         .list_user_roles_by_user_id( | ||||
|             user_from_token.user_id.as_str(), | ||||
|             None, | ||||
|             None, | ||||
| @ -2638,7 +2641,7 @@ pub async fn list_merchants_for_user_in_org( | ||||
|     } else { | ||||
|         let merchant_ids = state | ||||
|             .store | ||||
|             .list_user_roles( | ||||
|             .list_user_roles_by_user_id( | ||||
|                 user_from_token.user_id.as_str(), | ||||
|                 Some(&user_from_token.org_id), | ||||
|                 None, | ||||
| @ -2721,7 +2724,7 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account( | ||||
|         } else { | ||||
|             let profile_ids = state | ||||
|                 .store | ||||
|                 .list_user_roles( | ||||
|                 .list_user_roles_by_user_id( | ||||
|                     user_from_token.user_id.as_str(), | ||||
|                     Some(&user_from_token.org_id), | ||||
|                     Some(&user_from_token.merchant_id), | ||||
| @ -2793,7 +2796,7 @@ pub async fn switch_org_for_user( | ||||
|  | ||||
|     let user_role = state | ||||
|         .store | ||||
|         .list_user_roles( | ||||
|         .list_user_roles_by_user_id( | ||||
|             &user_from_token.user_id, | ||||
|             Some(&request.org_id), | ||||
|             None, | ||||
| @ -3012,7 +3015,7 @@ pub async fn switch_merchant_for_user_in_org( | ||||
|         EntityType::Merchant | EntityType::Profile => { | ||||
|             let user_role = state | ||||
|                 .store | ||||
|                 .list_user_roles( | ||||
|                 .list_user_roles_by_user_id( | ||||
|                     &user_from_token.user_id, | ||||
|                     Some(&user_from_token.org_id), | ||||
|                     Some(&request.merchant_id), | ||||
| @ -3152,7 +3155,7 @@ pub async fn switch_profile_for_user_in_org_and_merchant( | ||||
|         EntityType::Profile => { | ||||
|             let user_role = state | ||||
|                 .store | ||||
|                 .list_user_roles( | ||||
|                 .list_user_roles_by_user_id( | ||||
|                     &user_from_token.user_id, | ||||
|                     Some(&user_from_token.org_id), | ||||
|                     Some(&user_from_token.merchant_id), | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| use std::collections::HashMap; | ||||
| use std::collections::{HashMap, HashSet}; | ||||
|  | ||||
| use api_models::{user as user_api, user_role as user_role_api}; | ||||
| use diesel_models::{ | ||||
| @ -10,6 +10,7 @@ use once_cell::sync::Lazy; | ||||
|  | ||||
| use crate::{ | ||||
|     core::errors::{StorageErrorExt, UserErrors, UserResponse}, | ||||
|     db::user_role::ListUserRolesByOrgIdPayload, | ||||
|     routes::{app::ReqState, SessionState}, | ||||
|     services::{ | ||||
|         authentication as auth, | ||||
| @ -20,7 +21,7 @@ use crate::{ | ||||
|     utils, | ||||
| }; | ||||
| pub mod role; | ||||
| use common_enums::PermissionGroup; | ||||
| use common_enums::{EntityType, PermissionGroup}; | ||||
| use strum::IntoEnumIterator; | ||||
|  | ||||
| // TODO: To be deprecated once groups are stable | ||||
| @ -618,13 +619,13 @@ pub async fn delete_user_role( | ||||
|     // Check if user has any more role associations | ||||
|     let user_roles_v2 = state | ||||
|         .store | ||||
|         .list_user_roles_by_user_id(user_from_db.get_user_id(), UserRoleVersion::V2) | ||||
|         .list_user_roles_by_user_id_and_version(user_from_db.get_user_id(), UserRoleVersion::V2) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)?; | ||||
|  | ||||
|     let user_roles_v1 = state | ||||
|         .store | ||||
|         .list_user_roles_by_user_id(user_from_db.get_user_id(), UserRoleVersion::V1) | ||||
|         .list_user_roles_by_user_id_and_version(user_from_db.get_user_id(), UserRoleVersion::V1) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)?; | ||||
|  | ||||
| @ -641,3 +642,135 @@ pub async fn delete_user_role( | ||||
|     auth::blacklist::insert_user_in_blacklist(&state, user_from_db.get_user_id()).await?; | ||||
|     Ok(ApplicationResponse::StatusOk) | ||||
| } | ||||
|  | ||||
| pub async fn list_users_in_lineage( | ||||
|     state: SessionState, | ||||
|     user_from_token: auth::UserFromToken, | ||||
| ) -> UserResponse<Vec<user_api::ListUsersInEntityResponse>> { | ||||
|     let requestor_role_info = roles::RoleInfo::from_role_id( | ||||
|         &state, | ||||
|         &user_from_token.role_id, | ||||
|         &user_from_token.merchant_id, | ||||
|         &user_from_token.org_id, | ||||
|     ) | ||||
|     .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 { | ||||
|                 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 { | ||||
|                 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(), | ||||
|         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 { | ||||
|                     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() | ||||
|         } | ||||
|         EntityType::Internal => HashSet::new(), | ||||
|     }; | ||||
|  | ||||
|     let mut email_map = state | ||||
|         .global_store | ||||
|         .find_users_by_user_ids( | ||||
|             user_roles_set | ||||
|                 .iter() | ||||
|                 .map(|user_role| user_role.user_id.clone()) | ||||
|                 .collect(), | ||||
|         ) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)? | ||||
|         .into_iter() | ||||
|         .map(|user| (user.user_id.clone(), user.email)) | ||||
|         .collect::<HashMap<_, _>>(); | ||||
|  | ||||
|     let role_info_map = | ||||
|         futures::future::try_join_all(user_roles_set.iter().map(|user_role| async { | ||||
|             roles::RoleInfo::from_role_id( | ||||
|                 &state, | ||||
|                 &user_role.role_id, | ||||
|                 &user_from_token.merchant_id, | ||||
|                 &user_from_token.org_id, | ||||
|             ) | ||||
|             .await | ||||
|             .map(|role_info| { | ||||
|                 ( | ||||
|                     user_role.role_id.clone(), | ||||
|                     user_api::MinimalRoleInfo { | ||||
|                         role_id: user_role.role_id.clone(), | ||||
|                         role_name: role_info.get_role_name().to_string(), | ||||
|                     }, | ||||
|                 ) | ||||
|             }) | ||||
|         })) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)? | ||||
|         .into_iter() | ||||
|         .collect::<HashMap<_, _>>(); | ||||
|  | ||||
|     let user_role_map = user_roles_set | ||||
|         .into_iter() | ||||
|         .fold(HashMap::new(), |mut map, user_role| { | ||||
|             map.entry(user_role.user_id) | ||||
|                 .or_insert(Vec::with_capacity(1)) | ||||
|                 .push(user_role.role_id); | ||||
|             map | ||||
|         }); | ||||
|  | ||||
|     Ok(ApplicationResponse::Json( | ||||
|         user_role_map | ||||
|             .into_iter() | ||||
|             .map(|(user_id, role_id_vec)| { | ||||
|                 Ok::<_, error_stack::Report<UserErrors>>(user_api::ListUsersInEntityResponse { | ||||
|                     email: email_map | ||||
|                         .remove(&user_id) | ||||
|                         .ok_or(UserErrors::InternalServerError)?, | ||||
|                     roles: role_id_vec | ||||
|                         .into_iter() | ||||
|                         .map(|role_id| { | ||||
|                             role_info_map | ||||
|                                 .get(&role_id) | ||||
|                                 .cloned() | ||||
|                                 .ok_or(UserErrors::InternalServerError) | ||||
|                         }) | ||||
|                         .collect::<Result<Vec<_>, _>>()?, | ||||
|                 }) | ||||
|             }) | ||||
|             .collect::<Result<Vec<_>, _>>()?, | ||||
|     )) | ||||
| } | ||||
|  | ||||
| @ -5,7 +5,6 @@ use diesel_models::role::{RoleNew, RoleUpdate}; | ||||
| use error_stack::{report, ResultExt}; | ||||
|  | ||||
| use crate::{ | ||||
|     consts, | ||||
|     core::errors::{StorageErrorExt, UserErrors, UserResponse}, | ||||
|     routes::{app::ReqState, SessionState}, | ||||
|     services::{ | ||||
| @ -72,7 +71,7 @@ pub async fn create_role( | ||||
|     .await?; | ||||
|  | ||||
|     if matches!(req.role_scope, RoleScope::Organization) | ||||
|         && user_from_token.role_id != consts::user_role::ROLE_ID_ORGANIZATION_ADMIN | ||||
|         && user_from_token.role_id != common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN | ||||
|     { | ||||
|         return Err(report!(UserErrors::InvalidRoleOperation)) | ||||
|             .attach_printable("Non org admin user creating org level role"); | ||||
| @ -292,7 +291,7 @@ pub async fn update_role( | ||||
|     .to_not_found_response(UserErrors::InvalidRoleOperation)?; | ||||
|  | ||||
|     if matches!(role_info.get_scope(), RoleScope::Organization) | ||||
|         && user_from_token.role_id != consts::user_role::ROLE_ID_ORGANIZATION_ADMIN | ||||
|         && user_from_token.role_id != common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN | ||||
|     { | ||||
|         return Err(report!(UserErrors::InvalidRoleOperation)) | ||||
|             .attach_printable("Non org admin user changing org level role"); | ||||
|  | ||||
| @ -35,7 +35,7 @@ use super::{ | ||||
|     user::{sample_data::BatchSampleDataInterface, UserInterface}, | ||||
|     user_authentication_method::UserAuthenticationMethodInterface, | ||||
|     user_key_store::UserKeyStoreInterface, | ||||
|     user_role::UserRoleInterface, | ||||
|     user_role::{ListUserRolesByOrgIdPayload, UserRoleInterface}, | ||||
| }; | ||||
| #[cfg(feature = "payouts")] | ||||
| use crate::services::kafka::payout::KafkaPayout; | ||||
| @ -2792,13 +2792,13 @@ impl UserRoleInterface for KafkaStore { | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     async fn list_user_roles_by_user_id( | ||||
|     async fn list_user_roles_by_user_id_and_version( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         version: enums::UserRoleVersion, | ||||
|     ) -> CustomResult<Vec<user_storage::UserRole>, errors::StorageError> { | ||||
|         self.diesel_store | ||||
|             .list_user_roles_by_user_id(user_id, version) | ||||
|             .list_user_roles_by_user_id_and_version(user_id, version) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
| @ -2871,7 +2871,7 @@ impl UserRoleInterface for KafkaStore { | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     async fn list_user_roles( | ||||
|     async fn list_user_roles_by_user_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         org_id: Option<&id_type::OrganizationId>, | ||||
| @ -2881,9 +2881,23 @@ impl UserRoleInterface for KafkaStore { | ||||
|         version: Option<enums::UserRoleVersion>, | ||||
|     ) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> { | ||||
|         self.diesel_store | ||||
|             .list_user_roles(user_id, org_id, merchant_id, profile_id, entity_id, version) | ||||
|             .list_user_roles_by_user_id( | ||||
|                 user_id, | ||||
|                 org_id, | ||||
|                 merchant_id, | ||||
|                 profile_id, | ||||
|                 entity_id, | ||||
|                 version, | ||||
|             ) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     async fn list_user_roles_by_org_id<'a>( | ||||
|         &self, | ||||
|         payload: ListUserRolesByOrgIdPayload<'a>, | ||||
|     ) -> CustomResult<Vec<user_storage::UserRole>, errors::StorageError> { | ||||
|         self.diesel_store.list_user_roles_by_org_id(payload).await | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
|  | ||||
| @ -30,7 +30,7 @@ pub trait UserRoleInterface { | ||||
|         version: enums::UserRoleVersion, | ||||
|     ) -> CustomResult<storage::UserRole, errors::StorageError>; | ||||
|  | ||||
|     async fn list_user_roles_by_user_id( | ||||
|     async fn list_user_roles_by_user_id_and_version( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         version: enums::UserRoleVersion, | ||||
| @ -70,7 +70,7 @@ pub trait UserRoleInterface { | ||||
|         version: enums::UserRoleVersion, | ||||
|     ) -> CustomResult<storage::UserRole, errors::StorageError>; | ||||
|  | ||||
|     async fn list_user_roles( | ||||
|     async fn list_user_roles_by_user_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         org_id: Option<&id_type::OrganizationId>, | ||||
| @ -79,6 +79,19 @@ pub trait UserRoleInterface { | ||||
|         entity_id: Option<&String>, | ||||
|         version: Option<enums::UserRoleVersion>, | ||||
|     ) -> CustomResult<Vec<storage::UserRole>, errors::StorageError>; | ||||
|  | ||||
|     async fn list_user_roles_by_org_id<'a>( | ||||
|         &self, | ||||
|         payload: ListUserRolesByOrgIdPayload<'a>, | ||||
|     ) -> CustomResult<Vec<storage::UserRole>, errors::StorageError>; | ||||
| } | ||||
|  | ||||
| pub struct ListUserRolesByOrgIdPayload<'a> { | ||||
|     pub user_id: Option<&'a String>, | ||||
|     pub org_id: &'a id_type::OrganizationId, | ||||
|     pub merchant_id: Option<&'a id_type::MerchantId>, | ||||
|     pub profile_id: Option<&'a id_type::ProfileId>, | ||||
|     pub version: Option<enums::UserRoleVersion>, | ||||
| } | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
| @ -126,7 +139,7 @@ impl UserRoleInterface for Store { | ||||
|     } | ||||
|  | ||||
|     #[instrument(skip_all)] | ||||
|     async fn list_user_roles_by_user_id( | ||||
|     async fn list_user_roles_by_user_id_and_version( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         version: enums::UserRoleVersion, | ||||
| @ -217,7 +230,7 @@ impl UserRoleInterface for Store { | ||||
|         .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
|  | ||||
|     async fn list_user_roles( | ||||
|     async fn list_user_roles_by_user_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         org_id: Option<&id_type::OrganizationId>, | ||||
| @ -227,7 +240,7 @@ impl UserRoleInterface for Store { | ||||
|         version: Option<enums::UserRoleVersion>, | ||||
|     ) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> { | ||||
|         let conn = connection::pg_connection_read(self).await?; | ||||
|         storage::UserRole::generic_user_roles_list( | ||||
|         storage::UserRole::generic_user_roles_list_for_user( | ||||
|             &conn, | ||||
|             user_id.to_owned(), | ||||
|             org_id.cloned(), | ||||
| @ -239,6 +252,23 @@ impl UserRoleInterface for Store { | ||||
|         .await | ||||
|         .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
|  | ||||
|     async fn list_user_roles_by_org_id<'a>( | ||||
|         &self, | ||||
|         payload: ListUserRolesByOrgIdPayload<'a>, | ||||
|     ) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> { | ||||
|         let conn = connection::pg_connection_read(self).await?; | ||||
|         storage::UserRole::generic_user_roles_list_for_org_and_extra( | ||||
|             &conn, | ||||
|             payload.user_id.cloned(), | ||||
|             payload.org_id.to_owned(), | ||||
|             payload.merchant_id.cloned(), | ||||
|             payload.profile_id.cloned(), | ||||
|             payload.version, | ||||
|         ) | ||||
|         .await | ||||
|         .map_err(|error| report!(errors::StorageError::from(error))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
| @ -328,7 +358,7 @@ impl UserRoleInterface for MockDb { | ||||
|         .into()) | ||||
|     } | ||||
|  | ||||
|     async fn list_user_roles_by_user_id( | ||||
|     async fn list_user_roles_by_user_id_and_version( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         version: enums::UserRoleVersion, | ||||
| @ -505,7 +535,7 @@ impl UserRoleInterface for MockDb { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async fn list_user_roles( | ||||
|     async fn list_user_roles_by_user_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         org_id: Option<&id_type::OrganizationId>, | ||||
| @ -551,4 +581,48 @@ impl UserRoleInterface for MockDb { | ||||
|  | ||||
|         Ok(filtered_roles) | ||||
|     } | ||||
|  | ||||
|     async fn list_user_roles_by_org_id<'a>( | ||||
|         &self, | ||||
|         payload: ListUserRolesByOrgIdPayload<'a>, | ||||
|     ) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> { | ||||
|         let user_roles = self.user_roles.lock().await; | ||||
|  | ||||
|         let mut filtered_roles = Vec::new(); | ||||
|  | ||||
|         for role in user_roles.iter() { | ||||
|             let role_org_id = role | ||||
|                 .org_id | ||||
|                 .as_ref() | ||||
|                 .ok_or(report!(errors::StorageError::MockDbError))?; | ||||
|  | ||||
|             let mut filter_condition = role_org_id == payload.org_id; | ||||
|  | ||||
|             if let Some(user_id) = payload.user_id { | ||||
|                 filter_condition = filter_condition && user_id == &role.user_id | ||||
|             } | ||||
|  | ||||
|             role.merchant_id.as_ref().zip(payload.merchant_id).inspect( | ||||
|                 |(role_merchant_id, merchant_id)| { | ||||
|                     filter_condition = filter_condition && role_merchant_id == merchant_id | ||||
|                 }, | ||||
|             ); | ||||
|  | ||||
|             role.profile_id.as_ref().zip(payload.profile_id).inspect( | ||||
|                 |(role_profile_id, profile_id)| { | ||||
|                     filter_condition = filter_condition && role_profile_id == profile_id | ||||
|                 }, | ||||
|             ); | ||||
|  | ||||
|             payload | ||||
|                 .version | ||||
|                 .inspect(|ver| filter_condition = filter_condition && ver == &role.version); | ||||
|  | ||||
|             if filter_condition { | ||||
|                 filtered_roles.push(role.clone()) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Ok(filtered_roles) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1799,6 +1799,7 @@ impl User { | ||||
|                 .service( | ||||
|                     web::resource("/list").route(web::get().to(list_users_for_merchant_account)), | ||||
|                 ) | ||||
|                 .service(web::resource("/v2/list").route(web::get().to(list_users_in_lineage))) | ||||
|                 .service( | ||||
|                     web::resource("/invite_multiple").route(web::post().to(invite_multiple_user)), | ||||
|                 ) | ||||
|  | ||||
| @ -263,7 +263,8 @@ impl From<Flow> for ApiIdentifier { | ||||
|             | Flow::DeleteUserRole | ||||
|             | Flow::CreateRole | ||||
|             | Flow::UpdateRole | ||||
|             | Flow::UserFromEmail => Self::UserRole, | ||||
|             | Flow::UserFromEmail | ||||
|             | Flow::ListUsersInLineage => Self::UserRole, | ||||
|  | ||||
|             Flow::GetActionUrl | Flow::SyncOnboardingStatus | Flow::ResetTrackingId => { | ||||
|                 Self::ConnectorOnboarding | ||||
|  | ||||
| @ -269,3 +269,20 @@ pub async fn get_role_information( | ||||
|     )) | ||||
|     .await | ||||
| } | ||||
|  | ||||
| pub async fn list_users_in_lineage(state: web::Data<AppState>, req: HttpRequest) -> 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) | ||||
|         }, | ||||
|         &auth::DashboardNoPermissionAuth, | ||||
|         api_locking::LockAction::NotApplicable, | ||||
|     )) | ||||
|     .await | ||||
| } | ||||
|  | ||||
| @ -9,7 +9,7 @@ use crate::consts; | ||||
| pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|| { | ||||
|     let mut roles = HashMap::new(); | ||||
|     roles.insert( | ||||
|         consts::user_role::ROLE_ID_INTERNAL_ADMIN, | ||||
|         common_utils::consts::ROLE_ID_INTERNAL_ADMIN, | ||||
|         RoleInfo { | ||||
|             groups: vec![ | ||||
|                 PermissionGroup::OperationsView, | ||||
| @ -25,7 +25,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(| | ||||
|                 PermissionGroup::MerchantDetailsManage, | ||||
|                 PermissionGroup::OrganizationManage, | ||||
|             ], | ||||
|             role_id: consts::user_role::ROLE_ID_INTERNAL_ADMIN.to_string(), | ||||
|             role_id: common_utils::consts::ROLE_ID_INTERNAL_ADMIN.to_string(), | ||||
|             role_name: "internal_admin".to_string(), | ||||
|             scope: RoleScope::Organization, | ||||
|             entity_type: EntityType::Internal, | ||||
| @ -36,7 +36,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(| | ||||
|         }, | ||||
|     ); | ||||
|     roles.insert( | ||||
|         consts::user_role::ROLE_ID_INTERNAL_VIEW_ONLY_USER, | ||||
|         common_utils::consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER, | ||||
|         RoleInfo { | ||||
|             groups: vec![ | ||||
|                 PermissionGroup::OperationsView, | ||||
| @ -46,7 +46,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(| | ||||
|                 PermissionGroup::UsersView, | ||||
|                 PermissionGroup::MerchantDetailsView, | ||||
|             ], | ||||
|             role_id: consts::user_role::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), | ||||
|             role_id: common_utils::consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), | ||||
|             role_name: "internal_view_only".to_string(), | ||||
|             scope: RoleScope::Organization, | ||||
|             entity_type: EntityType::Internal, | ||||
| @ -58,7 +58,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(| | ||||
|     ); | ||||
|  | ||||
|     roles.insert( | ||||
|         consts::user_role::ROLE_ID_ORGANIZATION_ADMIN, | ||||
|         common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN, | ||||
|         RoleInfo { | ||||
|             groups: vec![ | ||||
|                 PermissionGroup::OperationsView, | ||||
| @ -74,7 +74,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(| | ||||
|                 PermissionGroup::MerchantDetailsManage, | ||||
|                 PermissionGroup::OrganizationManage, | ||||
|             ], | ||||
|             role_id: consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|             role_id: common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), | ||||
|             role_name: "organization_admin".to_string(), | ||||
|             scope: RoleScope::Organization, | ||||
|             entity_type: EntityType::Organization, | ||||
|  | ||||
| @ -868,7 +868,7 @@ impl UserFromStorage { | ||||
|     pub async fn get_roles_from_db(&self, state: &SessionState) -> UserResult<Vec<UserRole>> { | ||||
|         state | ||||
|             .store | ||||
|             .list_user_roles_by_user_id(&self.0.user_id, UserRoleVersion::V1) | ||||
|             .list_user_roles_by_user_id_and_version(&self.0.user_id, UserRoleVersion::V1) | ||||
|             .await | ||||
|             .change_context(UserErrors::InternalServerError) | ||||
|     } | ||||
| @ -949,7 +949,7 @@ impl UserFromStorage { | ||||
|         } else { | ||||
|             state | ||||
|                 .store | ||||
|                 .list_user_roles_by_user_id(&self.0.user_id, UserRoleVersion::V1) | ||||
|                 .list_user_roles_by_user_id_and_version(&self.0.user_id, UserRoleVersion::V1) | ||||
|                 .await? | ||||
|                 .into_iter() | ||||
|                 .find(|role| role.status == UserStatus::Active) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Mani Chandra
					Mani Chandra