mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat: add profile_id authentication for business profile update and list (#5673)
This commit is contained in:
@ -3717,6 +3717,7 @@ pub async fn create_business_profile(
|
|||||||
pub async fn list_business_profile(
|
pub async fn list_business_profile(
|
||||||
state: SessionState,
|
state: SessionState,
|
||||||
merchant_id: id_type::MerchantId,
|
merchant_id: id_type::MerchantId,
|
||||||
|
profile_id_list: Option<Vec<id_type::ProfileId>>,
|
||||||
) -> RouterResponse<Vec<api_models::admin::BusinessProfileResponse>> {
|
) -> RouterResponse<Vec<api_models::admin::BusinessProfileResponse>> {
|
||||||
let db = state.store.as_ref();
|
let db = state.store.as_ref();
|
||||||
let key_store = db
|
let key_store = db
|
||||||
@ -3732,6 +3733,7 @@ pub async fn list_business_profile(
|
|||||||
.await
|
.await
|
||||||
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)?
|
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)?
|
||||||
.clone();
|
.clone();
|
||||||
|
let profiles = core_utils::filter_objects_based_on_profile_id_list(profile_id_list, profiles);
|
||||||
let mut business_profiles = Vec::new();
|
let mut business_profiles = Vec::new();
|
||||||
for profile in profiles {
|
for profile in profiles {
|
||||||
let business_profile =
|
let business_profile =
|
||||||
|
|||||||
@ -1334,7 +1334,7 @@ pub fn get_incremental_authorization_allowed_value(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) trait GetProfileId {
|
pub(crate) trait GetProfileId {
|
||||||
fn get_profile_id(&self) -> Option<&common_utils::id_type::ProfileId>;
|
fn get_profile_id(&self) -> Option<&common_utils::id_type::ProfileId>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1381,6 +1381,12 @@ impl<T, F> GetProfileId for (storage::Payouts, T, F) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GetProfileId for domain::BusinessProfile {
|
||||||
|
fn get_profile_id(&self) -> Option<&common_utils::id_type::ProfileId> {
|
||||||
|
Some(self.get_id())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Filter Objects based on profile ids
|
/// Filter Objects based on profile ids
|
||||||
pub(super) fn filter_objects_based_on_profile_id_list<T: GetProfileId>(
|
pub(super) fn filter_objects_based_on_profile_id_list<T: GetProfileId>(
|
||||||
profile_id_list_auth_layer: Option<Vec<common_utils::id_type::ProfileId>>,
|
profile_id_list_auth_layer: Option<Vec<common_utils::id_type::ProfileId>>,
|
||||||
@ -1406,7 +1412,7 @@ pub(super) fn filter_objects_based_on_profile_id_list<T: GetProfileId>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn validate_profile_id_from_auth_layer<T: GetProfileId + std::fmt::Debug>(
|
pub(crate) fn validate_profile_id_from_auth_layer<T: GetProfileId + std::fmt::Debug>(
|
||||||
profile_id_auth_layer: Option<common_utils::id_type::ProfileId>,
|
profile_id_auth_layer: Option<common_utils::id_type::ProfileId>,
|
||||||
object: &T,
|
object: &T,
|
||||||
) -> RouterResult<()> {
|
) -> RouterResult<()> {
|
||||||
|
|||||||
@ -119,7 +119,9 @@ pub fn mk_app(
|
|||||||
{
|
{
|
||||||
// This is a more specific route as compared to `MerchantConnectorAccount`
|
// This is a more specific route as compared to `MerchantConnectorAccount`
|
||||||
// so it is registered before `MerchantConnectorAccount`.
|
// so it is registered before `MerchantConnectorAccount`.
|
||||||
server_app = server_app.service(routes::BusinessProfile::server(state.clone()))
|
server_app = server_app
|
||||||
|
.service(routes::BusinessProfileNew::server(state.clone()))
|
||||||
|
.service(routes::BusinessProfile::server(state.clone()))
|
||||||
}
|
}
|
||||||
server_app = server_app
|
server_app = server_app
|
||||||
.service(routes::Payments::server(state.clone()))
|
.service(routes::Payments::server(state.clone()))
|
||||||
|
|||||||
@ -59,10 +59,10 @@ pub use self::app::Forex;
|
|||||||
#[cfg(all(feature = "olap", feature = "recon"))]
|
#[cfg(all(feature = "olap", feature = "recon"))]
|
||||||
pub use self::app::Recon;
|
pub use self::app::Recon;
|
||||||
pub use self::app::{
|
pub use self::app::{
|
||||||
ApiKeys, AppState, ApplePayCertificatesMigration, BusinessProfile, Cache, Cards, Configs,
|
ApiKeys, AppState, ApplePayCertificatesMigration, BusinessProfile, BusinessProfileNew, Cache,
|
||||||
ConnectorOnboarding, Customers, Disputes, EphemeralKey, Files, Gsm, Health, Mandates,
|
Cards, Configs, ConnectorOnboarding, Customers, Disputes, EphemeralKey, Files, Gsm, Health,
|
||||||
MerchantAccount, MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Poll,
|
Mandates, MerchantAccount, MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments,
|
||||||
Refunds, SessionState, User, Webhooks,
|
Poll, Refunds, SessionState, User, Webhooks,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(feature = "olap")]
|
||||||
pub use self::app::{Blocklist, Organization, Routing, Verify, WebhookEvents};
|
pub use self::app::{Blocklist, Organization, Routing, Verify, WebhookEvents};
|
||||||
|
|||||||
@ -892,8 +892,9 @@ pub async fn business_profile_update(
|
|||||||
},
|
},
|
||||||
auth::auth_type(
|
auth::auth_type(
|
||||||
&auth::AdminApiAuthWithMerchantIdFromRoute(merchant_id.clone()),
|
&auth::AdminApiAuthWithMerchantIdFromRoute(merchant_id.clone()),
|
||||||
&auth::JWTAuthMerchantFromRoute {
|
&auth::JWTAuthMerchantAndProfileFromRoute {
|
||||||
merchant_id: merchant_id.clone(),
|
merchant_id: merchant_id.clone(),
|
||||||
|
profile_id: profile_id.clone(),
|
||||||
required_permission: Permission::MerchantAccountWrite,
|
required_permission: Permission::MerchantAccountWrite,
|
||||||
},
|
},
|
||||||
req.headers(),
|
req.headers(),
|
||||||
@ -971,9 +972,43 @@ pub async fn business_profiles_list(
|
|||||||
state,
|
state,
|
||||||
&req,
|
&req,
|
||||||
merchant_id.clone(),
|
merchant_id.clone(),
|
||||||
|state, _, merchant_id, _| list_business_profile(state, merchant_id),
|
|state, _auth, merchant_id, _| list_business_profile(state, merchant_id, None),
|
||||||
auth::auth_type(
|
auth::auth_type(
|
||||||
&auth::AdminApiAuth,
|
&auth::AdminApiAuthWithMerchantIdFromHeader,
|
||||||
|
&auth::JWTAuthMerchantFromRoute {
|
||||||
|
merchant_id,
|
||||||
|
required_permission: Permission::MerchantAccountRead,
|
||||||
|
},
|
||||||
|
req.headers(),
|
||||||
|
),
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileList))]
|
||||||
|
pub async fn business_profiles_list_at_profile_level(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
req: HttpRequest,
|
||||||
|
path: web::Path<common_utils::id_type::MerchantId>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let flow = Flow::BusinessProfileList;
|
||||||
|
let merchant_id = path.into_inner();
|
||||||
|
|
||||||
|
Box::pin(api::server_wrap(
|
||||||
|
flow,
|
||||||
|
state,
|
||||||
|
&req,
|
||||||
|
merchant_id.clone(),
|
||||||
|
|state, auth, merchant_id, _| {
|
||||||
|
list_business_profile(
|
||||||
|
state,
|
||||||
|
merchant_id,
|
||||||
|
auth.profile_id.map(|profile_id| vec![profile_id]),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
auth::auth_type(
|
||||||
|
&auth::AdminApiAuthWithMerchantIdFromHeader,
|
||||||
&auth::JWTAuthMerchantFromRoute {
|
&auth::JWTAuthMerchantFromRoute {
|
||||||
merchant_id,
|
merchant_id,
|
||||||
required_permission: Permission::MerchantAccountRead,
|
required_permission: Permission::MerchantAccountRead,
|
||||||
|
|||||||
@ -1639,6 +1639,19 @@ impl BusinessProfile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct BusinessProfileNew;
|
||||||
|
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
|
impl BusinessProfileNew {
|
||||||
|
pub fn server(state: AppState) -> Scope {
|
||||||
|
web::scope("/account/{account_id}/profile")
|
||||||
|
.app_data(web::Data::new(state))
|
||||||
|
.service(
|
||||||
|
web::resource("").route(web::get().to(business_profiles_list_at_profile_level)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Gsm;
|
pub struct Gsm;
|
||||||
|
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(feature = "olap")]
|
||||||
|
|||||||
@ -88,6 +88,11 @@ pub enum AuthenticationType {
|
|||||||
merchant_id: id_type::MerchantId,
|
merchant_id: id_type::MerchantId,
|
||||||
user_id: Option<String>,
|
user_id: Option<String>,
|
||||||
},
|
},
|
||||||
|
MerchantJwtWithProfileId {
|
||||||
|
merchant_id: id_type::MerchantId,
|
||||||
|
profile_id: Option<id_type::ProfileId>,
|
||||||
|
user_id: String,
|
||||||
|
},
|
||||||
UserJwt {
|
UserJwt {
|
||||||
user_id: String,
|
user_id: String,
|
||||||
},
|
},
|
||||||
@ -137,6 +142,7 @@ impl AuthenticationType {
|
|||||||
merchant_id,
|
merchant_id,
|
||||||
user_id: _,
|
user_id: _,
|
||||||
}
|
}
|
||||||
|
| Self::MerchantJwtWithProfileId { merchant_id, .. }
|
||||||
| Self::WebhookAuth { merchant_id } => Some(merchant_id),
|
| Self::WebhookAuth { merchant_id } => Some(merchant_id),
|
||||||
Self::AdminApiKey
|
Self::AdminApiKey
|
||||||
| Self::UserJwt { .. }
|
| Self::UserJwt { .. }
|
||||||
@ -1324,6 +1330,80 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct JWTAuthMerchantAndProfileFromRoute {
|
||||||
|
pub merchant_id: id_type::MerchantId,
|
||||||
|
pub profile_id: id_type::ProfileId,
|
||||||
|
pub required_permission: Permission,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<A> AuthenticateAndFetch<AuthenticationData, A> for JWTAuthMerchantAndProfileFromRoute
|
||||||
|
where
|
||||||
|
A: SessionStateInfo + Sync,
|
||||||
|
{
|
||||||
|
async fn authenticate_and_fetch(
|
||||||
|
&self,
|
||||||
|
request_headers: &HeaderMap,
|
||||||
|
state: &A,
|
||||||
|
) -> RouterResult<(AuthenticationData, AuthenticationType)> {
|
||||||
|
let payload = parse_jwt_payload::<A, AuthToken>(request_headers, state).await?;
|
||||||
|
if payload.check_in_blacklist(state).await? {
|
||||||
|
return Err(errors::ApiErrorResponse::InvalidJwtToken.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.merchant_id != self.merchant_id {
|
||||||
|
return Err(report!(errors::ApiErrorResponse::InvalidJwtToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload
|
||||||
|
.profile_id
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|profile_id| *profile_id != self.profile_id)
|
||||||
|
{
|
||||||
|
return Err(report!(errors::ApiErrorResponse::InvalidJwtToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
let permissions = authorization::get_permissions(state, &payload).await?;
|
||||||
|
authorization::check_authorization(&self.required_permission, &permissions)?;
|
||||||
|
let key_manager_state = &(&state.session_state()).into();
|
||||||
|
let key_store = state
|
||||||
|
.store()
|
||||||
|
.get_merchant_key_store_by_merchant_id(
|
||||||
|
key_manager_state,
|
||||||
|
&payload.merchant_id,
|
||||||
|
&state.store().get_master_key().to_vec().into(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.to_not_found_response(errors::ApiErrorResponse::InvalidJwtToken)
|
||||||
|
.attach_printable("Failed to fetch merchant key store for the merchant id")?;
|
||||||
|
|
||||||
|
let merchant = state
|
||||||
|
.store()
|
||||||
|
.find_merchant_account_by_merchant_id(
|
||||||
|
key_manager_state,
|
||||||
|
&payload.merchant_id,
|
||||||
|
&key_store,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.to_not_found_response(errors::ApiErrorResponse::InvalidJwtToken)
|
||||||
|
.attach_printable("Failed to fetch merchant account for the merchant id")?;
|
||||||
|
|
||||||
|
let auth = AuthenticationData {
|
||||||
|
merchant_account: merchant,
|
||||||
|
key_store,
|
||||||
|
profile_id: payload.profile_id,
|
||||||
|
};
|
||||||
|
Ok((
|
||||||
|
auth.clone(),
|
||||||
|
AuthenticationType::MerchantJwtWithProfileId {
|
||||||
|
merchant_id: auth.merchant_account.get_id().clone(),
|
||||||
|
profile_id: auth.profile_id.clone(),
|
||||||
|
user_id: payload.user_id,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn parse_jwt_payload<A, T>(headers: &HeaderMap, state: &A) -> RouterResult<T>
|
pub async fn parse_jwt_payload<A, T>(headers: &HeaderMap, state: &A) -> RouterResult<T>
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned,
|
T: serde::de::DeserializeOwned,
|
||||||
|
|||||||
Reference in New Issue
Block a user