feat(user_roles): support switch for new hierarchy (#5692)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Apoorv Dixit
2024-08-28 13:30:07 +05:30
committed by GitHub
parent 771f48cfe0
commit 53b3163881
11 changed files with 559 additions and 27 deletions

View File

@ -1320,12 +1320,12 @@ pub async fn create_internal_user(
pub async fn switch_merchant_id(
state: SessionState,
request: user_api::SwitchMerchantIdRequest,
request: user_api::SwitchMerchantRequest,
user_from_token: auth::UserFromToken,
) -> UserResponse<user_api::DashboardEntryResponse> {
if user_from_token.merchant_id == request.merchant_id {
return Err(UserErrors::InvalidRoleOperationWithMessage(
"User switching to same merchant id".to_string(),
"User switching to same merchant_id".to_string(),
)
.into());
}
@ -1375,9 +1375,9 @@ pub async fn switch_merchant_id(
})?
.organization_id;
let token = utils::user::generate_jwt_auth_token_with_custom_role_attributes(
let token = utils::user::generate_jwt_auth_token_with_attributes(
&state,
&user,
user_from_token.user_id,
request.merchant_id.clone(),
org_id.clone(),
user_from_token.role_id.clone(),
@ -1557,7 +1557,7 @@ pub async fn list_users_for_merchant_account(
.list_user_roles_by_merchant_id(&user_from_token.merchant_id, UserRoleVersion::V1)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("No user roles for given merchant id")?
.attach_printable("No user roles for given merchant_id")?
.into_iter()
.map(|role| (role.user_id.clone(), role))
.collect();
@ -1569,7 +1569,7 @@ pub async fn list_users_for_merchant_account(
.find_users_by_user_ids(user_ids)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("No users for given merchant id")?;
.attach_printable("No users for given merchant_id")?;
let users_and_user_roles: Vec<_> = users
.into_iter()
@ -2134,7 +2134,7 @@ pub async fn verify_recovery_code(
state: SessionState,
user_token: auth::UserIdFromAuth,
req: user_api::VerifyRecoveryCodeRequest,
) -> UserResponse<user_api::TokenResponse> {
) -> UserResponse<()> {
let user_from_db: domain::UserFromStorage = state
.global_store
.find_user_by_id(&user_token.user_id)
@ -2760,3 +2760,442 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account(
Ok(ApplicationResponse::Json(profiles))
}
pub async fn switch_org_for_user(
state: SessionState,
request: user_api::SwitchOrganizationRequest,
user_from_token: auth::UserFromToken,
) -> UserResponse<user_api::TokenResponse> {
if user_from_token.org_id == request.org_id {
return Err(UserErrors::InvalidRoleOperationWithMessage(
"User switching to same org".to_string(),
)
.into());
}
let key_manager_state = &(&state).into();
let 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)
.attach_printable("Failed to retrieve role information")?;
if role_info.get_entity_type() == EntityType::Internal {
return Err(UserErrors::InvalidRoleOperationWithMessage(
"Org switching not allowed for Internal role".to_string(),
)
.into());
}
let user_role = state
.store
.list_user_roles(
&user_from_token.user_id,
Some(&request.org_id),
None,
None,
None,
None,
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to list user roles by user_id and org_id")?
.into_iter()
.find(|role| role.status == UserStatus::Active)
.ok_or(UserErrors::InvalidRoleOperationWithMessage(
"No user role found for the requested org_id".to_string(),
))?
.to_owned();
let merchant_id = if let Some(merchant_id) = &user_role.merchant_id {
merchant_id.clone()
} else {
state
.store
.list_merchant_accounts_by_organization_id(
key_manager_state,
request.org_id.get_string_repr(),
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to list merchant accounts by organization_id")?
.first()
.ok_or(UserErrors::InternalServerError)
.attach_printable("No merchant account found for the given organization_id")?
.get_id()
.clone()
};
let profile_id = if let Some(profile_id) = &user_role.profile_id {
profile_id.clone()
} else {
let merchant_key_store = state
.store
.get_merchant_key_store_by_merchant_id(
key_manager_state,
&merchant_id,
&state.store.get_master_key().to_vec().into(),
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to retrieve merchant key store by merchant_id")?;
state
.store
.list_business_profile_by_merchant_id(
key_manager_state,
&merchant_key_store,
&merchant_id,
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to list business profiles by merchant_id")?
.first()
.ok_or(UserErrors::InternalServerError)
.attach_printable("No business profile found for the merchant_id")?
.profile_id
.clone()
};
let token = utils::user::generate_jwt_auth_token_with_attributes(
&state,
user_from_token.user_id,
merchant_id.clone(),
request.org_id.clone(),
user_role.role_id.clone(),
Some(profile_id.clone()),
)
.await?;
utils::user_role::set_role_permissions_in_cache_by_role_id_merchant_id_org_id(
&state,
&user_role.role_id,
&merchant_id,
&request.org_id,
)
.await;
let response = user_api::TokenResponse {
token: token.clone(),
token_type: common_enums::TokenPurpose::UserInfo,
};
auth::cookies::set_cookie_response(response, token)
}
pub async fn switch_merchant_for_user_in_org(
state: SessionState,
request: user_api::SwitchMerchantRequest,
user_from_token: auth::UserFromToken,
) -> UserResponse<user_api::TokenResponse> {
if user_from_token.merchant_id == request.merchant_id {
return Err(UserErrors::InvalidRoleOperationWithMessage(
"User switching to same merchant".to_string(),
)
.into());
}
let key_manager_state = &(&state).into();
let 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)
.attach_printable("Failed to retrieve role information")?;
let (org_id, merchant_id, profile_id, role_id) = match role_info.get_entity_type() {
EntityType::Internal => {
let merchant_key_store = state
.store
.get_merchant_key_store_by_merchant_id(
key_manager_state,
&request.merchant_id,
&state.store.get_master_key().to_vec().into(),
)
.await
.to_not_found_response(UserErrors::MerchantIdNotFound)?;
let merchant_account = state
.store
.find_merchant_account_by_merchant_id(
key_manager_state,
&request.merchant_id,
&merchant_key_store,
)
.await
.to_not_found_response(UserErrors::MerchantIdNotFound)?;
let profile_id = state
.store
.list_business_profile_by_merchant_id(
key_manager_state,
&merchant_key_store,
&request.merchant_id,
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to list business profiles by merchant_id")?
.first()
.ok_or(UserErrors::InternalServerError)
.attach_printable("No business profile found for the given merchant_id")?
.profile_id
.clone();
(
merchant_account.organization_id,
request.merchant_id,
profile_id,
user_from_token.role_id.clone(),
)
}
EntityType::Organization => {
let merchant_key_store = state
.store
.get_merchant_key_store_by_merchant_id(
key_manager_state,
&request.merchant_id,
&state.store.get_master_key().to_vec().into(),
)
.await
.to_not_found_response(UserErrors::MerchantIdNotFound)?;
let merchant_id = state
.store
.find_merchant_account_by_merchant_id(
key_manager_state,
&request.merchant_id,
&merchant_key_store,
)
.await
.change_context(UserErrors::MerchantIdNotFound)?
.organization_id
.eq(&user_from_token.org_id)
.then(|| request.merchant_id.clone())
.ok_or_else(|| {
UserErrors::InvalidRoleOperationWithMessage(
"No such merchant_id found for the user in the org".to_string(),
)
})?;
let profile_id = state
.store
.list_business_profile_by_merchant_id(
key_manager_state,
&merchant_key_store,
&merchant_id,
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to list business profiles by merchant_id")?
.first()
.ok_or(UserErrors::InternalServerError)
.attach_printable("No business profile found for the merchant_id")?
.profile_id
.clone();
(
user_from_token.org_id.clone(),
merchant_id,
profile_id,
user_from_token.role_id.clone(),
)
}
EntityType::Merchant | EntityType::Profile => {
let user_role = state
.store
.list_user_roles(
&user_from_token.user_id,
Some(&user_from_token.org_id),
Some(&request.merchant_id),
None,
None,
None,
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable(
"Failed to list user roles for the given user_id, org_id and merchant_id",
)?
.into_iter()
.find(|role| role.status == UserStatus::Active)
.ok_or(UserErrors::InvalidRoleOperationWithMessage(
"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 {
profile_id.clone()
} else {
let merchant_key_store = state
.store
.get_merchant_key_store_by_merchant_id(
key_manager_state,
&request.merchant_id,
&state.store.get_master_key().to_vec().into(),
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to retrieve merchant key store by merchant_id")?;
state
.store
.list_business_profile_by_merchant_id(
key_manager_state,
&merchant_key_store,
&request.merchant_id,
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to list business profiles for the given merchant_id")?
.first()
.ok_or(UserErrors::InternalServerError)
.attach_printable("No business profile found for the given merchant_id")?
.profile_id
.clone()
};
(
user_from_token.org_id,
request.merchant_id,
profile_id,
user_role.role_id,
)
}
};
let token = utils::user::generate_jwt_auth_token_with_attributes(
&state,
user_from_token.user_id,
merchant_id.clone(),
org_id.clone(),
role_id.clone(),
Some(profile_id),
)
.await?;
utils::user_role::set_role_permissions_in_cache_by_role_id_merchant_id_org_id(
&state,
&role_id,
&merchant_id,
&org_id,
)
.await;
let response = user_api::TokenResponse {
token: token.clone(),
token_type: common_enums::TokenPurpose::UserInfo,
};
auth::cookies::set_cookie_response(response, token)
}
pub async fn switch_profile_for_user_in_org_and_merchant(
state: SessionState,
request: user_api::SwitchProfileRequest,
user_from_token: auth::UserFromToken,
) -> UserResponse<user_api::TokenResponse> {
if user_from_token.profile_id == Some(request.profile_id.clone()) {
return Err(UserErrors::InvalidRoleOperationWithMessage(
"User switching to same profile".to_string(),
)
.into());
}
let key_manager_state = &(&state).into();
let 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)
.attach_printable("Failed to retrieve role information")?;
let (profile_id, role_id) = match role_info.get_entity_type() {
EntityType::Internal | EntityType::Organization | EntityType::Merchant => {
let merchant_key_store = state
.store
.get_merchant_key_store_by_merchant_id(
key_manager_state,
&user_from_token.merchant_id,
&state.store.get_master_key().to_vec().into(),
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to retrieve merchant key store by merchant_id")?;
let profile_id = state
.store
.find_business_profile_by_merchant_id_profile_id(
key_manager_state,
&merchant_key_store,
&user_from_token.merchant_id,
&request.profile_id,
)
.await
.change_context(UserErrors::InvalidRoleOperationWithMessage(
"No such profile found for the merchant".to_string(),
))?
.profile_id;
(profile_id, user_from_token.role_id)
}
EntityType::Profile => {
let user_role = state
.store
.list_user_roles(
&user_from_token.user_id,
Some(&user_from_token.org_id),
Some(&user_from_token.merchant_id),
Some(&request.profile_id),
None,
None,
)
.await
.change_context(UserErrors::InternalServerError)
.attach_printable("Failed to list user roles for the given user_id, org_id, merchant_id and profile_id")?
.into_iter()
.find(|role| role.status == UserStatus::Active)
.ok_or(UserErrors::InvalidRoleOperationWithMessage(
"No user role associated with the profile".to_string(),
))?
.to_owned();
(request.profile_id, user_role.role_id)
}
};
let token = utils::user::generate_jwt_auth_token_with_attributes(
&state,
user_from_token.user_id,
user_from_token.merchant_id.clone(),
user_from_token.org_id.clone(),
role_id.clone(),
Some(profile_id),
)
.await?;
utils::user_role::set_role_permissions_in_cache_by_role_id_merchant_id_org_id(
&state,
&role_id,
&user_from_token.merchant_id,
&user_from_token.org_id,
)
.await;
let response = user_api::TokenResponse {
token: token.clone(),
token_type: common_enums::TokenPurpose::UserInfo,
};
auth::cookies::set_cookie_response(response, token)
}