mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
refactor(router): Introduce ApiKeyId id type (#6324)
This commit is contained in:
@ -1,5 +1,8 @@
|
||||
[default]
|
||||
check-filename = true
|
||||
extend-ignore-identifiers-re = [
|
||||
"UE_[0-9]{3,4}", # Unified error codes
|
||||
]
|
||||
|
||||
[default.extend-identifiers]
|
||||
ABD = "ABD" # Aberdeenshire, UK ISO 3166-2 code
|
||||
@ -38,7 +41,6 @@ ws2ipdef = "ws2ipdef" # WinSock Extension
|
||||
ws2tcpip = "ws2tcpip" # WinSock Extension
|
||||
ZAR = "ZAR" # South African Rand currency code
|
||||
JOD = "JOD" # Jordan currency code
|
||||
UE_000 = "UE_000" #default unified error code
|
||||
|
||||
|
||||
[default.extend-words]
|
||||
|
||||
@ -29,8 +29,8 @@ pub struct CreateApiKeyRequest {
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct CreateApiKeyResponse {
|
||||
/// The identifier for the API Key.
|
||||
#[schema(max_length = 64, example = "5hEEqkgJUyuxgSKGArHA4mWSnX")]
|
||||
pub key_id: String,
|
||||
#[schema(max_length = 64, example = "5hEEqkgJUyuxgSKGArHA4mWSnX", value_type = String)]
|
||||
pub key_id: common_utils::id_type::ApiKeyId,
|
||||
|
||||
/// The identifier for the Merchant Account.
|
||||
#[schema(max_length = 64, example = "y3oqhf46pyzuxjbcn2giaqnb44", value_type = String)]
|
||||
@ -72,8 +72,8 @@ pub struct CreateApiKeyResponse {
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct RetrieveApiKeyResponse {
|
||||
/// The identifier for the API Key.
|
||||
#[schema(max_length = 64, example = "5hEEqkgJUyuxgSKGArHA4mWSnX")]
|
||||
pub key_id: String,
|
||||
#[schema(max_length = 64, example = "5hEEqkgJUyuxgSKGArHA4mWSnX", value_type = String)]
|
||||
pub key_id: common_utils::id_type::ApiKeyId,
|
||||
|
||||
/// The identifier for the Merchant Account.
|
||||
#[schema(max_length = 64, example = "y3oqhf46pyzuxjbcn2giaqnb44", value_type = String)]
|
||||
@ -131,7 +131,8 @@ pub struct UpdateApiKeyRequest {
|
||||
pub expiration: Option<ApiKeyExpiration>,
|
||||
|
||||
#[serde(skip_deserializing)]
|
||||
pub key_id: String,
|
||||
#[schema(value_type = String)]
|
||||
pub key_id: common_utils::id_type::ApiKeyId,
|
||||
|
||||
#[serde(skip_deserializing)]
|
||||
#[schema(value_type = String)]
|
||||
@ -146,8 +147,8 @@ pub struct RevokeApiKeyResponse {
|
||||
pub merchant_id: common_utils::id_type::MerchantId,
|
||||
|
||||
/// The identifier for the API Key.
|
||||
#[schema(max_length = 64, example = "5hEEqkgJUyuxgSKGArHA4mWSnX")]
|
||||
pub key_id: String,
|
||||
#[schema(max_length = 64, example = "5hEEqkgJUyuxgSKGArHA4mWSnX", value_type = String)]
|
||||
pub key_id: common_utils::id_type::ApiKeyId,
|
||||
/// Indicates whether the API key was revoked or not.
|
||||
#[schema(example = "true")]
|
||||
pub revoked: bool,
|
||||
|
||||
@ -45,6 +45,9 @@ pub enum ApiEventsType {
|
||||
BusinessProfile {
|
||||
profile_id: id_type::ProfileId,
|
||||
},
|
||||
ApiKey {
|
||||
key_id: id_type::ApiKeyId,
|
||||
},
|
||||
User {
|
||||
user_id: String,
|
||||
},
|
||||
@ -130,10 +133,6 @@ impl_api_event_type!(
|
||||
(
|
||||
String,
|
||||
id_type::MerchantId,
|
||||
(id_type::MerchantId, String),
|
||||
(id_type::MerchantId, &String),
|
||||
(&id_type::MerchantId, &String),
|
||||
(&String, &String),
|
||||
(Option<i64>, Option<i64>, String),
|
||||
(Option<i64>, Option<i64>, id_type::MerchantId),
|
||||
bool
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
use std::{borrow::Cow, fmt::Debug};
|
||||
|
||||
mod api_key;
|
||||
mod customer;
|
||||
mod merchant;
|
||||
mod merchant_connector_account;
|
||||
@ -14,6 +15,7 @@ mod routing;
|
||||
#[cfg(feature = "v2")]
|
||||
mod global_id;
|
||||
|
||||
pub use api_key::ApiKeyId;
|
||||
pub use customer::CustomerId;
|
||||
use diesel::{
|
||||
backend::Backend,
|
||||
|
||||
46
crates/common_utils/src/id_type/api_key.rs
Normal file
46
crates/common_utils/src/id_type/api_key.rs
Normal file
@ -0,0 +1,46 @@
|
||||
crate::id_type!(
|
||||
ApiKeyId,
|
||||
"A type for key_id that can be used for API key IDs"
|
||||
);
|
||||
crate::impl_id_type_methods!(ApiKeyId, "key_id");
|
||||
|
||||
// This is to display the `ApiKeyId` as ApiKeyId(abcd)
|
||||
crate::impl_debug_id_type!(ApiKeyId);
|
||||
crate::impl_try_from_cow_str_id_type!(ApiKeyId, "key_id");
|
||||
|
||||
crate::impl_serializable_secret_id_type!(ApiKeyId);
|
||||
crate::impl_queryable_id_type!(ApiKeyId);
|
||||
crate::impl_to_sql_from_sql_id_type!(ApiKeyId);
|
||||
|
||||
impl ApiKeyId {
|
||||
/// Generate Api Key Id from prefix
|
||||
pub fn generate_key_id(prefix: &'static str) -> Self {
|
||||
Self(crate::generate_ref_id_with_default_length(prefix))
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::events::ApiEventMetric for ApiKeyId {
|
||||
fn get_api_event_type(&self) -> Option<crate::events::ApiEventsType> {
|
||||
Some(crate::events::ApiEventsType::ApiKey {
|
||||
key_id: self.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::events::ApiEventMetric for (super::MerchantId, ApiKeyId) {
|
||||
fn get_api_event_type(&self) -> Option<crate::events::ApiEventsType> {
|
||||
Some(crate::events::ApiEventsType::ApiKey {
|
||||
key_id: self.1.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::events::ApiEventMetric for (&super::MerchantId, &ApiKeyId) {
|
||||
fn get_api_event_type(&self) -> Option<crate::events::ApiEventsType> {
|
||||
Some(crate::events::ApiEventsType::ApiKey {
|
||||
key_id: self.1.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
crate::impl_default_id_type!(ApiKeyId, "key");
|
||||
@ -9,7 +9,7 @@ use crate::schema::api_keys;
|
||||
)]
|
||||
#[diesel(table_name = api_keys, primary_key(key_id), check_for_backend(diesel::pg::Pg))]
|
||||
pub struct ApiKey {
|
||||
pub key_id: String,
|
||||
pub key_id: common_utils::id_type::ApiKeyId,
|
||||
pub merchant_id: common_utils::id_type::MerchantId,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
@ -23,7 +23,7 @@ pub struct ApiKey {
|
||||
#[derive(Debug, Insertable)]
|
||||
#[diesel(table_name = api_keys)]
|
||||
pub struct ApiKeyNew {
|
||||
pub key_id: String,
|
||||
pub key_id: common_utils::id_type::ApiKeyId,
|
||||
pub merchant_id: common_utils::id_type::MerchantId,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
@ -141,7 +141,7 @@ mod diesel_impl {
|
||||
// Tracking data by process_tracker
|
||||
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct ApiKeyExpiryTrackingData {
|
||||
pub key_id: String,
|
||||
pub key_id: common_utils::id_type::ApiKeyId,
|
||||
pub merchant_id: common_utils::id_type::MerchantId,
|
||||
pub api_key_name: String,
|
||||
pub prefix: String,
|
||||
|
||||
@ -18,7 +18,7 @@ impl ApiKey {
|
||||
pub async fn update_by_merchant_id_key_id(
|
||||
conn: &PgPooledConn,
|
||||
merchant_id: common_utils::id_type::MerchantId,
|
||||
key_id: String,
|
||||
key_id: common_utils::id_type::ApiKeyId,
|
||||
api_key_update: ApiKeyUpdate,
|
||||
) -> StorageResult<Self> {
|
||||
match generics::generic_update_with_unique_predicate_get_result::<
|
||||
@ -57,7 +57,7 @@ impl ApiKey {
|
||||
pub async fn revoke_by_merchant_id_key_id(
|
||||
conn: &PgPooledConn,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: &common_utils::id_type::ApiKeyId,
|
||||
) -> StorageResult<bool> {
|
||||
generics::generic_delete::<<Self as HasTable>::Table, _>(
|
||||
conn,
|
||||
@ -71,7 +71,7 @@ impl ApiKey {
|
||||
pub async fn find_optional_by_merchant_id_key_id(
|
||||
conn: &PgPooledConn,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: &common_utils::id_type::ApiKeyId,
|
||||
) -> StorageResult<Option<Self>> {
|
||||
generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
|
||||
conn,
|
||||
|
||||
@ -13,7 +13,6 @@ use crate::{
|
||||
routes::{metrics, SessionState},
|
||||
services::{authentication, ApplicationResponse},
|
||||
types::{api, storage, transformers::ForeignInto},
|
||||
utils,
|
||||
};
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
@ -63,9 +62,9 @@ impl PlaintextApiKey {
|
||||
Self(format!("{env}_{key}").into())
|
||||
}
|
||||
|
||||
pub fn new_key_id() -> String {
|
||||
pub fn new_key_id() -> common_utils::id_type::ApiKeyId {
|
||||
let env = router_env::env::prefix_for_env();
|
||||
utils::generate_id(consts::ID_LENGTH, env)
|
||||
common_utils::id_type::ApiKeyId::generate_key_id(env)
|
||||
}
|
||||
|
||||
pub fn prefix(&self) -> String {
|
||||
@ -223,7 +222,7 @@ pub async fn add_api_key_expiry_task(
|
||||
expiry_reminder_days: expiry_reminder_days.clone(),
|
||||
};
|
||||
|
||||
let process_tracker_id = generate_task_id_for_api_key_expiry_workflow(api_key.key_id.as_str());
|
||||
let process_tracker_id = generate_task_id_for_api_key_expiry_workflow(&api_key.key_id);
|
||||
let process_tracker_entry = storage::ProcessTrackerNew::new(
|
||||
process_tracker_id,
|
||||
API_KEY_EXPIRY_NAME,
|
||||
@ -241,7 +240,7 @@ pub async fn add_api_key_expiry_task(
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable_lazy(|| {
|
||||
format!(
|
||||
"Failed while inserting API key expiry reminder to process_tracker: api_key_id: {}",
|
||||
"Failed while inserting API key expiry reminder to process_tracker: {:?}",
|
||||
api_key.key_id
|
||||
)
|
||||
})?;
|
||||
@ -258,11 +257,11 @@ pub async fn add_api_key_expiry_task(
|
||||
pub async fn retrieve_api_key(
|
||||
state: SessionState,
|
||||
merchant_id: common_utils::id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: common_utils::id_type::ApiKeyId,
|
||||
) -> RouterResponse<api::RetrieveApiKeyResponse> {
|
||||
let store = state.store.as_ref();
|
||||
let api_key = store
|
||||
.find_api_key_by_merchant_id_key_id_optional(&merchant_id, key_id)
|
||||
.find_api_key_by_merchant_id_key_id_optional(&merchant_id, &key_id)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError) // If retrieve failed
|
||||
.attach_printable("Failed to retrieve API key")?
|
||||
@ -388,7 +387,7 @@ pub async fn update_api_key_expiry_task(
|
||||
}
|
||||
}
|
||||
|
||||
let task_id = generate_task_id_for_api_key_expiry_workflow(api_key.key_id.as_str());
|
||||
let task_id = generate_task_id_for_api_key_expiry_workflow(&api_key.key_id);
|
||||
|
||||
let task_ids = vec![task_id.clone()];
|
||||
|
||||
@ -430,7 +429,7 @@ pub async fn update_api_key_expiry_task(
|
||||
pub async fn revoke_api_key(
|
||||
state: SessionState,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: &common_utils::id_type::ApiKeyId,
|
||||
) -> RouterResponse<api::RevokeApiKeyResponse> {
|
||||
let store = state.store.as_ref();
|
||||
|
||||
@ -496,7 +495,7 @@ pub async fn revoke_api_key(
|
||||
#[instrument(skip_all)]
|
||||
pub async fn revoke_api_key_expiry_task(
|
||||
store: &dyn crate::db::StorageInterface,
|
||||
key_id: &str,
|
||||
key_id: &common_utils::id_type::ApiKeyId,
|
||||
) -> Result<(), errors::ProcessTrackerError> {
|
||||
let task_id = generate_task_id_for_api_key_expiry_workflow(key_id);
|
||||
let task_ids = vec![task_id];
|
||||
@ -535,8 +534,13 @@ pub async fn list_api_keys(
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
fn generate_task_id_for_api_key_expiry_workflow(key_id: &str) -> String {
|
||||
format!("{API_KEY_EXPIRY_RUNNER}_{API_KEY_EXPIRY_NAME}_{key_id}")
|
||||
fn generate_task_id_for_api_key_expiry_workflow(
|
||||
key_id: &common_utils::id_type::ApiKeyId,
|
||||
) -> String {
|
||||
format!(
|
||||
"{API_KEY_EXPIRY_RUNNER}_{API_KEY_EXPIRY_NAME}_{}",
|
||||
key_id.get_string_repr()
|
||||
)
|
||||
}
|
||||
|
||||
impl From<&str> for PlaintextApiKey {
|
||||
|
||||
@ -20,20 +20,20 @@ pub trait ApiKeyInterface {
|
||||
async fn update_api_key(
|
||||
&self,
|
||||
merchant_id: common_utils::id_type::MerchantId,
|
||||
key_id: String,
|
||||
key_id: common_utils::id_type::ApiKeyId,
|
||||
api_key: storage::ApiKeyUpdate,
|
||||
) -> CustomResult<storage::ApiKey, errors::StorageError>;
|
||||
|
||||
async fn revoke_api_key(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: &common_utils::id_type::ApiKeyId,
|
||||
) -> CustomResult<bool, errors::StorageError>;
|
||||
|
||||
async fn find_api_key_by_merchant_id_key_id_optional(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: &common_utils::id_type::ApiKeyId,
|
||||
) -> CustomResult<Option<storage::ApiKey>, errors::StorageError>;
|
||||
|
||||
async fn find_api_key_by_hash_optional(
|
||||
@ -67,7 +67,7 @@ impl ApiKeyInterface for Store {
|
||||
async fn update_api_key(
|
||||
&self,
|
||||
merchant_id: common_utils::id_type::MerchantId,
|
||||
key_id: String,
|
||||
key_id: common_utils::id_type::ApiKeyId,
|
||||
api_key: storage::ApiKeyUpdate,
|
||||
) -> CustomResult<storage::ApiKey, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
@ -99,7 +99,8 @@ impl ApiKeyInterface for Store {
|
||||
.await
|
||||
.map_err(|error| report!(errors::StorageError::from(error)))?
|
||||
.ok_or(report!(errors::StorageError::ValueNotFound(format!(
|
||||
"ApiKey of {_key_id} not found"
|
||||
"ApiKey of {} not found",
|
||||
_key_id.get_string_repr()
|
||||
))))?;
|
||||
|
||||
cache::publish_and_redact(
|
||||
@ -115,7 +116,7 @@ impl ApiKeyInterface for Store {
|
||||
async fn revoke_api_key(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: &common_utils::id_type::ApiKeyId,
|
||||
) -> CustomResult<bool, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
let delete_call = || async {
|
||||
@ -141,7 +142,8 @@ impl ApiKeyInterface for Store {
|
||||
.await
|
||||
.map_err(|error| report!(errors::StorageError::from(error)))?
|
||||
.ok_or(report!(errors::StorageError::ValueNotFound(format!(
|
||||
"ApiKey of {key_id} not found"
|
||||
"ApiKey of {} not found",
|
||||
key_id.get_string_repr()
|
||||
))))?;
|
||||
|
||||
cache::publish_and_redact(
|
||||
@ -157,7 +159,7 @@ impl ApiKeyInterface for Store {
|
||||
async fn find_api_key_by_merchant_id_key_id_optional(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: &common_utils::id_type::ApiKeyId,
|
||||
) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
|
||||
let conn = connection::pg_connection_read(self).await?;
|
||||
storage::ApiKey::find_optional_by_merchant_id_key_id(&conn, merchant_id, key_id)
|
||||
@ -240,7 +242,7 @@ impl ApiKeyInterface for MockDb {
|
||||
async fn update_api_key(
|
||||
&self,
|
||||
merchant_id: common_utils::id_type::MerchantId,
|
||||
key_id: String,
|
||||
key_id: common_utils::id_type::ApiKeyId,
|
||||
api_key: storage::ApiKeyUpdate,
|
||||
) -> CustomResult<storage::ApiKey, errors::StorageError> {
|
||||
let mut locked_api_keys = self.api_keys.lock().await;
|
||||
@ -282,13 +284,13 @@ impl ApiKeyInterface for MockDb {
|
||||
async fn revoke_api_key(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: &common_utils::id_type::ApiKeyId,
|
||||
) -> CustomResult<bool, errors::StorageError> {
|
||||
let mut locked_api_keys = self.api_keys.lock().await;
|
||||
// find the key to remove, if it exists
|
||||
if let Some(pos) = locked_api_keys
|
||||
.iter()
|
||||
.position(|k| k.merchant_id == *merchant_id && k.key_id == key_id)
|
||||
.position(|k| k.merchant_id == *merchant_id && k.key_id == *key_id)
|
||||
{
|
||||
// use `remove` instead of `swap_remove` so we have a consistent order, which might
|
||||
// matter to someone using limit/offset in `list_api_keys_by_merchant_id`
|
||||
@ -302,14 +304,14 @@ impl ApiKeyInterface for MockDb {
|
||||
async fn find_api_key_by_merchant_id_key_id_optional(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: &common_utils::id_type::ApiKeyId,
|
||||
) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
|
||||
Ok(self
|
||||
.api_keys
|
||||
.lock()
|
||||
.await
|
||||
.iter()
|
||||
.find(|k| k.merchant_id == *merchant_id && k.key_id == key_id)
|
||||
.find(|k| k.merchant_id == *merchant_id && k.key_id == *key_id)
|
||||
.cloned())
|
||||
}
|
||||
|
||||
@ -397,9 +399,16 @@ mod tests {
|
||||
let merchant_id =
|
||||
common_utils::id_type::MerchantId::try_from(Cow::from("merchant1")).unwrap();
|
||||
|
||||
let key_id1 = common_utils::id_type::ApiKeyId::try_from(Cow::from("key_id1")).unwrap();
|
||||
|
||||
let key_id2 = common_utils::id_type::ApiKeyId::try_from(Cow::from("key_id2")).unwrap();
|
||||
|
||||
let non_existent_key_id =
|
||||
common_utils::id_type::ApiKeyId::try_from(Cow::from("does_not_exist")).unwrap();
|
||||
|
||||
let key1 = mockdb
|
||||
.insert_api_key(storage::ApiKeyNew {
|
||||
key_id: "key_id1".into(),
|
||||
key_id: key_id1.clone(),
|
||||
merchant_id: merchant_id.clone(),
|
||||
name: "Key 1".into(),
|
||||
description: None,
|
||||
@ -414,7 +423,7 @@ mod tests {
|
||||
|
||||
mockdb
|
||||
.insert_api_key(storage::ApiKeyNew {
|
||||
key_id: "key_id2".into(),
|
||||
key_id: key_id2.clone(),
|
||||
merchant_id: merchant_id.clone(),
|
||||
name: "Key 2".into(),
|
||||
description: None,
|
||||
@ -428,13 +437,13 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let found_key1 = mockdb
|
||||
.find_api_key_by_merchant_id_key_id_optional(&merchant_id, "key_id1")
|
||||
.find_api_key_by_merchant_id_key_id_optional(&merchant_id, &key_id1)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(found_key1.key_id, key1.key_id);
|
||||
assert!(mockdb
|
||||
.find_api_key_by_merchant_id_key_id_optional(&merchant_id, "does_not_exist")
|
||||
.find_api_key_by_merchant_id_key_id_optional(&merchant_id, &non_existent_key_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.is_none());
|
||||
@ -442,7 +451,7 @@ mod tests {
|
||||
mockdb
|
||||
.update_api_key(
|
||||
merchant_id.clone(),
|
||||
"key_id1".into(),
|
||||
key_id1.clone(),
|
||||
storage::ApiKeyUpdate::LastUsedUpdate {
|
||||
last_used: datetime!(2023-02-04 1:11),
|
||||
},
|
||||
@ -450,7 +459,7 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
let updated_key1 = mockdb
|
||||
.find_api_key_by_merchant_id_key_id_optional(&merchant_id, "key_id1")
|
||||
.find_api_key_by_merchant_id_key_id_optional(&merchant_id, &key_id1)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
@ -464,10 +473,7 @@ mod tests {
|
||||
.len(),
|
||||
2
|
||||
);
|
||||
mockdb
|
||||
.revoke_api_key(&merchant_id, "key_id1")
|
||||
.await
|
||||
.unwrap();
|
||||
mockdb.revoke_api_key(&merchant_id, &key_id1).await.unwrap();
|
||||
assert_eq!(
|
||||
mockdb
|
||||
.list_api_keys_by_merchant_id(&merchant_id, None, None)
|
||||
@ -495,8 +501,10 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let test_key = common_utils::id_type::ApiKeyId::try_from(Cow::from("test_ey")).unwrap();
|
||||
|
||||
let api = storage::ApiKeyNew {
|
||||
key_id: "test_key".into(),
|
||||
key_id: test_key.clone(),
|
||||
merchant_id: merchant_id.clone(),
|
||||
name: "My test key".into(),
|
||||
description: None,
|
||||
|
||||
@ -226,7 +226,7 @@ impl ApiKeyInterface for KafkaStore {
|
||||
async fn update_api_key(
|
||||
&self,
|
||||
merchant_id: id_type::MerchantId,
|
||||
key_id: String,
|
||||
key_id: id_type::ApiKeyId,
|
||||
api_key: storage::ApiKeyUpdate,
|
||||
) -> CustomResult<storage::ApiKey, errors::StorageError> {
|
||||
self.diesel_store
|
||||
@ -237,7 +237,7 @@ impl ApiKeyInterface for KafkaStore {
|
||||
async fn revoke_api_key(
|
||||
&self,
|
||||
merchant_id: &id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: &id_type::ApiKeyId,
|
||||
) -> CustomResult<bool, errors::StorageError> {
|
||||
self.diesel_store.revoke_api_key(merchant_id, key_id).await
|
||||
}
|
||||
@ -245,7 +245,7 @@ impl ApiKeyInterface for KafkaStore {
|
||||
async fn find_api_key_by_merchant_id_key_id_optional(
|
||||
&self,
|
||||
merchant_id: &id_type::MerchantId,
|
||||
key_id: &str,
|
||||
key_id: &id_type::ApiKeyId,
|
||||
) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
|
||||
self.diesel_store
|
||||
.find_api_key_by_merchant_id_key_id_optional(merchant_id, key_id)
|
||||
|
||||
@ -79,7 +79,7 @@ pub async fn api_key_create(
|
||||
pub async fn api_key_retrieve(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<String>,
|
||||
path: web::Path<common_utils::id_type::ApiKeyId>,
|
||||
) -> impl Responder {
|
||||
let flow = Flow::ApiKeyRetrieve;
|
||||
let key_id = path.into_inner();
|
||||
@ -93,7 +93,7 @@ pub async fn api_key_retrieve(
|
||||
api_keys::retrieve_api_key(
|
||||
state,
|
||||
auth_data.merchant_account.get_id().to_owned(),
|
||||
key_id,
|
||||
key_id.to_owned(),
|
||||
)
|
||||
},
|
||||
auth::auth_type(
|
||||
@ -114,7 +114,10 @@ pub async fn api_key_retrieve(
|
||||
pub async fn api_key_retrieve(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<(common_utils::id_type::MerchantId, String)>,
|
||||
path: web::Path<(
|
||||
common_utils::id_type::MerchantId,
|
||||
common_utils::id_type::ApiKeyId,
|
||||
)>,
|
||||
) -> impl Responder {
|
||||
let flow = Flow::ApiKeyRetrieve;
|
||||
let (merchant_id, key_id) = path.into_inner();
|
||||
@ -123,7 +126,7 @@ pub async fn api_key_retrieve(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
(merchant_id.clone(), &key_id),
|
||||
(merchant_id.clone(), key_id.clone()),
|
||||
|state, _, (merchant_id, key_id), _| api_keys::retrieve_api_key(state, merchant_id, key_id),
|
||||
auth::auth_type(
|
||||
&auth::AdminApiAuth,
|
||||
@ -144,7 +147,10 @@ pub async fn api_key_retrieve(
|
||||
pub async fn api_key_update(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<(common_utils::id_type::MerchantId, String)>,
|
||||
path: web::Path<(
|
||||
common_utils::id_type::MerchantId,
|
||||
common_utils::id_type::ApiKeyId,
|
||||
)>,
|
||||
json_payload: web::Json<api_types::UpdateApiKeyRequest>,
|
||||
) -> impl Responder {
|
||||
let flow = Flow::ApiKeyUpdate;
|
||||
@ -177,7 +183,7 @@ pub async fn api_key_update(
|
||||
pub async fn api_key_update(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
key_id: web::Path<String>,
|
||||
key_id: web::Path<common_utils::id_type::ApiKeyId>,
|
||||
json_payload: web::Json<api_types::UpdateApiKeyRequest>,
|
||||
) -> impl Responder {
|
||||
let flow = Flow::ApiKeyUpdate;
|
||||
@ -212,7 +218,10 @@ pub async fn api_key_update(
|
||||
pub async fn api_key_revoke(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<(common_utils::id_type::MerchantId, String)>,
|
||||
path: web::Path<(
|
||||
common_utils::id_type::MerchantId,
|
||||
common_utils::id_type::ApiKeyId,
|
||||
)>,
|
||||
) -> impl Responder {
|
||||
let flow = Flow::ApiKeyRevoke;
|
||||
let (merchant_id, key_id) = path.into_inner();
|
||||
@ -242,7 +251,10 @@ pub async fn api_key_revoke(
|
||||
pub async fn api_key_revoke(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<(common_utils::id_type::MerchantId, String)>,
|
||||
path: web::Path<(
|
||||
common_utils::id_type::MerchantId,
|
||||
common_utils::id_type::ApiKeyId,
|
||||
)>,
|
||||
) -> impl Responder {
|
||||
let flow = Flow::ApiKeyRevoke;
|
||||
let (merchant_id, key_id) = path.into_inner();
|
||||
|
||||
@ -89,7 +89,7 @@ pub struct AuthenticationDataWithUser {
|
||||
pub enum AuthenticationType {
|
||||
ApiKey {
|
||||
merchant_id: id_type::MerchantId,
|
||||
key_id: String,
|
||||
key_id: id_type::ApiKeyId,
|
||||
},
|
||||
AdminApiKey,
|
||||
AdminApiAuthWithMerchantId {
|
||||
|
||||
@ -67,7 +67,7 @@ pub enum Identifiers {
|
||||
/// [`ApiKey`] is an authentication method that uses an API key. This is used with [`ApiKey`]
|
||||
ApiKey {
|
||||
merchant_id: common_utils::id_type::MerchantId,
|
||||
key_id: String,
|
||||
key_id: common_utils::id_type::ApiKeyId,
|
||||
},
|
||||
/// [`PublishableKey`] is an authentication method that uses a publishable key. This is used with [`PublishableKey`]
|
||||
PublishableKey { merchant_id: String },
|
||||
@ -80,7 +80,7 @@ pub async fn add_api_key(
|
||||
state: &SessionState,
|
||||
api_key: Secret<String>,
|
||||
merchant_id: common_utils::id_type::MerchantId,
|
||||
key_id: String,
|
||||
key_id: common_utils::id_type::ApiKeyId,
|
||||
expiry: Option<u64>,
|
||||
) -> CustomResult<(), ApiClientError> {
|
||||
let decision_config = if let Some(config) = &state.conf.decision {
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
use std::{borrow::Cow, string::ToString};
|
||||
|
||||
use actix_web::http::header::HeaderMap;
|
||||
use common_utils::{crypto::VerifySignature, id_type::MerchantId};
|
||||
use common_utils::{
|
||||
crypto::VerifySignature,
|
||||
id_type::{ApiKeyId, MerchantId},
|
||||
};
|
||||
use error_stack::ResultExt;
|
||||
use hyperswitch_domain_models::errors::api_error_response::ApiErrorResponse;
|
||||
|
||||
@ -16,7 +19,7 @@ const HEADER_CHECKSUM: &str = "x-checksum";
|
||||
pub struct ExtractedPayload {
|
||||
pub payload_type: PayloadType,
|
||||
pub merchant_id: Option<MerchantId>,
|
||||
pub key_id: Option<String>,
|
||||
pub key_id: Option<ApiKeyId>,
|
||||
}
|
||||
|
||||
#[derive(strum::EnumString, strum::Display, PartialEq, Debug)]
|
||||
@ -61,13 +64,19 @@ impl ExtractedPayload {
|
||||
message: format!("`{}` header not present", HEADER_AUTH_TYPE),
|
||||
})?;
|
||||
|
||||
let key_id = headers
|
||||
.get(HEADER_KEY_ID)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(|key_id| ApiKeyId::try_from(Cow::from(key_id.to_string())))
|
||||
.transpose()
|
||||
.change_context(ApiErrorResponse::InvalidRequestData {
|
||||
message: format!("`{}` header is invalid or not present", HEADER_KEY_ID),
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
payload_type: auth_type,
|
||||
merchant_id: Some(merchant_id),
|
||||
key_id: headers
|
||||
.get(HEADER_KEY_ID)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|v| v.to_string()),
|
||||
key_id,
|
||||
})
|
||||
}
|
||||
|
||||
@ -95,7 +104,7 @@ impl ExtractedPayload {
|
||||
&self
|
||||
.merchant_id
|
||||
.as_ref()
|
||||
.map(|inner| append_option(inner.get_string_repr(), &self.key_id)),
|
||||
.map(|inner| append_api_key(inner.get_string_repr(), &self.key_id)),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -107,3 +116,11 @@ fn append_option(prefix: &str, data: &Option<String>) -> String {
|
||||
None => prefix.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn append_api_key(prefix: &str, data: &Option<ApiKeyId>) -> String {
|
||||
match data {
|
||||
Some(inner) => format!("{}:{}", prefix, inner.get_string_repr()),
|
||||
None => prefix.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user