feat: use admin_api_key auth along with merchant_id for connector list, retrieve and update apis (#5613)

This commit is contained in:
Hrithikesh
2024-08-21 14:53:10 +05:30
committed by GitHub
parent beb4fb050f
commit b60ced02ff
13 changed files with 135 additions and 226 deletions

View File

@ -434,7 +434,7 @@ pub async fn connector_retrieve(
)
},
auth::auth_type(
&auth::HeaderAuth(auth::ApiKeyAuth),
&auth::AdminApiAuthWithMerchantId::default(),
&auth::JWTAuthMerchantFromRoute {
merchant_id,
required_permission: Permission::MerchantConnectorAccountRead,
@ -533,7 +533,7 @@ pub async fn payment_connector_list(
merchant_id.to_owned(),
|state, _auth, merchant_id, _| list_payment_connectors(state, merchant_id, None),
auth::auth_type(
&auth::HeaderAuth(auth::ApiKeyAuth),
&auth::AdminApiAuthWithMerchantId::default(),
&auth::JWTAuthMerchantFromRoute {
merchant_id,
required_permission: Permission::MerchantConnectorAccountRead,
@ -593,7 +593,7 @@ pub async fn connector_update(
)
},
auth::auth_type(
&auth::HeaderAuth(auth::ApiKeyAuth),
&auth::AdminApiAuthWithMerchantId::default(),
&auth::JWTAuthMerchantFromRoute {
merchant_id: merchant_id.clone(),
required_permission: Permission::MerchantConnectorAccountWrite,

View File

@ -37,6 +37,7 @@ use crate::{
api_keys,
errors::{self, utils::StorageErrorExt, RouterResult},
},
headers,
routes::app::SessionStateInfo,
services::api,
types::domain,
@ -76,6 +77,9 @@ pub enum AuthenticationType {
key_id: String,
},
AdminApiKey,
AdminApiAuthWithMerchantId {
merchant_id: id_type::MerchantId,
},
MerchantJwt {
merchant_id: id_type::MerchantId,
user_id: Option<String>,
@ -122,6 +126,7 @@ impl AuthenticationType {
merchant_id,
key_id: _,
}
| Self::AdminApiAuthWithMerchantId { merchant_id }
| Self::MerchantId { merchant_id }
| Self::PublishableKey { merchant_id }
| Self::MerchantJwt {
@ -655,7 +660,7 @@ where
}
}
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct AdminApiAuth;
#[async_trait]
@ -683,6 +688,81 @@ where
}
}
#[derive(Debug, Default)]
pub struct AdminApiAuthWithMerchantId(AdminApiAuth);
#[async_trait]
impl<A> AuthenticateAndFetch<AuthenticationData, A> for AdminApiAuthWithMerchantId
where
A: SessionStateInfo + Sync,
{
async fn authenticate_and_fetch(
&self,
request_headers: &HeaderMap,
state: &A,
) -> RouterResult<(AuthenticationData, AuthenticationType)> {
self.0
.authenticate_and_fetch(request_headers, state)
.await?;
let merchant_id =
get_header_value_by_key(headers::X_MERCHANT_ID.to_string(), request_headers)?
.get_required_value(headers::X_MERCHANT_ID)
.change_context(errors::ApiErrorResponse::InvalidRequestData {
message: format!("`{}` header is missing", headers::X_MERCHANT_ID),
})
.and_then(|merchant_id_str| {
id_type::MerchantId::try_from(std::borrow::Cow::from(
merchant_id_str.to_string(),
))
.change_context(
errors::ApiErrorResponse::InvalidRequestData {
message: format!("`{}` header is invalid", headers::X_MERCHANT_ID),
},
)
})?;
let key_manager_state = &(&state.session_state()).into();
let key_store = state
.store()
.get_merchant_key_store_by_merchant_id(
key_manager_state,
&merchant_id,
&state.store().get_master_key().to_vec().into(),
)
.await
.map_err(|e| {
if e.current_context().is_db_not_found() {
e.change_context(errors::ApiErrorResponse::Unauthorized)
} else {
e.change_context(errors::ApiErrorResponse::InternalServerError)
.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, &merchant_id, &key_store)
.await
.map_err(|e| {
if e.current_context().is_db_not_found() {
e.change_context(errors::ApiErrorResponse::Unauthorized)
} else {
e.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to fetch merchant account for the merchant id")
}
})?;
let auth = AuthenticationData {
merchant_account: merchant,
key_store,
profile_id: None,
};
Ok((
auth,
AuthenticationType::AdminApiAuthWithMerchantId { merchant_id },
))
}
}
#[derive(Debug)]
pub struct EphemeralKeyAuth;
@ -1425,7 +1505,7 @@ pub fn is_ephemeral_auth<A: SessionStateInfo + Sync + Send>(
}
pub fn is_jwt_auth(headers: &HeaderMap) -> bool {
headers.get(crate::headers::AUTHORIZATION).is_some()
headers.get(headers::AUTHORIZATION).is_some()
|| get_cookie_from_header(headers)
.and_then(cookies::parse_cookie)
.is_ok()
@ -1465,8 +1545,8 @@ pub fn get_header_value_by_key(key: String, headers: &HeaderMap) -> RouterResult
pub fn get_jwt_from_authorization_header(headers: &HeaderMap) -> RouterResult<&str> {
headers
.get(crate::headers::AUTHORIZATION)
.get_required_value(crate::headers::AUTHORIZATION)?
.get(headers::AUTHORIZATION)
.get_required_value(headers::AUTHORIZATION)?
.to_str()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to convert JWT token to string")?

View File

@ -484,7 +484,8 @@ Cypress.Commands.add("connectorRetrieveCall", (globalState) => {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
"api-key": globalState.get("adminApiKey"),
"x-merchant-id": merchant_id,
},
failOnStatusCode: false,
}).then((response) => {
@ -530,7 +531,8 @@ Cypress.Commands.add(
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
"api-key": globalState.get("adminApiKey"),
"x-merchant-id": merchant_id,
},
body: updateConnectorBody,
failOnStatusCode: false,
@ -554,7 +556,8 @@ Cypress.Commands.add("connectorListByMid", (globalState) => {
url: `${globalState.get("baseUrl")}/account/${merchant_id}/connectors`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
"api-key": globalState.get("adminApiKey"),
"X-Merchant-Id": merchant_id,
},
failOnStatusCode: false,
}).then((response) => {
@ -2062,7 +2065,8 @@ Cypress.Commands.add("ListMCAbyMID", (globalState) => {
url: `${globalState.get("baseUrl")}/account/${merchantId}/connectors`,
headers: {
"Content-Type": "application/json",
"api-key": globalState.get("apiKey"),
"api-key": globalState.get("adminApiKey"),
"X-Merchant-Id": merchantId,
},
failOnStatusCode: false,
}).then((response) => {

View File

@ -1,6 +1,5 @@
{
"childrenOrder": [
"API Key - Create",
"Payment Connector - Create",
"Payment Connector - Retrieve",
"Payment Connector - Update",

View File

@ -1,3 +0,0 @@
{
"eventOrder": ["event.test.js"]
}

View File

@ -1,46 +0,0 @@
// Validate status 2xx
pm.test("[POST]::/api_keys/:merchant_id - Status code is 2xx", function () {
pm.response.to.be.success;
});
// Validate if response header has matching content-type
pm.test(
"[POST]::/api_keys/:merchant_id - Content-Type is application/json",
function () {
pm.expect(pm.response.headers.get("Content-Type")).to.include(
"application/json",
);
},
);
// Set response object as internal variable
let jsonData = {};
try {
jsonData = pm.response.json();
} catch (e) {}
// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id
if (jsonData?.key_id) {
pm.collectionVariables.set("api_key_id", jsonData.key_id);
console.log(
"- use {{api_key_id}} as collection variable for value",
jsonData.key_id,
);
} else {
console.log(
"INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.",
);
}
// pm.collectionVariables - Set api_key as variable for jsonData.api_key
if (jsonData?.api_key) {
pm.collectionVariables.set("api_key", jsonData.api_key);
console.log(
"- use {{api_key}} as collection variable for value",
jsonData.api_key,
);
} else {
console.log(
"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.",
);
}

View File

@ -1,47 +0,0 @@
{
"auth": {
"type": "apikey",
"apikey": [
{
"key": "value",
"value": "{{admin_api_key}}",
"type": "string"
},
{
"key": "key",
"value": "api-key",
"type": "string"
}
]
},
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Accept",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw_json_formatted": {
"name": "API Key 1",
"description": null,
"expiration": "2069-09-23T01:02:03.000Z"
}
},
"url": {
"raw": "{{baseUrl}}/api_keys/:merchant_id",
"host": ["{{baseUrl}}"],
"path": ["api_keys", ":merchant_id"],
"variable": [
{
"key": "merchant_id",
"value": "{{merchant_id}}"
}
]
}
}

View File

@ -4,7 +4,7 @@
"apikey": [
{
"key": "value",
"value": "{{api_key}}",
"value": "{{admin_api_key}}",
"type": "string"
},
{
@ -19,6 +19,10 @@
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "x-merchant-id",
"value": "{{merchant_id}}"
}
],
"url": {

View File

@ -4,7 +4,7 @@
"apikey": [
{
"key": "value",
"value": "{{api_key}}",
"value": "{{admin_api_key}}",
"type": "string"
},
{
@ -24,6 +24,10 @@
{
"key": "Accept",
"value": "application/json"
},
{
"key": "x-merchant-id",
"value": "{{merchant_id}}"
}
],
"url": {

View File

@ -4,7 +4,7 @@
"apikey": [
{
"key": "value",
"value": "{{api_key}}",
"value": "{{admin_api_key}}",
"type": "string"
},
{
@ -28,6 +28,10 @@
{
"key": "Accept",
"value": "application/json"
},
{
"key": "x-merchant-id",
"value": "{{merchant_id}}"
}
],
"body": {

View File

@ -4,7 +4,7 @@
"apikey": [
{
"key": "value",
"value": "{{api_key}}",
"value": "{{admin_api_key}}",
"type": "string"
},
{
@ -28,6 +28,10 @@
{
"key": "Accept",
"value": "application/json"
},
{
"key": "x-merchant-id",
"value": "{{merchant_id}}"
}
],
"body": {

View File

@ -1348,115 +1348,6 @@
}
]
},
{
"name": "API Key - Create",
"event": [
{
"listen": "test",
"script": {
"exec": [
"// Validate status 2xx",
"pm.test(\"[POST]::/api_keys/:merchant_id - Status code is 2xx\", function () {",
" pm.response.to.be.success;",
"});",
"",
"// Validate if response header has matching content-type",
"pm.test(",
" \"[POST]::/api_keys/:merchant_id - Content-Type is application/json\",",
" function () {",
" pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(",
" \"application/json\",",
" );",
" },",
");",
"",
"// Set response object as internal variable",
"let jsonData = {};",
"try {",
" jsonData = pm.response.json();",
"} catch (e) {}",
"",
"// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id",
"if (jsonData?.key_id) {",
" pm.collectionVariables.set(\"api_key_id\", jsonData.key_id);",
" console.log(",
" \"- use {{api_key_id}} as collection variable for value\",",
" jsonData.key_id,",
" );",
"} else {",
" console.log(",
" \"INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.\",",
" );",
"}",
"",
"// pm.collectionVariables - Set api_key as variable for jsonData.api_key",
"if (jsonData?.api_key) {",
" pm.collectionVariables.set(\"api_key\", jsonData.api_key);",
" console.log(",
" \"- use {{api_key}} as collection variable for value\",",
" jsonData.api_key,",
" );",
"} else {",
" console.log(",
" \"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.\",",
" );",
"}",
""
],
"type": "text/javascript"
}
}
],
"request": {
"auth": {
"type": "apikey",
"apikey": [
{
"key": "value",
"value": "{{admin_api_key}}",
"type": "string"
},
{
"key": "key",
"value": "api-key",
"type": "string"
}
]
},
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Accept",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\"name\":\"API Key 1\",\"description\":null,\"expiration\":\"2069-09-23T01:02:03.000Z\"}"
},
"url": {
"raw": "{{baseUrl}}/api_keys/:merchant_id",
"host": [
"{{baseUrl}}"
],
"path": [
"api_keys",
":merchant_id"
],
"variable": [
{
"key": "merchant_id",
"value": "{{merchant_id}}"
}
]
}
},
"response": []
},
{
"name": "Payment Connector - Create",
"event": [
@ -1637,7 +1528,7 @@
"apikey": [
{
"key": "value",
"value": "{{api_key}}",
"value": "{{admin_api_key}}",
"type": "string"
},
{
@ -1657,6 +1548,10 @@
{
"key": "Accept",
"value": "application/json"
},
{
"key": "x-merchant-id",
"value": "{{merchant_id}}"
}
],
"url": {
@ -1752,7 +1647,7 @@
"apikey": [
{
"key": "value",
"value": "{{api_key}}",
"value": "{{admin_api_key}}",
"type": "string"
},
{
@ -1776,6 +1671,10 @@
{
"key": "Accept",
"value": "application/json"
},
{
"key": "x-merchant-id",
"value": "{{merchant_id}}"
}
],
"body": {
@ -1849,7 +1748,7 @@
"apikey": [
{
"key": "value",
"value": "{{api_key}}",
"value": "{{admin_api_key}}",
"type": "string"
},
{
@ -1864,6 +1763,10 @@
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "x-merchant-id",
"value": "{{merchant_id}}"
}
],
"url": {
@ -2684,7 +2587,7 @@
"apikey": [
{
"key": "value",
"value": "{{api_key}}",
"value": "{{admin_api_key}}",
"type": "string"
},
{
@ -2708,6 +2611,10 @@
{
"key": "Accept",
"value": "application/json"
},
{
"key": "x-merchant-id",
"value": "{{merchant_id}}"
}
],
"body": {