From 3fa7de10dc19fed052ee916ec8f37b54248cea22 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit Date: Tue, 26 Nov 2024 14:51:15 +0530 Subject: [PATCH 1/6] feat(users): add tenant id reads in user roles --- crates/diesel_models/src/query/user_role.rs | 210 +++++++------- crates/router/src/analytics.rs | 2 + crates/router/src/core/user.rs | 269 +++++++++++------- crates/router/src/core/user_role.rs | 65 +++++ crates/router/src/db/kafka_store.rs | 6 + crates/router/src/db/user_role.rs | 81 ++++-- .../src/types/domain/user/decision_manager.rs | 2 + crates/router/src/utils/user_role.rs | 6 + 8 files changed, 416 insertions(+), 225 deletions(-) diff --git a/crates/diesel_models/src/query/user_role.rs b/crates/diesel_models/src/query/user_role.rs index ed018cc2381e..dce379be6a09 100644 --- a/crates/diesel_models/src/query/user_role.rs +++ b/crates/diesel_models/src/query/user_role.rs @@ -1,7 +1,11 @@ use async_bb8_diesel::AsyncRunQueryDsl; use common_utils::id_type; use diesel::{ - associations::HasTable, debug_query, pg::Pg, result::Error as DieselError, + associations::HasTable, + debug_query, + pg::Pg, + result::Error as DieselError, + sql_types::{Bool, Nullable}, BoolExpressionMethods, ExpressionMethods, QueryDsl, }; use error_stack::{report, ResultExt}; @@ -22,89 +26,70 @@ impl UserRoleNew { } impl UserRole { - pub async fn find_by_user_id( - conn: &PgPooledConn, - user_id: String, - version: UserRoleVersion, - ) -> StorageResult { - generics::generic_find_one::<::Table, _, _>( - conn, - dsl::user_id.eq(user_id).and(dsl::version.eq(version)), - ) - .await - } - - pub async fn find_by_user_id_merchant_id( - conn: &PgPooledConn, - user_id: String, - merchant_id: id_type::MerchantId, - version: UserRoleVersion, - ) -> StorageResult { - generics::generic_find_one::<::Table, _, _>( - conn, - dsl::user_id - .eq(user_id) - .and(dsl::merchant_id.eq(merchant_id)) - .and(dsl::version.eq(version)), - ) - .await - } - - pub async fn list_by_user_id( - conn: &PgPooledConn, - user_id: String, - version: UserRoleVersion, - ) -> StorageResult> { - generics::generic_filter::<::Table, _, _, _>( - conn, - dsl::user_id.eq(user_id).and(dsl::version.eq(version)), - None, - None, - Some(dsl::created_at.asc()), + fn check_user_in_lineage( + tenant_id: id_type::TenantId, + org_id: Option, + merchant_id: Option, + profile_id: Option, + ) -> Box< + dyn diesel::BoxableExpression<::Table, Pg, SqlType = Nullable> + + 'static, + > { + // Checking in user roles, for a user in token hierarchy, only one of the relations will be true: + // either tenant level, org level, merchant level, or profile level + // Tenant-level: (tenant_id = ? && org_id = null && merchant_id = null && profile_id = null) + // Org-level: (org_id = ? && merchant_id = null && profile_id = null) + // Merchant-level: (org_id = ? && merchant_id = ? && profile_id = null) + // Profile-level: (org_id = ? && merchant_id = ? && profile_id = ?) + Box::new( + // Tenant-level condtion + dsl::tenant_id + .eq(tenant_id.clone()) + .and(dsl::org_id.is_null()) + .and(dsl::merchant_id.is_null()) + .and(dsl::profile_id.is_null()) + .or( + // Org-level condition + dsl::tenant_id + .eq(tenant_id.clone()) + .and(dsl::org_id.eq(org_id.clone())) + .and(dsl::merchant_id.is_null()) + .and(dsl::profile_id.is_null()), + ) + .or( + // Merchant-level condition + dsl::tenant_id + .eq(tenant_id.clone()) + .and(dsl::org_id.eq(org_id.clone())) + .and(dsl::merchant_id.eq(merchant_id.clone())) + .and(dsl::profile_id.is_null()), + ) + .or( + // Profile-level condition + dsl::tenant_id + .eq(tenant_id) + .and(dsl::org_id.eq(org_id)) + .and(dsl::merchant_id.eq(merchant_id)) + .and(dsl::profile_id.eq(profile_id)), + ), ) - .await } - pub async fn list_by_merchant_id( - conn: &PgPooledConn, - merchant_id: id_type::MerchantId, - version: UserRoleVersion, - ) -> StorageResult> { - generics::generic_filter::<::Table, _, _, _>( - conn, - dsl::merchant_id - .eq(merchant_id) - .and(dsl::version.eq(version)), - None, - None, - Some(dsl::created_at.asc()), - ) - .await - } - - pub async fn find_by_user_id_org_id_merchant_id_profile_id( + pub async fn find_by_user_id_tenant_id_org_id_merchant_id_profile_id( conn: &PgPooledConn, user_id: String, + tenant_id: id_type::TenantId, org_id: id_type::OrganizationId, merchant_id: id_type::MerchantId, profile_id: id_type::ProfileId, version: UserRoleVersion, ) -> StorageResult { - // Checking in user roles, for a user in token hierarchy, only one of the relation will be true, either org level, merchant level or profile level - // (org_id = ? && merchant_id = null && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = ?) - let check_lineage = dsl::org_id - .eq(org_id.clone()) - .and(dsl::merchant_id.is_null().and(dsl::profile_id.is_null())) - .or(dsl::org_id.eq(org_id.clone()).and( - dsl::merchant_id - .eq(merchant_id.clone()) - .and(dsl::profile_id.is_null()), - )) - .or(dsl::org_id.eq(org_id).and( - dsl::merchant_id - .eq(merchant_id) - .and(dsl::profile_id.eq(profile_id)), - )); + let check_lineage = Self::check_user_in_lineage( + tenant_id, + Some(org_id), + Some(merchant_id), + Some(profile_id), + ); let predicate = dsl::user_id .eq(user_id) @@ -114,30 +99,45 @@ impl UserRole { generics::generic_find_one::<::Table, _, _>(conn, predicate).await } - pub async fn update_by_user_id_org_id_merchant_id_profile_id( + pub async fn update_by_user_id_tenant_id_org_id_merchant_id_profile_id( conn: &PgPooledConn, user_id: String, + tenant_id: id_type::TenantId, org_id: id_type::OrganizationId, merchant_id: Option, profile_id: Option, update: UserRoleUpdate, version: UserRoleVersion, ) -> StorageResult { - // Checking in user roles, for a user in token hierarchy, only one of the relation will be true, either org level, merchant level or profile level - // (org_id = ? && merchant_id = null && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = ?) - let check_lineage = dsl::org_id - .eq(org_id.clone()) - .and(dsl::merchant_id.is_null().and(dsl::profile_id.is_null())) - .or(dsl::org_id.eq(org_id.clone()).and( - dsl::merchant_id - .eq(merchant_id.clone()) + let check_lineage = dsl::tenant_id + .eq(tenant_id.clone()) + .and(dsl::org_id.is_null()) + .and(dsl::merchant_id.is_null()) + .and(dsl::profile_id.is_null()) + .or( + // Org-level condition + dsl::tenant_id + .eq(tenant_id.clone()) + .and(dsl::org_id.eq(org_id.clone())) + .and(dsl::merchant_id.is_null()) .and(dsl::profile_id.is_null()), - )) - .or(dsl::org_id.eq(org_id).and( - dsl::merchant_id - .eq(merchant_id) + ) + .or( + // Merchant-level condition + dsl::tenant_id + .eq(tenant_id.clone()) + .and(dsl::org_id.eq(org_id.clone())) + .and(dsl::merchant_id.eq(merchant_id.clone())) + .and(dsl::profile_id.is_null()), + ) + .or( + // Profile-level condition + dsl::tenant_id + .eq(tenant_id) + .and(dsl::org_id.eq(org_id)) + .and(dsl::merchant_id.eq(merchant_id)) .and(dsl::profile_id.eq(profile_id)), - )); + ); let predicate = dsl::user_id .eq(user_id) @@ -153,29 +153,21 @@ impl UserRole { .await } - pub async fn delete_by_user_id_org_id_merchant_id_profile_id( + pub async fn delete_by_user_id_tenant_id_org_id_merchant_id_profile_id( conn: &PgPooledConn, user_id: String, + tenant_id: id_type::TenantId, org_id: id_type::OrganizationId, merchant_id: id_type::MerchantId, profile_id: id_type::ProfileId, version: UserRoleVersion, ) -> StorageResult { - // Checking in user roles, for a user in token hierarchy, only one of the relation will be true, either org level, merchant level or profile level - // (org_id = ? && merchant_id = null && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = ?) - let check_lineage = dsl::org_id - .eq(org_id.clone()) - .and(dsl::merchant_id.is_null().and(dsl::profile_id.is_null())) - .or(dsl::org_id.eq(org_id.clone()).and( - dsl::merchant_id - .eq(merchant_id.clone()) - .and(dsl::profile_id.is_null()), - )) - .or(dsl::org_id.eq(org_id).and( - dsl::merchant_id - .eq(merchant_id) - .and(dsl::profile_id.eq(profile_id)), - )); + let check_lineage = Self::check_user_in_lineage( + tenant_id, + Some(org_id), + Some(merchant_id), + Some(profile_id), + ); let predicate = dsl::user_id .eq(user_id) @@ -190,6 +182,7 @@ impl UserRole { pub async fn generic_user_roles_list_for_user( conn: &PgPooledConn, user_id: String, + tenant_id: Option, org_id: Option, merchant_id: Option, profile_id: Option, @@ -202,6 +195,10 @@ impl UserRole { .filter(dsl::user_id.eq(user_id)) .into_boxed(); + if let Some(tenant_id) = tenant_id { + query = query.filter(dsl::tenant_id.eq(tenant_id)); + } + if let Some(org_id) = org_id { query = query.filter(dsl::org_id.eq(org_id)); } @@ -251,6 +248,7 @@ impl UserRole { pub async fn generic_user_roles_list_for_org_and_extra( conn: &PgPooledConn, user_id: Option, + tenant_id: Option, org_id: id_type::OrganizationId, merchant_id: Option, profile_id: Option, @@ -261,6 +259,10 @@ impl UserRole { .filter(dsl::org_id.eq(org_id)) .into_boxed(); + if let Some(tenant_id) = tenant_id { + query = query.filter(dsl::tenant_id.eq(tenant_id)); + } + if let Some(user_id) = user_id { query = query.filter(dsl::user_id.eq(user_id)); } diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs index 96f41f75ee07..f2dd657e00b0 100644 --- a/crates/router/src/analytics.rs +++ b/crates/router/src/analytics.rs @@ -1864,6 +1864,7 @@ pub mod routes { .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: &auth.user_id, + tenant_id: Some(auth.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id)), org_id: Some(&auth.org_id), merchant_id: None, profile_id: None, @@ -1987,6 +1988,7 @@ pub mod routes { .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: &auth.user_id, + tenant_id: Some(auth.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id)), org_id: Some(&auth.org_id), merchant_id: None, profile_id: None, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index f01f6c5d7496..5ca5cbaff368 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -614,6 +614,10 @@ async fn handle_existing_user_invitation( .global_store .find_user_role_by_user_id_and_lineage( invitee_user_from_db.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, &user_from_token.merchant_id, &user_from_token.profile_id, @@ -630,6 +634,10 @@ async fn handle_existing_user_invitation( .global_store .find_user_role_by_user_id_and_lineage( invitee_user_from_db.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, &user_from_token.merchant_id, &user_from_token.profile_id, @@ -930,6 +938,10 @@ pub async fn resend_invite( .global_store .find_user_role_by_user_id_and_lineage( user.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, &user_from_token.merchant_id, &user_from_token.profile_id, @@ -953,6 +965,10 @@ pub async fn resend_invite( .global_store .find_user_role_by_user_id_and_lineage( user.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, &user_from_token.merchant_id, &user_from_token.profile_id, @@ -1042,6 +1058,10 @@ pub async fn accept_invite_from_email_token_only_flow( let (update_v1_result, update_v2_result) = utils::user_role::update_v1_and_v2_user_roles_in_db( &state, user_from_db.get_user_id(), + user_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &org_id, merchant_id.as_ref(), profile_id.as_ref(), @@ -1228,6 +1248,12 @@ pub async fn list_user_roles_details( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: required_user.get_user_id(), + tenant_id: Some( + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), + ), org_id: Some(&user_from_token.org_id), merchant_id: (requestor_role_info.get_entity_type() <= EntityType::Merchant) .then_some(&user_from_token.merchant_id), @@ -2403,6 +2429,12 @@ pub async fn list_orgs_for_user( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: user_from_token.user_id.as_str(), + tenant_id: Some( + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), + ), org_id: None, merchant_id: None, profile_id: None, @@ -2468,6 +2500,12 @@ pub async fn list_merchants_for_user_in_org( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: user_from_token.user_id.as_str(), + tenant_id: Some( + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), + ), org_id: Some(&user_from_token.org_id), merchant_id: None, profile_id: None, @@ -2547,6 +2585,12 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: user_from_token.user_id.as_str(), + tenant_id: Some( + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), + ), org_id: Some(&user_from_token.org_id), merchant_id: Some(&user_from_token.merchant_id), profile_id: None, @@ -2623,6 +2667,12 @@ pub async fn switch_org_for_user( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: &user_from_token.user_id, + tenant_id: Some( + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), + ), org_id: Some(&request.org_id), merchant_id: None, profile_id: None, @@ -2693,109 +2743,114 @@ pub async fn switch_merchant_for_user_in_org( .attach_printable("Failed to retrieve role information")?; // Check if the role is internal and handle separately - let (org_id, merchant_id, profile_id, role_id) = if role_info.is_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 (org_id, merchant_id, profile_id, role_id) = + if role_info.is_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 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_profile_by_merchant_id( - key_manager_state, - &merchant_key_store, - &request.merchant_id, + let profile_id = state + .store + .list_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")? + .pop() + .ok_or(UserErrors::InternalServerError) + .attach_printable("No business profile found for the given merchant_id")? + .get_id() + .to_owned(); + + ( + merchant_account.organization_id, + request.merchant_id, + profile_id, + user_from_token.role_id.clone(), ) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to list business profiles by merchant_id")? - .pop() - .ok_or(UserErrors::InternalServerError) - .attach_printable("No business profile found for the given merchant_id")? - .get_id() - .to_owned(); - - ( - merchant_account.organization_id, - request.merchant_id, - profile_id, - user_from_token.role_id.clone(), - ) - } else { - // Match based on the other entity types - match role_info.get_entity_type() { - 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(), + } else { + // Match based on the other entity types + match role_info.get_entity_type() { + 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, ) - })?; - - let profile_id = state - .store - .list_profile_by_merchant_id( - key_manager_state, - &merchant_key_store, - &merchant_id, + .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_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")? + .pop() + .ok_or(UserErrors::InternalServerError) + .attach_printable("No business profile found for the merchant_id")? + .get_id() + .to_owned(); + ( + user_from_token.org_id.clone(), + merchant_id, + profile_id, + user_from_token.role_id.clone(), ) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to list business profiles by merchant_id")? - .pop() - .ok_or(UserErrors::InternalServerError) - .attach_printable("No business profile found for the merchant_id")? - .get_id() - .to_owned(); - ( - user_from_token.org_id.clone(), - merchant_id, - profile_id, - user_from_token.role_id.clone(), - ) - } + } - EntityType::Merchant | EntityType::Profile => { - let user_role = state + EntityType::Merchant | EntityType::Profile => { + let user_role = state .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: &user_from_token.user_id, + tenant_id: Some(user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id)), org_id: Some(&user_from_token.org_id), merchant_id: Some(&request.merchant_id), profile_id: None, @@ -2814,18 +2869,18 @@ pub async fn switch_merchant_for_user_in_org( "No user role associated with the requested merchant_id".to_string(), ))?; - let (merchant_id, profile_id) = - utils::user_role::get_single_merchant_id_and_profile_id(&state, &user_role) - .await?; - ( - user_from_token.org_id, - merchant_id, - profile_id, - user_role.role_id, - ) + let (merchant_id, profile_id) = + utils::user_role::get_single_merchant_id_and_profile_id(&state, &user_role) + .await?; + ( + user_from_token.org_id, + merchant_id, + profile_id, + user_role.role_id, + ) + } } - } - }; + }; let token = utils::user::generate_jwt_auth_token_with_attributes( &state, @@ -2912,6 +2967,10 @@ pub async fn switch_profile_for_user_in_org_and_merchant( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload{ user_id:&user_from_token.user_id, + tenant_id: Some(user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id)), org_id: Some(&user_from_token.org_id), merchant_id: Some(&user_from_token.merchant_id), profile_id:Some(&request.profile_id), diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs index 6641e553fd80..85d89fcd233f 100644 --- a/crates/router/src/core/user_role.rs +++ b/crates/router/src/core/user_role.rs @@ -159,6 +159,10 @@ pub async fn update_user_role( .global_store .find_user_role_by_user_id_and_lineage( user_to_be_updated.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, &user_from_token.merchant_id, &user_from_token.profile_id, @@ -213,6 +217,10 @@ pub async fn update_user_role( .global_store .update_user_role_by_user_id_and_lineage( user_to_be_updated.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, Some(&user_from_token.merchant_id), Some(&user_from_token.profile_id), @@ -232,6 +240,10 @@ pub async fn update_user_role( .global_store .find_user_role_by_user_id_and_lineage( user_to_be_updated.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, &user_from_token.merchant_id, &user_from_token.profile_id, @@ -286,6 +298,10 @@ pub async fn update_user_role( .global_store .update_user_role_by_user_id_and_lineage( user_to_be_updated.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, Some(&user_from_token.merchant_id), Some(&user_from_token.profile_id), @@ -335,6 +351,10 @@ pub async fn accept_invitations_v2( utils::user_role::update_v1_and_v2_user_roles_in_db( &state, user_from_token.user_id.as_str(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id, merchant_id.as_ref(), profile_id.as_ref(), @@ -387,6 +407,10 @@ pub async fn accept_invitations_pre_auth( utils::user_role::update_v1_and_v2_user_roles_in_db( &state, user_token.user_id.as_str(), + user_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id, merchant_id.as_ref(), profile_id.as_ref(), @@ -473,6 +497,10 @@ pub async fn delete_user_role( .global_store .find_user_role_by_user_id_and_lineage( user_from_db.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, &user_from_token.merchant_id, &user_from_token.profile_id, @@ -520,6 +548,10 @@ pub async fn delete_user_role( .global_store .delete_user_role_by_user_id_and_lineage( user_from_db.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, &user_from_token.merchant_id, &user_from_token.profile_id, @@ -535,6 +567,10 @@ pub async fn delete_user_role( .global_store .find_user_role_by_user_id_and_lineage( user_from_db.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, &user_from_token.merchant_id, &user_from_token.profile_id, @@ -582,6 +618,10 @@ pub async fn delete_user_role( .global_store .delete_user_role_by_user_id_and_lineage( user_from_db.get_user_id(), + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), &user_from_token.org_id, &user_from_token.merchant_id, &user_from_token.profile_id, @@ -602,6 +642,12 @@ pub async fn delete_user_role( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: user_from_db.get_user_id(), + tenant_id: Some( + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), + ), org_id: None, merchant_id: None, profile_id: None, @@ -650,6 +696,12 @@ pub async fn list_users_in_lineage( &state, ListUserRolesByOrgIdPayload { user_id: None, + tenant_id: Some( + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), + ), org_id: &user_from_token.org_id, merchant_id: None, profile_id: None, @@ -665,6 +717,12 @@ pub async fn list_users_in_lineage( &state, ListUserRolesByOrgIdPayload { user_id: None, + tenant_id: Some( + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), + ), org_id: &user_from_token.org_id, merchant_id: Some(&user_from_token.merchant_id), profile_id: None, @@ -680,6 +738,12 @@ pub async fn list_users_in_lineage( &state, ListUserRolesByOrgIdPayload { user_id: None, + tenant_id: Some( + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), + ), org_id: &user_from_token.org_id, merchant_id: Some(&user_from_token.merchant_id), profile_id: Some(&user_from_token.profile_id), @@ -779,6 +843,7 @@ pub async fn list_invitations_for_user( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: &user_from_token.user_id, + tenant_id: Some(&state.tenant.tenant_id), org_id: None, merchant_id: None, profile_id: None, diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index a9df1abbc083..8b7e7dc03067 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -3048,6 +3048,7 @@ impl UserRoleInterface for KafkaStore { async fn find_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, profile_id: &id_type::ProfileId, @@ -3056,6 +3057,7 @@ impl UserRoleInterface for KafkaStore { self.diesel_store .find_user_role_by_user_id_and_lineage( user_id, + tenant_id, org_id, merchant_id, profile_id, @@ -3067,6 +3069,7 @@ impl UserRoleInterface for KafkaStore { async fn update_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: Option<&id_type::MerchantId>, profile_id: Option<&id_type::ProfileId>, @@ -3076,6 +3079,7 @@ impl UserRoleInterface for KafkaStore { self.diesel_store .update_user_role_by_user_id_and_lineage( user_id, + tenant_id, org_id, merchant_id, profile_id, @@ -3088,6 +3092,7 @@ impl UserRoleInterface for KafkaStore { async fn delete_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, profile_id: &id_type::ProfileId, @@ -3096,6 +3101,7 @@ impl UserRoleInterface for KafkaStore { self.diesel_store .delete_user_role_by_user_id_and_lineage( user_id, + tenant_id, org_id, merchant_id, profile_id, diff --git a/crates/router/src/db/user_role.rs b/crates/router/src/db/user_role.rs index e4e564dc9a40..5dee3659858a 100644 --- a/crates/router/src/db/user_role.rs +++ b/crates/router/src/db/user_role.rs @@ -15,6 +15,7 @@ use crate::{ pub struct ListUserRolesByOrgIdPayload<'a> { pub user_id: Option<&'a String>, + pub tenant_id: Option<&'a id_type::TenantId>, pub org_id: &'a id_type::OrganizationId, pub merchant_id: Option<&'a id_type::MerchantId>, pub profile_id: Option<&'a id_type::ProfileId>, @@ -24,6 +25,7 @@ pub struct ListUserRolesByOrgIdPayload<'a> { pub struct ListUserRolesByUserIdPayload<'a> { pub user_id: &'a str, + pub tenant_id: Option<&'a id_type::TenantId>, pub org_id: Option<&'a id_type::OrganizationId>, pub merchant_id: Option<&'a id_type::MerchantId>, pub profile_id: Option<&'a id_type::ProfileId>, @@ -43,6 +45,7 @@ pub trait UserRoleInterface { async fn find_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, profile_id: &id_type::ProfileId, @@ -52,6 +55,7 @@ pub trait UserRoleInterface { async fn update_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: Option<&id_type::MerchantId>, profile_id: Option<&id_type::ProfileId>, @@ -62,6 +66,7 @@ pub trait UserRoleInterface { async fn delete_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, profile_id: &id_type::ProfileId, @@ -98,15 +103,17 @@ impl UserRoleInterface for Store { async fn find_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, profile_id: &id_type::ProfileId, version: enums::UserRoleVersion, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; - storage::UserRole::find_by_user_id_org_id_merchant_id_profile_id( + storage::UserRole::find_by_user_id_tenant_id_org_id_merchant_id_profile_id( &conn, user_id.to_owned(), + tenant_id.to_owned(), org_id.to_owned(), merchant_id.to_owned(), profile_id.to_owned(), @@ -120,6 +127,7 @@ impl UserRoleInterface for Store { async fn update_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: Option<&id_type::MerchantId>, profile_id: Option<&id_type::ProfileId>, @@ -127,9 +135,10 @@ impl UserRoleInterface for Store { version: enums::UserRoleVersion, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; - storage::UserRole::update_by_user_id_org_id_merchant_id_profile_id( + storage::UserRole::update_by_user_id_tenant_id_org_id_merchant_id_profile_id( &conn, user_id.to_owned(), + tenant_id.to_owned(), org_id.to_owned(), merchant_id.cloned(), profile_id.cloned(), @@ -144,15 +153,17 @@ impl UserRoleInterface for Store { async fn delete_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, profile_id: &id_type::ProfileId, version: enums::UserRoleVersion, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; - storage::UserRole::delete_by_user_id_org_id_merchant_id_profile_id( + storage::UserRole::delete_by_user_id_tenant_id_org_id_merchant_id_profile_id( &conn, user_id.to_owned(), + tenant_id.to_owned(), org_id.to_owned(), merchant_id.to_owned(), profile_id.to_owned(), @@ -170,6 +181,7 @@ impl UserRoleInterface for Store { storage::UserRole::generic_user_roles_list_for_user( &conn, payload.user_id.to_owned(), + payload.tenant_id.cloned(), payload.org_id.cloned(), payload.merchant_id.cloned(), payload.profile_id.cloned(), @@ -190,6 +202,7 @@ impl UserRoleInterface for Store { storage::UserRole::generic_user_roles_list_for_org_and_extra( &conn, payload.user_id.cloned(), + payload.tenant_id.cloned(), payload.org_id.to_owned(), payload.merchant_id.cloned(), payload.profile_id.cloned(), @@ -243,6 +256,7 @@ impl UserRoleInterface for MockDb { async fn find_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, profile_id: &id_type::ProfileId, @@ -251,21 +265,32 @@ impl UserRoleInterface for MockDb { let user_roles = self.user_roles.lock().await; for user_role in user_roles.iter() { - let org_level_check = user_role.org_id.as_ref() == Some(org_id) + let tenant_level_check = user_role.tenant_id == *tenant_id + && user_role.org_id.is_none() && user_role.merchant_id.is_none() && user_role.profile_id.is_none(); - let merchant_level_check = user_role.org_id.as_ref() == Some(org_id) + let org_level_check = user_role.tenant_id == *tenant_id + && user_role.org_id.as_ref() == Some(org_id) + && user_role.merchant_id.is_none() + && user_role.profile_id.is_none(); + + let merchant_level_check = user_role.tenant_id == *tenant_id + && user_role.org_id.as_ref() == Some(org_id) && user_role.merchant_id.as_ref() == Some(merchant_id) && user_role.profile_id.is_none(); - let profile_level_check = user_role.org_id.as_ref() == Some(org_id) + let profile_level_check = user_role.tenant_id == *tenant_id + && user_role.org_id.as_ref() == Some(org_id) && user_role.merchant_id.as_ref() == Some(merchant_id) && user_role.profile_id.as_ref() == Some(profile_id); // Check if any condition matches and the version matches if user_role.user_id == user_id - && (org_level_check || merchant_level_check || profile_level_check) + && (tenant_level_check + || org_level_check + || merchant_level_check + || profile_level_check) && user_role.version == version { return Ok(user_role.clone()); @@ -282,6 +307,7 @@ impl UserRoleInterface for MockDb { async fn update_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: Option<&id_type::MerchantId>, profile_id: Option<&id_type::ProfileId>, @@ -291,21 +317,32 @@ impl UserRoleInterface for MockDb { let mut user_roles = self.user_roles.lock().await; for user_role in user_roles.iter_mut() { - let org_level_check = user_role.org_id.as_ref() == Some(org_id) + let tenant_level_check = user_role.tenant_id == *tenant_id + && user_role.org_id.is_none() + && user_role.merchant_id.is_none() + && user_role.profile_id.is_none(); + + let org_level_check = user_role.tenant_id == *tenant_id + && user_role.org_id.as_ref() == Some(org_id) && user_role.merchant_id.is_none() && user_role.profile_id.is_none(); - let merchant_level_check = user_role.org_id.as_ref() == Some(org_id) + let merchant_level_check = user_role.tenant_id == *tenant_id + && user_role.org_id.as_ref() == Some(org_id) && user_role.merchant_id.as_ref() == merchant_id && user_role.profile_id.is_none(); - let profile_level_check = user_role.org_id.as_ref() == Some(org_id) + let profile_level_check = user_role.tenant_id == *tenant_id + && user_role.org_id.as_ref() == Some(org_id) && user_role.merchant_id.as_ref() == merchant_id && user_role.profile_id.as_ref() == profile_id; - // Check if the user role matches the conditions and the version matches + // Check if any condition matches and the version matches if user_role.user_id == user_id - && (org_level_check || merchant_level_check || profile_level_check) + && (tenant_level_check + || org_level_check + || merchant_level_check + || profile_level_check) && user_role.version == version { match &update { @@ -336,6 +373,7 @@ impl UserRoleInterface for MockDb { async fn delete_user_role_by_user_id_and_lineage( &self, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, profile_id: &id_type::ProfileId, @@ -345,21 +383,32 @@ impl UserRoleInterface for MockDb { // Find the position of the user role to delete let index = user_roles.iter().position(|role| { - let org_level_check = role.org_id.as_ref() == Some(org_id) + let tenant_level_check = role.tenant_id == *tenant_id + && role.org_id.is_none() + && role.merchant_id.is_none() + && role.profile_id.is_none(); + + let org_level_check = role.tenant_id == *tenant_id + && role.org_id.as_ref() == Some(org_id) && role.merchant_id.is_none() && role.profile_id.is_none(); - let merchant_level_check = role.org_id.as_ref() == Some(org_id) + let merchant_level_check = role.tenant_id == *tenant_id + && role.org_id.as_ref() == Some(org_id) && role.merchant_id.as_ref() == Some(merchant_id) && role.profile_id.is_none(); - let profile_level_check = role.org_id.as_ref() == Some(org_id) + let profile_level_check = role.tenant_id == *tenant_id + && role.org_id.as_ref() == Some(org_id) && role.merchant_id.as_ref() == Some(merchant_id) && role.profile_id.as_ref() == Some(profile_id); // Check if the user role matches the conditions and the version matches role.user_id == user_id - && (org_level_check || merchant_level_check || profile_level_check) + && (tenant_level_check + || org_level_check + || merchant_level_check + || profile_level_check) && role.version == version }); diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 634c781da7fc..8f0bfdedf214 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -68,6 +68,7 @@ impl SPTFlow { .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: user.get_user_id(), + tenant_id: Some(&state.tenant.tenant_id), org_id: None, merchant_id: None, profile_id: None, @@ -304,6 +305,7 @@ impl NextFlow { .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: self.user.get_user_id(), + tenant_id: Some(&state.tenant.tenant_id), org_id: None, merchant_id: None, profile_id: None, diff --git a/crates/router/src/utils/user_role.rs b/crates/router/src/utils/user_role.rs index aaf313a196e5..f1e3bdadf783 100644 --- a/crates/router/src/utils/user_role.rs +++ b/crates/router/src/utils/user_role.rs @@ -133,6 +133,7 @@ pub async fn set_role_permissions_in_cache_if_required( pub async fn update_v1_and_v2_user_roles_in_db( state: &SessionState, user_id: &str, + tenant_id: &id_type::TenantId, org_id: &id_type::OrganizationId, merchant_id: Option<&id_type::MerchantId>, profile_id: Option<&id_type::ProfileId>, @@ -145,6 +146,7 @@ pub async fn update_v1_and_v2_user_roles_in_db( .global_store .update_user_role_by_user_id_and_lineage( user_id, + tenant_id, org_id, merchant_id, profile_id, @@ -161,6 +163,7 @@ pub async fn update_v1_and_v2_user_roles_in_db( .global_store .update_user_role_by_user_id_and_lineage( user_id, + tenant_id, org_id, merchant_id, profile_id, @@ -231,6 +234,7 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id, + tenant_id: Some(&state.tenant.tenant_id), org_id: Some(&org_id), merchant_id: None, profile_id: None, @@ -275,6 +279,7 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id, + tenant_id: Some(&state.tenant.tenant_id), org_id: None, merchant_id: Some(&merchant_id), profile_id: None, @@ -320,6 +325,7 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id, + tenant_id: Some(&state.tenant.tenant_id), org_id: None, merchant_id: None, profile_id: Some(&profile_id), From c125cfffe5266f2534412b33e0ee7c6b9b612f87 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit Date: Tue, 26 Nov 2024 17:34:05 +0530 Subject: [PATCH 2/6] fix: resolve review comments --- crates/diesel_models/src/query/user_role.rs | 20 +- crates/router/src/analytics.rs | 4 +- crates/router/src/core/user.rs | 277 +++++++++--------- crates/router/src/core/user_role.rs | 54 ++-- crates/router/src/db/user_role.rs | 8 +- crates/router/src/services/authentication.rs | 2 + .../src/types/domain/user/decision_manager.rs | 35 ++- crates/router/src/utils/user_role.rs | 7 +- 8 files changed, 212 insertions(+), 195 deletions(-) diff --git a/crates/diesel_models/src/query/user_role.rs b/crates/diesel_models/src/query/user_role.rs index dce379be6a09..bb07f6718245 100644 --- a/crates/diesel_models/src/query/user_role.rs +++ b/crates/diesel_models/src/query/user_role.rs @@ -42,7 +42,7 @@ impl UserRole { // Merchant-level: (org_id = ? && merchant_id = ? && profile_id = null) // Profile-level: (org_id = ? && merchant_id = ? && profile_id = ?) Box::new( - // Tenant-level condtion + // Tenant-level condition dsl::tenant_id .eq(tenant_id.clone()) .and(dsl::org_id.is_null()) @@ -99,6 +99,7 @@ impl UserRole { generics::generic_find_one::<::Table, _, _>(conn, predicate).await } + #[allow(clippy::too_many_arguments)] pub async fn update_by_user_id_tenant_id_org_id_merchant_id_profile_id( conn: &PgPooledConn, user_id: String, @@ -182,7 +183,7 @@ impl UserRole { pub async fn generic_user_roles_list_for_user( conn: &PgPooledConn, user_id: String, - tenant_id: Option, + tenant_id: id_type::TenantId, org_id: Option, merchant_id: Option, profile_id: Option, @@ -192,13 +193,9 @@ impl UserRole { limit: Option, ) -> StorageResult> { let mut query = ::table() - .filter(dsl::user_id.eq(user_id)) + .filter(dsl::user_id.eq(user_id).and(dsl::tenant_id.eq(tenant_id))) .into_boxed(); - if let Some(tenant_id) = tenant_id { - query = query.filter(dsl::tenant_id.eq(tenant_id)); - } - if let Some(org_id) = org_id { query = query.filter(dsl::org_id.eq(org_id)); } @@ -245,10 +242,11 @@ impl UserRole { } } + #[allow(clippy::too_many_arguments)] pub async fn generic_user_roles_list_for_org_and_extra( conn: &PgPooledConn, user_id: Option, - tenant_id: Option, + tenant_id: id_type::TenantId, org_id: id_type::OrganizationId, merchant_id: Option, profile_id: Option, @@ -256,13 +254,9 @@ impl UserRole { limit: Option, ) -> StorageResult> { let mut query = ::table() - .filter(dsl::org_id.eq(org_id)) + .filter(dsl::org_id.eq(org_id).and(dsl::tenant_id.eq(tenant_id))) .into_boxed(); - if let Some(tenant_id) = tenant_id { - query = query.filter(dsl::tenant_id.eq(tenant_id)); - } - if let Some(user_id) = user_id { query = query.filter(dsl::user_id.eq(user_id)); } diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs index f2dd657e00b0..d957c3071ff0 100644 --- a/crates/router/src/analytics.rs +++ b/crates/router/src/analytics.rs @@ -1864,7 +1864,7 @@ pub mod routes { .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: &auth.user_id, - tenant_id: Some(auth.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id)), + tenant_id: auth.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id), org_id: Some(&auth.org_id), merchant_id: None, profile_id: None, @@ -1988,7 +1988,7 @@ pub mod routes { .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: &auth.user_id, - tenant_id: Some(auth.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id)), + tenant_id: auth.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id), org_id: Some(&auth.org_id), merchant_id: None, profile_id: None, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 5ca5cbaff368..ac45e7ea5cb6 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1048,6 +1048,10 @@ pub async fn accept_invite_from_email_token_only_flow( utils::user_role::get_lineage_for_user_id_and_entity_for_accepting_invite( &state, &user_token.user_id, + user_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), entity.entity_id.clone(), entity.entity_type, ) @@ -1248,12 +1252,10 @@ pub async fn list_user_roles_details( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: required_user.get_user_id(), - tenant_id: Some( - user_from_token - .tenant_id - .as_ref() - .unwrap_or(&state.tenant.tenant_id), - ), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id: Some(&user_from_token.org_id), merchant_id: (requestor_role_info.get_entity_type() <= EntityType::Merchant) .then_some(&user_from_token.merchant_id), @@ -2429,12 +2431,10 @@ pub async fn list_orgs_for_user( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: user_from_token.user_id.as_str(), - tenant_id: Some( - user_from_token - .tenant_id - .as_ref() - .unwrap_or(&state.tenant.tenant_id), - ), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id: None, merchant_id: None, profile_id: None, @@ -2500,12 +2500,10 @@ pub async fn list_merchants_for_user_in_org( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: user_from_token.user_id.as_str(), - tenant_id: Some( - user_from_token - .tenant_id - .as_ref() - .unwrap_or(&state.tenant.tenant_id), - ), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id: Some(&user_from_token.org_id), merchant_id: None, profile_id: None, @@ -2585,12 +2583,10 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: user_from_token.user_id.as_str(), - tenant_id: Some( - user_from_token - .tenant_id - .as_ref() - .unwrap_or(&state.tenant.tenant_id), - ), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id: Some(&user_from_token.org_id), merchant_id: Some(&user_from_token.merchant_id), profile_id: None, @@ -2667,12 +2663,10 @@ pub async fn switch_org_for_user( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: &user_from_token.user_id, - tenant_id: Some( - user_from_token - .tenant_id - .as_ref() - .unwrap_or(&state.tenant.tenant_id), - ), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id: Some(&request.org_id), merchant_id: None, profile_id: None, @@ -2743,114 +2737,113 @@ pub async fn switch_merchant_for_user_in_org( .attach_printable("Failed to retrieve role information")?; // Check if the role is internal and handle separately - let (org_id, merchant_id, profile_id, role_id) = - if role_info.is_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 (org_id, merchant_id, profile_id, role_id) = if role_info.is_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 profile_id = state - .store - .list_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")? - .pop() - .ok_or(UserErrors::InternalServerError) - .attach_printable("No business profile found for the given merchant_id")? - .get_id() - .to_owned(); + 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)?; - ( - merchant_account.organization_id, - request.merchant_id, - profile_id, - user_from_token.role_id.clone(), + let profile_id = state + .store + .list_profile_by_merchant_id( + key_manager_state, + &merchant_key_store, + &request.merchant_id, ) - } else { - // Match based on the other entity types - match role_info.get_entity_type() { - 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_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")? + .pop() + .ok_or(UserErrors::InternalServerError) + .attach_printable("No business profile found for the given merchant_id")? + .get_id() + .to_owned(); + + ( + merchant_account.organization_id, + request.merchant_id, + profile_id, + user_from_token.role_id.clone(), + ) + } else { + // Match based on the other entity types + match role_info.get_entity_type() { + 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(), ) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to list business profiles by merchant_id")? - .pop() - .ok_or(UserErrors::InternalServerError) - .attach_printable("No business profile found for the merchant_id")? - .get_id() - .to_owned(); - ( - user_from_token.org_id.clone(), - merchant_id, - profile_id, - user_from_token.role_id.clone(), + })?; + + let profile_id = state + .store + .list_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")? + .pop() + .ok_or(UserErrors::InternalServerError) + .attach_printable("No business profile found for the merchant_id")? + .get_id() + .to_owned(); + ( + user_from_token.org_id.clone(), + merchant_id, + profile_id, + user_from_token.role_id.clone(), + ) + } - EntityType::Merchant | EntityType::Profile => { - let user_role = state + EntityType::Merchant | EntityType::Profile => { + let user_role = state .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: &user_from_token.user_id, - tenant_id: Some(user_from_token - .tenant_id - .as_ref() - .unwrap_or(&state.tenant.tenant_id)), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id: Some(&user_from_token.org_id), merchant_id: Some(&request.merchant_id), profile_id: None, @@ -2869,18 +2862,18 @@ pub async fn switch_merchant_for_user_in_org( "No user role associated with the requested merchant_id".to_string(), ))?; - let (merchant_id, profile_id) = - utils::user_role::get_single_merchant_id_and_profile_id(&state, &user_role) - .await?; - ( - user_from_token.org_id, - merchant_id, - profile_id, - user_role.role_id, - ) - } + let (merchant_id, profile_id) = + utils::user_role::get_single_merchant_id_and_profile_id(&state, &user_role) + .await?; + ( + user_from_token.org_id, + merchant_id, + profile_id, + user_role.role_id, + ) } - }; + } + }; let token = utils::user::generate_jwt_auth_token_with_attributes( &state, @@ -2967,10 +2960,10 @@ pub async fn switch_profile_for_user_in_org_and_merchant( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload{ user_id:&user_from_token.user_id, - tenant_id: Some(user_from_token + tenant_id: user_from_token .tenant_id .as_ref() - .unwrap_or(&state.tenant.tenant_id)), + .unwrap_or(&state.tenant.tenant_id), org_id: Some(&user_from_token.org_id), merchant_id: Some(&user_from_token.merchant_id), profile_id:Some(&request.profile_id), diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs index 85d89fcd233f..eaa655a07f3a 100644 --- a/crates/router/src/core/user_role.rs +++ b/crates/router/src/core/user_role.rs @@ -336,6 +336,10 @@ pub async fn accept_invitations_v2( utils::user_role::get_lineage_for_user_id_and_entity_for_accepting_invite( &state, &user_from_token.user_id, + user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), entity.entity_id, entity.entity_type, ) @@ -392,6 +396,10 @@ pub async fn accept_invitations_pre_auth( utils::user_role::get_lineage_for_user_id_and_entity_for_accepting_invite( &state, &user_token.user_id, + user_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), entity.entity_id, entity.entity_type, ) @@ -642,12 +650,11 @@ pub async fn delete_user_role( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: user_from_db.get_user_id(), - tenant_id: Some( - user_from_token - .tenant_id - .as_ref() - .unwrap_or(&state.tenant.tenant_id), - ), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), + org_id: None, merchant_id: None, profile_id: None, @@ -696,12 +703,10 @@ pub async fn list_users_in_lineage( &state, ListUserRolesByOrgIdPayload { user_id: None, - tenant_id: Some( - user_from_token - .tenant_id - .as_ref() - .unwrap_or(&state.tenant.tenant_id), - ), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id: &user_from_token.org_id, merchant_id: None, profile_id: None, @@ -717,12 +722,10 @@ pub async fn list_users_in_lineage( &state, ListUserRolesByOrgIdPayload { user_id: None, - tenant_id: Some( - user_from_token - .tenant_id - .as_ref() - .unwrap_or(&state.tenant.tenant_id), - ), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id: &user_from_token.org_id, merchant_id: Some(&user_from_token.merchant_id), profile_id: None, @@ -738,12 +741,10 @@ pub async fn list_users_in_lineage( &state, ListUserRolesByOrgIdPayload { user_id: None, - tenant_id: Some( - user_from_token - .tenant_id - .as_ref() - .unwrap_or(&state.tenant.tenant_id), - ), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id: &user_from_token.org_id, merchant_id: Some(&user_from_token.merchant_id), profile_id: Some(&user_from_token.profile_id), @@ -843,7 +844,10 @@ pub async fn list_invitations_for_user( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: &user_from_token.user_id, - tenant_id: Some(&state.tenant.tenant_id), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id: None, merchant_id: None, profile_id: None, diff --git a/crates/router/src/db/user_role.rs b/crates/router/src/db/user_role.rs index 5dee3659858a..18d70ffeab8b 100644 --- a/crates/router/src/db/user_role.rs +++ b/crates/router/src/db/user_role.rs @@ -15,7 +15,7 @@ use crate::{ pub struct ListUserRolesByOrgIdPayload<'a> { pub user_id: Option<&'a String>, - pub tenant_id: Option<&'a id_type::TenantId>, + pub tenant_id: &'a id_type::TenantId, pub org_id: &'a id_type::OrganizationId, pub merchant_id: Option<&'a id_type::MerchantId>, pub profile_id: Option<&'a id_type::ProfileId>, @@ -25,7 +25,7 @@ pub struct ListUserRolesByOrgIdPayload<'a> { pub struct ListUserRolesByUserIdPayload<'a> { pub user_id: &'a str, - pub tenant_id: Option<&'a id_type::TenantId>, + pub tenant_id: &'a id_type::TenantId, pub org_id: Option<&'a id_type::OrganizationId>, pub merchant_id: Option<&'a id_type::MerchantId>, pub profile_id: Option<&'a id_type::ProfileId>, @@ -181,7 +181,7 @@ impl UserRoleInterface for Store { storage::UserRole::generic_user_roles_list_for_user( &conn, payload.user_id.to_owned(), - payload.tenant_id.cloned(), + payload.tenant_id.to_owned(), payload.org_id.cloned(), payload.merchant_id.cloned(), payload.profile_id.cloned(), @@ -202,7 +202,7 @@ impl UserRoleInterface for Store { storage::UserRole::generic_user_roles_list_for_org_and_extra( &conn, payload.user_id.cloned(), - payload.tenant_id.cloned(), + payload.tenant_id.to_owned(), payload.org_id.to_owned(), payload.merchant_id.cloned(), payload.profile_id.cloned(), diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 2f5f55b84345..6abc7a95995a 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -273,6 +273,7 @@ pub struct UserFromToken { pub struct UserIdFromAuth { pub user_id: String, + pub tenant_id: Option, } #[cfg(feature = "olap")] @@ -858,6 +859,7 @@ where Ok(( UserIdFromAuth { user_id: payload.user_id.clone(), + tenant_id: payload.tenant_id, }, AuthenticationType::SinglePurposeOrLoginJwt { user_id: payload.user_id, diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 8f0bfdedf214..10990da6ccbd 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -1,4 +1,5 @@ use common_enums::TokenPurpose; +use common_utils::id_type; use diesel_models::{enums::UserStatus, user_role::UserRole}; use error_stack::{report, ResultExt}; use masking::Secret; @@ -24,9 +25,10 @@ impl UserFlow { user: &UserFromStorage, path: &[TokenPurpose], state: &SessionState, + user_tenant_id: &id_type::TenantId, ) -> UserResult { match self { - Self::SPTFlow(flow) => flow.is_required(user, path, state).await, + Self::SPTFlow(flow) => flow.is_required(user, path, state, user_tenant_id).await, Self::JWTFlow(flow) => flow.is_required(user, state).await, } } @@ -50,6 +52,7 @@ impl SPTFlow { user: &UserFromStorage, path: &[TokenPurpose], state: &SessionState, + user_tenant_id: &id_type::TenantId, ) -> UserResult { match self { // Auth @@ -68,7 +71,7 @@ impl SPTFlow { .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: user.get_user_id(), - tenant_id: Some(&state.tenant.tenant_id), + tenant_id: user_tenant_id, org_id: None, merchant_id: None, profile_id: None, @@ -221,6 +224,7 @@ pub struct CurrentFlow { origin: Origin, current_flow_index: usize, path: Vec, + tenant_id: Option, } impl CurrentFlow { @@ -240,6 +244,7 @@ impl CurrentFlow { origin: token.origin, current_flow_index: index, path, + tenant_id: token.tenant_id, }) } @@ -248,12 +253,21 @@ impl CurrentFlow { let remaining_flows = flows.iter().skip(self.current_flow_index + 1); for flow in remaining_flows { - if flow.is_required(&user, &self.path, state).await? { + if flow + .is_required( + &user, + &self.path, + state, + self.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id), + ) + .await? + { return Ok(NextFlow { origin: self.origin.clone(), next_flow: *flow, user, path: self.path, + tenant_id: self.tenant_id, }); } } @@ -266,6 +280,7 @@ pub struct NextFlow { next_flow: UserFlow, user: UserFromStorage, path: Vec, + tenant_id: Option, } impl NextFlow { @@ -277,12 +292,16 @@ impl NextFlow { let flows = origin.get_flows(); let path = vec![]; for flow in flows { - if flow.is_required(&user, &path, state).await? { + if flow + .is_required(&user, &path, state, &state.tenant.tenant_id) + .await? + { return Ok(Self { origin, next_flow: *flow, user, path, + tenant_id: Some(state.tenant.tenant_id.clone()), }); } } @@ -305,7 +324,7 @@ impl NextFlow { .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: self.user.get_user_id(), - tenant_id: Some(&state.tenant.tenant_id), + tenant_id: self.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id), org_id: None, merchant_id: None, profile_id: None, @@ -354,12 +373,16 @@ impl NextFlow { .ok_or(UserErrors::InternalServerError)?; let remaining_flows = flows.iter().skip(index + 1); for flow in remaining_flows { - if flow.is_required(&user, &self.path, state).await? { + if flow + .is_required(&user, &self.path, state, &state.tenant.tenant_id) + .await? + { return Ok(Self { origin: self.origin.clone(), next_flow: *flow, user, path: self.path, + tenant_id: Some(state.tenant.tenant_id.clone()), }); } } diff --git a/crates/router/src/utils/user_role.rs b/crates/router/src/utils/user_role.rs index f1e3bdadf783..0bd0e81149f5 100644 --- a/crates/router/src/utils/user_role.rs +++ b/crates/router/src/utils/user_role.rs @@ -213,6 +213,7 @@ pub async fn get_single_merchant_id( pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite( state: &SessionState, user_id: &str, + tenant_id: &id_type::TenantId, entity_id: String, entity_type: EntityType, ) -> UserResult< @@ -234,7 +235,7 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id, - tenant_id: Some(&state.tenant.tenant_id), + tenant_id, org_id: Some(&org_id), merchant_id: None, profile_id: None, @@ -279,7 +280,7 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id, - tenant_id: Some(&state.tenant.tenant_id), + tenant_id, org_id: None, merchant_id: Some(&merchant_id), profile_id: None, @@ -325,7 +326,7 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id, - tenant_id: Some(&state.tenant.tenant_id), + tenant_id: &state.tenant.tenant_id, org_id: None, merchant_id: None, profile_id: Some(&profile_id), From d2065f3ac4978eaa54ddad7e1a02d6638fb9f762 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit Date: Tue, 26 Nov 2024 18:56:27 +0530 Subject: [PATCH 3/6] fix: v2 check --- crates/router/src/db/user_role.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/router/src/db/user_role.rs b/crates/router/src/db/user_role.rs index 18d70ffeab8b..0da518983265 100644 --- a/crates/router/src/db/user_role.rs +++ b/crates/router/src/db/user_role.rs @@ -52,6 +52,7 @@ pub trait UserRoleInterface { version: enums::UserRoleVersion, ) -> CustomResult; + #[allow(clippy::too_many_arguments)] async fn update_user_role_by_user_id_and_lineage( &self, user_id: &str, From 19301fd8d8c983395a6a31e7fe29b58c27a0643d Mon Sep 17 00:00:00 2001 From: Apoorv Dixit Date: Tue, 26 Nov 2024 19:26:42 +0530 Subject: [PATCH 4/6] fix: v2 with main --- crates/router/src/services/authentication.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index ace252539ad3..82899d1eab82 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -901,6 +901,7 @@ where Ok(( UserIdFromAuth { user_id: payload.user_id.clone(), + tenant_id: payload.tenant_id }, AuthenticationType::SinglePurposeOrLoginJwt { user_id: payload.user_id, From 1ccef95763eacdffce9c57bf78f805009e967f03 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:02:36 +0000 Subject: [PATCH 5/6] chore: run formatter --- crates/router/src/services/authentication.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 82899d1eab82..d50933b708da 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -901,7 +901,7 @@ where Ok(( UserIdFromAuth { user_id: payload.user_id.clone(), - tenant_id: payload.tenant_id + tenant_id: payload.tenant_id, }, AuthenticationType::SinglePurposeOrLoginJwt { user_id: payload.user_id, From 998f3694774f713098e52dcd450be3f59eff8cc4 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit Date: Thu, 28 Nov 2024 13:40:56 +0530 Subject: [PATCH 6/6] fix: new check changes --- crates/router/src/core/user.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 9c6fe0d70693..53db3cf07e1d 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -668,6 +668,10 @@ async fn handle_existing_user_invitation( .global_store .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { user_id: invitee_user_from_db.get_user_id(), + tenant_id: user_from_token + .tenant_id + .as_ref() + .unwrap_or(&state.tenant.tenant_id), org_id, merchant_id, profile_id,