db: Added ephemeral key functions (#84)

This commit is contained in:
Kartikeya Hegde
2022-12-08 20:23:39 +05:30
committed by GitHub
parent 4eaa54e138
commit 5b470bf8f5
11 changed files with 190 additions and 6 deletions

View File

@ -13,7 +13,7 @@ use fred::{
interfaces::{KeysInterface, StreamsInterface},
types::{
Expiration, FromRedis, MultipleIDs, MultipleKeys, MultipleOrderedPairs, MultipleStrings,
RedisValue, SetOptions, XReadResponse,
RedisMap, RedisValue, SetOptions, XReadResponse,
},
};
use router_env::{tracing, tracing::instrument};
@ -43,6 +43,18 @@ impl super::RedisConnectionPool {
.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))]
pub async fn serialize_and_set_key<V>(
&self,

View File

@ -4,6 +4,7 @@ pub mod commands;
pub mod errors;
pub mod types;
pub use fred::prelude::*;
use router_env::logger;
pub use self::{commands::*, types::*};

View File

@ -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"

View File

@ -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")]

View File

@ -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
{

View 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())
}
}

View File

@ -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
}

View File

@ -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