refactor(router): Refactored Authentication (#327)

This commit is contained in:
Rachit Naithani
2023-01-10 15:06:34 +05:30
committed by GitHub
parent 98816c05bd
commit 59573efe91
17 changed files with 363 additions and 384 deletions

View File

@ -8,7 +8,7 @@ use crate::{
compatibility::{stripe::errors, wrap},
core::customers,
routes,
services::api,
services::{api, authentication as auth},
types::api::customers as customer_types,
};
@ -44,7 +44,7 @@ pub async fn customer_create(
|state, merchant_account, req| {
customers::create_customer(&*state.store, merchant_account, req)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -75,7 +75,7 @@ pub async fn customer_retrieve(
|state, merchant_account, req| {
customers::retrieve_customer(&*state.store, merchant_account, req)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -115,7 +115,7 @@ pub async fn customer_update(
|state, merchant_account, req| {
customers::update_customer(&*state.store, merchant_account, req)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -144,7 +144,7 @@ pub async fn customer_delete(
&req,
payload,
customers::delete_customer,
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}

View File

@ -9,7 +9,7 @@ use crate::{
compatibility::{stripe::errors, wrap},
core::payments,
routes,
services::api,
services::{api, authentication as auth},
types::api::{self as api_types},
};
@ -58,7 +58,7 @@ pub async fn payment_intents_create(
payments::CallConnectorAction::Trigger,
)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -78,11 +78,10 @@ pub async fn payment_intents_retrieve(
param: None,
};
let auth_type = match api::get_auth_type(&req) {
Ok(auth_type) => auth_type,
let (auth_type, auth_flow) = match auth::get_auth_type_and_flow(req.headers()) {
Ok(auth) => auth,
Err(err) => return api::log_and_return_error_response(report!(err)),
};
let auth_flow = api::get_auth_flow(&auth_type);
wrap::compatibility_api_wrap::<
_,
@ -107,7 +106,7 @@ pub async fn payment_intents_retrieve(
payments::CallConnectorAction::Trigger,
)
},
auth_type,
&*auth_type,
)
.await
}
@ -138,12 +137,11 @@ pub async fn payment_intents_update(
payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(payment_id));
let auth_type;
(payload, auth_type) = match api::get_auth_type_and_check_client_secret(&req, payload) {
Ok(values) => values,
Err(err) => return api::log_and_return_error_response(err),
let (auth_type, auth_flow) = match auth::get_auth_type_and_flow(req.headers()) {
Ok(auth) => auth,
Err(err) => return api::log_and_return_error_response(report!(err)),
};
let auth_flow = api::get_auth_flow(&auth_type);
wrap::compatibility_api_wrap::<
_,
_,
@ -168,7 +166,7 @@ pub async fn payment_intents_update(
payments::CallConnectorAction::Trigger,
)
},
auth_type,
&*auth_type,
)
.await
}
@ -200,12 +198,12 @@ pub async fn payment_intents_confirm(
payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(payment_id));
payload.confirm = Some(true);
let auth_type;
(payload, auth_type) = match api::get_auth_type_and_check_client_secret(&req, payload) {
Ok(values) => values,
Err(err) => return api::log_and_return_error_response(err),
};
let auth_flow = api::get_auth_flow(&auth_type);
let (auth_type, auth_flow) =
match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
Ok(auth) => auth,
Err(err) => return api::log_and_return_error_response(err),
};
wrap::compatibility_api_wrap::<
_,
_,
@ -230,7 +228,7 @@ pub async fn payment_intents_confirm(
payments::CallConnectorAction::Trigger,
)
},
auth_type,
&*auth_type,
)
.await
}
@ -280,7 +278,7 @@ pub async fn payment_intents_capture(
payments::CallConnectorAction::Trigger,
)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -307,11 +305,11 @@ pub async fn payment_intents_cancel(
let mut payload: payment_types::PaymentsCancelRequest = stripe_payload.into();
payload.payment_id = payment_id;
let auth_type = match api::get_auth_type(&req) {
Ok(values) => values,
Err(err) => return api::log_and_return_error_response(err),
let (auth_type, auth_flow) = match auth::get_auth_type_and_flow(req.headers()) {
Ok(auth) => auth,
Err(err) => return api::log_and_return_error_response(report!(err)),
};
let auth_flow = api::get_auth_flow(&auth_type);
wrap::compatibility_api_wrap::<
_,
_,
@ -335,7 +333,7 @@ pub async fn payment_intents_cancel(
payments::CallConnectorAction::Trigger,
)
},
auth_type,
&*auth_type,
)
.await
}
@ -366,7 +364,7 @@ pub async fn payment_intent_list(
|state, merchant_account, req| {
payments::list_payments(&*state.store, merchant_account, req)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}

View File

@ -7,7 +7,7 @@ use crate::{
compatibility::{stripe::errors, wrap},
core::refunds,
routes,
services::api,
services::authentication as auth,
types::api::refunds as refund_types,
};
@ -34,7 +34,7 @@ pub async fn refund_create(
&req,
create_refund_req,
refunds::refund_create_core,
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -67,7 +67,7 @@ pub async fn refund_retrieve(
refunds::refund_retrieve_core,
)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -99,7 +99,7 @@ pub async fn refund_update(
|state, merchant_account, req| {
refunds::refund_update_core(&*state.store, merchant_account, &refund_id, req)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}

View File

@ -9,7 +9,7 @@ use crate::{
compatibility::{stripe::errors, wrap},
core::payments,
routes,
services::api,
services::{api, authentication as auth},
types::api as api_types,
};
@ -55,7 +55,7 @@ pub async fn setup_intents_create(
payments::CallConnectorAction::Trigger,
)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -75,11 +75,10 @@ pub async fn setup_intents_retrieve(
param: None,
};
let auth_type = match api::get_auth_type(&req) {
Ok(auth_type) => auth_type,
let (auth_type, auth_flow) = match auth::get_auth_type_and_flow(req.headers()) {
Ok(auth) => auth,
Err(err) => return api::log_and_return_error_response(report!(err)),
};
let auth_flow = api::get_auth_flow(&auth_type);
wrap::compatibility_api_wrap::<
_,
@ -104,7 +103,7 @@ pub async fn setup_intents_retrieve(
payments::CallConnectorAction::Trigger,
)
},
auth_type,
&*auth_type,
)
.await
}
@ -131,12 +130,12 @@ pub async fn setup_intents_update(
let mut payload: payment_types::PaymentsRequest = stripe_payload.into();
payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(setup_id));
let auth_type;
(payload, auth_type) = match api::get_auth_type_and_check_client_secret(&req, payload) {
Ok(values) => values,
Err(err) => return api::log_and_return_error_response(err),
};
let auth_flow = api::get_auth_flow(&auth_type);
let (auth_type, auth_flow) =
match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
Ok(auth) => auth,
Err(err) => return api::log_and_return_error_response(err),
};
wrap::compatibility_api_wrap::<
_,
_,
@ -161,7 +160,7 @@ pub async fn setup_intents_update(
payments::CallConnectorAction::Trigger,
)
},
auth_type,
&*auth_type,
)
.await
}
@ -189,12 +188,12 @@ pub async fn setup_intents_confirm(
payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(setup_id));
payload.confirm = Some(true);
let auth_type;
(payload, auth_type) = match api::get_auth_type_and_check_client_secret(&req, payload) {
Ok(values) => values,
Err(err) => return api::log_and_return_error_response(err),
};
let auth_flow = api::get_auth_flow(&auth_type);
let (auth_type, auth_flow) =
match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
Ok(auth) => auth,
Err(err) => return api::log_and_return_error_response(err),
};
wrap::compatibility_api_wrap::<
_,
_,
@ -219,7 +218,7 @@ pub async fn setup_intents_confirm(
payments::CallConnectorAction::Trigger,
)
},
auth_type,
&*auth_type,
)
.await
}

