mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 20:23:43 +08:00
db: Added ephemeral key functions (#84)
This commit is contained in:
@ -36,6 +36,9 @@ locker_encryption_key2 = ""
|
|||||||
locker_decryption_key1 = ""
|
locker_decryption_key1 = ""
|
||||||
locker_decryption_key2 = ""
|
locker_decryption_key2 = ""
|
||||||
|
|
||||||
|
[eph_key]
|
||||||
|
validity = 1
|
||||||
|
|
||||||
[connectors.aci]
|
[connectors.aci]
|
||||||
base_url = "https://eu-test.oppwa.com/"
|
base_url = "https://eu-test.oppwa.com/"
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,11 @@ locker_encryption_key2 = "" # public key 2 in pem format, corresponding private
|
|||||||
locker_decryption_key1 = "" # private key 1 in pem format, corresponding public key in basilisk
|
locker_decryption_key1 = "" # private key 1 in pem format, corresponding public key in basilisk
|
||||||
locker_decryption_key2 = "" # private key 2 in pem format, corresponding public key in basilisk
|
locker_decryption_key2 = "" # private key 2 in pem format, corresponding public key in basilisk
|
||||||
|
|
||||||
|
|
||||||
|
# Validity of an Ephemeral Key in Hours
|
||||||
|
[eph_key]
|
||||||
|
validity = 1
|
||||||
|
|
||||||
# Connector configuration, provided attributes will be used to fulfill API requests.
|
# Connector configuration, provided attributes will be used to fulfill API requests.
|
||||||
# Examples provided here are sandbox/test base urls, can be replaced by live or mock
|
# Examples provided here are sandbox/test base urls, can be replaced by live or mock
|
||||||
# base urls based on your need.
|
# base urls based on your need.
|
||||||
|
|||||||
@ -13,7 +13,7 @@ use fred::{
|
|||||||
interfaces::{KeysInterface, StreamsInterface},
|
interfaces::{KeysInterface, StreamsInterface},
|
||||||
types::{
|
types::{
|
||||||
Expiration, FromRedis, MultipleIDs, MultipleKeys, MultipleOrderedPairs, MultipleStrings,
|
Expiration, FromRedis, MultipleIDs, MultipleKeys, MultipleOrderedPairs, MultipleStrings,
|
||||||
RedisValue, SetOptions, XReadResponse,
|
RedisMap, RedisValue, SetOptions, XReadResponse,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use router_env::{tracing, tracing::instrument};
|
use router_env::{tracing, tracing::instrument};
|
||||||
@ -43,6 +43,18 @@ impl super::RedisConnectionPool {
|
|||||||
.change_context(errors::RedisError::SetFailed)
|
.change_context(errors::RedisError::SetFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn msetnx<V>(&self, value: V) -> CustomResult<u8, errors::RedisError>
|
||||||
|
where
|
||||||
|
V: TryInto<RedisMap> + Debug,
|
||||||
|
V::Error: Into<fred::error::RedisError>,
|
||||||
|
{
|
||||||
|
self.pool
|
||||||
|
.msetnx::<u8, V>(value)
|
||||||
|
.await
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::RedisError::SetFailed)
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(level = "DEBUG", skip(self))]
|
#[instrument(level = "DEBUG", skip(self))]
|
||||||
pub async fn serialize_and_set_key<V>(
|
pub async fn serialize_and_set_key<V>(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ pub mod commands;
|
|||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
pub use fred::prelude::*;
|
||||||
use router_env::logger;
|
use router_env::logger;
|
||||||
|
|
||||||
pub use self::{commands::*, types::*};
|
pub use self::{commands::*, types::*};
|
||||||
|
|||||||
@ -37,6 +37,9 @@ temp_card_key = "OJobAzAwOlibOhygIZOqOGideGUdEBeX" # 32 character long key
|
|||||||
|
|
||||||
[jwekey]
|
[jwekey]
|
||||||
|
|
||||||
|
[eph_key]
|
||||||
|
validity = 1
|
||||||
|
|
||||||
[scheduler]
|
[scheduler]
|
||||||
stream = "SCHEDULER_STREAM"
|
stream = "SCHEDULER_STREAM"
|
||||||
consumer_group = "SCHEDULER_GROUP"
|
consumer_group = "SCHEDULER_GROUP"
|
||||||
|
|||||||
@ -33,6 +33,7 @@ pub struct Settings {
|
|||||||
pub keys: Keys, //remove this during refactoring
|
pub keys: Keys, //remove this during refactoring
|
||||||
pub locker: Locker,
|
pub locker: Locker,
|
||||||
pub connectors: Connectors,
|
pub connectors: Connectors,
|
||||||
|
pub eph_key: EphemeralConfig,
|
||||||
pub scheduler: Option<SchedulerSettings>,
|
pub scheduler: Option<SchedulerSettings>,
|
||||||
#[cfg(feature = "kv_store")]
|
#[cfg(feature = "kv_store")]
|
||||||
pub drainer: DrainerSettings,
|
pub drainer: DrainerSettings,
|
||||||
@ -55,6 +56,11 @@ pub struct Locker {
|
|||||||
pub basilisk_host: String,
|
pub basilisk_host: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct EphemeralConfig {
|
||||||
|
pub validity: i64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct Jwekey {
|
pub struct Jwekey {
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
|
|||||||
@ -2,6 +2,7 @@ pub mod address;
|
|||||||
pub mod configs;
|
pub mod configs;
|
||||||
pub mod connector_response;
|
pub mod connector_response;
|
||||||
pub mod customers;
|
pub mod customers;
|
||||||
|
pub mod ephemeral_key;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod locker_mock_up;
|
pub mod locker_mock_up;
|
||||||
pub mod mandate;
|
pub mod mandate;
|
||||||
@ -54,6 +55,7 @@ pub trait StorageInterface:
|
|||||||
+ process_tracker::ProcessTrackerInterface
|
+ process_tracker::ProcessTrackerInterface
|
||||||
+ refund::RefundInterface
|
+ refund::RefundInterface
|
||||||
+ queue::QueueInterface
|
+ queue::QueueInterface
|
||||||
|
+ ephemeral_key::EphemeralKeyInterface
|
||||||
+ connector_response::ConnectorResponseInterface
|
+ connector_response::ConnectorResponseInterface
|
||||||
+ 'static
|
+ '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::{
|
use actix_web::{
|
||||||
body::{BoxBody, MessageBody},
|
body::{BoxBody, MessageBody},
|
||||||
web, HttpRequest, HttpResponse, Responder,
|
web, HttpRequest, HttpResponse, Responder,
|
||||||
@ -105,7 +107,7 @@ pub async fn payments_start(
|
|||||||
payments::CallConnectorAction::Trigger,
|
payments::CallConnectorAction::Trigger,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
api::MerchantAuthentication::MerchantId(&merchant_id),
|
api::MerchantAuthentication::MerchantId(Cow::Borrowed(&merchant_id)),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -321,7 +323,7 @@ pub async fn payments_response(
|
|||||||
|state, merchant_account, req| {
|
|state, merchant_account, req| {
|
||||||
payments::handle_payments_redirect_response::<PSync>(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
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
mod client;
|
mod client;
|
||||||
pub(crate) mod request;
|
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 actix_web::{body, HttpRequest, HttpResponse, Responder};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
@ -360,7 +360,7 @@ pub enum ApiAuthentication<'a> {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum MerchantAuthentication<'a> {
|
pub enum MerchantAuthentication<'a> {
|
||||||
ApiKey,
|
ApiKey,
|
||||||
MerchantId(&'a str),
|
MerchantId(Cow<'a, str>),
|
||||||
AdminApiKey,
|
AdminApiKey,
|
||||||
PublishableKey,
|
PublishableKey,
|
||||||
}
|
}
|
||||||
@ -521,7 +521,7 @@ pub async fn authenticate_merchant<'a>(
|
|||||||
|
|
||||||
MerchantAuthentication::MerchantId(merchant_id) => {
|
MerchantAuthentication::MerchantId(merchant_id) => {
|
||||||
store
|
store
|
||||||
.find_merchant_account_by_merchant_id(merchant_id)
|
.find_merchant_account_by_merchant_id(&merchant_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|error| {
|
.map_err(|error| {
|
||||||
// TODO: The BadCredentials error is too specific for api keys, and inappropriate for AdminApiKey/MerchantID
|
// TODO: The BadCredentials error is too specific for api keys, and inappropriate for AdminApiKey/MerchantID
|
||||||
|
|||||||
@ -29,6 +29,9 @@ host = ""
|
|||||||
mock_locker = true
|
mock_locker = true
|
||||||
basilisk_host = ""
|
basilisk_host = ""
|
||||||
|
|
||||||
|
[eph_key]
|
||||||
|
validity = 1
|
||||||
|
|
||||||
[jwekey]
|
[jwekey]
|
||||||
locker_key_identifier1 = ""
|
locker_key_identifier1 = ""
|
||||||
locker_key_identifier2 = ""
|
locker_key_identifier2 = ""
|
||||||
|
|||||||
Reference in New Issue
Block a user