diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 619d2fb19375..5c08323dfd00 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -10,7 +10,7 @@ license.workspace = true [features] default = ["common_default", "v1"] -common_default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls", "partial-auth", "km_forward_x_request_id"] +common_default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls", "partial-auth", "km_forward_x_request_id","platform"] olap = ["hyperswitch_domain_models/olap", "storage_impl/olap", "scheduler/olap", "api_models/olap", "dep:analytics"] tls = ["actix-web/rustls-0_22"] email = ["external_services/email", "scheduler/email", "olap"] @@ -38,6 +38,7 @@ v1 = ["common_default", "api_models/v1", "diesel_models/v1", "hyperswitch_domain customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2", "hyperswitch_domain_models/customer_v2", "storage_impl/customer_v2"] payment_methods_v2 = ["api_models/payment_methods_v2", "diesel_models/payment_methods_v2", "hyperswitch_domain_models/payment_methods_v2", "storage_impl/payment_methods_v2", "common_utils/payment_methods_v2"] dynamic_routing = ["external_services/dynamic_routing", "storage_impl/dynamic_routing", "api_models/dynamic_routing"] +platform = [] # Partial Auth # The feature reduces the overhead of the router authenticating the merchant for every request, and trusts on `x-merchant-id` header to be present in the request. diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index fd5693293132..57bee9b60b8e 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -4767,6 +4767,7 @@ async fn locker_recipient_create_call( Ok(store_resp.card_reference) } +#[cfg(feature = "platform")] pub async fn enable_platform_account( state: SessionState, merchant_id: id_type::MerchantId, diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index c9f292f8a499..789fa602b413 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -914,6 +914,7 @@ pub async fn merchant_account_transfer_keys( /// Merchant Account - Platform Account /// /// Enable platform account +#[cfg(feature = "platform")] #[instrument(skip_all)] pub async fn merchant_account_enable_platform_account( state: web::Data, @@ -927,7 +928,7 @@ pub async fn merchant_account_enable_platform_account( state, &req, merchant_id, - |state, _, req, _| enable_platform_account(state, req), + |state, _, req, _| enable_platform_account(state, req), &auth::AdminApiAuth, api_locking::LockAction::NotApplicable, )) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 1048c7929364..a03e9e586854 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1315,7 +1315,8 @@ impl MerchantAccount { #[cfg(all(feature = "olap", feature = "v1"))] impl MerchantAccount { pub fn server(state: AppState) -> Scope { - web::scope("/accounts") + #[allow(unused_mut)] + let mut routes = web::scope("/accounts") .app_data(web::Data::new(state)) .service(web::resource("").route(web::post().to(admin::merchant_account_create))) .service(web::resource("/list").route(web::get().to(admin::merchant_account_list))) @@ -1324,7 +1325,6 @@ impl MerchantAccount { .route(web::post().to(admin::merchant_account_toggle_kv)) .route(web::get().to(admin::merchant_account_kv_status)), ) - .service(web::resource("/{id}/platform").route(web::post().to(admin::merchant_account_enable_platform_account))) .service( web::resource("/transfer") .route(web::post().to(admin::merchant_account_transfer_keys)), @@ -1337,7 +1337,15 @@ impl MerchantAccount { .route(web::get().to(admin::retrieve_merchant_account)) .route(web::post().to(admin::update_merchant_account)) .route(web::delete().to(admin::delete_merchant_account)), + ); + #[cfg(feature = "platform")] + { + routes = routes.service( + web::resource("/{id}/platform") + .route(web::post().to(admin::merchant_account_enable_platform_account)), ) + } + routes } } diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index cde953064f59..6bd69b9d2845 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -48,7 +48,7 @@ impl From for ApiIdentifier { | Flow::MerchantsAccountDelete | Flow::MerchantTransferKey | Flow::MerchantAccountList - | Flow::EnablePlatformAccount=> Self::MerchantAccount, + | Flow::EnablePlatformAccount => Self::MerchantAccount, Flow::OrganizationCreate | Flow::OrganizationRetrieve | Flow::OrganizationUpdate => { Self::Organization diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 56c0e63966b2..2fe84ccd4435 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -11,7 +11,9 @@ use api_models::payouts; use api_models::{payment_methods::PaymentMethodListRequest, payments}; use async_trait::async_trait; use common_enums::TokenPurpose; -use common_utils::{date_time, ext_traits::AsyncExt, id_type}; +#[cfg(feature = "platform")] +use common_utils::ext_traits::AsyncExt; +use common_utils::{date_time, id_type}; use error_stack::{report, ResultExt}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use masking::PeekInterface; @@ -463,8 +465,16 @@ where .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account - let (merchant, platform_merchant_account) = - get_platform_merchant_account(state, request_headers, merchant).await?; + let (merchant, platform_merchant_account) = { + #[cfg(feature = "platform")] + { + get_platform_merchant_account(state, request_headers, merchant).await? + } + #[cfg(not(feature = "platform"))] + { + (merchant, None) + } + }; let key_store = { if platform_merchant_account.is_some() { @@ -573,8 +583,16 @@ where .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account - let (merchant, platform_merchant_account) = - get_platform_merchant_account(state, request_headers, merchant).await?; + let (merchant, platform_merchant_account) = { + #[cfg(feature = "platform")] + { + get_platform_merchant_account(state, request_headers, merchant).await? + } + #[cfg(not(feature = "platform"))] + { + (merchant, None) + } + }; let auth = AuthenticationData { merchant_account: merchant, @@ -741,6 +759,7 @@ where } #[cfg(all(feature = "partial-auth", feature = "v1"))] +#[allow(unused)] async fn construct_authentication_data( state: &A, merchant_id: &id_type::MerchantId, @@ -771,8 +790,16 @@ where .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account - let (merchant, platform_merchant_account) = - get_platform_merchant_account(state, request_headers, merchant).await?; + let (merchant, platform_merchant_account) = { + #[cfg(feature = "platform")] + { + get_platform_merchant_account(state, request_headers, merchant).await? + } + #[cfg(not(feature = "platform"))] + { + (merchant, None) + } + }; let auth = AuthenticationData { merchant_account: merchant, @@ -1049,7 +1076,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1110,7 +1139,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationDataWithoutProfile, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1210,6 +1241,8 @@ impl<'a> HeaderMapStruct<'a> { }) } + // Remove feature flag if required. + #[cfg(feature = "platform")] pub fn get_id_type_from_header_if_present(&self, key: &str) -> RouterResult> where T: TryFrom< @@ -1302,7 +1335,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1364,7 +1399,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationDataWithoutProfile, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1431,6 +1468,7 @@ pub struct MerchantIdAuth(pub id_type::MerchantId); #[cfg(feature = "v1")] #[async_trait] +#[allow(unused)] impl AuthenticateAndFetch for MerchantIdAuth where A: SessionStateInfo + Sync, @@ -1440,6 +1478,7 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; let key_manager_state = &(&state.session_state()).into(); @@ -1485,7 +1524,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + let key_manager_state = &(&state.session_state()).into(); let profile_id = get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)? @@ -1549,7 +1590,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() @@ -1669,6 +1712,7 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; let publishable_key = @@ -3332,6 +3376,7 @@ where } } +#[cfg(feature = "platform")] async fn get_connected_merchant_account( state: &A, connected_merchant_id: id_type::MerchantId, @@ -3367,6 +3412,7 @@ where Ok(connected_merchant_account) } +#[cfg(feature = "platform")] async fn get_platform_merchant_account( state: &A, request_headers: &HeaderMap, @@ -3404,6 +3450,7 @@ where } } +#[cfg(feature = "platform")] fn throw_error_if_platform_merchant_authentication_required( request_headers: &HeaderMap, ) -> RouterResult<()> {