View File

@ -8,28 +8,25 @@ use serde::Serialize;
use crate::{
core::errors::{self, RouterResult},
routes,
services::{api, logger},
types::storage,
services::{api, authentication as auth, logger},
};
#[instrument(skip(request, payload, state, func))]
pub async fn compatibility_api_wrap<'a, 'b, A, T, Q, F, Fut, S, E>(
#[instrument(skip(request, payload, state, func, api_authentication))]
pub async fn compatibility_api_wrap<'a, 'b, U, T, Q, F, Fut, S, E>(
state: &'b routes::AppState,
request: &'a HttpRequest,
payload: T,
func: F,
api_authentication: A,
api_authentication: &dyn auth::AuthenticateAndFetch<U>,
) -> HttpResponse
where
A: Into<api::ApiAuthentication<'a>> + std::fmt::Debug,
F: Fn(&'b routes::AppState, storage::MerchantAccount, T) -> Fut,
F: Fn(&'b routes::AppState, U, T) -> Fut,
Fut: Future<Output = RouterResult<api::BachResponse<Q>>>,
Q: Serialize + std::fmt::Debug + 'a,
S: From<Q> + Serialize,
E: From<errors::ApiErrorResponse> + Serialize + error_stack::Context + actix_web::ResponseError,
T: std::fmt::Debug,
{
let api_authentication = api_authentication.into();
let resp = api::server_wrap_util(state, request, payload, func, api_authentication).await;
match resp {
Ok(api::BachResponse::Json(router_resp)) => {

View File

@ -593,37 +593,6 @@ pub(crate) async fn call_payment_method(
}
}
pub(crate) fn client_secret_auth<P>(
payload: P,
auth_type: &services::api::MerchantAuthentication<'_>,
) -> RouterResult<P>
where
P: services::Authenticate,
{
match auth_type {
services::MerchantAuthentication::PublishableKey => {
payload
.get_client_secret()
.check_value_present("client_secret")
.change_context(errors::ApiErrorResponse::MissingRequiredField {
field_name: "client_secret".to_owned(),
})?;
Ok(payload)
}
services::api::MerchantAuthentication::ApiKey => {
if payload.get_client_secret().is_some() {
Err(report!(errors::ApiErrorResponse::InvalidRequestData {
message: "client_secret is not a valid parameter".to_owned(),
}))
} else {
Ok(payload)
}
}
_ => Err(report!(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unexpected Auth type")),
}
}
pub async fn get_customer_from_details(
db: &dyn StorageInterface,
customer_id: Option<String>,

View File

@ -2,7 +2,11 @@ use actix_web::{web, HttpRequest, HttpResponse};
use router_env::{instrument, tracing, Flow};
use super::app::AppState;
use crate::{core::admin::*, services::api, types::api::admin};
use crate::{
core::admin::*,
services::{api, authentication as auth},
types::api::admin,
};
/// Merchant Account - Create
///
@ -28,7 +32,7 @@ pub async fn merchant_account_create(
&req,
json_payload.into_inner(),
|state, _, req| create_merchant_account(&*state.store, req),
api::MerchantAuthentication::AdminApiKey,
&auth::AdminApiAuth,
)
.await
}
@ -49,7 +53,7 @@ pub async fn retrieve_merchant_account(
&req,
payload,
|state, _, req| get_merchant_account(&*state.store, req),
api::MerchantAuthentication::AdminApiKey,
&auth::AdminApiAuth,
)
.await
}
@ -68,7 +72,7 @@ pub async fn update_merchant_account(
&req,
json_payload.into_inner(),
|state, _, req| merchant_account_update(&*state.store, &merchant_id, req),
api::MerchantAuthentication::AdminApiKey,
&auth::AdminApiAuth,
)
.await
}
@ -89,7 +93,7 @@ pub async fn delete_merchant_account(
&req,
payload,
|state, _, req| merchant_account_delete(&*state.store, req.merchant_id),
api::MerchantAuthentication::AdminApiKey,
&auth::AdminApiAuth,
)
.await
}
@ -109,7 +113,7 @@ pub async fn payment_connector_create(
&req,
json_payload.into_inner(),
|state, _, req| create_payment_connector(&*state.store, req, &merchant_id),
api::MerchantAuthentication::AdminApiKey,
&auth::AdminApiAuth,
)
.await
}
@ -134,7 +138,7 @@ pub async fn payment_connector_retrieve(
|state, _, req| {
retrieve_payment_connector(&*state.store, req.merchant_id, req.merchant_connector_id)
},
api::MerchantAuthentication::AdminApiKey,
&auth::AdminApiAuth,
)
.await
}
@ -152,7 +156,7 @@ pub async fn payment_connector_list(
&req,
merchant_id,
|state, _, merchant_id| list_payment_connectors(&*state.store, merchant_id),
api::MerchantAuthentication::AdminApiKey,
&auth::AdminApiAuth,
)
.await
}
@ -173,7 +177,7 @@ pub async fn payment_connector_update(
|state, _, req| {
update_payment_connector(&*state.store, &merchant_id, merchant_connector_id, req)
},
api::MerchantAuthentication::AdminApiKey,
&auth::AdminApiAuth,
)
.await
}
@ -198,7 +202,7 @@ pub async fn payment_connector_delete(
|state, _, req| {
delete_payment_connector(&*state.store, req.merchant_id, req.merchant_connector_id)
},
api::MerchantAuthentication::AdminApiKey,
&auth::AdminApiAuth,
)
.await
}

