mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(user): implement invitations api (#5769)
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
use common_enums::PermissionGroup;
|
use common_enums::PermissionGroup;
|
||||||
use common_utils::pii;
|
use common_utils::pii;
|
||||||
|
use masking::Secret;
|
||||||
|
|
||||||
pub mod role;
|
pub mod role;
|
||||||
|
|
||||||
@ -138,3 +139,11 @@ pub struct ListUsersInEntityResponse {
|
|||||||
pub email: pii::Email,
|
pub email: pii::Email,
|
||||||
pub roles: Vec<role::MinimalRoleInfo>,
|
pub roles: Vec<role::MinimalRoleInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct ListInvitationForUserResponse {
|
||||||
|
pub entity_id: String,
|
||||||
|
pub entity_type: common_enums::EntityType,
|
||||||
|
pub entity_name: Option<Secret<String>>,
|
||||||
|
pub role_id: String,
|
||||||
|
}
|
||||||
|
|||||||
@ -8,7 +8,11 @@ use error_stack::{report, ResultExt};
|
|||||||
use router_env::logger;
|
use router_env::logger;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
enums::UserRoleVersion, errors, query::generics, schema::user_roles::dsl, user_role::*,
|
enums::{UserRoleVersion, UserStatus},
|
||||||
|
errors,
|
||||||
|
query::generics,
|
||||||
|
schema::user_roles::dsl,
|
||||||
|
user_role::*,
|
||||||
PgPooledConn, StorageResult,
|
PgPooledConn, StorageResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -201,6 +205,7 @@ impl UserRole {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn generic_user_roles_list_for_user(
|
pub async fn generic_user_roles_list_for_user(
|
||||||
conn: &PgPooledConn,
|
conn: &PgPooledConn,
|
||||||
user_id: String,
|
user_id: String,
|
||||||
@ -208,7 +213,9 @@ impl UserRole {
|
|||||||
merchant_id: Option<id_type::MerchantId>,
|
merchant_id: Option<id_type::MerchantId>,
|
||||||
profile_id: Option<id_type::ProfileId>,
|
profile_id: Option<id_type::ProfileId>,
|
||||||
entity_id: Option<String>,
|
entity_id: Option<String>,
|
||||||
|
status: Option<UserStatus>,
|
||||||
version: Option<UserRoleVersion>,
|
version: Option<UserRoleVersion>,
|
||||||
|
limit: Option<u32>,
|
||||||
) -> StorageResult<Vec<Self>> {
|
) -> StorageResult<Vec<Self>> {
|
||||||
let mut query = <Self as HasTable>::table()
|
let mut query = <Self as HasTable>::table()
|
||||||
.filter(dsl::user_id.eq(user_id))
|
.filter(dsl::user_id.eq(user_id))
|
||||||
@ -234,6 +241,14 @@ impl UserRole {
|
|||||||
query = query.filter(dsl::version.eq(version));
|
query = query.filter(dsl::version.eq(version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(status) = status {
|
||||||
|
query = query.filter(dsl::status.eq(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(limit) = limit {
|
||||||
|
query = query.limit(limit.into());
|
||||||
|
}
|
||||||
|
|
||||||
router_env::logger::debug!(query = %debug_query::<Pg,_>(&query).to_string());
|
router_env::logger::debug!(query = %debug_query::<Pg,_>(&query).to_string());
|
||||||
|
|
||||||
match generics::db_metrics::track_database_call::<Self, _, _>(
|
match generics::db_metrics::track_database_call::<Self, _, _>(
|
||||||
|
|||||||
@ -26,7 +26,7 @@ pub struct UserRole {
|
|||||||
pub version: enums::UserRoleVersion,
|
pub version: enums::UserRoleVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_entity_id_and_type(user_role: &UserRole) -> (Option<String>, Option<EntityType>) {
|
pub fn get_entity_id_and_type(user_role: &UserRole) -> (Option<String>, Option<EntityType>) {
|
||||||
match (user_role.version, user_role.role_id.as_str()) {
|
match (user_role.version, user_role.role_id.as_str()) {
|
||||||
(enums::UserRoleVersion::V1, consts::ROLE_ID_ORGANIZATION_ADMIN) => (
|
(enums::UserRoleVersion::V1, consts::ROLE_ID_ORGANIZATION_ADMIN) => (
|
||||||
user_role
|
user_role
|
||||||
|
|||||||
@ -33,7 +33,10 @@ use crate::services::email::types as email_types;
|
|||||||
use crate::{
|
use crate::{
|
||||||
consts,
|
consts,
|
||||||
core::encryption::send_request_to_key_service_for_user,
|
core::encryption::send_request_to_key_service_for_user,
|
||||||
db::domain::user_authentication_method::DEFAULT_USER_AUTH_METHOD,
|
db::{
|
||||||
|
domain::user_authentication_method::DEFAULT_USER_AUTH_METHOD,
|
||||||
|
user_role::ListUserRolesByUserIdPayload,
|
||||||
|
},
|
||||||
routes::{app::ReqState, SessionState},
|
routes::{app::ReqState, SessionState},
|
||||||
services::{authentication as auth, authorization::roles, openidconnect, ApplicationResponse},
|
services::{authentication as auth, authorization::roles, openidconnect, ApplicationResponse},
|
||||||
types::{domain, transformers::ForeignInto},
|
types::{domain, transformers::ForeignInto},
|
||||||
@ -2282,22 +2285,20 @@ pub async fn list_orgs_for_user(
|
|||||||
) -> UserResponse<Vec<user_api::ListOrgsForUserResponse>> {
|
) -> UserResponse<Vec<user_api::ListOrgsForUserResponse>> {
|
||||||
let orgs = state
|
let orgs = state
|
||||||
.store
|
.store
|
||||||
.list_user_roles_by_user_id(
|
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||||
user_from_token.user_id.as_str(),
|
user_id: user_from_token.user_id.as_str(),
|
||||||
None,
|
org_id: None,
|
||||||
None,
|
merchant_id: None,
|
||||||
None,
|
profile_id: None,
|
||||||
None,
|
entity_id: None,
|
||||||
None,
|
version: None,
|
||||||
)
|
status: Some(UserStatus::Active),
|
||||||
|
limit: None,
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)?
|
.change_context(UserErrors::InternalServerError)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|user_role| {
|
.filter_map(|user_role| user_role.org_id)
|
||||||
(user_role.status == UserStatus::Active)
|
|
||||||
.then_some(user_role.org_id)
|
|
||||||
.flatten()
|
|
||||||
})
|
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
let resp = futures::future::try_join_all(
|
let resp = futures::future::try_join_all(
|
||||||
@ -2311,7 +2312,11 @@ pub async fn list_orgs_for_user(
|
|||||||
org_id: org.get_organization_id(),
|
org_id: org.get_organization_id(),
|
||||||
org_name: org.get_organization_name(),
|
org_name: org.get_organization_name(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if resp.is_empty() {
|
||||||
|
Err(UserErrors::InternalServerError).attach_printable("No orgs found for a user")?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(resp))
|
Ok(ApplicationResponse::Json(resp))
|
||||||
}
|
}
|
||||||
@ -2344,26 +2349,24 @@ pub async fn list_merchants_for_user_in_org(
|
|||||||
merchant_id: merchant_account.get_id().to_owned(),
|
merchant_id: merchant_account.get_id().to_owned(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.collect()
|
.collect::<Vec<_>>()
|
||||||
} else {
|
} else {
|
||||||
let merchant_ids = state
|
let merchant_ids = state
|
||||||
.store
|
.store
|
||||||
.list_user_roles_by_user_id(
|
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||||
user_from_token.user_id.as_str(),
|
user_id: user_from_token.user_id.as_str(),
|
||||||
Some(&user_from_token.org_id),
|
org_id: Some(&user_from_token.org_id),
|
||||||
None,
|
merchant_id: None,
|
||||||
None,
|
profile_id: None,
|
||||||
None,
|
entity_id: None,
|
||||||
None,
|
version: None,
|
||||||
)
|
status: Some(UserStatus::Active),
|
||||||
|
limit: None,
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)?
|
.change_context(UserErrors::InternalServerError)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|user_role| {
|
.filter_map(|user_role| user_role.merchant_id)
|
||||||
(user_role.status == UserStatus::Active)
|
|
||||||
.then_some(user_role.merchant_id)
|
|
||||||
.flatten()
|
|
||||||
})
|
|
||||||
.collect::<HashSet<_>>()
|
.collect::<HashSet<_>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
@ -2379,9 +2382,13 @@ pub async fn list_merchants_for_user_in_org(
|
|||||||
merchant_id: merchant_account.get_id().to_owned(),
|
merchant_id: merchant_account.get_id().to_owned(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.collect()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if merchant_accounts.is_empty() {
|
||||||
|
Err(UserErrors::InternalServerError).attach_printable("No merchant found for a user")?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(merchant_accounts))
|
Ok(ApplicationResponse::Json(merchant_accounts))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2427,26 +2434,24 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account(
|
|||||||
profile_name: profile.profile_name,
|
profile_name: profile.profile_name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.collect()
|
.collect::<Vec<_>>()
|
||||||
} else {
|
} else {
|
||||||
let profile_ids = state
|
let profile_ids = state
|
||||||
.store
|
.store
|
||||||
.list_user_roles_by_user_id(
|
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||||
user_from_token.user_id.as_str(),
|
user_id: user_from_token.user_id.as_str(),
|
||||||
Some(&user_from_token.org_id),
|
org_id: Some(&user_from_token.org_id),
|
||||||
Some(&user_from_token.merchant_id),
|
merchant_id: Some(&user_from_token.merchant_id),
|
||||||
None,
|
profile_id: None,
|
||||||
None,
|
entity_id: None,
|
||||||
None,
|
version: None,
|
||||||
)
|
status: Some(UserStatus::Active),
|
||||||
|
limit: None,
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)?
|
.change_context(UserErrors::InternalServerError)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|user_role| {
|
.filter_map(|user_role| user_role.profile_id)
|
||||||
(user_role.status == UserStatus::Active)
|
|
||||||
.then_some(user_role.profile_id)
|
|
||||||
.flatten()
|
|
||||||
})
|
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
futures::future::try_join_all(profile_ids.iter().map(|profile_id| {
|
futures::future::try_join_all(profile_ids.iter().map(|profile_id| {
|
||||||
@ -2465,9 +2470,13 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account(
|
|||||||
profile_name: profile.profile_name,
|
profile_name: profile.profile_name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.collect()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if profiles.is_empty() {
|
||||||
|
Err(UserErrors::InternalServerError).attach_printable("No profile found for a user")?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(profiles))
|
Ok(ApplicationResponse::Json(profiles))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2503,23 +2512,23 @@ pub async fn switch_org_for_user(
|
|||||||
|
|
||||||
let user_role = state
|
let user_role = state
|
||||||
.store
|
.store
|
||||||
.list_user_roles_by_user_id(
|
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||||
&user_from_token.user_id,
|
user_id: &user_from_token.user_id,
|
||||||
Some(&request.org_id),
|
org_id: Some(&request.org_id),
|
||||||
None,
|
merchant_id: None,
|
||||||
None,
|
profile_id: None,
|
||||||
None,
|
entity_id: None,
|
||||||
None,
|
version: None,
|
||||||
)
|
status: Some(UserStatus::Active),
|
||||||
|
limit: Some(1),
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)
|
.change_context(UserErrors::InternalServerError)
|
||||||
.attach_printable("Failed to list user roles by user_id and org_id")?
|
.attach_printable("Failed to list user roles by user_id and org_id")?
|
||||||
.into_iter()
|
.pop()
|
||||||
.find(|role| role.status == UserStatus::Active)
|
|
||||||
.ok_or(UserErrors::InvalidRoleOperationWithMessage(
|
.ok_or(UserErrors::InvalidRoleOperationWithMessage(
|
||||||
"No user role found for the requested org_id".to_string(),
|
"No user role found for the requested org_id".to_string(),
|
||||||
))?
|
))?;
|
||||||
.to_owned();
|
|
||||||
|
|
||||||
let merchant_id = utils::user_role::get_single_merchant_id(&state, &user_role).await?;
|
let merchant_id = utils::user_role::get_single_merchant_id(&state, &user_role).await?;
|
||||||
|
|
||||||
@ -2547,7 +2556,7 @@ pub async fn switch_org_for_user(
|
|||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)
|
.change_context(UserErrors::InternalServerError)
|
||||||
.attach_printable("Failed to list business profiles by merchant_id")?
|
.attach_printable("Failed to list business profiles by merchant_id")?
|
||||||
.first()
|
.pop()
|
||||||
.ok_or(UserErrors::InternalServerError)
|
.ok_or(UserErrors::InternalServerError)
|
||||||
.attach_printable("No business profile found for the merchant_id")?
|
.attach_printable("No business profile found for the merchant_id")?
|
||||||
.get_id()
|
.get_id()
|
||||||
@ -2635,7 +2644,7 @@ pub async fn switch_merchant_for_user_in_org(
|
|||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)
|
.change_context(UserErrors::InternalServerError)
|
||||||
.attach_printable("Failed to list business profiles by merchant_id")?
|
.attach_printable("Failed to list business profiles by merchant_id")?
|
||||||
.first()
|
.pop()
|
||||||
.ok_or(UserErrors::InternalServerError)
|
.ok_or(UserErrors::InternalServerError)
|
||||||
.attach_printable("No business profile found for the given merchant_id")?
|
.attach_printable("No business profile found for the given merchant_id")?
|
||||||
.get_id()
|
.get_id()
|
||||||
@ -2688,12 +2697,11 @@ pub async fn switch_merchant_for_user_in_org(
|
|||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)
|
.change_context(UserErrors::InternalServerError)
|
||||||
.attach_printable("Failed to list business profiles by merchant_id")?
|
.attach_printable("Failed to list business profiles by merchant_id")?
|
||||||
.first()
|
.pop()
|
||||||
.ok_or(UserErrors::InternalServerError)
|
.ok_or(UserErrors::InternalServerError)
|
||||||
.attach_printable("No business profile found for the merchant_id")?
|
.attach_printable("No business profile found for the merchant_id")?
|
||||||
.get_id()
|
.get_id()
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
(
|
(
|
||||||
user_from_token.org_id.clone(),
|
user_from_token.org_id.clone(),
|
||||||
merchant_id,
|
merchant_id,
|
||||||
@ -2705,25 +2713,25 @@ pub async fn switch_merchant_for_user_in_org(
|
|||||||
EntityType::Merchant | EntityType::Profile => {
|
EntityType::Merchant | EntityType::Profile => {
|
||||||
let user_role = state
|
let user_role = state
|
||||||
.store
|
.store
|
||||||
.list_user_roles_by_user_id(
|
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||||
&user_from_token.user_id,
|
user_id: &user_from_token.user_id,
|
||||||
Some(&user_from_token.org_id),
|
org_id: Some(&user_from_token.org_id),
|
||||||
Some(&request.merchant_id),
|
merchant_id: Some(&request.merchant_id),
|
||||||
None,
|
profile_id: None,
|
||||||
None,
|
entity_id: None,
|
||||||
None,
|
version: None,
|
||||||
)
|
status: Some(UserStatus::Active),
|
||||||
|
limit: Some(1),
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)
|
.change_context(UserErrors::InternalServerError)
|
||||||
.attach_printable(
|
.attach_printable(
|
||||||
"Failed to list user roles for the given user_id, org_id and merchant_id",
|
"Failed to list user roles for the given user_id, org_id and merchant_id",
|
||||||
)?
|
)?
|
||||||
.into_iter()
|
.pop()
|
||||||
.find(|role| role.status == UserStatus::Active)
|
|
||||||
.ok_or(UserErrors::InvalidRoleOperationWithMessage(
|
.ok_or(UserErrors::InvalidRoleOperationWithMessage(
|
||||||
"No user role associated with the requested merchant_id".to_string(),
|
"No user role associated with the requested merchant_id".to_string(),
|
||||||
))?
|
))?;
|
||||||
.to_owned();
|
|
||||||
|
|
||||||
let profile_id = if let Some(profile_id) = &user_role.profile_id {
|
let profile_id = if let Some(profile_id) = &user_role.profile_id {
|
||||||
profile_id.clone()
|
profile_id.clone()
|
||||||
@ -2749,7 +2757,7 @@ pub async fn switch_merchant_for_user_in_org(
|
|||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)
|
.change_context(UserErrors::InternalServerError)
|
||||||
.attach_printable("Failed to list business profiles for the given merchant_id")?
|
.attach_printable("Failed to list business profiles for the given merchant_id")?
|
||||||
.first()
|
.pop()
|
||||||
.ok_or(UserErrors::InternalServerError)
|
.ok_or(UserErrors::InternalServerError)
|
||||||
.attach_printable("No business profile found for the given merchant_id")?
|
.attach_printable("No business profile found for the given merchant_id")?
|
||||||
.get_id()
|
.get_id()
|
||||||
@ -2846,23 +2854,24 @@ pub async fn switch_profile_for_user_in_org_and_merchant(
|
|||||||
EntityType::Profile => {
|
EntityType::Profile => {
|
||||||
let user_role = state
|
let user_role = state
|
||||||
.store
|
.store
|
||||||
.list_user_roles_by_user_id(
|
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload{
|
||||||
&user_from_token.user_id,
|
user_id:&user_from_token.user_id,
|
||||||
Some(&user_from_token.org_id),
|
org_id: Some(&user_from_token.org_id),
|
||||||
Some(&user_from_token.merchant_id),
|
merchant_id: Some(&user_from_token.merchant_id),
|
||||||
Some(&request.profile_id),
|
profile_id:Some(&request.profile_id),
|
||||||
None,
|
entity_id: None,
|
||||||
None,
|
version:None,
|
||||||
|
status: Some(UserStatus::Active),
|
||||||
|
limit: Some(1)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.change_context(UserErrors::InternalServerError)
|
.change_context(UserErrors::InternalServerError)
|
||||||
.attach_printable("Failed to list user roles for the given user_id, org_id, merchant_id and profile_id")?
|
.attach_printable("Failed to list user roles for the given user_id, org_id, merchant_id and profile_id")?
|
||||||
.into_iter()
|
.pop()
|
||||||
.find(|role| role.status == UserStatus::Active)
|
|
||||||
.ok_or(UserErrors::InvalidRoleOperationWithMessage(
|
.ok_or(UserErrors::InvalidRoleOperationWithMessage(
|
||||||
"No user role associated with the profile".to_string(),
|
"No user role associated with the profile".to_string(),
|
||||||
))?
|
))?;
|
||||||
.to_owned();
|
|
||||||
|
|
||||||
(request.profile_id, user_role.role_id)
|
(request.profile_id, user_role.role_id)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,14 +3,14 @@ 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 as user_role_api};
|
||||||
use diesel_models::{
|
use diesel_models::{
|
||||||
enums::{UserRoleVersion, UserStatus},
|
enums::{UserRoleVersion, UserStatus},
|
||||||
user_role::UserRoleUpdate,
|
user_role::{get_entity_id_and_type, UserRoleUpdate},
|
||||||
};
|
};
|
||||||
use error_stack::{report, ResultExt};
|
use error_stack::{report, ResultExt};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::errors::{StorageErrorExt, UserErrors, UserResponse},
|
core::errors::{StorageErrorExt, UserErrors, UserResponse},
|
||||||
db::user_role::ListUserRolesByOrgIdPayload,
|
db::user_role::{ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload},
|
||||||
routes::{app::ReqState, SessionState},
|
routes::{app::ReqState, SessionState},
|
||||||
services::{
|
services::{
|
||||||
authentication as auth,
|
authentication as auth,
|
||||||
@ -687,3 +687,41 @@ pub async fn list_users_in_lineage(
|
|||||||
.collect::<Result<Vec<_>, _>>()?,
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn list_invitations_for_user(
|
||||||
|
state: SessionState,
|
||||||
|
user_from_token: auth::UserIdFromAuth,
|
||||||
|
) -> UserResponse<Vec<user_role_api::ListInvitationForUserResponse>> {
|
||||||
|
let invitations = state
|
||||||
|
.store
|
||||||
|
.list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
|
||||||
|
user_id: &user_from_token.user_id,
|
||||||
|
org_id: None,
|
||||||
|
merchant_id: None,
|
||||||
|
profile_id: None,
|
||||||
|
entity_id: None,
|
||||||
|
version: None,
|
||||||
|
status: Some(UserStatus::InvitationSent),
|
||||||
|
limit: None,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.change_context(UserErrors::InternalServerError)
|
||||||
|
.attach_printable("Failed to list user roles by user id and invitation sent")?
|
||||||
|
.into_iter()
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|user_role| {
|
||||||
|
let (entity_id, entity_type) = get_entity_id_and_type(&user_role);
|
||||||
|
entity_id.zip(entity_type).map(|(entity_id, entity_type)| {
|
||||||
|
user_role_api::ListInvitationForUserResponse {
|
||||||
|
entity_id,
|
||||||
|
entity_type,
|
||||||
|
entity_name: None,
|
||||||
|
role_id: user_role.role_id,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(ApplicationResponse::Json(invitations))
|
||||||
|
}
|
||||||
|
|||||||
@ -35,7 +35,10 @@ use super::{
|
|||||||
user::{sample_data::BatchSampleDataInterface, UserInterface},
|
user::{sample_data::BatchSampleDataInterface, UserInterface},
|
||||||
user_authentication_method::UserAuthenticationMethodInterface,
|
user_authentication_method::UserAuthenticationMethodInterface,
|
||||||
user_key_store::UserKeyStoreInterface,
|
user_key_store::UserKeyStoreInterface,
|
||||||
user_role::{InsertUserRolePayload, ListUserRolesByOrgIdPayload, UserRoleInterface},
|
user_role::{
|
||||||
|
InsertUserRolePayload, ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload,
|
||||||
|
UserRoleInterface,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "payouts")]
|
#[cfg(feature = "payouts")]
|
||||||
use crate::services::kafka::payout::KafkaPayout;
|
use crate::services::kafka::payout::KafkaPayout;
|
||||||
@ -2872,25 +2875,11 @@ impl UserRoleInterface for KafkaStore {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_user_roles_by_user_id(
|
async fn list_user_roles_by_user_id<'a>(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
payload: ListUserRolesByUserIdPayload<'a>,
|
||||||
org_id: Option<&id_type::OrganizationId>,
|
|
||||||
merchant_id: Option<&id_type::MerchantId>,
|
|
||||||
profile_id: Option<&id_type::ProfileId>,
|
|
||||||
entity_id: Option<&String>,
|
|
||||||
version: Option<enums::UserRoleVersion>,
|
|
||||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
||||||
self.diesel_store
|
self.diesel_store.list_user_roles_by_user_id(payload).await
|
||||||
.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>(
|
async fn list_user_roles_by_org_id<'a>(
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
use common_utils::id_type;
|
use common_utils::id_type;
|
||||||
use diesel_models::{enums, user_role as storage};
|
use diesel_models::{
|
||||||
|
enums::{self, UserStatus},
|
||||||
|
user_role as storage,
|
||||||
|
};
|
||||||
use error_stack::{report, ResultExt};
|
use error_stack::{report, ResultExt};
|
||||||
use router_env::{instrument, tracing};
|
use router_env::{instrument, tracing};
|
||||||
|
|
||||||
@ -33,6 +36,17 @@ pub struct ListUserRolesByOrgIdPayload<'a> {
|
|||||||
pub version: Option<enums::UserRoleVersion>,
|
pub version: Option<enums::UserRoleVersion>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ListUserRolesByUserIdPayload<'a> {
|
||||||
|
pub user_id: &'a str,
|
||||||
|
pub org_id: Option<&'a id_type::OrganizationId>,
|
||||||
|
pub merchant_id: Option<&'a id_type::MerchantId>,
|
||||||
|
pub profile_id: Option<&'a id_type::ProfileId>,
|
||||||
|
pub entity_id: Option<&'a String>,
|
||||||
|
pub version: Option<enums::UserRoleVersion>,
|
||||||
|
pub status: Option<UserStatus>,
|
||||||
|
pub limit: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait UserRoleInterface {
|
pub trait UserRoleInterface {
|
||||||
async fn insert_user_role(
|
async fn insert_user_role(
|
||||||
@ -93,14 +107,9 @@ pub trait UserRoleInterface {
|
|||||||
version: enums::UserRoleVersion,
|
version: enums::UserRoleVersion,
|
||||||
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
||||||
|
|
||||||
async fn list_user_roles_by_user_id(
|
async fn list_user_roles_by_user_id<'a>(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
payload: ListUserRolesByUserIdPayload<'a>,
|
||||||
org_id: Option<&id_type::OrganizationId>,
|
|
||||||
merchant_id: Option<&id_type::MerchantId>,
|
|
||||||
profile_id: Option<&id_type::ProfileId>,
|
|
||||||
entity_id: Option<&String>,
|
|
||||||
version: Option<enums::UserRoleVersion>,
|
|
||||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError>;
|
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError>;
|
||||||
|
|
||||||
async fn list_user_roles_by_org_id<'a>(
|
async fn list_user_roles_by_org_id<'a>(
|
||||||
@ -244,24 +253,21 @@ impl UserRoleInterface for Store {
|
|||||||
.map_err(|error| report!(errors::StorageError::from(error)))
|
.map_err(|error| report!(errors::StorageError::from(error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_user_roles_by_user_id(
|
async fn list_user_roles_by_user_id<'a>(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
payload: ListUserRolesByUserIdPayload<'a>,
|
||||||
org_id: Option<&id_type::OrganizationId>,
|
|
||||||
merchant_id: Option<&id_type::MerchantId>,
|
|
||||||
profile_id: Option<&id_type::ProfileId>,
|
|
||||||
entity_id: Option<&String>,
|
|
||||||
version: Option<enums::UserRoleVersion>,
|
|
||||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
||||||
let conn = connection::pg_connection_read(self).await?;
|
let conn = connection::pg_connection_read(self).await?;
|
||||||
storage::UserRole::generic_user_roles_list_for_user(
|
storage::UserRole::generic_user_roles_list_for_user(
|
||||||
&conn,
|
&conn,
|
||||||
user_id.to_owned(),
|
payload.user_id.to_owned(),
|
||||||
org_id.cloned(),
|
payload.org_id.cloned(),
|
||||||
merchant_id.cloned(),
|
payload.merchant_id.cloned(),
|
||||||
profile_id.cloned(),
|
payload.profile_id.cloned(),
|
||||||
entity_id.cloned(),
|
payload.entity_id.cloned(),
|
||||||
version,
|
payload.status,
|
||||||
|
payload.version,
|
||||||
|
payload.limit,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|error| report!(errors::StorageError::from(error)))
|
.map_err(|error| report!(errors::StorageError::from(error)))
|
||||||
@ -552,50 +558,52 @@ impl UserRoleInterface for MockDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_user_roles_by_user_id(
|
async fn list_user_roles_by_user_id<'a>(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
payload: ListUserRolesByUserIdPayload<'a>,
|
||||||
org_id: Option<&id_type::OrganizationId>,
|
|
||||||
merchant_id: Option<&id_type::MerchantId>,
|
|
||||||
profile_id: Option<&id_type::ProfileId>,
|
|
||||||
entity_id: Option<&String>,
|
|
||||||
version: Option<enums::UserRoleVersion>,
|
|
||||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
||||||
let user_roles = self.user_roles.lock().await;
|
let user_roles = self.user_roles.lock().await;
|
||||||
|
|
||||||
let filtered_roles: Vec<_> = user_roles
|
let mut filtered_roles: Vec<_> = user_roles
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|role| {
|
.filter_map(|role| {
|
||||||
let mut filter_condition = role.user_id == user_id;
|
let mut filter_condition = role.user_id == payload.user_id;
|
||||||
|
|
||||||
role.org_id
|
role.org_id
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.zip(org_id)
|
.zip(payload.org_id)
|
||||||
.inspect(|(role_org_id, org_id)| {
|
.inspect(|(role_org_id, org_id)| {
|
||||||
filter_condition = filter_condition && role_org_id == org_id
|
filter_condition = filter_condition && role_org_id == org_id
|
||||||
});
|
});
|
||||||
role.merchant_id.as_ref().zip(merchant_id).inspect(
|
role.merchant_id.as_ref().zip(payload.merchant_id).inspect(
|
||||||
|(role_merchant_id, merchant_id)| {
|
|(role_merchant_id, merchant_id)| {
|
||||||
filter_condition = filter_condition && role_merchant_id == merchant_id
|
filter_condition = filter_condition && role_merchant_id == merchant_id
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
role.profile_id.as_ref().zip(profile_id).inspect(
|
role.profile_id.as_ref().zip(payload.profile_id).inspect(
|
||||||
|(role_profile_id, profile_id)| {
|
|(role_profile_id, profile_id)| {
|
||||||
filter_condition = filter_condition && role_profile_id == profile_id
|
filter_condition = filter_condition && role_profile_id == profile_id
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
role.entity_id
|
role.entity_id.as_ref().zip(payload.entity_id).inspect(
|
||||||
.as_ref()
|
|(role_entity_id, entity_id)| {
|
||||||
.zip(entity_id)
|
|
||||||
.inspect(|(role_entity_id, entity_id)| {
|
|
||||||
filter_condition = filter_condition && role_entity_id == entity_id
|
filter_condition = filter_condition && role_entity_id == entity_id
|
||||||
});
|
},
|
||||||
version.inspect(|ver| filter_condition = filter_condition && ver == &role.version);
|
);
|
||||||
|
payload
|
||||||
|
.version
|
||||||
|
.inspect(|ver| filter_condition = filter_condition && ver == &role.version);
|
||||||
|
payload.status.inspect(|status| {
|
||||||
|
filter_condition = filter_condition && status == &role.status
|
||||||
|
});
|
||||||
|
|
||||||
filter_condition.then(|| role.to_owned())
|
filter_condition.then(|| role.to_owned())
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
if let Some(Ok(limit)) = payload.limit.map(|val| val.try_into()) {
|
||||||
|
filtered_roles = filtered_roles.into_iter().take(limit).collect();
|
||||||
|
}
|
||||||
Ok(filtered_roles)
|
Ok(filtered_roles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1725,6 +1725,9 @@ impl User {
|
|||||||
.service(
|
.service(
|
||||||
web::resource("/profile")
|
web::resource("/profile")
|
||||||
.route(web::get().to(list_profiles_for_user_in_org_and_merchant)),
|
.route(web::get().to(list_profiles_for_user_in_org_and_merchant)),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/invitation").route(web::get().to(list_invitations_for_user)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -250,6 +250,7 @@ impl From<Flow> for ApiIdentifier {
|
|||||||
| Flow::ListOrgForUser
|
| Flow::ListOrgForUser
|
||||||
| Flow::ListMerchantsForUserInOrg
|
| Flow::ListMerchantsForUserInOrg
|
||||||
| Flow::ListProfileForUserInOrgAndMerchant
|
| Flow::ListProfileForUserInOrgAndMerchant
|
||||||
|
| Flow::ListInvitationsForUser
|
||||||
| Flow::AuthSelect => Self::User,
|
| Flow::AuthSelect => Self::User,
|
||||||
|
|
||||||
Flow::ListRoles
|
Flow::ListRoles
|
||||||
|
|||||||
@ -310,3 +310,23 @@ pub async fn list_updatable_roles_at_entity_level(
|
|||||||
))
|
))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn list_invitations_for_user(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
req: HttpRequest,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let flow = Flow::ListInvitationsForUser;
|
||||||
|
|
||||||
|
Box::pin(api::server_wrap(
|
||||||
|
flow,
|
||||||
|
state.clone(),
|
||||||
|
&req,
|
||||||
|
(),
|
||||||
|
|state, user_id_from_token, _, _| {
|
||||||
|
user_role_core::list_invitations_for_user(state, user_id_from_token)
|
||||||
|
},
|
||||||
|
&auth::SinglePurposeOrLoginTokenAuth(TokenPurpose::AcceptInvite),
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|||||||
@ -474,6 +474,8 @@ pub enum Flow {
|
|||||||
ListProfileForUserInOrgAndMerchant,
|
ListProfileForUserInOrgAndMerchant,
|
||||||
/// List Users in Org
|
/// List Users in Org
|
||||||
ListUsersInLineage,
|
ListUsersInLineage,
|
||||||
|
/// List invitations for user
|
||||||
|
ListInvitationsForUser,
|
||||||
/// List initial webhook delivery attempts
|
/// List initial webhook delivery attempts
|
||||||
WebhookEventInitialDeliveryAttemptList,
|
WebhookEventInitialDeliveryAttemptList,
|
||||||
/// List delivery attempts for a webhook event
|
/// List delivery attempts for a webhook event
|
||||||
|
|||||||
Reference in New Issue
Block a user