diff --git a/.typos.toml b/.typos.toml index 79c86a39c6..d2ffb8a5b1 100644 --- a/.typos.toml +++ b/.typos.toml @@ -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] diff --git a/crates/api_models/src/api_keys.rs b/crates/api_models/src/api_keys.rs index 65cc6b9a25..d25cd989b0 100644 --- a/crates/api_models/src/api_keys.rs +++ b/crates/api_models/src/api_keys.rs @@ -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, #[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, diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs index 1b03f8f19f..e30aa70897 100644 --- a/crates/common_utils/src/events.rs +++ b/crates/common_utils/src/events.rs @@ -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, Option, String), (Option, Option, id_type::MerchantId), bool diff --git a/crates/common_utils/src/id_type.rs b/crates/common_utils/src/id_type.rs index 78e3841690..c256559fc1 100644 --- a/crates/common_utils/src/id_type.rs +++ b/crates/common_utils/src/id_type.rs @@ -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, diff --git a/crates/common_utils/src/id_type/api_key.rs b/crates/common_utils/src/id_type/api_key.rs new file mode 100644 index 0000000000..f252846e6a --- /dev/null +++ b/crates/common_utils/src/id_type/api_key.rs @@ -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 { + Some(crate::events::ApiEventsType::ApiKey { + key_id: self.clone(), + }) + } +} + +impl crate::events::ApiEventMetric for (super::MerchantId, ApiKeyId) { + fn get_api_event_type(&self) -> Option { + 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 { + Some(crate::events::ApiEventsType::ApiKey { + key_id: self.1.clone(), + }) + } +} + +crate::impl_default_id_type!(ApiKeyId, "key"); diff --git a/crates/diesel_models/src/api_keys.rs b/crates/diesel_models/src/api_keys.rs index 1781e65cde..7076bf597e 100644 --- a/crates/diesel_models/src/api_keys.rs +++ b/crates/diesel_models/src/api_keys.rs @@ -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, @@ -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, @@ -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, diff --git a/crates/diesel_models/src/query/api_keys.rs b/crates/diesel_models/src/query/api_keys.rs index 5dd1450102..479e226c1d 100644 --- a/crates/diesel_models/src/query/api_keys.rs +++ b/crates/diesel_models/src/query/api_keys.rs @@ -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 { 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 { generics::generic_delete::<::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> { generics::generic_find_one_optional::<::Table, _, _>( conn, diff --git a/crates/router/src/core/api_keys.rs b/crates/router/src/core/api_keys.rs index 6e907dbd91..de4a8931c7 100644 --- a/crates/router/src/core/api_keys.rs +++ b/crates/router/src/core/api_keys.rs @@ -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 { 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 { 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 { diff --git a/crates/router/src/db/api_keys.rs b/crates/router/src/db/api_keys.rs index 62d261aa3c..0d3ec5dc8c 100644 --- a/crates/router/src/db/api_keys.rs +++ b/crates/router/src/db/api_keys.rs @@ -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; async fn revoke_api_key( &self, merchant_id: &common_utils::id_type::MerchantId, - key_id: &str, + key_id: &common_utils::id_type::ApiKeyId, ) -> CustomResult; 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, 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 { 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 { 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, 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 { 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 { 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, 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, diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index e92fe6b41b..3ef9a2d037 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -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 { 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 { 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, errors::StorageError> { self.diesel_store .find_api_key_by_merchant_id_key_id_optional(merchant_id, key_id) diff --git a/crates/router/src/routes/api_keys.rs b/crates/router/src/routes/api_keys.rs index 047e0d9b88..bbecaae9e8 100644 --- a/crates/router/src/routes/api_keys.rs +++ b/crates/router/src/routes/api_keys.rs @@ -79,7 +79,7 @@ pub async fn api_key_create( pub async fn api_key_retrieve( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, ) -> 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, 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, 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, ) -> 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, req: HttpRequest, - key_id: web::Path, + key_id: web::Path, json_payload: web::Json, ) -> 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, 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, 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(); diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index a731b7e6db..92e2177633 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -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 { diff --git a/crates/router/src/services/authentication/decision.rs b/crates/router/src/services/authentication/decision.rs index d665b0caaf..c31a4696bc 100644 --- a/crates/router/src/services/authentication/decision.rs +++ b/crates/router/src/services/authentication/decision.rs @@ -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, merchant_id: common_utils::id_type::MerchantId, - key_id: String, + key_id: common_utils::id_type::ApiKeyId, expiry: Option, ) -> CustomResult<(), ApiClientError> { let decision_config = if let Some(config) = &state.conf.decision { diff --git a/crates/router/src/services/authentication/detached.rs b/crates/router/src/services/authentication/detached.rs index af373d3e55..9d77d2b1f0 100644 --- a/crates/router/src/services/authentication/detached.rs +++ b/crates/router/src/services/authentication/detached.rs @@ -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, - pub key_id: Option, + pub key_id: Option, } #[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 { None => prefix.to_string(), } } + +#[inline] +fn append_api_key(prefix: &str, data: &Option) -> String { + match data { + Some(inner) => format!("{}:{}", prefix, inner.get_string_repr()), + None => prefix.to_string(), + } +}