View File

@ -4,7 +4,7 @@ use router_env::{instrument, tracing, Flow};
use super::app::AppState;
use crate::{
core::customers::*,
services::{self, api},
services::{api, authentication as auth},
types::api::customers,
};
@ -20,7 +20,7 @@ pub async fn customers_create(
&req,
json_payload.into_inner(),
|state, merchant_account, req| create_customer(&*state.store, merchant_account, req),
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -36,22 +36,19 @@ pub async fn customers_retrieve(
customer_id: path.into_inner(),
})
.into_inner();
let auth_type = match services::authenticate_eph_key(
&req,
&*state.store,
payload.customer_id.clone(),
)
.await
{
Ok(auth_type) => auth_type,
Err(err) => return api::log_and_return_error_response(err),
};
let auth =
match auth::is_ephemeral_auth(req.headers(), &*state.store, &payload.customer_id).await {
Ok(auth) => auth,
Err(err) => return api::log_and_return_error_response(err),
};
api::server_wrap(
&state,
&req,
payload,
|state, merchant_account, req| retrieve_customer(&*state.store, merchant_account, req),
auth_type,
&*auth,
)
.await
}
@ -71,7 +68,7 @@ pub async fn customers_update(
&req,
json_payload.into_inner(),
|state, merchant_account, req| update_customer(&*state.store, merchant_account, req),
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -87,14 +84,7 @@ pub async fn customers_delete(
customer_id: path.into_inner(),
})
.into_inner();
api::server_wrap(
&state,
&req,
payload,
delete_customer,
api::MerchantAuthentication::ApiKey,
)
.await
api::server_wrap(&state, &req, payload, delete_customer, &auth::ApiKeyAuth).await
}
#[instrument(skip_all, fields(flow = ?Flow::CustomersGetMandates))]
@ -115,7 +105,7 @@ pub async fn get_customer_mandates(
|state, merchant_account, req| {
crate::core::mandate::get_customer_mandates(state, merchant_account, req)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}

View File

@ -2,7 +2,11 @@ use actix_web::{web, HttpRequest, HttpResponse};
use router_env::{instrument, tracing, Flow};
use super::AppState;
use crate::{core::payments::helpers, services::api, types::api::customers};
use crate::{
core::payments::helpers,
services::{api, authentication as auth},
types::api::customers,
};
#[instrument(skip_all, fields(flow = ?Flow::EphemeralKeyCreate))]
pub async fn ephemeral_key_create(
@ -18,7 +22,7 @@ pub async fn ephemeral_key_create(
|state, merchant_account, req| {
helpers::make_ephemeral_key(state, req.customer_id, merchant_account.merchant_id)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -35,7 +39,7 @@ pub async fn ephemeral_key_delete(
&req,
payload,
|state, _, req| helpers::delete_ephemeral_key(&*state.store, req),
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}

View File

@ -2,7 +2,11 @@ use actix_web::{web, HttpRequest, HttpResponse};
use router_env::{instrument, tracing, Flow};
use super::app::AppState;
use crate::{core::mandate, services::api, types::api::mandates};
use crate::{
core::mandate,
services::{api, authentication as auth},
types::api::mandates,
};
#[instrument(skip_all, fields(flow = ?Flow::MandatesRetrieve))]
// #[get("/{id}")]
@ -19,7 +23,7 @@ pub async fn get_mandate(
&req,
mandate_id,
mandate::get_mandate,
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -41,7 +45,7 @@ pub async fn revoke_mandate(
|state, merchant_account, req| {
mandate::revoke_mandate(&*state.store, merchant_account, req)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}

View File

@ -4,8 +4,7 @@ use router_env::{instrument, tracing, Flow};
use super::app::AppState;
use crate::{
core::payment_methods::cards,
services,
services::api,
services::{api, authentication as auth},
types::api::payment_methods::{self, PaymentMethodId},
};
@ -23,7 +22,7 @@ pub async fn create_payment_method_api(
|state, merchant_account, req| async move {
cards::add_payment_method(state, req, &merchant_account).await
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -36,12 +35,12 @@ pub async fn list_payment_method_api(
_merchant_id: web::Path<String>,
json_payload: web::Query<payment_methods::ListPaymentMethodRequest>,
) -> HttpResponse {
//let merchant_id = merchant_id.into_inner();
let (payload, auth_type) =
match api::get_auth_type_and_check_client_secret(&req, json_payload.into_inner()) {
Ok(values) => values,
Err(err) => return api::log_and_return_error_response(err),
};
let payload = json_payload.into_inner();
let (auth, _) = match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
Ok((auth, _auth_flow)) => (auth, _auth_flow),
Err(e) => return api::log_and_return_error_response(e),
};
api::server_wrap(
&state,
@ -50,7 +49,7 @@ pub async fn list_payment_method_api(
|state, merchant_account, req| {
cards::list_payment_methods(&*state.store, merchant_account, req)
},
auth_type,
&*auth,
)
.await
}
@ -65,11 +64,11 @@ pub async fn list_customer_payment_method_api(
) -> HttpResponse {
let customer_id = customer_id.into_inner().0;
let auth_type =
match services::authenticate_eph_key(&req, &*state.store, customer_id.clone()).await {
Ok(auth_type) => auth_type,
Err(err) => return api::log_and_return_error_response(err),
};
let auth_type = match auth::is_ephemeral_auth(req.headers(), &*state.store, &customer_id).await
{
Ok(auth_type) => auth_type,
Err(err) => return api::log_and_return_error_response(err),
};
api::server_wrap(
&state,
@ -78,7 +77,7 @@ pub async fn list_customer_payment_method_api(
|state, merchant_account, _| {
cards::list_customer_payment_method(state, merchant_account, &customer_id)
},
auth_type,
&*auth_type,
)
.await
}
@ -100,7 +99,7 @@ pub async fn payment_method_retrieve_api(
&req,
payload,
|state, merchant_account, pm| cards::retrieve_payment_method(state, pm, merchant_account),
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -126,7 +125,7 @@ pub async fn payment_method_update_api(
&payment_method_id,
)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -146,7 +145,7 @@ pub async fn payment_method_delete_api(
&req,
pm,
cards::delete_payment_method,
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}

View File

@ -1,5 +1,3 @@
use std::borrow::Cow;
use actix_web::{web, Responder};
use error_stack::report;
use router_env::{instrument, tracing, Flow};
@ -7,7 +5,7 @@ use router_env::{instrument, tracing, Flow};
use crate::{
self as app,
core::{errors::http_not_implemented, payments},
services::api,
services::{api, authentication as auth},
types::api::{self as api_types, enums as api_enums, payments as payment_types},
};
@ -37,7 +35,7 @@ pub async fn payments_create(
api::AuthFlow::Merchant,
)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -69,7 +67,7 @@ pub async fn payments_start(
payments::CallConnectorAction::Trigger,
)
},
api::MerchantAuthentication::MerchantId(Cow::Borrowed(&merchant_id)),
&auth::MerchantIdAuth(merchant_id),
)
.await
}
@ -89,11 +87,10 @@ pub async fn payments_retrieve(
param: None,
connector: None,
};
let auth_type = match api::get_auth_type(&req) {
Ok(auth_type) => auth_type,
let (auth_type, _auth_flow) = match auth::get_auth_type_and_flow(req.headers()) {
Ok(auth) => auth,
Err(err) => return api::log_and_return_error_response(report!(err)),
};
let _auth_flow = api::get_auth_flow(&auth_type);
api::server_wrap(
&state,
@ -110,7 +107,7 @@ pub async fn payments_retrieve(
payments::CallConnectorAction::Trigger,
)
},
auth_type,
&*auth_type,
)
.await
}
@ -133,14 +130,11 @@ pub async fn payments_update(
payload.payment_id = Some(payment_types::PaymentIdType::PaymentIntentId(payment_id));
let auth_type;
(payload, auth_type) = match api::get_auth_type_and_check_client_secret(&req, payload) {
Ok(values) => values,
Err(err) => return api::log_and_return_error_response(err),
let (auth_type, auth_flow) = match auth::get_auth_type_and_flow(req.headers()) {
Ok(auth) => auth,
Err(err) => return api::log_and_return_error_response(report!(err)),
};
let auth_flow = api::get_auth_flow(&auth_type);
// return http_not_implemented();
api::server_wrap(
&state,
&req,
@ -154,7 +148,7 @@ pub async fn payments_update(
auth_flow,
)
},
auth_type,
&*auth_type,
)
.await
}
@ -177,13 +171,12 @@ pub async fn payments_confirm(
payload.payment_id = Some(payment_types::PaymentIdType::PaymentIntentId(payment_id));
payload.confirm = Some(true);
let auth_type;
(payload, auth_type) = match api::get_auth_type_and_check_client_secret(&req, payload) {
Ok(values) => values,
Err(err) => return api::log_and_return_error_response(err),
};
let (auth_type, auth_flow) =
match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
Ok(auth) => auth,
Err(e) => return api::log_and_return_error_response(e),
};
let auth_flow = api::get_auth_flow(&auth_type);
api::server_wrap(
&state,
&req,
@ -197,7 +190,7 @@ pub async fn payments_confirm(
auth_flow,
)
},
auth_type,
&*auth_type,
)
.await
}
@ -230,7 +223,7 @@ pub async fn payments_capture(
payments::CallConnectorAction::Trigger,
)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -264,7 +257,7 @@ pub async fn payments_connector_session(
payments::CallConnectorAction::Trigger,
)
},
api::MerchantAuthentication::PublishableKey,
&auth::PublishableKeyAuth,
)
.await
}
@ -296,7 +289,7 @@ pub async fn payments_response(
req,
)
},
api::MerchantAuthentication::MerchantId(Cow::Borrowed(&merchant_id)),
&auth::MerchantIdAuth(merchant_id),
)
.await
}
@ -328,7 +321,7 @@ pub async fn payments_cancel(
payments::CallConnectorAction::Trigger,
)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -348,7 +341,7 @@ pub async fn payments_list(
|state, merchant_account, req| {
payments::list_payments(&*state.store, merchant_account, req)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}

View File

@ -2,7 +2,11 @@ use actix_web::{web, HttpRequest, HttpResponse};
use router_env::{instrument, tracing, Flow};
use super::app::AppState;
use crate::{core::refunds::*, services::api, types::api::refunds};
use crate::{
core::refunds::*,
services::{api, authentication as auth},
types::api::refunds,
};
/// Refunds - Create
///
@ -28,7 +32,7 @@ pub async fn refunds_create(
&req,
json_payload.into_inner(),
refund_create_core,
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -49,7 +53,7 @@ pub async fn refunds_retrieve(
|state, merchant_account, refund_id| {
refund_response_wrapper(state, merchant_account, refund_id, refund_retrieve_core)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -70,7 +74,7 @@ pub async fn refunds_update(
|state, merchant_account, req| {
refund_update_core(&*state.store, merchant_account, &refund_id, req)
},
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}
@ -87,7 +91,7 @@ pub async fn refunds_list(
&req,
payload.into_inner(),
|state, merchant_account, req| refund_list(&*state.store, merchant_account, req),
api::MerchantAuthentication::ApiKey,
&auth::ApiKeyAuth,
)
.await
}

View File

@ -2,7 +2,10 @@ use actix_web::{web, HttpRequest, Responder};
use router_env::{instrument, tracing, Flow};
use super::app::AppState;
use crate::{core::webhooks, services::api};
use crate::{
core::webhooks,
services::{api, authentication as auth},
};
#[instrument(skip_all, fields(flow = ?Flow::IncomingWebhookReceive))]
pub async fn receive_incoming_webhook(
@ -20,7 +23,7 @@ pub async fn receive_incoming_webhook(
|state, merchant_account, body| {
webhooks::webhooks_core(state, &req, merchant_account, &connector_name, body)
},
api::ConnectorAuthentication::MerchantId(&merchant_id),
&auth::MerchantIdAuth(merchant_id),
)
.await
}

View File

@ -1,4 +1,5 @@
pub mod api;
pub mod authentication;
pub mod encryption;
pub mod logger;

View File

@ -1,7 +1,7 @@
mod client;
pub(crate) mod request;
use std::{borrow::Cow, collections::HashMap, fmt::Debug, future::Future, str, time::Instant};
use std::{collections::HashMap, fmt::Debug, future::Future, str, time::Instant};
use actix_web::{body, HttpRequest, HttpResponse, Responder};
use bytes::Bytes;
@ -15,18 +15,18 @@ pub use self::request::{Method, Request, RequestBuilder};
use crate::{
configs::settings::Connectors,
core::{
errors::{self, CustomResult, RouterResponse, RouterResult, StorageErrorExt},
errors::{self, CustomResult, RouterResponse, RouterResult},
payments,
},
db::StorageInterface,
logger,
routes::AppState,
services::authentication as auth,
types::{
self, api,
storage::{self, enums},
storage::{self},
ErrorResponse,
},
utils::{self, OptionExt},
};
pub type BoxedConnectorIntegration<'a, T, Req, Resp> =
@ -363,104 +363,49 @@ impl RedirectForm {
}
}
#[derive(Clone, Debug)]
pub enum ApiAuthentication<'a> {
Merchant(MerchantAuthentication<'a>),
Connector(ConnectorAuthentication<'a>),
}
#[derive(Clone, Debug)]
pub enum MerchantAuthentication<'a> {
ApiKey,
MerchantId(Cow<'a, str>),
AdminApiKey,
PublishableKey,
}
#[derive(Clone, Debug)]
pub enum ConnectorAuthentication<'a> {
MerchantId(&'a str),
}
impl<'a> From<MerchantAuthentication<'a>> for ApiAuthentication<'a> {
fn from(merchant_auth: MerchantAuthentication<'a>) -> Self {
ApiAuthentication::Merchant(merchant_auth)
}
}
impl<'a> From<ConnectorAuthentication<'a>> for ApiAuthentication<'a> {
fn from(connector_auth: ConnectorAuthentication<'a>) -> Self {
ApiAuthentication::Connector(connector_auth)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum AuthFlow {
Client,
Merchant,
}
pub fn get_auth_flow(auth_type: &MerchantAuthentication<'_>) -> AuthFlow {
match auth_type {
MerchantAuthentication::ApiKey => AuthFlow::Merchant,
_ => AuthFlow::Client,
}
}
pub fn get_auth_type(request: &HttpRequest) -> RouterResult<MerchantAuthentication<'_>> {
let api_key = get_api_key(request).change_context(errors::ApiErrorResponse::Unauthorized)?;
if api_key.starts_with("pk_") {
Ok(MerchantAuthentication::PublishableKey)
} else {
Ok(MerchantAuthentication::ApiKey)
}
}
#[instrument(skip(request, payload, state, func))]
pub async fn server_wrap_util<'a, 'b, T, Q, F, Fut>(
#[instrument(skip(request, payload, state, func, api_auth))]
pub async fn server_wrap_util<'a, 'b, U, T, Q, F, Fut>(
state: &'b AppState,
request: &'a HttpRequest,
payload: T,
func: F,
api_authentication: ApiAuthentication<'a>,
api_auth: &dyn auth::AuthenticateAndFetch<U>,
) -> RouterResult<BachResponse<Q>>
where
F: Fn(&'b AppState, storage::MerchantAccount, T) -> Fut,
F: Fn(&'b AppState, U, T) -> Fut,
Fut: Future<Output = RouterResponse<Q>>,
Q: Serialize + Debug + 'a,
T: Debug,
{
let merchant_account = match api_authentication {
ApiAuthentication::Merchant(merchant_auth) => {
authenticate_merchant(request, state, merchant_auth).await?
}
ApiAuthentication::Connector(connector_auth) => {
authenticate_connector(request, &*state.store, connector_auth).await?
}
};
logger::debug!(request=?payload);
func(state, merchant_account, payload).await
let auth_out = api_auth
.authenticate_and_fetch(request.headers(), state)
.await?;
func(state, auth_out, payload).await
}
#[instrument(
skip(request, payload, state, func),
skip(request, payload, state, func, api_auth),
fields(request_method, request_url_path)
)]
pub async fn server_wrap<'a, 'b, A, T, Q, F, Fut>(
pub async fn server_wrap<'a, 'b, T, U, Q, F, Fut>(
state: &'b AppState,
request: &'a HttpRequest,
payload: T,
func: F,
api_authentication: A,
api_auth: &dyn auth::AuthenticateAndFetch<U>,
) -> HttpResponse
where
A: Into<ApiAuthentication<'a>> + Debug,
F: Fn(&'b AppState, storage::MerchantAccount, T) -> Fut,
F: Fn(&'b AppState, U, T) -> Fut,
Fut: Future<Output = RouterResult<BachResponse<Q>>>,
Q: Serialize + Debug + 'a,
T: Debug,
{
let api_authentication = api_authentication.into();
let request_method = request.method().as_str();
let url_path = request.path();
tracing::Span::current().record("request_method", request_method);
@ -469,7 +414,7 @@ where
let start_instant = Instant::now();
logger::info!(tag = ?Tag::BeginRequest);
let res = match server_wrap_util(state, request, payload, func, api_authentication).await {
let res = match server_wrap_util(state, request, payload, func, api_auth).await {
Ok(BachResponse::Json(response)) => match serde_json::to_string(&response) {
Ok(res) => http_response_json(res),
Err(_) => http_response_err(
@ -519,120 +464,6 @@ where
error.current_context().error_response()
}
pub async fn authenticate_merchant<'a>(
request: &HttpRequest,
state: &AppState,
merchant_authentication: MerchantAuthentication<'a>,
) -> RouterResult<storage::MerchantAccount> {
match merchant_authentication {
MerchantAuthentication::ApiKey => {
let api_key =
get_api_key(request).change_context(errors::ApiErrorResponse::Unauthorized)?;
authenticate_by_api_key(&*state.store, api_key).await
}
MerchantAuthentication::MerchantId(merchant_id) => (*state.store)
.find_merchant_account_by_merchant_id(&merchant_id)
.await
.map_err(|error| error.to_not_found_response(errors::ApiErrorResponse::Unauthorized)),
MerchantAuthentication::AdminApiKey => {
let admin_api_key =
get_api_key(request).change_context(errors::ApiErrorResponse::Unauthorized)?;
utils::when(admin_api_key != state.conf.keys.admin_api_key, || {
Err(errors::ApiErrorResponse::Unauthorized)
.into_report()
.attach_printable("Admin Authentication Failure")
})?;
Ok(storage::MerchantAccount {
id: -1,
merchant_id: String::from("juspay"),
merchant_name: None,
api_key: None,
merchant_details: None,
return_url: None,
webhook_details: None,
routing_algorithm: None,
custom_routing_rules: None,
sub_merchants_enabled: None,
parent_merchant_id: None,
enable_payment_response_hash: false,
payment_response_hash_key: None,
redirect_to_merchant_with_http_post: false,
publishable_key: None,
storage_scheme: enums::MerchantStorageScheme::PostgresOnly,
locker_id: None,
})
}
MerchantAuthentication::PublishableKey => {
let api_key =
get_api_key(request).change_context(errors::ApiErrorResponse::Unauthorized)?;
authenticate_by_publishable_key(&*state.store, api_key).await
}
}
}
pub async fn authenticate_connector<'a>(
_request: &HttpRequest,
store: &dyn StorageInterface,
connector_authentication: ConnectorAuthentication<'a>,
) -> RouterResult<storage::MerchantAccount> {
match connector_authentication {
ConnectorAuthentication::MerchantId(merchant_id) => store
.find_merchant_account_by_merchant_id(merchant_id)
.await
.map_err(|error| error.to_not_found_response(errors::ApiErrorResponse::Unauthorized)),
}
}
pub fn get_auth_type_and_check_client_secret<P>(
req: &HttpRequest,
payload: P,
) -> RouterResult<(P, MerchantAuthentication<'_>)>
where
P: Authenticate,
{
let auth_type = get_auth_type(req)?;
Ok((
payments::helpers::client_secret_auth(payload, &auth_type)?,
auth_type,
))
}
pub async fn authenticate_eph_key<'a>(
req: &'a HttpRequest,
store: &dyn StorageInterface,
customer_id: String,
) -> RouterResult<MerchantAuthentication<'a>> {
let api_key = get_api_key(req)?;
if api_key.starts_with("epk") {
let ek = store
.get_ephemeral_key(api_key)
.await
.change_context(errors::ApiErrorResponse::Unauthorized)?;
utils::when(ek.customer_id.ne(&customer_id), || {
Err(report!(errors::ApiErrorResponse::InvalidEphermeralKey))
})?;
Ok(MerchantAuthentication::MerchantId(Cow::Owned(
ek.merchant_id,
)))
} else {
Ok(MerchantAuthentication::ApiKey)
}
}
fn get_api_key(req: &HttpRequest) -> RouterResult<&str> {
req.headers()
.get("api-key")
.get_required_value("api-key")?
.to_str()
.into_report()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to convert API key to string")
}
pub async fn authenticate_by_api_key(
store: &dyn StorageInterface,
api_key: &str,
@ -644,17 +475,6 @@ pub async fn authenticate_by_api_key(
.attach_printable("Merchant not authenticated")
}
async fn authenticate_by_publishable_key(
store: &dyn StorageInterface,
publishable_key: &str,
) -> RouterResult<storage::MerchantAccount> {
store
.find_merchant_account_by_publishable_key(publishable_key)
.await
.change_context(errors::ApiErrorResponse::Unauthorized)
.attach_printable("Merchant not authenticated")
}
pub fn http_response_json<T: body::MessageBody + 'static>(response: T) -> HttpResponse {
HttpResponse::Ok()
.content_type("application/json")

View File

@ -0,0 +1,194 @@
use actix_web::http::header::HeaderMap;
use api_models::{payment_methods::ListPaymentMethodRequest, payments::PaymentsRequest};
use async_trait::async_trait;
use error_stack::{report, IntoReport, ResultExt};
use crate::{
core::errors::{self, RouterResult, StorageErrorExt},
db::StorageInterface,
routes::AppState,
services::api,
types::storage,
utils::OptionExt,
};
#[async_trait]
pub trait AuthenticateAndFetch<T> {
async fn authenticate_and_fetch(
&self,
request_headers: &HeaderMap,
state: &AppState,
) -> RouterResult<T>;
}
#[derive(Debug)]
pub struct ApiKeyAuth;
#[async_trait]
impl AuthenticateAndFetch<storage::MerchantAccount> for ApiKeyAuth {
async fn authenticate_and_fetch(
&self,
request_headers: &HeaderMap,
state: &AppState,
) -> RouterResult<storage::MerchantAccount> {
let api_key =
get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?;
state
.store
.find_merchant_account_by_api_key(api_key)
.await
.change_context(errors::ApiErrorResponse::Unauthorized)
.attach_printable("Merchant not authenticated")
}
}
#[derive(Debug)]
pub struct AdminApiAuth;
#[async_trait]
impl AuthenticateAndFetch<()> for AdminApiAuth {
async fn authenticate_and_fetch(
&self,
request_headers: &HeaderMap,
state: &AppState,
) -> RouterResult<()> {
let admin_api_key =
get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?;
if admin_api_key != state.conf.keys.admin_api_key {
Err(report!(errors::ApiErrorResponse::Unauthorized)
.attach_printable("Admin Authentication Failure"))?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct MerchantIdAuth(pub String);
#[async_trait]
impl AuthenticateAndFetch<storage::MerchantAccount> for MerchantIdAuth {
async fn authenticate_and_fetch(
&self,
_request_headers: &HeaderMap,
state: &AppState,
) -> RouterResult<storage::MerchantAccount> {
state
.store
.find_merchant_account_by_merchant_id(self.0.as_ref())
.await
.map_err(|error| error.to_not_found_response(errors::ApiErrorResponse::Unauthorized))
}
}
#[derive(Debug)]
pub struct PublishableKeyAuth;
#[async_trait]
impl AuthenticateAndFetch<storage::MerchantAccount> for PublishableKeyAuth {
async fn authenticate_and_fetch(
&self,
request_headers: &HeaderMap,
state: &AppState,
) -> RouterResult<storage::MerchantAccount> {
let publishable_key =
get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?;
state
.store
.find_merchant_account_by_publishable_key(publishable_key)
.await
.change_context(errors::ApiErrorResponse::Unauthorized)
.attach_printable("Merchant not authenticated")
}
}
pub trait ClientSecretFetch {
fn get_client_secret(&self) -> Option<&String>;
}
impl ClientSecretFetch for PaymentsRequest {
fn get_client_secret(&self) -> Option<&String> {
self.client_secret.as_ref()
}
}
impl ClientSecretFetch for ListPaymentMethodRequest {
fn get_client_secret(&self) -> Option<&String> {
self.client_secret.as_ref()
}
}
pub fn get_auth_type_and_flow(
headers: &HeaderMap,
) -> RouterResult<(
Box<dyn AuthenticateAndFetch<storage::MerchantAccount>>,
api::AuthFlow,
)> {
let api_key = get_api_key(headers)?;
if api_key.starts_with("pk_") {
return Ok((Box::new(PublishableKeyAuth), api::AuthFlow::Client));
}
Ok((Box::new(PublishableKeyAuth), api::AuthFlow::Merchant))
}
pub fn check_client_secret_and_get_auth(
headers: &HeaderMap,
payload: &impl ClientSecretFetch,
) -> RouterResult<(
Box<dyn AuthenticateAndFetch<storage::MerchantAccount>>,
api::AuthFlow,
)> {
let api_key = get_api_key(headers)?;
if api_key.starts_with("pk_") {
payload
.get_client_secret()
.check_value_present("client_secret")
.map_err(|_| errors::ApiErrorResponse::MissingRequiredField {
field_name: "client_secret".to_owned(),
})?;
return Ok((Box::new(PublishableKeyAuth), api::AuthFlow::Client));
}
if payload.get_client_secret().is_some() {
return Err(errors::ApiErrorResponse::InvalidRequestData {
message: "client_secret is not a valid parameter".to_owned(),
}
.into());
}
Ok((Box::new(ApiKeyAuth), api::AuthFlow::Merchant))
}
pub async fn is_ephemeral_auth(
headers: &HeaderMap,
db: &dyn StorageInterface,
customer_id: &str,
) -> RouterResult<Box<dyn AuthenticateAndFetch<storage::MerchantAccount>>> {
let api_key = get_api_key(headers)?;
if !api_key.starts_with("epk") {
return Ok(Box::new(ApiKeyAuth));
}
let ephemeral_key = db
.get_ephemeral_key(api_key)
.await
.change_context(errors::ApiErrorResponse::Unauthorized)?;
if ephemeral_key.customer_id.ne(customer_id) {
return Err(report!(errors::ApiErrorResponse::InvalidEphermeralKey));
}
Ok(Box::new(MerchantIdAuth(ephemeral_key.merchant_id)))
}
fn get_api_key(headers: &HeaderMap) -> RouterResult<&str> {
headers
.get("api-key")
.get_required_value("api-key")?
.to_str()
.into_report()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to convert API key to string")
}