mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +08:00 
			
		
		
		
	db: Added ephemeral key functions (#84)
This commit is contained in:
		| @ -37,6 +37,9 @@ temp_card_key = "OJobAzAwOlibOhygIZOqOGideGUdEBeX" # 32 character long key | ||||
|  | ||||
| [jwekey] | ||||
|  | ||||
| [eph_key] | ||||
| validity = 1 | ||||
|  | ||||
| [scheduler] | ||||
| stream = "SCHEDULER_STREAM" | ||||
| consumer_group = "SCHEDULER_GROUP" | ||||
|  | ||||
| @ -33,6 +33,7 @@ pub struct Settings { | ||||
|     pub keys: Keys, //remove this during refactoring | ||||
|     pub locker: Locker, | ||||
|     pub connectors: Connectors, | ||||
|     pub eph_key: EphemeralConfig, | ||||
|     pub scheduler: Option<SchedulerSettings>, | ||||
|     #[cfg(feature = "kv_store")] | ||||
|     pub drainer: DrainerSettings, | ||||
| @ -55,6 +56,11 @@ pub struct Locker { | ||||
|     pub basilisk_host: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize, Clone)] | ||||
| pub struct EphemeralConfig { | ||||
|     pub validity: i64, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize, Clone)] | ||||
| pub struct Jwekey { | ||||
|     #[cfg(feature = "kms")] | ||||
|  | ||||
| @ -2,6 +2,7 @@ pub mod address; | ||||
| pub mod configs; | ||||
| pub mod connector_response; | ||||
| pub mod customers; | ||||
| pub mod ephemeral_key; | ||||
| pub mod events; | ||||
| pub mod locker_mock_up; | ||||
| pub mod mandate; | ||||
| @ -54,6 +55,7 @@ pub trait StorageInterface: | ||||
|     + process_tracker::ProcessTrackerInterface | ||||
|     + refund::RefundInterface | ||||
|     + queue::QueueInterface | ||||
|     + ephemeral_key::EphemeralKeyInterface | ||||
|     + connector_response::ConnectorResponseInterface | ||||
|     + 'static | ||||
| { | ||||
|  | ||||
							
								
								
									
										147
									
								
								crates/router/src/db/ephemeral_key.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								crates/router/src/db/ephemeral_key.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,147 @@ | ||||
| use crate::{ | ||||
|     core::errors::{self, CustomResult}, | ||||
|     db::MockDb, | ||||
|     types::storage::ephemeral_key::{EphemeralKey, EphemeralKeyNew}, | ||||
| }; | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
| pub trait EphemeralKeyInterface { | ||||
|     async fn create_ephemeral_key( | ||||
|         &self, | ||||
|         _ek: EphemeralKeyNew, | ||||
|         _validity: i64, | ||||
|     ) -> CustomResult<EphemeralKey, errors::StorageError>; | ||||
|     async fn get_ephemeral_key( | ||||
|         &self, | ||||
|         _key: &str, | ||||
|     ) -> CustomResult<EphemeralKey, errors::StorageError>; | ||||
|     async fn delete_ephemeral_key( | ||||
|         &self, | ||||
|         _id: &str, | ||||
|     ) -> CustomResult<EphemeralKey, errors::StorageError>; | ||||
| } | ||||
|  | ||||
| mod storage { | ||||
|     use common_utils::{date_time, ext_traits::StringExt}; | ||||
|     use error_stack::{IntoReport, ResultExt}; | ||||
|     use redis_interface::RedisValue; | ||||
|     use time::ext::NumericalDuration; | ||||
|  | ||||
|     use super::EphemeralKeyInterface; | ||||
|     use crate::{ | ||||
|         core::errors::{self, CustomResult}, | ||||
|         services::Store, | ||||
|         types::storage::ephemeral_key::{EphemeralKey, EphemeralKeyNew}, | ||||
|         utils, | ||||
|     }; | ||||
|  | ||||
|     #[async_trait::async_trait] | ||||
|     impl EphemeralKeyInterface for Store { | ||||
|         async fn create_ephemeral_key( | ||||
|             &self, | ||||
|             new: EphemeralKeyNew, | ||||
|             validity: i64, | ||||
|         ) -> CustomResult<EphemeralKey, errors::StorageError> { | ||||
|             let secret_key = new.secret.to_string(); | ||||
|             let id_key = new.id.to_string(); | ||||
|  | ||||
|             let created_at = date_time::now(); | ||||
|             let expires = created_at.saturating_add(validity.hours()); | ||||
|             let created_ek = EphemeralKey { | ||||
|                 id: new.id, | ||||
|                 created_at, | ||||
|                 expires, | ||||
|                 customer_id: new.customer_id, | ||||
|                 merchant_id: new.merchant_id, | ||||
|                 secret: new.secret, | ||||
|             }; | ||||
|             let redis_value = &utils::Encode::<EphemeralKey>::encode_to_string_of_json(&created_ek) | ||||
|                 .change_context(errors::StorageError::KVError) | ||||
|                 .attach_printable("Unable to serialize ephemeral key")?; | ||||
|  | ||||
|             let redis_map: Vec<(&str, RedisValue)> = vec![ | ||||
|                 (&secret_key, redis_value.into()), | ||||
|                 (&id_key, redis_value.into()), | ||||
|             ]; | ||||
|             match self | ||||
|                 .redis_conn | ||||
|                 .msetnx::<Vec<(&str, RedisValue)>>(redis_map) | ||||
|                 .await | ||||
|             { | ||||
|                 Ok(1) => { | ||||
|                     let expire_at = expires.assume_utc().unix_timestamp(); | ||||
|                     self.redis_conn | ||||
|                         .set_expire_at(&secret_key, expire_at) | ||||
|                         .await | ||||
|                         .change_context(errors::StorageError::KVError)?; | ||||
|                     self.redis_conn | ||||
|                         .set_expire_at(&id_key, expire_at) | ||||
|                         .await | ||||
|                         .change_context(errors::StorageError::KVError)?; | ||||
|                     Ok(created_ek) | ||||
|                 } | ||||
|                 Ok(0) => { | ||||
|                     Err(errors::StorageError::DuplicateValue("ephimeral_key".to_string()).into()) | ||||
|                 } | ||||
|                 Ok(i) => Err(errors::StorageError::KVError) | ||||
|                     .into_report() | ||||
|                     .attach_printable_lazy(|| format!("Invalid response for HSETNX: {i}")), | ||||
|                 Err(er) => Err(er).change_context(errors::StorageError::KVError), | ||||
|             } | ||||
|         } | ||||
|         async fn get_ephemeral_key( | ||||
|             &self, | ||||
|             key: &str, | ||||
|         ) -> CustomResult<EphemeralKey, errors::StorageError> { | ||||
|             let value: String = self | ||||
|                 .redis_conn | ||||
|                 .get_key(key) | ||||
|                 .await | ||||
|                 .change_context(errors::StorageError::KVError)?; | ||||
|  | ||||
|             value | ||||
|                 .parse_struct("EphemeralKey") | ||||
|                 .change_context(errors::StorageError::KVError) | ||||
|         } | ||||
|         async fn delete_ephemeral_key( | ||||
|             &self, | ||||
|             id: &str, | ||||
|         ) -> CustomResult<EphemeralKey, errors::StorageError> { | ||||
|             let ek = self.get_ephemeral_key(id).await?; | ||||
|  | ||||
|             self.redis_conn | ||||
|                 .delete_key(&ek.id) | ||||
|                 .await | ||||
|                 .change_context(errors::StorageError::KVError)?; | ||||
|  | ||||
|             self.redis_conn | ||||
|                 .delete_key(&ek.secret) | ||||
|                 .await | ||||
|                 .change_context(errors::StorageError::KVError)?; | ||||
|             Ok(ek) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
| impl EphemeralKeyInterface for MockDb { | ||||
|     async fn create_ephemeral_key( | ||||
|         &self, | ||||
|         _ek: EphemeralKeyNew, | ||||
|         _validity: i64, | ||||
|     ) -> CustomResult<EphemeralKey, errors::StorageError> { | ||||
|         Err(errors::StorageError::KVError.into()) | ||||
|     } | ||||
|     async fn get_ephemeral_key( | ||||
|         &self, | ||||
|         _key: &str, | ||||
|     ) -> CustomResult<EphemeralKey, errors::StorageError> { | ||||
|         Err(errors::StorageError::KVError.into()) | ||||
|     } | ||||
|     async fn delete_ephemeral_key( | ||||
|         &self, | ||||
|         _id: &str, | ||||
|     ) -> CustomResult<EphemeralKey, errors::StorageError> { | ||||
|         Err(errors::StorageError::KVError.into()) | ||||
|     } | ||||
| } | ||||
| @ -1,3 +1,5 @@ | ||||
| use std::borrow::Cow; | ||||
|  | ||||
| use actix_web::{ | ||||
|     body::{BoxBody, MessageBody}, | ||||
|     web, HttpRequest, HttpResponse, Responder, | ||||
| @ -105,7 +107,7 @@ pub async fn payments_start( | ||||
|                 payments::CallConnectorAction::Trigger, | ||||
|             ) | ||||
|         }, | ||||
|         api::MerchantAuthentication::MerchantId(&merchant_id), | ||||
|         api::MerchantAuthentication::MerchantId(Cow::Borrowed(&merchant_id)), | ||||
|     ) | ||||
|     .await | ||||
| } | ||||
| @ -321,7 +323,7 @@ pub async fn payments_response( | ||||
|         |state, merchant_account, req| { | ||||
|             payments::handle_payments_redirect_response::<PSync>(state, merchant_account, req) | ||||
|         }, | ||||
|         api::MerchantAuthentication::MerchantId(&merchant_id), | ||||
|         api::MerchantAuthentication::MerchantId(Cow::Borrowed(&merchant_id)), | ||||
|     ) | ||||
|     .await | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| mod client; | ||||
| pub(crate) mod request; | ||||
|  | ||||
| use std::{collections::HashMap, fmt::Debug, future::Future, str, time::Instant}; | ||||
| use std::{borrow::Cow, collections::HashMap, fmt::Debug, future::Future, str, time::Instant}; | ||||
|  | ||||
| use actix_web::{body, HttpRequest, HttpResponse, Responder}; | ||||
| use bytes::Bytes; | ||||
| @ -360,7 +360,7 @@ pub enum ApiAuthentication<'a> { | ||||
| #[derive(Clone, Debug)] | ||||
| pub enum MerchantAuthentication<'a> { | ||||
|     ApiKey, | ||||
|     MerchantId(&'a str), | ||||
|     MerchantId(Cow<'a, str>), | ||||
|     AdminApiKey, | ||||
|     PublishableKey, | ||||
| } | ||||
| @ -521,7 +521,7 @@ pub async fn authenticate_merchant<'a>( | ||||
|  | ||||
|         MerchantAuthentication::MerchantId(merchant_id) => { | ||||
|             store | ||||
|                 .find_merchant_account_by_merchant_id(merchant_id) | ||||
|                 .find_merchant_account_by_merchant_id(&merchant_id) | ||||
|                 .await | ||||
|                 .map_err(|error| { | ||||
|                     // TODO: The BadCredentials error is too specific for api keys, and inappropriate for AdminApiKey/MerchantID | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Kartikeya Hegde
					Kartikeya Hegde