From 33298b38081c46fe4ee38f8ad6ddffd2b98a1d5c Mon Sep 17 00:00:00 2001 From: Arjun Karthik Date: Fri, 19 Jul 2024 13:08:58 +0530 Subject: [PATCH] feat: encryption service integration to support batch encryption and decryption (#5164) Co-authored-by: dracarys18 Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- Cargo.lock | 3 + crates/api_models/src/customers.rs | 90 +- crates/api_models/src/payments.rs | 73 +- crates/common_utils/Cargo.toml | 2 + crates/common_utils/src/consts.rs | 2 + .../src/encryption.rs | 62 +- crates/common_utils/src/keymanager.rs | 72 +- crates/common_utils/src/lib.rs | 3 + crates/common_utils/src/transformers.rs | 15 + crates/common_utils/src/types/keymanager.rs | 440 +++++++++- crates/diesel_models/Cargo.toml | 1 + crates/diesel_models/src/address.rs | 68 +- crates/diesel_models/src/business_profile.rs | 4 +- crates/diesel_models/src/customers.rs | 4 +- crates/diesel_models/src/events.rs | 40 +- crates/diesel_models/src/lib.rs | 1 - crates/diesel_models/src/merchant_account.rs | 4 +- .../src/merchant_connector_account.rs | 4 +- .../diesel_models/src/merchant_key_store.rs | 4 +- crates/diesel_models/src/payment_intent.rs | 4 +- crates/diesel_models/src/payment_method.rs | 4 +- crates/diesel_models/src/user.rs | 6 +- .../src/user_authentication_method.rs | 3 +- crates/diesel_models/src/user_key_store.rs | 3 +- crates/hyperswitch_domain_models/Cargo.toml | 4 +- .../src/behaviour.rs | 23 +- .../src/merchant_account.rs | 13 +- .../src/merchant_key_store.rs | 6 +- .../src/payments/payment_attempt.rs | 17 +- .../src/payments/payment_intent.rs | 11 +- .../src/type_encryption.rs | 766 +++++++++++++++++- crates/router/Cargo.toml | 7 +- crates/router/src/bin/scheduler.rs | 6 +- crates/router/src/consts.rs | 6 +- crates/router/src/core/admin.rs | 332 +++++--- crates/router/src/core/api_keys.rs | 1 + .../core/apple_pay_certificates_migration.rs | 8 +- crates/router/src/core/blocklist/utils.rs | 1 + crates/router/src/core/cards_info.rs | 2 +- crates/router/src/core/conditional_config.rs | 6 +- crates/router/src/core/customers.rs | 196 +++-- crates/router/src/core/disputes.rs | 2 + crates/router/src/core/encryption.rs | 2 +- crates/router/src/core/files/helpers.rs | 1 + crates/router/src/core/fraud_check.rs | 16 +- .../router/src/core/fraud_check/operation.rs | 3 +- .../fraud_check/operation/fraud_check_post.rs | 5 +- .../fraud_check/operation/fraud_check_pre.rs | 4 +- crates/router/src/core/locker_migration.rs | 7 +- crates/router/src/core/mandate/helpers.rs | 1 + crates/router/src/core/payment_link.rs | 2 + crates/router/src/core/payment_methods.rs | 1 + .../router/src/core/payment_methods/cards.rs | 355 ++++---- .../src/core/payment_methods/validator.rs | 1 + crates/router/src/core/payments.rs | 22 +- crates/router/src/core/payments/helpers.rs | 413 +++++----- crates/router/src/core/payments/operations.rs | 17 +- .../payments/operations/payment_approve.rs | 15 +- .../payments/operations/payment_cancel.rs | 15 +- .../payments/operations/payment_capture.rs | 7 +- .../operations/payment_complete_authorize.rs | 13 +- .../payments/operations/payment_confirm.rs | 40 +- .../payments/operations/payment_create.rs | 40 +- .../payments/operations/payment_reject.rs | 8 +- .../payments/operations/payment_response.rs | 19 +- .../payments/operations/payment_session.rs | 14 +- .../core/payments/operations/payment_start.rs | 12 +- .../payments/operations/payment_status.rs | 29 +- .../payments/operations/payment_update.rs | 17 +- .../payments_incremental_authorization.rs | 15 +- crates/router/src/core/payments/retry.rs | 1 + crates/router/src/core/payments/routing.rs | 1 + .../router/src/core/payments/tokenization.rs | 20 +- crates/router/src/core/payout_link.rs | 7 +- crates/router/src/core/payouts.rs | 16 +- crates/router/src/core/payouts/helpers.rs | 70 +- crates/router/src/core/pm_auth.rs | 33 +- crates/router/src/core/refunds.rs | 30 +- crates/router/src/core/routing.rs | 2 +- crates/router/src/core/routing/helpers.rs | 12 +- .../src/core/surcharge_decision_config.rs | 6 +- crates/router/src/core/user.rs | 47 +- .../src/core/user/dashboard_metadata.rs | 2 + crates/router/src/core/user/sample_data.rs | 8 +- crates/router/src/core/verification.rs | 8 +- crates/router/src/core/verification/utils.rs | 4 + crates/router/src/core/webhooks/incoming.rs | 3 +- crates/router/src/core/webhooks/outgoing.rs | 25 +- .../src/core/webhooks/webhook_events.rs | 41 +- crates/router/src/db/address.rs | 164 +++- crates/router/src/db/customers.rs | 103 ++- crates/router/src/db/events.rs | 170 +++- crates/router/src/db/kafka_store.rs | 165 +++- crates/router/src/db/merchant_account.rs | 90 +- .../src/db/merchant_connector_account.rs | 190 ++++- crates/router/src/db/merchant_key_store.rs | 87 +- crates/router/src/db/user/sample_data.rs | 22 +- crates/router/src/db/user_key_store.rs | 28 +- crates/router/src/lib.rs | 6 +- crates/router/src/routes/app.rs | 4 + crates/router/src/routes/payment_methods.rs | 8 +- crates/router/src/routes/recon.rs | 21 +- crates/router/src/services/api.rs | 17 +- crates/router/src/services/api/client.rs | 14 +- crates/router/src/services/authentication.rs | 44 +- crates/router/src/types/api/admin.rs | 12 +- crates/router/src/types/api/mandates.rs | 3 +- crates/router/src/types/domain/address.rs | 78 +- crates/router/src/types/domain/customer.rs | 67 +- crates/router/src/types/domain/event.rs | 68 +- .../domain/merchant_connector_account.rs | 16 +- crates/router/src/types/domain/types.rs | 3 +- crates/router/src/types/domain/user.rs | 36 +- .../router/src/types/domain/user_key_store.rs | 6 +- crates/router/src/utils.rs | 258 +++--- .../router/src/utils/connector_onboarding.rs | 3 + crates/router/src/utils/user.rs | 15 +- crates/router/src/utils/user/sample_data.rs | 5 +- crates/router/src/workflows/api_key_expiry.rs | 9 +- .../attach_payout_account_workflow.rs | 5 +- .../src/workflows/outgoing_webhook_retry.rs | 13 +- .../workflows/payment_method_status_update.rs | 5 +- crates/router/src/workflows/payment_sync.rs | 5 +- crates/router/tests/payments.rs | 10 +- crates/router/tests/payments2.rs | 10 +- .../src/mock_db/payment_intent.rs | 10 +- .../src/payments/payment_intent.rs | 98 ++- 127 files changed, 4239 insertions(+), 1378 deletions(-) rename crates/{diesel_models => common_utils}/src/encryption.rs (88%) create mode 100644 crates/common_utils/src/transformers.rs diff --git a/Cargo.lock b/Cargo.lock index 193f19453b..66b38d09c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2006,6 +2006,7 @@ name = "common_utils" version = "0.1.0" dependencies = [ "async-trait", + "base64 0.22.0", "blake3", "bytes 1.6.0", "common_enums", @@ -2704,6 +2705,7 @@ dependencies = [ "masking", "router_derive", "router_env", + "rustc-hash", "serde", "serde_json", "strum 0.26.2", @@ -3858,6 +3860,7 @@ dependencies = [ "mime", "router_derive", "router_env", + "rustc-hash", "serde", "serde_json", "serde_with", diff --git a/crates/api_models/src/customers.rs b/crates/api_models/src/customers.rs index 37c4cd0f1b..73fb5264ed 100644 --- a/crates/api_models/src/customers.rs +++ b/crates/api_models/src/customers.rs @@ -1,5 +1,12 @@ -use common_utils::{crypto, custom_serde, id_type, pii}; -use masking::Secret; +use common_utils::{ + crypto, custom_serde, + encryption::Encryption, + id_type, + pii::{self, EmailStrategy}, + types::keymanager::ToEncryptable, +}; +use euclid::dssa::graph::euclid_graph_prelude::FxHashMap; +use masking::{ExposeInterface, Secret, SwitchStrategy}; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -40,6 +47,85 @@ pub struct CustomerRequest { pub metadata: Option, } +pub struct CustomerRequestWithEmail { + pub name: Option>, + pub email: Option, + pub phone: Option>, +} + +pub struct CustomerRequestWithEncryption { + pub name: Option, + pub phone: Option, + pub email: Option, +} + +pub struct EncryptableCustomer { + pub name: crypto::OptionalEncryptableName, + pub phone: crypto::OptionalEncryptablePhone, + pub email: crypto::OptionalEncryptableEmail, +} + +impl ToEncryptable, Encryption> + for CustomerRequestWithEncryption +{ + fn to_encryptable(self) -> FxHashMap { + let mut map = FxHashMap::with_capacity_and_hasher(3, Default::default()); + self.name.map(|x| map.insert("name".to_string(), x)); + self.phone.map(|x| map.insert("phone".to_string(), x)); + self.email.map(|x| map.insert("email".to_string(), x)); + map + } + + fn from_encryptable( + mut hashmap: FxHashMap>>, + ) -> common_utils::errors::CustomResult + { + Ok(EncryptableCustomer { + name: hashmap.remove("name"), + phone: hashmap.remove("phone"), + email: hashmap.remove("email").map(|email| { + let encryptable: crypto::Encryptable> = + crypto::Encryptable::new( + email.clone().into_inner().switch_strategy(), + email.into_encrypted(), + ); + encryptable + }), + }) + } +} + +impl ToEncryptable, Secret> + for CustomerRequestWithEmail +{ + fn to_encryptable(self) -> FxHashMap> { + let mut map = FxHashMap::with_capacity_and_hasher(3, Default::default()); + self.name.map(|x| map.insert("name".to_string(), x)); + self.phone.map(|x| map.insert("phone".to_string(), x)); + self.email + .map(|x| map.insert("email".to_string(), x.expose().switch_strategy())); + map + } + + fn from_encryptable( + mut hashmap: FxHashMap>>, + ) -> common_utils::errors::CustomResult + { + Ok(EncryptableCustomer { + name: hashmap.remove("name"), + email: hashmap.remove("email").map(|email| { + let encryptable: crypto::Encryptable> = + crypto::Encryptable::new( + email.clone().into_inner().switch_strategy(), + email.into_encrypted(), + ); + encryptable + }), + phone: hashmap.remove("phone"), + }) + } +} + #[derive(Debug, Clone, Serialize, ToSchema)] pub struct CustomerResponse { /// The identifier for the customer object diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 619f345a33..5ac8d0edcc 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -11,10 +11,11 @@ use common_utils::{ ext_traits::{ConfigExt, Encode}, hashing::HashedString, id_type, - pii::{self, Email}, - types::{MinorUnit, StringMajorUnit}, + pii::{self, Email, EmailStrategy}, + types::{keymanager::ToEncryptable, MinorUnit, StringMajorUnit}, }; -use masking::{PeekInterface, Secret, WithType}; +use euclid::dssa::graph::euclid_graph_prelude::FxHashMap; +use masking::{ExposeInterface, PeekInterface, Secret, SwitchStrategy, WithType}; use router_derive::Setter; use serde::{ de::{self, Unexpected, Visitor}, @@ -3187,6 +3188,72 @@ impl AddressDetails { } } +pub struct AddressDetailsWithPhone { + pub address: Option, + pub phone_number: Option>, + pub email: Option, +} + +pub struct EncryptableAddressDetails { + pub line1: crypto::OptionalEncryptableSecretString, + pub line2: crypto::OptionalEncryptableSecretString, + pub line3: crypto::OptionalEncryptableSecretString, + pub state: crypto::OptionalEncryptableSecretString, + pub zip: crypto::OptionalEncryptableSecretString, + pub first_name: crypto::OptionalEncryptableSecretString, + pub last_name: crypto::OptionalEncryptableSecretString, + pub phone_number: crypto::OptionalEncryptableSecretString, + pub email: crypto::OptionalEncryptableEmail, +} + +impl ToEncryptable, Secret> + for AddressDetailsWithPhone +{ + fn from_encryptable( + mut hashmap: FxHashMap>>, + ) -> common_utils::errors::CustomResult< + EncryptableAddressDetails, + common_utils::errors::ParsingError, + > { + Ok(EncryptableAddressDetails { + line1: hashmap.remove("line1"), + line2: hashmap.remove("line2"), + line3: hashmap.remove("line3"), + state: hashmap.remove("state"), + zip: hashmap.remove("zip"), + first_name: hashmap.remove("first_name"), + last_name: hashmap.remove("last_name"), + phone_number: hashmap.remove("phone_number"), + email: hashmap.remove("email").map(|x| { + let inner: Secret = x.clone().into_inner().switch_strategy(); + crypto::Encryptable::new(inner, x.into_encrypted()) + }), + }) + } + + fn to_encryptable(self) -> FxHashMap> { + let mut map = FxHashMap::with_capacity_and_hasher(9, Default::default()); + self.address.map(|address| { + address.line1.map(|x| map.insert("line1".to_string(), x)); + address.line2.map(|x| map.insert("line2".to_string(), x)); + address.line3.map(|x| map.insert("line3".to_string(), x)); + address.state.map(|x| map.insert("state".to_string(), x)); + address.zip.map(|x| map.insert("zip".to_string(), x)); + address + .first_name + .map(|x| map.insert("first_name".to_string(), x)); + address + .last_name + .map(|x| map.insert("last_name".to_string(), x)); + }); + self.email + .map(|x| map.insert("email".to_string(), x.expose().switch_strategy())); + self.phone_number + .map(|x| map.insert("phone_number".to_string(), x)); + map + } +} + #[derive(Debug, Clone, Default, Eq, PartialEq, ToSchema, serde::Deserialize, serde::Serialize)] pub struct PhoneDetails { /// The contact number diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index df6b440063..61d96a2173 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true [features] keymanager = ["dep:router_env"] keymanager_mtls = ["reqwest/rustls-tls"] +encryption_service = ["dep:router_env"] signals = ["dep:signal-hook-tokio", "dep:signal-hook", "dep:tokio", "dep:router_env", "dep:futures"] async_ext = ["dep:async-trait", "dep:futures"] logs = ["dep:router_env"] @@ -17,6 +18,7 @@ metrics = ["dep:router_env", "dep:futures"] [dependencies] async-trait = { version = "0.1.79", optional = true } +base64 = "0.22.0" blake3 = { version = "1.5.1", features = ["serde"] } bytes = "1.6.0" diesel = "2.1.5" diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 848189cd89..e52d24c9ba 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -99,6 +99,8 @@ pub const MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 64; /// Minimum allowed length for MerchantReferenceId pub const MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 1; +/// General purpose base64 engine +pub const BASE64_ENGINE: base64::engine::GeneralPurpose = base64::engine::general_purpose::STANDARD; /// Regex for matching a domain /// Eg - /// http://www.example.com diff --git a/crates/diesel_models/src/encryption.rs b/crates/common_utils/src/encryption.rs similarity index 88% rename from crates/diesel_models/src/encryption.rs rename to crates/common_utils/src/encryption.rs index 6c6063c160..9c56643278 100644 --- a/crates/diesel_models/src/encryption.rs +++ b/crates/common_utils/src/encryption.rs @@ -1,40 +1,13 @@ -use common_utils::pii::EncryptionStrategy; use diesel::{ backend::Backend, deserialize::{self, FromSql, Queryable}, + expression::AsExpression, serialize::ToSql, - sql_types, AsExpression, + sql_types, }; use masking::Secret; -#[derive(Debug, AsExpression, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)] -#[diesel(sql_type = sql_types::Binary)] -#[repr(transparent)] -pub struct Encryption { - inner: Secret, EncryptionStrategy>, -} - -impl From> for Encryption { - fn from(value: common_utils::crypto::Encryptable) -> Self { - Self::new(value.into_encrypted()) - } -} - -impl Encryption { - pub fn new(item: Secret, EncryptionStrategy>) -> Self { - Self { inner: item } - } - - #[inline] - pub fn into_inner(self) -> Secret, EncryptionStrategy> { - self.inner - } - - #[inline] - pub fn get_inner(&self) -> &Secret, EncryptionStrategy> { - &self.inner - } -} +use crate::{crypto::Encryptable, pii::EncryptionStrategy}; impl FromSql for Encryption where @@ -69,3 +42,32 @@ where Ok(Self { inner: row }) } } + +#[derive(Debug, AsExpression, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)] +#[diesel(sql_type = sql_types::Binary)] +#[repr(transparent)] +pub struct Encryption { + inner: Secret, EncryptionStrategy>, +} + +impl From> for Encryption { + fn from(value: Encryptable) -> Self { + Self::new(value.into_encrypted()) + } +} + +impl Encryption { + pub fn new(item: Secret, EncryptionStrategy>) -> Self { + Self { inner: item } + } + + #[inline] + pub fn into_inner(self) -> Secret, EncryptionStrategy> { + self.inner + } + + #[inline] + pub fn get_inner(&self) -> &Secret, EncryptionStrategy> { + &self.inner + } +} diff --git a/crates/common_utils/src/keymanager.rs b/crates/common_utils/src/keymanager.rs index ecdb9b63ad..9d6e931507 100644 --- a/crates/common_utils/src/keymanager.rs +++ b/crates/common_utils/src/keymanager.rs @@ -3,22 +3,26 @@ use core::fmt::Debug; use std::str::FromStr; +use base64::Engine; use error_stack::ResultExt; use http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; -#[cfg(feature = "keymanager_mtls")] -use masking::PeekInterface; +use masking::{PeekInterface, StrongSecret}; use once_cell::sync::OnceCell; use router_env::{instrument, logger, tracing}; use crate::{ + consts::BASE64_ENGINE, errors, types::keymanager::{ - DataKeyCreateResponse, EncryptionCreateRequest, EncryptionTransferRequest, KeyManagerState, + BatchDecryptDataRequest, DataKeyCreateResponse, DecryptDataRequest, + EncryptionCreateRequest, EncryptionTransferRequest, KeyManagerState, + TransientBatchDecryptDataRequest, TransientDecryptDataRequest, }, }; const CONTENT_TYPE: &str = "Content-Type"; static ENCRYPTION_API_CLIENT: OnceCell = OnceCell::new(); +static DEFAULT_ENCRYPTION_VERSION: &str = "v1"; /// Get keymanager client constructed from the url and state #[instrument(skip_all)] @@ -68,7 +72,7 @@ pub async fn send_encryption_request( request_body: T, ) -> errors::CustomResult where - T: serde::Serialize, + T: ConvertRaw, { let client = get_api_encryption_client(state)?; let url = reqwest::Url::parse(&url) @@ -76,7 +80,7 @@ where client .request(method, url) - .json(&request_body) + .json(&ConvertRaw::convert_raw(request_body)?) .headers(headers) .send() .await @@ -94,7 +98,7 @@ pub async fn call_encryption_service( request_body: T, ) -> errors::CustomResult where - T: serde::Serialize + Send + Sync + 'static + Debug, + T: ConvertRaw + Send + Sync + 'static + Debug, R: serde::de::DeserializeOwned, { let url = format!("{}/{endpoint}", &state.url); @@ -152,6 +156,62 @@ where } } +/// Trait to convert the raw data to the required format for encryption service request +pub trait ConvertRaw { + /// Return type of the convert_raw function + type Output: serde::Serialize; + /// Function to convert the raw data to the required format for encryption service request + fn convert_raw(self) -> Result; +} + +impl ConvertRaw for T { + type Output = T; + fn convert_raw(self) -> Result { + Ok(self) + } +} + +impl ConvertRaw for TransientDecryptDataRequest { + type Output = DecryptDataRequest; + fn convert_raw(self) -> Result { + let data = match String::from_utf8(self.data.peek().clone()) { + Ok(data) => data, + Err(_) => { + let data = BASE64_ENGINE.encode(self.data.peek().clone()); + format!("{DEFAULT_ENCRYPTION_VERSION}:{data}") + } + }; + Ok(DecryptDataRequest { + identifier: self.identifier, + data: StrongSecret::new(data), + }) + } +} + +impl ConvertRaw for TransientBatchDecryptDataRequest { + type Output = BatchDecryptDataRequest; + fn convert_raw(self) -> Result { + let data = self + .data + .iter() + .map(|(k, v)| { + let value = match String::from_utf8(v.peek().clone()) { + Ok(data) => data, + Err(_) => { + let data = BASE64_ENGINE.encode(v.peek().clone()); + format!("{DEFAULT_ENCRYPTION_VERSION}:{data}") + } + }; + (k.to_owned(), StrongSecret::new(value)) + }) + .collect(); + Ok(BatchDecryptDataRequest { + data, + identifier: self.identifier, + }) + } +} + /// A function to create the key in keymanager #[instrument(skip_all)] pub async fn create_key_in_key_manager( diff --git a/crates/common_utils/src/lib.rs b/crates/common_utils/src/lib.rs index 9b339fb956..e93b0c66ce 100644 --- a/crates/common_utils/src/lib.rs +++ b/crates/common_utils/src/lib.rs @@ -13,6 +13,8 @@ pub mod access_token; pub mod consts; pub mod crypto; pub mod custom_serde; +#[allow(missing_docs)] // Todo: add docs +pub mod encryption; pub mod errors; #[allow(missing_docs)] // Todo: add docs pub mod events; @@ -31,6 +33,7 @@ pub mod request; pub mod signals; #[allow(missing_docs)] // Todo: add docs pub mod static_cache; +pub mod transformers; pub mod types; pub mod validation; diff --git a/crates/common_utils/src/transformers.rs b/crates/common_utils/src/transformers.rs new file mode 100644 index 0000000000..3799522e16 --- /dev/null +++ b/crates/common_utils/src/transformers.rs @@ -0,0 +1,15 @@ +//! Utilities for converting between foreign types + +/// Trait for converting from one foreign type to another +pub trait ForeignFrom { + /// Convert from a foreign type to the current type + fn foreign_from(from: F) -> Self; +} + +/// Trait for converting from one foreign type to another +pub trait ForeignTryFrom: Sized { + /// Custom error for conversion failure + type Error; + /// Convert from a foreign type to the current type and return an error if the conversion fails + fn foreign_try_from(from: F) -> Result; +} diff --git a/crates/common_utils/src/types/keymanager.rs b/crates/common_utils/src/types/keymanager.rs index 356ccdd483..5a7b87d0ce 100644 --- a/crates/common_utils/src/types/keymanager.rs +++ b/crates/common_utils/src/types/keymanager.rs @@ -1,8 +1,24 @@ #![allow(missing_docs)] -#[cfg(feature = "keymanager_mtls")] -use masking::Secret; -use serde::{Deserialize, Serialize}; +use core::fmt; + +use base64::Engine; +use masking::{ExposeInterface, PeekInterface, Secret, Strategy, StrongSecret}; +#[cfg(feature = "encryption_service")] +use router_env::logger; +use rustc_hash::FxHashMap; +use serde::{ + de::{self, Unexpected, Visitor}, + ser, Deserialize, Deserializer, Serialize, +}; + +use crate::{ + consts::BASE64_ENGINE, + crypto::Encryptable, + encryption::Encryption, + errors::{self, CustomResult}, + transformers::{ForeignFrom, ForeignTryFrom}, +}; #[derive(Debug)] pub struct KeyManagerState { @@ -18,6 +34,7 @@ pub struct KeyManagerState { pub enum Identifier { User(String), Merchant(String), + UserAuth(String), } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] @@ -39,3 +56,420 @@ pub struct DataKeyCreateResponse { pub identifier: Identifier, pub key_version: String, } + +#[derive(Serialize, Deserialize, Debug)] +pub struct BatchEncryptDataRequest { + #[serde(flatten)] + pub identifier: Identifier, + pub data: DecryptedDataGroup, +} + +impl From<(Secret, S>, Identifier)> for EncryptDataRequest +where + S: Strategy>, +{ + fn from((secret, identifier): (Secret, S>, Identifier)) -> Self { + Self { + identifier, + data: DecryptedData(StrongSecret::new(secret.expose())), + } + } +} + +impl From<(FxHashMap, S>>, Identifier)> for BatchEncryptDataRequest +where + S: Strategy>, +{ + fn from((map, identifier): (FxHashMap, S>>, Identifier)) -> Self { + let group = map + .into_iter() + .map(|(key, value)| (key, DecryptedData(StrongSecret::new(value.expose())))) + .collect(); + Self { + identifier, + data: DecryptedDataGroup(group), + } + } +} + +impl From<(Secret, Identifier)> for EncryptDataRequest +where + S: Strategy, +{ + fn from((secret, identifier): (Secret, Identifier)) -> Self { + Self { + data: DecryptedData(StrongSecret::new(secret.expose().as_bytes().to_vec())), + identifier, + } + } +} + +impl From<(Secret, Identifier)> for EncryptDataRequest +where + S: Strategy, +{ + fn from((secret, identifier): (Secret, Identifier)) -> Self { + Self { + data: DecryptedData(StrongSecret::new( + secret.expose().to_string().as_bytes().to_vec(), + )), + identifier, + } + } +} + +impl From<(FxHashMap>, Identifier)> + for BatchEncryptDataRequest +where + S: Strategy, +{ + fn from( + (map, identifier): (FxHashMap>, Identifier), + ) -> Self { + let group = map + .into_iter() + .map(|(key, value)| { + ( + key, + DecryptedData(StrongSecret::new( + value.expose().to_string().as_bytes().to_vec(), + )), + ) + }) + .collect(); + Self { + data: DecryptedDataGroup(group), + identifier, + } + } +} + +impl From<(FxHashMap>, Identifier)> for BatchEncryptDataRequest +where + S: Strategy, +{ + fn from((map, identifier): (FxHashMap>, Identifier)) -> Self { + let group = map + .into_iter() + .map(|(key, value)| { + ( + key, + DecryptedData(StrongSecret::new(value.expose().as_bytes().to_vec())), + ) + }) + .collect(); + Self { + data: DecryptedDataGroup(group), + identifier, + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct EncryptDataRequest { + #[serde(flatten)] + pub identifier: Identifier, + pub data: DecryptedData, +} + +#[derive(Debug, Serialize, serde::Deserialize)] +pub struct DecryptedDataGroup(pub FxHashMap); + +#[derive(Debug, Serialize, Deserialize)] +pub struct BatchEncryptDataResponse { + pub data: EncryptedDataGroup, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct EncryptDataResponse { + pub data: EncryptedData, +} + +#[derive(Debug, Serialize, serde::Deserialize)] +pub struct EncryptedDataGroup(pub FxHashMap); +#[derive(Debug)] +pub struct TransientBatchDecryptDataRequest { + pub identifier: Identifier, + pub data: FxHashMap>>, +} + +#[derive(Debug)] +pub struct TransientDecryptDataRequest { + pub identifier: Identifier, + pub data: StrongSecret>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct BatchDecryptDataRequest { + #[serde(flatten)] + pub identifier: Identifier, + pub data: FxHashMap>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct DecryptDataRequest { + #[serde(flatten)] + pub identifier: Identifier, + pub data: StrongSecret, +} + +impl ForeignFrom<(FxHashMap>, BatchEncryptDataResponse)> + for FxHashMap>> +where + T: Clone, + S: Strategy + Send, +{ + fn foreign_from( + (mut masked_data, response): (FxHashMap>, BatchEncryptDataResponse), + ) -> Self { + response + .data + .0 + .into_iter() + .flat_map(|(k, v)| { + masked_data.remove(&k).map(|inner| { + ( + k, + Encryptable::new(inner.clone(), v.data.peek().clone().into()), + ) + }) + }) + .collect() + } +} + +impl ForeignFrom<(Secret, EncryptDataResponse)> for Encryptable> +where + T: Clone, + S: Strategy + Send, +{ + fn foreign_from((masked_data, response): (Secret, EncryptDataResponse)) -> Self { + Self::new(masked_data, response.data.data.peek().clone().into()) + } +} + +pub trait DecryptedDataConversion + Send>: Sized { + fn convert( + value: &DecryptedData, + encryption: Encryption, + ) -> CustomResult; +} + +impl + Send> DecryptedDataConversion + for Encryptable> +{ + fn convert( + value: &DecryptedData, + encryption: Encryption, + ) -> CustomResult { + let string = String::from_utf8(value.clone().inner().peek().clone()).map_err(|_err| { + #[cfg(feature = "encryption_service")] + logger::error!("Decryption error {:?}", _err); + errors::CryptoError::DecodingFailed + })?; + Ok(Self::new(Secret::new(string), encryption.into_inner())) + } +} + +impl + Send> DecryptedDataConversion + for Encryptable> +{ + fn convert( + value: &DecryptedData, + encryption: Encryption, + ) -> CustomResult { + let val = serde_json::from_slice(value.clone().inner().peek()).map_err(|_err| { + #[cfg(feature = "encryption_service")] + logger::error!("Decryption error {:?}", _err); + errors::CryptoError::DecodingFailed + })?; + Ok(Self::new(Secret::new(val), encryption.clone().into_inner())) + } +} + +impl> + Send> DecryptedDataConversion, S> + for Encryptable, S>> +{ + fn convert( + value: &DecryptedData, + encryption: Encryption, + ) -> CustomResult { + Ok(Self::new( + Secret::new(value.clone().inner().peek().clone()), + encryption.clone().into_inner(), + )) + } +} + +impl ForeignTryFrom<(Encryption, DecryptDataResponse)> for Encryptable> +where + T: Clone, + S: Strategy + Send, + Self: DecryptedDataConversion, +{ + type Error = error_stack::Report; + fn foreign_try_from( + (encrypted_data, response): (Encryption, DecryptDataResponse), + ) -> Result { + Self::convert(&response.data, encrypted_data) + } +} + +impl ForeignTryFrom<(FxHashMap, BatchDecryptDataResponse)> + for FxHashMap>> +where + T: Clone, + S: Strategy + Send, + Encryptable>: DecryptedDataConversion, +{ + type Error = error_stack::Report; + fn foreign_try_from( + (mut encrypted_data, response): (FxHashMap, BatchDecryptDataResponse), + ) -> Result { + response + .data + .0 + .into_iter() + .map(|(k, v)| match encrypted_data.remove(&k) { + Some(encrypted) => Ok((k.clone(), Encryptable::convert(&v, encrypted.clone())?)), + None => Err(errors::CryptoError::DecodingFailed)?, + }) + .collect() + } +} + +impl From<(Encryption, Identifier)> for TransientDecryptDataRequest { + fn from((encryption, identifier): (Encryption, Identifier)) -> Self { + Self { + data: StrongSecret::new(encryption.clone().into_inner().expose()), + identifier, + } + } +} + +impl From<(FxHashMap, Identifier)> for TransientBatchDecryptDataRequest { + fn from((map, identifier): (FxHashMap, Identifier)) -> Self { + let data = map + .into_iter() + .map(|(k, v)| (k, StrongSecret::new(v.clone().into_inner().expose()))) + .collect(); + Self { data, identifier } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct BatchDecryptDataResponse { + pub data: DecryptedDataGroup, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct DecryptDataResponse { + pub data: DecryptedData, +} + +#[derive(Clone, Debug)] +pub struct DecryptedData(StrongSecret>); + +impl Serialize for DecryptedData { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let data = BASE64_ENGINE.encode(self.0.peek()); + serializer.serialize_str(&data) + } +} + +impl<'de> Deserialize<'de> for DecryptedData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct DecryptedDataVisitor; + + impl<'de> Visitor<'de> for DecryptedDataVisitor { + type Value = DecryptedData; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("string of the format {version}:{base64_encoded_data}'") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + let dec_data = BASE64_ENGINE.decode(value).map_err(|err| { + let err = err.to_string(); + E::invalid_value(Unexpected::Str(value), &err.as_str()) + })?; + + Ok(DecryptedData(dec_data.into())) + } + } + + deserializer.deserialize_str(DecryptedDataVisitor) + } +} + +impl DecryptedData { + pub fn from_data(data: StrongSecret>) -> Self { + Self(data) + } + + pub fn inner(self) -> StrongSecret> { + self.0 + } +} + +#[derive(Debug)] +pub struct EncryptedData { + pub data: StrongSecret>, +} + +impl Serialize for EncryptedData { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let data = String::from_utf8(self.data.peek().clone()).map_err(ser::Error::custom)?; + serializer.serialize_str(data.as_str()) + } +} + +impl<'de> Deserialize<'de> for EncryptedData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct EncryptedDataVisitor; + + impl<'de> Visitor<'de> for EncryptedDataVisitor { + type Value = EncryptedData; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("string of the format {version}:{base64_encoded_data}'") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + Ok(EncryptedData { + data: StrongSecret::new(value.as_bytes().to_vec()), + }) + } + } + + deserializer.deserialize_str(EncryptedDataVisitor) + } +} + +/// A trait which converts the struct to Hashmap required for encryption and back to struct +pub trait ToEncryptable: Sized { + /// Serializes the type to a hashmap + fn to_encryptable(self) -> FxHashMap; + /// Deserializes the hashmap back to the type + fn from_encryptable( + hashmap: FxHashMap>, + ) -> CustomResult; +} diff --git a/crates/diesel_models/Cargo.toml b/crates/diesel_models/Cargo.toml index f33427aaf3..10890ef0cf 100644 --- a/crates/diesel_models/Cargo.toml +++ b/crates/diesel_models/Cargo.toml @@ -15,6 +15,7 @@ kv_store = [] async-bb8-diesel = { git = "https://github.com/jarnura/async-bb8-diesel", rev = "53b4ab901aab7635c8215fd1c2d542c8db443094" } diesel = { version = "2.1.5", features = ["postgres", "serde_json", "time", "64-column-tables"] } error-stack = "0.4.1" +rustc-hash = "1.1.0" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" strum = { version = "0.26.2", features = ["derive"] } diff --git a/crates/diesel_models/src/address.rs b/crates/diesel_models/src/address.rs index d695d2a0e7..57c4e36c78 100644 --- a/crates/diesel_models/src/address.rs +++ b/crates/diesel_models/src/address.rs @@ -1,9 +1,17 @@ -use common_utils::id_type; +use common_utils::{ + crypto::{self, Encryptable}, + encryption::Encryption, + id_type, + pii::EmailStrategy, + types::keymanager::ToEncryptable, +}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; +use masking::{Secret, SwitchStrategy}; +use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, enums, schema::address}; +use crate::{enums, schema::address}; #[derive(Clone, Debug, Insertable, Serialize, Deserialize, router_derive::DebugAsDisplay)] #[diesel(table_name = address)] @@ -54,6 +62,62 @@ pub struct Address { pub email: Option, } +#[derive(Clone)] +// Intermediate struct to convert HashMap to Address +pub struct EncryptableAddress { + pub line1: crypto::OptionalEncryptableSecretString, + pub line2: crypto::OptionalEncryptableSecretString, + pub line3: crypto::OptionalEncryptableSecretString, + pub state: crypto::OptionalEncryptableSecretString, + pub zip: crypto::OptionalEncryptableSecretString, + pub first_name: crypto::OptionalEncryptableSecretString, + pub last_name: crypto::OptionalEncryptableSecretString, + pub phone_number: crypto::OptionalEncryptableSecretString, + pub email: crypto::OptionalEncryptableEmail, +} + +impl ToEncryptable, Encryption> for Address { + fn to_encryptable(self) -> FxHashMap { + let mut map = FxHashMap::with_capacity_and_hasher(9, Default::default()); + self.line1.map(|x| map.insert("line1".to_string(), x)); + self.line2.map(|x| map.insert("line2".to_string(), x)); + self.line3.map(|x| map.insert("line3".to_string(), x)); + self.zip.map(|x| map.insert("zip".to_string(), x)); + self.state.map(|x| map.insert("state".to_string(), x)); + self.first_name + .map(|x| map.insert("first_name".to_string(), x)); + self.last_name + .map(|x| map.insert("last_name".to_string(), x)); + self.phone_number + .map(|x| map.insert("phone_number".to_string(), x)); + self.email.map(|x| map.insert("email".to_string(), x)); + map + } + + fn from_encryptable( + mut hashmap: FxHashMap>>, + ) -> common_utils::errors::CustomResult + { + Ok(EncryptableAddress { + line1: hashmap.remove("line1"), + line2: hashmap.remove("line2"), + line3: hashmap.remove("line3"), + zip: hashmap.remove("zip"), + state: hashmap.remove("state"), + first_name: hashmap.remove("first_name"), + last_name: hashmap.remove("last_name"), + phone_number: hashmap.remove("phone_number"), + email: hashmap.remove("email").map(|email| { + let encryptable: Encryptable> = Encryptable::new( + email.clone().into_inner().switch_strategy(), + email.into_encrypted(), + ); + encryptable + }), + }) + } +} + #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay, Serialize, Deserialize)] #[diesel(table_name = address)] pub struct AddressUpdateInternal { diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 6bd1e1f9e4..b964b290a9 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -1,7 +1,7 @@ -use common_utils::pii; +use common_utils::{encryption::Encryption, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; -use crate::{encryption::Encryption, schema::business_profile}; +use crate::schema::business_profile; #[derive( Clone, diff --git a/crates/diesel_models/src/customers.rs b/crates/diesel_models/src/customers.rs index deb1ac73f4..302772b7f9 100644 --- a/crates/diesel_models/src/customers.rs +++ b/crates/diesel_models/src/customers.rs @@ -1,8 +1,8 @@ -use common_utils::{id_type, pii}; +use common_utils::{encryption::Encryption, id_type, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, schema::customers}; +use crate::schema::customers; #[derive( Clone, Debug, Insertable, router_derive::DebugAsDisplay, serde::Deserialize, serde::Serialize, diff --git a/crates/diesel_models/src/events.rs b/crates/diesel_models/src/events.rs index b3b1230441..99879dafaf 100644 --- a/crates/diesel_models/src/events.rs +++ b/crates/diesel_models/src/events.rs @@ -1,11 +1,15 @@ -use common_utils::custom_serde; +use common_utils::{ + crypto::OptionalEncryptableSecretString, custom_serde, encryption::Encryption, + types::keymanager::ToEncryptable, +}; use diesel::{ expression::AsExpression, AsChangeset, Identifiable, Insertable, Queryable, Selectable, }; +use masking::Secret; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, enums as storage_enums, schema::events}; +use crate::{enums as storage_enums, schema::events}; #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] #[diesel(table_name = events)] @@ -59,6 +63,38 @@ pub struct Event { pub metadata: Option, } +pub struct EventWithEncryption { + pub request: Option, + pub response: Option, +} + +pub struct EncryptableEvent { + pub request: OptionalEncryptableSecretString, + pub response: OptionalEncryptableSecretString, +} + +impl ToEncryptable, Encryption> for EventWithEncryption { + fn to_encryptable(self) -> rustc_hash::FxHashMap { + let mut map = rustc_hash::FxHashMap::default(); + self.request.map(|x| map.insert("request".to_string(), x)); + self.response.map(|x| map.insert("response".to_string(), x)); + map + } + + fn from_encryptable( + mut hashmap: rustc_hash::FxHashMap< + String, + common_utils::crypto::Encryptable>, + >, + ) -> common_utils::errors::CustomResult + { + Ok(EncryptableEvent { + request: hashmap.remove("request"), + response: hashmap.remove("response"), + }) + } +} + #[derive(Clone, Debug, Deserialize, Serialize, AsExpression, diesel::FromSqlRow)] #[diesel(sql_type = diesel::sql_types::Jsonb)] pub enum EventMetadata { diff --git a/crates/diesel_models/src/lib.rs b/crates/diesel_models/src/lib.rs index 67061bf6c3..6af1e0e520 100644 --- a/crates/diesel_models/src/lib.rs +++ b/crates/diesel_models/src/lib.rs @@ -12,7 +12,6 @@ pub mod blocklist; pub mod blocklist_fingerprint; pub mod customers; pub mod dispute; -pub mod encryption; pub mod enums; pub mod ephemeral_key; pub mod errors; diff --git a/crates/diesel_models/src/merchant_account.rs b/crates/diesel_models/src/merchant_account.rs index 71c437bbb4..5c48ade55c 100644 --- a/crates/diesel_models/src/merchant_account.rs +++ b/crates/diesel_models/src/merchant_account.rs @@ -1,7 +1,7 @@ -use common_utils::pii; +use common_utils::{encryption::Encryption, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; -use crate::{encryption::Encryption, enums as storage_enums, schema::merchant_account}; +use crate::{enums as storage_enums, schema::merchant_account}; #[derive( Clone, diff --git a/crates/diesel_models/src/merchant_connector_account.rs b/crates/diesel_models/src/merchant_connector_account.rs index 05707f7173..5a549892dc 100644 --- a/crates/diesel_models/src/merchant_connector_account.rs +++ b/crates/diesel_models/src/merchant_connector_account.rs @@ -1,10 +1,10 @@ use std::fmt::Debug; -use common_utils::pii; +use common_utils::{encryption::Encryption, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use masking::Secret; -use crate::{encryption::Encryption, enums as storage_enums, schema::merchant_connector_account}; +use crate::{enums as storage_enums, schema::merchant_connector_account}; #[derive( Clone, diff --git a/crates/diesel_models/src/merchant_key_store.rs b/crates/diesel_models/src/merchant_key_store.rs index 30781927e9..b11d997053 100644 --- a/crates/diesel_models/src/merchant_key_store.rs +++ b/crates/diesel_models/src/merchant_key_store.rs @@ -1,8 +1,8 @@ -use common_utils::custom_serde; +use common_utils::{custom_serde, encryption::Encryption}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, schema::merchant_key_store}; +use crate::schema::merchant_key_store; #[derive( Clone, diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 499d72a648..e4d8dcc315 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -1,10 +1,10 @@ use common_enums::RequestIncrementalAuthorization; -use common_utils::{id_type, pii, types::MinorUnit}; +use common_utils::{encryption::Encryption, id_type, pii, types::MinorUnit}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, enums as storage_enums, schema::payment_intent}; +use crate::{enums as storage_enums, schema::payment_intent}; #[derive(Clone, Debug, PartialEq, Identifiable, Queryable, Selectable, Serialize, Deserialize)] #[diesel(table_name = payment_intent, primary_key(payment_id, merchant_id), check_for_backend(diesel::pg::Pg))] diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index a9db90abb2..6bb40e4b16 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -1,11 +1,11 @@ use common_enums::MerchantStorageScheme; -use common_utils::{id_type, pii}; +use common_utils::{encryption::Encryption, id_type, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use masking::Secret; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, enums as storage_enums, schema::payment_methods}; +use crate::{enums as storage_enums, schema::payment_methods}; #[derive( Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Selectable, Serialize, Deserialize, diff --git a/crates/diesel_models/src/user.rs b/crates/diesel_models/src/user.rs index 56c6f00f2b..9d6514916d 100644 --- a/crates/diesel_models/src/user.rs +++ b/crates/diesel_models/src/user.rs @@ -1,11 +1,9 @@ -use common_utils::pii; +use common_utils::{encryption::Encryption, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use masking::Secret; use time::PrimitiveDateTime; -use crate::{ - diesel_impl::OptionalDieselArray, encryption::Encryption, enums::TotpStatus, schema::users, -}; +use crate::{diesel_impl::OptionalDieselArray, enums::TotpStatus, schema::users}; pub mod dashboard_metadata; diff --git a/crates/diesel_models/src/user_authentication_method.rs b/crates/diesel_models/src/user_authentication_method.rs index 18a65b9f10..76e1abe757 100644 --- a/crates/diesel_models/src/user_authentication_method.rs +++ b/crates/diesel_models/src/user_authentication_method.rs @@ -1,7 +1,8 @@ +use common_utils::encryption::Encryption; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, enums, schema::user_authentication_methods}; +use crate::{enums, schema::user_authentication_methods}; #[derive(Clone, Debug, Identifiable, Queryable, Selectable)] #[diesel(table_name = user_authentication_methods, check_for_backend(diesel::pg::Pg))] diff --git a/crates/diesel_models/src/user_key_store.rs b/crates/diesel_models/src/user_key_store.rs index 1b19ef31a4..4d880ebf8f 100644 --- a/crates/diesel_models/src/user_key_store.rs +++ b/crates/diesel_models/src/user_key_store.rs @@ -1,7 +1,8 @@ +use common_utils::encryption::Encryption; use diesel::{Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, schema::user_key_store}; +use crate::schema::user_key_store; #[derive( Clone, Debug, serde::Serialize, serde::Deserialize, Identifiable, Queryable, Selectable, diff --git a/crates/hyperswitch_domain_models/Cargo.toml b/crates/hyperswitch_domain_models/Cargo.toml index ab56946468..310c2731b4 100644 --- a/crates/hyperswitch_domain_models/Cargo.toml +++ b/crates/hyperswitch_domain_models/Cargo.toml @@ -9,6 +9,7 @@ license.workspace = true [features] default = ["olap", "payouts", "frm"] +encryption_service =[] olap = [] payouts = ["api_models/payouts"] frm = ["api_models/frm"] @@ -18,7 +19,7 @@ frm = ["api_models/frm"] api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] } cards = { version = "0.1.0", path = "../cards" } common_enums = { version = "0.1.0", path = "../common_enums" } -common_utils = { version = "0.1.0", path = "../common_utils", features = ["async_ext", "metrics"] } +common_utils = { version = "0.1.0", path = "../common_utils", features = ["async_ext", "metrics", "encryption_service", "keymanager"] } diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] } masking = { version = "0.1.0", path = "../masking" } router_derive = { version = "0.1.0", path = "../router_derive" } @@ -31,6 +32,7 @@ error-stack = "0.4.1" futures = "0.3.30" http = "0.2.12" mime = "0.3.17" +rustc-hash = "1.1.0" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" serde_with = "3.7.0" diff --git a/crates/hyperswitch_domain_models/src/behaviour.rs b/crates/hyperswitch_domain_models/src/behaviour.rs index 9976dd25a9..07911e8ea6 100644 --- a/crates/hyperswitch_domain_models/src/behaviour.rs +++ b/crates/hyperswitch_domain_models/src/behaviour.rs @@ -1,4 +1,7 @@ -use common_utils::errors::{CustomResult, ValidationError}; +use common_utils::{ + errors::{CustomResult, ValidationError}, + types::keymanager::KeyManagerState, +}; use masking::Secret; /// Trait for converting domain types to storage models @@ -9,8 +12,10 @@ pub trait Conversion { async fn convert(self) -> CustomResult; async fn convert_back( + state: &KeyManagerState, item: Self::DstType, key: &Secret>, + key_store_ref_id: String, ) -> CustomResult where Self: Sized; @@ -20,12 +25,22 @@ pub trait Conversion { #[async_trait::async_trait] pub trait ReverseConversion { - async fn convert(self, key: &Secret>) -> CustomResult; + async fn convert( + self, + state: &KeyManagerState, + key: &Secret>, + key_store_ref_id: String, + ) -> CustomResult; } #[async_trait::async_trait] impl> ReverseConversion for T { - async fn convert(self, key: &Secret>) -> CustomResult { - U::convert_back(self, key).await + async fn convert( + self, + state: &KeyManagerState, + key: &Secret>, + key_store_ref_id: String, + ) -> CustomResult { + U::convert_back(state, self, key, key_store_ref_id).await } } diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index e42f21dc7c..a430673c59 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -1,13 +1,14 @@ use common_utils::{ crypto::{OptionalEncryptableName, OptionalEncryptableValue}, date_time, + encryption::Encryption, errors::{CustomResult, ValidationError}, ext_traits::ValueExt, pii, + types::keymanager, }; use diesel_models::{ - encryption::Encryption, enums::MerchantStorageScheme, - merchant_account::MerchantAccountUpdateInternal, + enums::MerchantStorageScheme, merchant_account::MerchantAccountUpdateInternal, }; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -195,8 +196,10 @@ impl super::behaviour::Conversion for MerchantAccount { } async fn convert_back( + state: &keymanager::KeyManagerState, item: Self::DstType, key: &Secret>, + key_store_ref_id: String, ) -> CustomResult where Self: Sized, @@ -206,7 +209,7 @@ impl super::behaviour::Conversion for MerchantAccount { .ok_or(ValidationError::MissingRequiredField { field_name: "publishable_key".to_string(), })?; - + let identifier = keymanager::Identifier::Merchant(key_store_ref_id.clone()); async { Ok::>(Self { id: Some(item.id), @@ -217,11 +220,11 @@ impl super::behaviour::Conversion for MerchantAccount { redirect_to_merchant_with_http_post: item.redirect_to_merchant_with_http_post, merchant_name: item .merchant_name - .async_lift(|inner| decrypt(inner, key.peek())) + .async_lift(|inner| decrypt(state, inner, identifier.clone(), key.peek())) .await?, merchant_details: item .merchant_details - .async_lift(|inner| decrypt(inner, key.peek())) + .async_lift(|inner| decrypt(state, inner, identifier.clone(), key.peek())) .await?, webhook_details: item.webhook_details, sub_merchants_enabled: item.sub_merchants_enabled, diff --git a/crates/hyperswitch_domain_models/src/merchant_key_store.rs b/crates/hyperswitch_domain_models/src/merchant_key_store.rs index 29050b7eb4..76acf0f140 100644 --- a/crates/hyperswitch_domain_models/src/merchant_key_store.rs +++ b/crates/hyperswitch_domain_models/src/merchant_key_store.rs @@ -2,6 +2,7 @@ use common_utils::{ crypto::{Encryptable, GcmAes256}, custom_serde, date_time, errors::{CustomResult, ValidationError}, + types::keymanager::{Identifier, KeyManagerState}, }; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -30,14 +31,17 @@ impl super::behaviour::Conversion for MerchantKeyStore { } async fn convert_back( + state: &KeyManagerState, item: Self::DstType, key: &Secret>, + _key_store_ref_id: String, ) -> CustomResult where Self: Sized, { + let identifier = Identifier::Merchant(item.merchant_id.clone()); Ok(Self { - key: Encryptable::decrypt(item.key, key.peek(), GcmAes256) + key: Encryptable::decrypt_via_api(state, item.key, identifier, key.peek(), GcmAes256) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting customer data".to_string(), diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index 6ff74a95e4..5f8dd3934a 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -1,9 +1,10 @@ use api_models::enums::Connector; use common_enums as storage_enums; use common_utils::{ + encryption::Encryption, errors::{CustomResult, ValidationError}, pii, - types::MinorUnit, + types::{keymanager::KeyManagerState, MinorUnit}, }; use error_stack::ResultExt; use masking::PeekInterface; @@ -479,8 +480,7 @@ impl ForeignIDRef for PaymentAttempt { } use diesel_models::{ - encryption::Encryption, PaymentIntent as DieselPaymentIntent, - PaymentIntentNew as DieselPaymentIntentNew, + PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew, }; #[async_trait::async_trait] @@ -542,14 +542,23 @@ impl behaviour::Conversion for PaymentIntent { } async fn convert_back( + state: &KeyManagerState, storage_model: Self::DstType, key: &masking::Secret>, + key_store_ref_id: String, ) -> CustomResult where Self: Sized, { async { - let inner_decrypt = |inner| decrypt(inner, key.peek()); + let inner_decrypt = |inner| { + decrypt( + state, + inner, + common_utils::types::keymanager::Identifier::Merchant(key_store_ref_id.clone()), + key.peek(), + ) + }; Ok::>(Self { payment_id: storage_model.payment_id, merchant_id: storage_model.merchant_id, diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 6eb3561672..f4f6be9705 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -2,9 +2,10 @@ use common_enums as storage_enums; use common_utils::{ consts::{PAYMENTS_LIST_MAX_LIMIT_V1, PAYMENTS_LIST_MAX_LIMIT_V2}, crypto::Encryptable, + encryption::Encryption, id_type, pii::{self, Email}, - types::MinorUnit, + types::{keymanager::KeyManagerState, MinorUnit}, }; use masking::{Deserialize, Secret}; use serde::Serialize; @@ -16,6 +17,7 @@ use crate::{errors, merchant_key_store::MerchantKeyStore, RemoteStorageObject}; pub trait PaymentIntentInterface { async fn update_payment_intent( &self, + state: &KeyManagerState, this: PaymentIntent, payment_intent: PaymentIntentUpdate, merchant_key_store: &MerchantKeyStore, @@ -24,6 +26,7 @@ pub trait PaymentIntentInterface { async fn insert_payment_intent( &self, + state: &KeyManagerState, new: PaymentIntent, merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, @@ -31,6 +34,7 @@ pub trait PaymentIntentInterface { async fn find_payment_intent_by_payment_id_merchant_id( &self, + state: &KeyManagerState, payment_id: &str, merchant_id: &str, merchant_key_store: &MerchantKeyStore, @@ -46,6 +50,7 @@ pub trait PaymentIntentInterface { #[cfg(feature = "olap")] async fn filter_payment_intent_by_constraints( &self, + state: &KeyManagerState, merchant_id: &str, filters: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -55,6 +60,7 @@ pub trait PaymentIntentInterface { #[cfg(feature = "olap")] async fn filter_payment_intents_by_time_range_constraints( &self, + state: &KeyManagerState, merchant_id: &str, time_range: &api_models::payments::TimeRange, merchant_key_store: &MerchantKeyStore, @@ -64,6 +70,7 @@ pub trait PaymentIntentInterface { #[cfg(feature = "olap")] async fn get_filtered_payment_intents_attempt( &self, + state: &KeyManagerState, merchant_id: &str, constraints: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -467,7 +474,7 @@ impl From for PaymentIntentUpdateInternal { } use diesel_models::{ - encryption::Encryption, PaymentIntentUpdate as DieselPaymentIntentUpdate, + PaymentIntentUpdate as DieselPaymentIntentUpdate, PaymentIntentUpdateFields as DieselPaymentIntentUpdateFields, }; diff --git a/crates/hyperswitch_domain_models/src/type_encryption.rs b/crates/hyperswitch_domain_models/src/type_encryption.rs index 82d904ccf4..d656afc925 100644 --- a/crates/hyperswitch_domain_models/src/type_encryption.rs +++ b/crates/hyperswitch_domain_models/src/type_encryption.rs @@ -1,14 +1,30 @@ use async_trait::async_trait; use common_utils::{ crypto, + encryption::Encryption, errors::{self, CustomResult}, ext_traits::AsyncExt, metrics::utils::record_operation_time, + types::keymanager::{Identifier, KeyManagerState}, }; -use diesel_models::encryption::Encryption; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; use router_env::{instrument, tracing}; +use rustc_hash::FxHashMap; +#[cfg(feature = "encryption_service")] +use { + common_utils::{ + keymanager::call_encryption_service, + transformers::{ForeignFrom, ForeignTryFrom}, + types::keymanager::{ + BatchDecryptDataResponse, BatchEncryptDataRequest, BatchEncryptDataResponse, + DecryptDataResponse, EncryptDataRequest, EncryptDataResponse, + TransientBatchDecryptDataRequest, TransientDecryptDataRequest, + }, + }, + http::Method, + router_env::logger, +}; #[async_trait] pub trait TypeEncryption< @@ -17,6 +33,22 @@ pub trait TypeEncryption< S: masking::Strategy, >: Sized { + async fn encrypt_via_api( + state: &KeyManagerState, + masked_data: Secret, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult; + + async fn decrypt_via_api( + state: &KeyManagerState, + encrypted_data: Encryption, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult; + async fn encrypt( masked_data: Secret, key: &[u8], @@ -28,31 +60,141 @@ pub trait TypeEncryption< key: &[u8], crypt_algo: V, ) -> CustomResult; + + async fn batch_encrypt_via_api( + state: &KeyManagerState, + masked_data: FxHashMap>, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError>; + + async fn batch_decrypt_via_api( + state: &KeyManagerState, + encrypted_data: FxHashMap, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError>; + + async fn batch_encrypt( + masked_data: FxHashMap>, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError>; + + async fn batch_decrypt( + encrypted_data: FxHashMap, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError>; } #[async_trait] impl< V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, - S: masking::Strategy + Send, + S: masking::Strategy + Send + Sync, > TypeEncryption for crypto::Encryptable> { #[instrument(skip_all)] + #[allow(unused_variables)] + async fn encrypt_via_api( + state: &KeyManagerState, + masked_data: Secret, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::encrypt(masked_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + EncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + EncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data.clone(), response))), + Err(err) => { + logger::error!("Encryption error {:?}", err); + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Encryption"); + Self::encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[instrument(skip_all)] + #[allow(unused_variables)] + async fn decrypt_via_api( + state: &KeyManagerState, + encrypted_data: Encryption, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::decrypt(encrypted_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + DecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(decrypted_data) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), decrypted_data)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::DecodingFailed)) + } + }; + + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + async fn encrypt( masked_data: Secret, key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let encrypted_data = crypt_algo.encode_message(key, masked_data.peek().as_bytes())?; - Ok(Self::new(masked_data, encrypted_data.into())) } - #[instrument(skip_all)] async fn decrypt( encrypted_data: Encryption, key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let encrypted = encrypted_data.into_inner(); let data = crypt_algo.decode_message(key, encrypted.clone())?; @@ -62,25 +204,227 @@ impl< Ok(Self::new(value.into(), encrypted)) } + + #[allow(unused_variables)] + async fn batch_encrypt_via_api( + state: &KeyManagerState, + masked_data: FxHashMap>, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchEncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + BatchEncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data, response))), + Err(err) => { + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::error!("Encryption error {:?}", err); + logger::info!("Fall back to Application Encryption"); + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[allow(unused_variables)] + async fn batch_decrypt_via_api( + state: &KeyManagerState, + encrypted_data: FxHashMap, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchDecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientBatchDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(decrypted_data) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), decrypted_data)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::DecodingFailed)) + } + }; + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + + async fn batch_encrypt( + masked_data: FxHashMap>, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + masked_data + .into_iter() + .map(|(k, v)| { + Ok(( + k, + Self::new( + v.clone(), + crypt_algo.encode_message(key, v.peek().as_bytes())?.into(), + ), + )) + }) + .collect() + } + + async fn batch_decrypt( + encrypted_data: FxHashMap, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + encrypted_data + .into_iter() + .map(|(k, v)| { + let data = crypt_algo.decode_message(key, v.clone().into_inner())?; + let value: String = std::str::from_utf8(&data) + .change_context(errors::CryptoError::DecodingFailed)? + .to_string(); + Ok((k, Self::new(value.into(), v.into_inner()))) + }) + .collect() + } } #[async_trait] impl< V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, - S: masking::Strategy + Send, + S: masking::Strategy + Send + Sync, > TypeEncryption for crypto::Encryptable> { + #[instrument(skip_all)] + #[allow(unused_variables)] + async fn encrypt_via_api( + state: &KeyManagerState, + masked_data: Secret, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::encrypt(masked_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + EncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + EncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data.clone(), response))), + Err(err) => { + logger::error!("Encryption error {:?}", err); + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Encryption"); + Self::encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[instrument(skip_all)] + #[allow(unused_variables)] + async fn decrypt_via_api( + state: &KeyManagerState, + encrypted_data: Encryption, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::decrypt(encrypted_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + DecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(decrypted_data) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), decrypted_data)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::EncodingFailed)) + } + }; + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + #[instrument(skip_all)] async fn encrypt( masked_data: Secret, key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let data = serde_json::to_vec(&masked_data.peek()) .change_context(errors::CryptoError::DecodingFailed)?; let encrypted_data = crypt_algo.encode_message(key, &data)?; - Ok(Self::new(masked_data, encrypted_data.into())) } @@ -90,30 +434,229 @@ impl< key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let encrypted = encrypted_data.into_inner(); let data = crypt_algo.decode_message(key, encrypted.clone())?; let value: serde_json::Value = serde_json::from_slice(&data).change_context(errors::CryptoError::DecodingFailed)?; - Ok(Self::new(value.into(), encrypted)) } + + #[allow(unused_variables)] + async fn batch_encrypt_via_api( + state: &KeyManagerState, + masked_data: FxHashMap>, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchEncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + BatchEncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data, response))), + Err(err) => { + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::error!("Encryption error {:?}", err); + logger::info!("Fall back to Application Encryption"); + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[allow(unused_variables)] + async fn batch_decrypt_via_api( + state: &KeyManagerState, + encrypted_data: FxHashMap, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchDecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientBatchDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(decrypted_data) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), decrypted_data)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::DecodingFailed)) + } + }; + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + + async fn batch_encrypt( + masked_data: FxHashMap>, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + masked_data + .into_iter() + .map(|(k, v)| { + let data = serde_json::to_vec(v.peek()) + .change_context(errors::CryptoError::DecodingFailed)?; + Ok(( + k, + Self::new(v, crypt_algo.encode_message(key, &data)?.into()), + )) + }) + .collect() + } + + async fn batch_decrypt( + encrypted_data: FxHashMap, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + encrypted_data + .into_iter() + .map(|(k, v)| { + let data = crypt_algo.decode_message(key, v.clone().into_inner().clone())?; + + let value: serde_json::Value = serde_json::from_slice(&data) + .change_context(errors::CryptoError::DecodingFailed)?; + Ok((k, Self::new(value.into(), v.into_inner()))) + }) + .collect() + } } #[async_trait] impl< V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, - S: masking::Strategy> + Send, + S: masking::Strategy> + Send + Sync, > TypeEncryption, V, S> for crypto::Encryptable, S>> { + #[instrument(skip_all)] + #[allow(unused_variables)] + async fn encrypt_via_api( + state: &KeyManagerState, + masked_data: Secret, S>, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::encrypt(masked_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + EncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + EncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data.clone(), response))), + Err(err) => { + logger::error!("Encryption error {:?}", err); + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Encryption"); + Self::encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[instrument(skip_all)] + #[allow(unused_variables)] + async fn decrypt_via_api( + state: &KeyManagerState, + encrypted_data: Encryption, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::decrypt(encrypted_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + DecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(decrypted_data) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), decrypted_data)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::DecodingFailed)) + } + }; + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + #[instrument(skip_all)] async fn encrypt( masked_data: Secret, S>, key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let encrypted_data = crypt_algo.encode_message(key, masked_data.peek())?; - Ok(Self::new(masked_data, encrypted_data.into())) } @@ -123,11 +666,131 @@ impl< key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let encrypted = encrypted_data.into_inner(); let data = crypt_algo.decode_message(key, encrypted.clone())?; - Ok(Self::new(data.into(), encrypted)) } + + #[allow(unused_variables)] + async fn batch_encrypt_via_api( + state: &KeyManagerState, + masked_data: FxHashMap, S>>, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchEncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + BatchEncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data, response))), + Err(err) => { + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::error!("Encryption error {:?}", err); + logger::info!("Fall back to Application Encryption"); + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[allow(unused_variables)] + async fn batch_decrypt_via_api( + state: &KeyManagerState, + encrypted_data: FxHashMap, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchDecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientBatchDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(response) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), response)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::DecodingFailed)) + } + }; + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + + async fn batch_encrypt( + masked_data: FxHashMap, S>>, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + masked_data + .into_iter() + .map(|(k, v)| { + Ok(( + k, + Self::new(v.clone(), crypt_algo.encode_message(key, v.peek())?.into()), + )) + }) + .collect() + } + + async fn batch_decrypt( + encrypted_data: FxHashMap, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + encrypted_data + .into_iter() + .map(|(k, v)| { + Ok(( + k, + Self::new( + crypt_algo + .decode_message(key, v.clone().into_inner().clone())? + .into(), + v.into_inner(), + ), + )) + }) + .collect() + } } pub trait Lift { @@ -178,7 +841,9 @@ impl + Lift = V> + Send> AsyncLift for V { #[inline] pub async fn encrypt( + state: &KeyManagerState, inner: Secret, + identifier: Identifier, key: &[u8], ) -> CustomResult>, errors::CryptoError> where @@ -186,7 +851,7 @@ where crypto::Encryptable>: TypeEncryption, { record_operation_time( - crypto::Encryptable::encrypt(inner, key, crypto::GcmAes256), + crypto::Encryptable::encrypt_via_api(state, inner, identifier, key, crypto::GcmAes256), &metrics::ENCRYPTION_TIME, &metrics::CONTEXT, &[], @@ -194,9 +859,41 @@ where .await } +#[inline] +pub async fn batch_encrypt( + state: &KeyManagerState, + inner: FxHashMap>, + identifier: Identifier, + key: &[u8], +) -> CustomResult>>, errors::CryptoError> +where + S: masking::Strategy, + crypto::Encryptable>: TypeEncryption, +{ + if !inner.is_empty() { + record_operation_time( + crypto::Encryptable::batch_encrypt_via_api( + state, + inner, + identifier, + key, + crypto::GcmAes256, + ), + &metrics::ENCRYPTION_TIME, + &metrics::CONTEXT, + &[], + ) + .await + } else { + Ok(FxHashMap::default()) + } +} + #[inline] pub async fn encrypt_optional( + state: &KeyManagerState, inner: Option>, + identifier: Identifier, key: &[u8], ) -> CustomResult>>, errors::CryptoError> where @@ -204,19 +901,26 @@ where S: masking::Strategy, crypto::Encryptable>: TypeEncryption, { - inner.async_map(|f| encrypt(f, key)).await.transpose() + inner + .async_map(|f| encrypt(state, f, identifier, key)) + .await + .transpose() } #[inline] pub async fn decrypt>( + state: &KeyManagerState, inner: Option, + identifier: Identifier, key: &[u8], ) -> CustomResult>>, errors::CryptoError> where crypto::Encryptable>: TypeEncryption, { record_operation_time( - inner.async_map(|item| crypto::Encryptable::decrypt(item, key, crypto::GcmAes256)), + inner.async_map(|item| { + crypto::Encryptable::decrypt_via_api(state, item, identifier, key, crypto::GcmAes256) + }), &metrics::DECRYPTION_TIME, &metrics::CONTEXT, &[], @@ -225,8 +929,38 @@ where .transpose() } +#[inline] +pub async fn batch_decrypt( + state: &KeyManagerState, + inner: FxHashMap, + identifier: Identifier, + key: &[u8], +) -> CustomResult>>, errors::CryptoError> +where + S: masking::Strategy, + crypto::Encryptable>: TypeEncryption, +{ + if !inner.is_empty() { + record_operation_time( + crypto::Encryptable::batch_decrypt_via_api( + state, + inner, + identifier, + key, + crypto::GcmAes256, + ), + &metrics::ENCRYPTION_TIME, + &metrics::CONTEXT, + &[], + ) + .await + } else { + Ok(FxHashMap::default()) + } +} + pub(crate) mod metrics { - use router_env::{global_meter, histogram_metric, metrics_context, once_cell}; + use router_env::{counter_metric, global_meter, histogram_metric, metrics_context, once_cell}; metrics_context!(CONTEXT); global_meter!(GLOBAL_METER, "ROUTER_API"); @@ -234,4 +968,8 @@ pub(crate) mod metrics { // Encryption and Decryption metrics histogram_metric!(ENCRYPTION_TIME, GLOBAL_METER); histogram_metric!(DECRYPTION_TIME, GLOBAL_METER); + counter_metric!(ENCRYPTION_API_FAILURES, GLOBAL_METER); + counter_metric!(DECRYPTION_API_FAILURES, GLOBAL_METER); + counter_metric!(APPLICATION_ENCRYPTION_COUNT, GLOBAL_METER); + counter_metric!(APPLICATION_DECRYPTION_COUNT, GLOBAL_METER); } diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 1379b17eec..b9488a0804 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -11,12 +11,13 @@ license.workspace = true [features] default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls"] tls = ["actix-web/rustls-0_22"] -keymanager_mtls = ["reqwest/rustls-tls", "common_utils/keymanager_mtls"] +keymanager_mtls = ["reqwest/rustls-tls","common_utils/keymanager_mtls"] +encryption_service = ["hyperswitch_domain_models/encryption_service", "common_utils/encryption_service"] email = ["external_services/email", "scheduler/email", "olap"] keymanager_create = [] frm = ["api_models/frm", "hyperswitch_domain_models/frm"] stripe = ["dep:serde_qs"] -release = ["stripe", "email", "accounts_cache", "kv_store", "vergen", "recon", "external_services/aws_kms", "external_services/aws_s3", "keymanager_mtls", "keymanager_create"] +release = ["stripe", "email", "accounts_cache", "kv_store", "vergen", "recon", "external_services/aws_kms", "external_services/aws_s3", "keymanager_mtls", "keymanager_create", "encryption_service"] olap = ["hyperswitch_domain_models/olap", "storage_impl/olap", "scheduler/olap", "api_models/olap", "dep:analytics"] oltp = ["storage_impl/oltp"] kv_store = ["scheduler/kv_store"] @@ -114,7 +115,7 @@ analytics = { version = "0.1.0", path = "../analytics", optional = true } api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] } cards = { version = "0.1.0", path = "../cards" } common_enums = { version = "0.1.0", path = "../common_enums" } -common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext", "logs", "metrics", "keymanager"] } +common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext", "logs", "metrics", "keymanager", "encryption_service"] } currency_conversion = { version = "0.1.0", path = "../currency_conversion" } diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] } euclid = { version = "0.1.0", path = "../euclid", features = ["valued_jit"] } diff --git a/crates/router/src/bin/scheduler.rs b/crates/router/src/bin/scheduler.rs index 61f9c473a2..269c7d5b3b 100644 --- a/crates/router/src/bin/scheduler.rs +++ b/crates/router/src/bin/scheduler.rs @@ -38,7 +38,11 @@ async fn main() -> CustomResult<(), ProcessTrackerError> { let api_client = Box::new( services::ProxyClient::new( conf.proxy.clone(), - services::proxy_bypass_urls(&conf.locker, &conf.proxy.bypass_proxy_urls), + services::proxy_bypass_urls( + conf.key_manager.get_inner(), + &conf.locker, + &conf.proxy.bypass_proxy_urls, + ), ) .change_context(ProcessTrackerError::ConfigurationError)?, ); diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index d3091875f4..bdf8db2828 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -2,6 +2,7 @@ pub mod opensearch; #[cfg(feature = "olap")] pub mod user; pub mod user_role; +use common_utils::consts; pub use hyperswitch_interfaces::consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE}; // ID generation pub(crate) const ID_LENGTH: usize = 20; @@ -43,8 +44,9 @@ pub(crate) const CANNOT_CONTINUE_AUTH: &str = pub(crate) const DEFAULT_NOTIFICATION_SCRIPT_LANGUAGE: &str = "en-US"; // General purpose base64 engines -pub(crate) const BASE64_ENGINE: base64::engine::GeneralPurpose = - base64::engine::general_purpose::STANDARD; + +pub(crate) const BASE64_ENGINE: base64::engine::GeneralPurpose = consts::BASE64_ENGINE; + pub(crate) const BASE64_ENGINE_URL_SAFE: base64::engine::GeneralPurpose = base64::engine::general_purpose::URL_SAFE; diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 8a7854d658..a851be95d5 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -8,9 +8,8 @@ use common_utils::{ date_time, ext_traits::{AsyncExt, ConfigExt, Encode, ValueExt}, id_type, pii, + types::keymanager as km_types, }; -#[cfg(all(feature = "keymanager_create", feature = "olap"))] -use common_utils::{keymanager, types::keymanager as km_types}; use diesel_models::configs; use error_stack::{report, FutureExt, ResultExt}; use futures::future::try_join_all; @@ -111,6 +110,9 @@ pub async fn create_merchant_account( state: SessionState, req: api::MerchantAccountCreate, ) -> RouterResponse { + #[cfg(feature = "keymanager_create")] + use common_utils::keymanager; + let db = state.store.as_ref(); let key = services::generate_aes256_key() @@ -119,27 +121,16 @@ pub async fn create_merchant_account( let master_key = db.get_master_key(); + let key_manager_state = &(&state).into(); let merchant_id = req.get_merchant_reference_id().get_string_repr().to_owned(); - - let key_store = domain::MerchantKeyStore { - merchant_id: merchant_id.clone(), - key: domain_types::encrypt(key.to_vec().into(), master_key) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to decrypt data from key store")?, - created_at: date_time::now(), - }; - - let domain_merchant_account = req - .create_domain_model_from_request(db, key_store.clone()) - .await?; + let identifier = km_types::Identifier::Merchant(merchant_id.clone()); #[cfg(feature = "keymanager_create")] { keymanager::create_key_in_key_manager( - &(&state).into(), + key_manager_state, km_types::EncryptionCreateRequest { - identifier: km_types::Identifier::Merchant(merchant_id.clone()), + identifier: identifier.clone(), }, ) .await @@ -147,12 +138,34 @@ pub async fn create_merchant_account( .attach_printable("Failed to insert key to KeyManager")?; } - db.insert_merchant_key_store(key_store.clone(), &master_key.to_vec().into()) + let key_store = domain::MerchantKeyStore { + merchant_id: merchant_id.clone(), + key: domain_types::encrypt( + key_manager_state, + key.to_vec().into(), + identifier.clone(), + master_key, + ) .await - .to_duplicate_response(errors::ApiErrorResponse::DuplicateMerchantAccount)?; + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to decrypt data from key store")?, + created_at: date_time::now(), + }; + + let domain_merchant_account = req + .create_domain_model_from_request(&state, key_store.clone()) + .await?; + let key_manager_state = &(&state).into(); + db.insert_merchant_key_store( + key_manager_state, + key_store.clone(), + &master_key.to_vec().into(), + ) + .await + .to_duplicate_response(errors::ApiErrorResponse::DuplicateMerchantAccount)?; let merchant_account = db - .insert_merchant(domain_merchant_account, &key_store) + .insert_merchant(key_manager_state, domain_merchant_account, &key_store) .await .to_duplicate_response(errors::ApiErrorResponse::DuplicateMerchantAccount)?; @@ -172,7 +185,7 @@ pub async fn create_merchant_account( trait MerchantAccountCreateBridge { async fn create_domain_model_from_request( self, - db: &dyn StorageInterface, + state: &SessionState, key: domain::MerchantKeyStore, ) -> RouterResult; } @@ -182,9 +195,10 @@ trait MerchantAccountCreateBridge { impl MerchantAccountCreateBridge for api::MerchantAccountCreate { async fn create_domain_model_from_request( self, - db: &dyn StorageInterface, + state: &SessionState, key_store: domain::MerchantKeyStore, ) -> RouterResult { + let db = &*state.store; let publishable_key = create_merchant_publishable_key(); let primary_business_details = self.get_primary_details_as_value().change_context( @@ -229,7 +243,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { let payment_response_hash_key = self.get_payment_response_hash_key(); let parent_merchant_id = get_parent_merchant( - db, + state, self.sub_merchants_enabled, self.parent_merchant_id, &key_store, @@ -241,6 +255,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { .await?; let key = key_store.key.clone().into_inner(); + let key_manager_state = state.into(); let mut merchant_account = async { Ok::<_, error_stack::Report>( @@ -248,10 +263,24 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { merchant_id: self.merchant_id.get_string_repr().to_owned(), merchant_name: self .merchant_name - .async_lift(|inner| domain_types::encrypt_optional(inner, key.peek())) + .async_lift(|inner| { + domain_types::encrypt_optional( + &key_manager_state, + inner, + km_types::Identifier::Merchant(key_store.merchant_id.clone()), + key.peek(), + ) + }) .await?, merchant_details: merchant_details - .async_lift(|inner| domain_types::encrypt_optional(inner, key.peek())) + .async_lift(|inner| { + domain_types::encrypt_optional( + &key_manager_state, + inner, + km_types::Identifier::Merchant(key_store.merchant_id.clone()), + key.peek(), + ) + }) .await?, return_url: self.return_url.map(|a| a.to_string()), webhook_details, @@ -293,7 +322,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { .change_context(errors::ApiErrorResponse::InternalServerError)?; CreateBusinessProfile::new(self.primary_business_details.clone()) - .create_business_profiles(db, &mut merchant_account, &key_store) + .create_business_profiles(state, &mut merchant_account, &key_store) .await?; Ok(merchant_account) @@ -385,7 +414,7 @@ impl CreateBusinessProfile { async fn create_business_profiles( &self, - db: &dyn StorageInterface, + state: &SessionState, merchant_account: &mut domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> RouterResult<()> { @@ -394,7 +423,7 @@ impl CreateBusinessProfile { primary_business_details, } => { let business_profiles = Self::create_business_profiles_for_each_business_details( - db, + state, merchant_account.clone(), primary_business_details, key_store, @@ -410,7 +439,7 @@ impl CreateBusinessProfile { } Self::CreateDefaultBusinessProfile => { let business_profile = self - .create_default_business_profile(db, merchant_account.clone(), key_store) + .create_default_business_profile(state, merchant_account.clone(), key_store) .await?; merchant_account.default_profile = Some(business_profile.profile_id); @@ -423,12 +452,12 @@ impl CreateBusinessProfile { /// Create default business profile async fn create_default_business_profile( &self, - db: &dyn StorageInterface, + state: &SessionState, merchant_account: domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> RouterResult { let business_profile = create_and_insert_business_profile( - db, + state, api_models::admin::BusinessProfileCreate::default(), merchant_account.clone(), key_store, @@ -442,7 +471,7 @@ impl CreateBusinessProfile { /// If there is no default profile in merchant account and only one primary_business_detail /// is available, then create a default business profile. async fn create_business_profiles_for_each_business_details( - db: &dyn StorageInterface, + state: &SessionState, merchant_account: domain::MerchantAccount, primary_business_details: &Vec, key_store: &domain::MerchantKeyStore, @@ -462,7 +491,7 @@ impl CreateBusinessProfile { }; create_and_insert_business_profile( - db, + state, business_profile_create_request, merchant_account.clone(), key_store, @@ -486,10 +515,11 @@ impl CreateBusinessProfile { impl MerchantAccountCreateBridge for api::MerchantAccountCreate { async fn create_domain_model_from_request( self, - db: &dyn StorageInterface, + state: &SessionState, key_store: domain::MerchantKeyStore, ) -> RouterResult { let publishable_key = create_merchant_publishable_key(); + let db = &*state.store; let metadata = self.get_metadata_as_secret().change_context( errors::ApiErrorResponse::InvalidDataValue { @@ -514,24 +544,36 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { .await?; let key = key_store.key.into_inner(); + let merchant_id = self + .get_merchant_reference_id() + .get_string_repr() + .to_owned(); + let key_manager_state = state.into(); + let identifier = km_types::Identifier::Merchant(merchant_id.clone()); async { Ok::<_, error_stack::Report>( domain::MerchantAccount { - merchant_id: self - .get_merchant_reference_id() - .get_string_repr() - .to_owned(), + merchant_id, merchant_name: Some( domain_types::encrypt( + &key_manager_state, self.merchant_name .map(|merchant_name| merchant_name.into_inner()), + identifier.clone(), key.peek(), ) .await?, ), merchant_details: merchant_details - .async_lift(|inner| domain_types::encrypt_optional(inner, key.peek())) + .async_lift(|inner| { + domain_types::encrypt_optional( + &key_manager_state, + inner, + identifier.clone(), + key.peek(), + ) + }) .await?, return_url: None, webhook_details: None, @@ -577,7 +619,7 @@ pub async fn list_merchant_account( ) -> RouterResponse> { let merchant_accounts = state .store - .list_merchant_accounts_by_organization_id(&req.organization_id) + .list_merchant_accounts_by_organization_id(&(&state).into(), &req.organization_id) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -600,8 +642,10 @@ pub async fn get_merchant_account( req: api::MerchantId, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, &req.merchant_id, &db.get_master_key().to_vec().into(), ) @@ -609,7 +653,7 @@ pub async fn get_merchant_account( .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_account = db - .find_merchant_account_by_merchant_id(&req.merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &req.merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -623,13 +667,15 @@ pub async fn get_merchant_account( /// For backwards compatibility, whenever new business labels are passed in /// primary_business_details, create a business profile pub async fn create_business_profile_from_business_labels( + state: &SessionState, db: &dyn StorageInterface, key_store: &domain::MerchantKeyStore, merchant_id: &str, new_business_details: Vec, ) -> RouterResult<()> { + let key_manager_state = &state.into(); let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id, key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -657,7 +703,7 @@ pub async fn create_business_profile_from_business_labels( }; let business_profile_create_result = create_and_insert_business_profile( - db, + state, business_profile_create_request, merchant_account.clone(), key_store, @@ -673,9 +719,14 @@ pub async fn create_business_profile_from_business_labels( // If a business_profile is created, then unset the default profile if business_profile_create_result.is_ok() && merchant_account.default_profile.is_some() { let unset_default_profile = domain::MerchantAccountUpdate::UnsetDefaultProfile; - db.update_merchant(merchant_account.clone(), unset_default_profile, key_store) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + db.update_merchant( + key_manager_state, + merchant_account.clone(), + unset_default_profile, + key_store, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; } } @@ -758,8 +809,10 @@ pub async fn merchant_account_update( req: api::MerchantAccountUpdate, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, &req.merchant_id, &db.get_master_key().to_vec().into(), ) @@ -817,6 +870,7 @@ pub async fn merchant_account_update( .clone() .async_map(|primary_business_details| async { let _ = create_business_profile_from_business_labels( + &state, db, &key_store, merchant_id, @@ -845,11 +899,14 @@ pub async fn merchant_account_update( // Update the business profile, This is for backwards compatibility update_business_profile_cascade(state.clone(), req.clone(), merchant_id.to_string()).await?; + let identifier = km_types::Identifier::Merchant(key_store.merchant_id.clone()); let updated_merchant_account = storage::MerchantAccountUpdate::Update { merchant_name: req .merchant_name .map(Secret::new) - .async_lift(|inner| domain_types::encrypt_optional(inner, key)) + .async_lift(|inner| { + domain_types::encrypt_optional(key_manager_state, inner, identifier.clone(), key) + }) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt merchant name")?, @@ -862,7 +919,9 @@ pub async fn merchant_account_update( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to convert merchant_details to a value")? .map(Secret::new) - .async_lift(|inner| domain_types::encrypt_optional(inner, key)) + .async_lift(|inner| { + domain_types::encrypt_optional(key_manager_state, inner, identifier.clone(), key) + }) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt merchant details")?, @@ -880,7 +939,7 @@ pub async fn merchant_account_update( sub_merchants_enabled: req.sub_merchants_enabled, parent_merchant_id: get_parent_merchant( - db, + &state, req.sub_merchants_enabled, req.parent_merchant_id, &key_store, @@ -905,7 +964,12 @@ pub async fn merchant_account_update( }; let response = db - .update_specific_fields_in_merchant(merchant_id, updated_merchant_account, &key_store) + .update_specific_fields_in_merchant( + key_manager_state, + merchant_id, + updated_merchant_account, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -924,9 +988,10 @@ pub async fn merchant_account_delete( ) -> RouterResponse { let mut is_deleted = false; let db = state.store.as_ref(); - + let key_manager_state = &(&state).into(); let merchant_key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -934,7 +999,7 @@ pub async fn merchant_account_delete( .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &merchant_key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &merchant_key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -988,7 +1053,7 @@ pub async fn merchant_account_delete( } async fn get_parent_merchant( - db: &dyn StorageInterface, + state: &SessionState, sub_merchants_enabled: Option, parent_merchant: Option, key_store: &domain::MerchantKeyStore, @@ -1004,7 +1069,7 @@ async fn get_parent_merchant( message: "If `sub_merchants_enabled` is `true`, then `parent_merchant_id` is mandatory".to_string(), }) }) - .map(|id| validate_merchant_id(db, id,key_store).change_context( + .map(|id| validate_merchant_id(state, id,key_store).change_context( errors::ApiErrorResponse::InvalidDataValue { field_name: "parent_merchant_id" } ))? .await? @@ -1016,11 +1081,12 @@ async fn get_parent_merchant( } async fn validate_merchant_id>( - db: &dyn StorageInterface, + state: &SessionState, merchant_id: S, key_store: &domain::MerchantKeyStore, ) -> RouterResult { - db.find_merchant_account_by_merchant_id(&merchant_id.into(), key_store) + let db = &*state.store; + db.find_merchant_account_by_merchant_id(&state.into(), &merchant_id.into(), key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound) } @@ -1066,10 +1132,12 @@ pub async fn create_payment_connector( merchant_id: &String, ) -> RouterResponse { let store = state.store.as_ref(); + let key_manager_state = &(&state).into(); #[cfg(feature = "dummy_connector")] validate_dummy_connector_enabled(&state, &req.connector_name).await?; let key_store = store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -1083,7 +1151,7 @@ pub async fn create_payment_connector( let merchant_account = state .store - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1221,6 +1289,7 @@ pub async fn create_payment_connector( state .store .update_specific_fields_in_merchant( + key_manager_state, merchant_id, storage::MerchantAccountUpdate::ModifiedAtUpdate, &key_store, @@ -1242,7 +1311,7 @@ pub async fn create_payment_connector( if let Some(val) = req.pm_auth_config.clone() { validate_pm_auth( val, - &*state.clone().store, + &state, merchant_id.clone().as_str(), &key_store, merchant_account, @@ -1256,14 +1325,15 @@ pub async fn create_payment_connector( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while encoding ConnectorAuthType to serde_json::Value")?; let conn_auth = Secret::new(connector_auth); - + let identifier = km_types::Identifier::Merchant(key_store.merchant_id.clone()); let merchant_connector_account = domain::MerchantConnectorAccount { merchant_id: merchant_id.to_string(), connector_type: req.connector_type, connector_name: req.connector_name.to_string(), merchant_connector_id: utils::generate_id(consts::ID_LENGTH, "mca"), - connector_account_details: domain_types::encrypt( + connector_account_details: domain_types::encrypt(key_manager_state, conn_auth, + identifier.clone(), key_store.key.peek(), ) .await @@ -1296,10 +1366,11 @@ pub async fn create_payment_connector( applepay_verified_domains: None, pm_auth_config: req.pm_auth_config.clone(), status: connector_status, - connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details(&key_store, &req.metadata).await?, + connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details(&state, &key_store, &req.metadata).await?, additional_merchant_data: if let Some(mcd) = merchant_recipient_data { - Some(domain_types::encrypt( + Some(domain_types::encrypt(key_manager_state, Secret::new(mcd), + identifier, key_store.key.peek(), ) .await @@ -1329,7 +1400,11 @@ pub async fn create_payment_connector( let mca = state .store - .insert_merchant_connector_account(merchant_connector_account, &key_store) + .insert_merchant_connector_account( + key_manager_state, + merchant_connector_account, + &key_store, + ) .await .to_duplicate_response( errors::ApiErrorResponse::DuplicateMerchantConnectorAccount { @@ -1382,7 +1457,7 @@ pub async fn create_payment_connector( async fn validate_pm_auth( val: serde_json::Value, - db: &dyn StorageInterface, + state: &SessionState, merchant_id: &str, key_store: &domain::MerchantKeyStore, merchant_account: domain::MerchantAccount, @@ -1394,8 +1469,10 @@ async fn validate_pm_auth( }) .attach_printable("Failed to deserialize Payment Method Auth config")?; - let all_mcas = db + let all_mcas = &*state + .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), merchant_id, true, key_store, @@ -1407,8 +1484,7 @@ async fn validate_pm_auth( for conn_choice in config.enabled_payment_methods { let pm_auth_mca = all_mcas - .clone() - .into_iter() + .iter() .find(|mca| mca.merchant_connector_id == conn_choice.mca_id) .ok_or(errors::ApiErrorResponse::GenericNotFoundError { message: "payment method auth connector account not found".to_string(), @@ -1432,8 +1508,10 @@ pub async fn retrieve_payment_connector( merchant_connector_id: String, ) -> RouterResponse { let store = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = store .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id, &store.get_master_key().to_vec().into(), ) @@ -1441,12 +1519,13 @@ pub async fn retrieve_payment_connector( .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let _merchant_account = store - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let mca = store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &merchant_id, &merchant_connector_id, &key_store, @@ -1464,8 +1543,10 @@ pub async fn list_payment_connectors( merchant_id: String, ) -> RouterResponse> { let store = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = store .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id, &store.get_master_key().to_vec().into(), ) @@ -1474,12 +1555,13 @@ pub async fn list_payment_connectors( // Validate merchant account store - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_connector_accounts = store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + key_manager_state, &merchant_id, true, &key_store, @@ -1503,18 +1585,24 @@ pub async fn update_payment_connector( req: api_models::admin::MerchantConnectorUpdate, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, merchant_id, merchant_connector_id, &key_store, @@ -1559,7 +1647,7 @@ pub async fn update_payment_connector( if let Some(val) = req.pm_auth_config.clone() { validate_pm_auth( val, - db, + &state, merchant_id, &key_store, merchant_account, @@ -1568,7 +1656,6 @@ pub async fn update_payment_connector( .await?; } } - let payment_connector = storage::MerchantConnectorAccountUpdate::Update { merchant_id: None, connector_type: Some(req.connector_type), @@ -1578,7 +1665,12 @@ pub async fn update_payment_connector( connector_account_details: req .connector_account_details .async_lift(|inner| { - domain_types::encrypt_optional(inner, key_store.key.get_inner().peek()) + domain_types::encrypt_optional( + key_manager_state, + inner, + km_types::Identifier::Merchant(key_store.merchant_id.clone()), + key_store.key.get_inner().peek(), + ) }) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1600,7 +1692,7 @@ pub async fn update_payment_connector( pm_auth_config: req.pm_auth_config, status: Some(connector_status), connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details( - &key_store, &metadata, + &state, &key_store, &metadata, ) .await?, }; @@ -1615,7 +1707,12 @@ pub async fn update_payment_connector( let request_connector_label = req.connector_label; let updated_mca = db - .update_merchant_connector_account(mca, payment_connector.into(), &key_store) + .update_merchant_connector_account( + key_manager_state, + mca, + payment_connector.into(), + &key_store, + ) .await .change_context( errors::ApiErrorResponse::DuplicateMerchantConnectorAccount { @@ -1638,18 +1735,24 @@ pub async fn delete_payment_connector( merchant_connector_id: String, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let _merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let _mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &merchant_id, &merchant_connector_id, &key_store, @@ -1683,14 +1786,19 @@ pub async fn kv_for_merchant( enable: bool, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; // check if the merchant account exists let merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1707,6 +1815,7 @@ pub async fn kv_for_merchant( } db.update_merchant( + key_manager_state, merchant_account, storage::MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme: MerchantStorageScheme::RedisKv, @@ -1717,6 +1826,7 @@ pub async fn kv_for_merchant( } (false, MerchantStorageScheme::RedisKv) => { db.update_merchant( + key_manager_state, merchant_account, storage::MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme: MerchantStorageScheme::PostgresOnly, @@ -1779,14 +1889,19 @@ pub async fn check_merchant_account_kv_status( merchant_id: String, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; // check if the merchant account exists let merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1825,17 +1940,19 @@ pub fn get_frm_config_as_secret( } pub async fn create_and_insert_business_profile( - db: &dyn StorageInterface, + state: &SessionState, request: api::BusinessProfileCreate, merchant_account: domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> RouterResult { let business_profile_new = - admin::create_business_profile(merchant_account, request, key_store).await?; + admin::create_business_profile(state, merchant_account, request, key_store).await?; let profile_name = business_profile_new.profile_name.clone(); - db.insert_business_profile(business_profile_new) + state + .store + .insert_business_profile(business_profile_new) .await .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError { message: format!( @@ -1859,15 +1976,20 @@ pub async fn create_business_profile( } let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; // Get the merchant account, if few fields are not passed, then they will be inherited from // merchant account let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1882,18 +2004,23 @@ pub async fn create_business_profile( } let business_profile = - create_and_insert_business_profile(db, request, merchant_account.clone(), &key_store) + create_and_insert_business_profile(&state, request, merchant_account.clone(), &key_store) .await?; if merchant_account.default_profile.is_some() { let unset_default_profile = domain::MerchantAccountUpdate::UnsetDefaultProfile; - db.update_merchant(merchant_account, unset_default_profile, &key_store) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + db.update_merchant( + key_manager_state, + merchant_account, + unset_default_profile, + &key_store, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; } Ok(service_api::ApplicationResponse::Json( - admin::business_profile_response(business_profile, &key_store) + admin::business_profile_response(&state, business_profile, &key_store) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse business profile details") .await?, @@ -1906,7 +2033,11 @@ pub async fn list_business_profile( ) -> RouterResponse> { let db = state.store.as_ref(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + &(&state).into(), + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let profiles = db @@ -1916,7 +2047,7 @@ pub async fn list_business_profile( .clone(); let mut business_profiles = Vec::new(); for profile in profiles { - let business_profile = admin::business_profile_response(profile, &key_store) + let business_profile = admin::business_profile_response(&state, profile, &key_store) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse business profile details")?; @@ -1933,7 +2064,11 @@ pub async fn retrieve_business_profile( ) -> RouterResponse { let db = state.store.as_ref(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + &(&state).into(), + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let business_profile = db @@ -1944,7 +2079,7 @@ pub async fn retrieve_business_profile( })?; Ok(service_api::ApplicationResponse::Json( - admin::business_profile_response(business_profile, &key_store) + admin::business_profile_response(&state, business_profile, &key_store) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse business profile details") .await?, @@ -1982,6 +2117,7 @@ pub async fn update_business_profile( })?; let key_store = db .get_merchant_key_store_by_merchant_id( + &(&state).into(), merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -2051,7 +2187,7 @@ pub async fn update_business_profile( .map(Secret::new); let outgoing_webhook_custom_http_headers = request .outgoing_webhook_custom_http_headers - .async_map(|headers| create_encrypted_data(&key_store, headers)) + .async_map(|headers| create_encrypted_data(&state, &key_store, headers)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -2119,7 +2255,7 @@ pub async fn update_business_profile( })?; Ok(service_api::ApplicationResponse::Json( - admin::business_profile_response(updated_business_profile, &key_store) + admin::business_profile_response(&state, updated_business_profile, &key_store) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse business profile details") .await?, diff --git a/crates/router/src/core/api_keys.rs b/crates/router/src/core/api_keys.rs index df8a55cd71..6af82a6b7a 100644 --- a/crates/router/src/core/api_keys.rs +++ b/crates/router/src/core/api_keys.rs @@ -122,6 +122,7 @@ pub async fn create_api_key( // non-existence of a merchant account. store .get_merchant_key_store_by_merchant_id( + &(&state).into(), merchant_id.as_str(), &store.get_master_key().to_vec().into(), ) diff --git a/crates/router/src/core/apple_pay_certificates_migration.rs b/crates/router/src/core/apple_pay_certificates_migration.rs index 327358bda5..87c1593571 100644 --- a/crates/router/src/core/apple_pay_certificates_migration.rs +++ b/crates/router/src/core/apple_pay_certificates_migration.rs @@ -1,5 +1,5 @@ use api_models::apple_pay_certificates_migration; -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, types::keymanager::Identifier}; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -28,11 +28,12 @@ pub async fn apple_pay_certificates_migration( let mut migration_successful_merchant_ids = vec![]; let mut migration_failed_merchant_ids = vec![]; - + let key_manager_state = &(&state).into(); for merchant_id in merchant_id_list { let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -41,6 +42,7 @@ pub async fn apple_pay_certificates_migration( let merchant_connector_accounts = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( + key_manager_state, merchant_id, true, &key_store, @@ -63,11 +65,13 @@ pub async fn apple_pay_certificates_migration( .ok(); if let Some(apple_pay_metadata) = connector_apple_pay_metadata { let encrypted_apple_pay_metadata = domain_types::encrypt( + &(&state).into(), Secret::new( serde_json::to_value(apple_pay_metadata) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to serialize apple pay metadata as JSON")?, ), + Identifier::Merchant(merchant_id.clone()), key_store.key.get_inner().peek(), ) .await diff --git a/crates/router/src/core/blocklist/utils.rs b/crates/router/src/core/blocklist/utils.rs index 97324effa8..c739a86ee5 100644 --- a/crates/router/src/core/blocklist/utils.rs +++ b/crates/router/src/core/blocklist/utils.rs @@ -398,6 +398,7 @@ where if should_payment_be_blocked { // Update db for attempt and intent status. db.update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), storage::PaymentIntentUpdate::RejectUpdate { status: common_enums::IntentStatus::Failed, diff --git a/crates/router/src/core/cards_info.rs b/crates/router/src/core/cards_info.rs index b0c1aca532..98719b5a1e 100644 --- a/crates/router/src/core/cards_info.rs +++ b/crates/router/src/core/cards_info.rs @@ -30,7 +30,7 @@ pub async fn retrieve_card_info( verify_iin_length(&request.card_iin)?; helpers::verify_payment_intent_time_and_client_secret( - db, + &state, &merchant_account, &key_store, request.client_secret, diff --git a/crates/router/src/core/conditional_config.rs b/crates/router/src/core/conditional_config.rs index f740c6dfcc..c34f8e7116 100644 --- a/crates/router/src/core/conditional_config.rs +++ b/crates/router/src/core/conditional_config.rs @@ -102,7 +102,7 @@ pub async fn upsert_conditional_config( algo_id.update_conditional_config_id(key.clone()); let config_key = cache::CacheKind::DecisionManager(key.into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update routing algorithm ref")?; @@ -138,7 +138,7 @@ pub async fn upsert_conditional_config( algo_id.update_conditional_config_id(key.clone()); let config_key = cache::CacheKind::DecisionManager(key.into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update routing algorithm ref")?; @@ -168,7 +168,7 @@ pub async fn delete_conditional_config( .unwrap_or_default(); algo_id.config_algo_id = None; let config_key = cache::CacheKind::DecisionManager(key.clone().into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update deleted algorithm ref")?; diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index c0e0291b8c..e9126200ac 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -1,10 +1,12 @@ +use api_models::customers::CustomerRequestWithEmail; use common_utils::{ crypto::{Encryptable, GcmAes256}, errors::ReportSwitchExt, ext_traits::OptionExt, + types::keymanager::{Identifier, ToEncryptable}, }; use error_stack::{report, ResultExt}; -use masking::ExposeInterface; +use masking::{Secret, SwitchStrategy}; use router_env::{instrument, tracing}; use crate::{ @@ -19,7 +21,7 @@ use crate::{ api::customers, domain::{ self, - types::{self, AsyncLift, TypeEncryption}, + types::{self, TypeEncryption}, }, storage::{self, enums}, }, @@ -43,6 +45,7 @@ pub async fn create_customer( let merchant_id = &merchant_account.merchant_id; merchant_id.clone_into(&mut customer_data.merchant_id); + let key_manager_state = &(&state).into(); // We first need to validate whether the customer with the given customer id already exists // this may seem like a redundant db call, as the insert_customer will anyway return this error @@ -51,6 +54,7 @@ pub async fn create_customer( // it errors out, now the address that was inserted is not deleted match db .find_customer_by_customer_id_merchant_id( + key_manager_state, &customer_id, merchant_id, &key_store, @@ -76,6 +80,7 @@ pub async fn create_customer( let address = customer_data .get_domain_address( + &state, customer_address, merchant_id, &customer_id, @@ -87,7 +92,7 @@ pub async fn create_customer( .attach_printable("Failed while encrypting address")?; Some( - db.insert_address_for_customers(address, &key_store) + db.insert_address_for_customers(key_manager_state, address, &key_store) .await .switch() .attach_printable("Failed while inserting new address")?, @@ -96,40 +101,46 @@ pub async fn create_customer( None }; - let new_customer = async { - Ok(domain::Customer { - customer_id: customer_id.to_owned(), - merchant_id: merchant_id.to_string(), - name: customer_data - .name - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - email: customer_data - .email - .async_lift(|inner| types::encrypt_optional(inner.map(|inner| inner.expose()), key)) - .await?, - phone: customer_data - .phone - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - description: customer_data.description, - phone_country_code: customer_data.phone_country_code, - metadata: customer_data.metadata, - id: None, - connector_customer: None, - address_id: address.clone().map(|addr| addr.address_id), - created_at: common_utils::date_time::now(), - modified_at: common_utils::date_time::now(), - default_payment_method_id: None, - updated_by: None, - }) - } + let encrypted_data = types::batch_encrypt( + &(&state).into(), + CustomerRequestWithEmail::to_encryptable(CustomerRequestWithEmail { + name: customer_data.name.clone(), + email: customer_data.email.clone(), + phone: customer_data.phone.clone(), + }), + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) .await .switch() .attach_printable("Failed while encrypting Customer")?; + let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data) + .change_context(errors::CustomersErrorResponse::InternalServerError)?; + let new_customer = domain::Customer { + customer_id: customer_id.to_owned(), + merchant_id: merchant_id.to_string(), + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: encryptable_customer.phone, + description: customer_data.description, + phone_country_code: customer_data.phone_country_code, + metadata: customer_data.metadata, + id: None, + connector_customer: None, + address_id: address.clone().map(|addr| addr.address_id), + created_at: common_utils::date_time::now(), + modified_at: common_utils::date_time::now(), + default_payment_method_id: None, + updated_by: None, + }; let customer = db - .insert_customer(new_customer, &key_store, merchant_account.storage_scheme) + .insert_customer( + key_manager_state, + new_customer, + &key_store, + merchant_account.storage_scheme, + ) .await .to_duplicate_response(errors::CustomersErrorResponse::CustomerAlreadyExists)?; @@ -148,8 +159,10 @@ pub async fn retrieve_customer( req: customers::CustomerId, ) -> errors::CustomerResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let response = db .find_customer_by_customer_id_merchant_id( + key_manager_state, &req.customer_id, &merchant_account.merchant_id, &key_store, @@ -159,7 +172,7 @@ pub async fn retrieve_customer( .switch()?; let address = match &response.address_id { Some(address_id) => Some(api_models::payments::AddressDetails::from( - db.find_address_by_address_id(address_id, &key_store) + db.find_address_by_address_id(key_manager_state, address_id, &key_store) .await .switch()?, )), @@ -179,7 +192,7 @@ pub async fn list_customers( let db = state.store.as_ref(); let domain_customers = db - .list_customers_by_merchant_id(&merchant_id, &key_store) + .list_customers_by_merchant_id(&(&state).into(), &merchant_id, &key_store) .await .switch()?; @@ -199,9 +212,10 @@ pub async fn delete_customer( key_store: domain::MerchantKeyStore, ) -> errors::CustomerResponse { let db = &state.store; - + let key_manager_state = &(&state).into(); let customer_orig = db .find_customer_by_customer_id_merchant_id( + key_manager_state, &req.customer_id, &merchant_account.merchant_id, &key_store, @@ -262,17 +276,24 @@ pub async fn delete_customer( }; let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let redacted_encrypted_value: Encryptable> = Encryptable::encrypt_via_api( + key_manager_state, + REDACTED.to_string().into(), + identifier.clone(), + key, + GcmAes256, + ) + .await + .switch()?; - let redacted_encrypted_value: Encryptable> = - Encryptable::encrypt(REDACTED.to_string().into(), key, GcmAes256) - .await - .switch()?; - - let redacted_encrypted_email: Encryptable< - masking::Secret<_, common_utils::pii::EmailStrategy>, - > = Encryptable::encrypt(REDACTED.to_string().into(), key, GcmAes256) - .await - .switch()?; + let redacted_encrypted_email = Encryptable::new( + redacted_encrypted_value + .clone() + .into_inner() + .switch_strategy(), + redacted_encrypted_value.clone().into_encrypted(), + ); let update_address = storage::AddressUpdate::Update { city: Some(REDACTED.to_string()), @@ -292,6 +313,7 @@ pub async fn delete_customer( match db .update_address_by_merchant_id_customer_id( + key_manager_state, &req.customer_id, &merchant_account.merchant_id, update_address, @@ -314,9 +336,15 @@ pub async fn delete_customer( let updated_customer = storage::CustomerUpdate::Update { name: Some(redacted_encrypted_value.clone()), email: Some( - Encryptable::encrypt(REDACTED.to_string().into(), key, GcmAes256) - .await - .switch()?, + Encryptable::encrypt_via_api( + key_manager_state, + REDACTED.to_string().into(), + identifier, + key, + GcmAes256, + ) + .await + .switch()?, ), phone: Box::new(Some(redacted_encrypted_value.clone())), description: Some(REDACTED.to_string()), @@ -326,6 +354,7 @@ pub async fn delete_customer( address_id: None, }; db.update_customer_by_customer_id_merchant_id( + key_manager_state, req.customer_id.clone(), merchant_account.merchant_id, customer_orig, @@ -362,9 +391,10 @@ pub async fn update_customer( .get_required_value("customer_id") .change_context(errors::CustomersErrorResponse::InternalServerError) .attach("Missing required field `customer_id`")?; - + let key_manager_state = &(&state).into(); let customer = db .find_customer_by_customer_id_merchant_id( + key_manager_state, customer_id, &merchant_account.merchant_id, &key_store, @@ -380,12 +410,18 @@ pub async fn update_customer( Some(address_id) => { let customer_address: api_models::payments::AddressDetails = addr.clone(); let update_address = update_customer - .get_address_update(customer_address, key, merchant_account.storage_scheme) + .get_address_update( + &state, + customer_address, + key, + merchant_account.storage_scheme, + merchant_account.merchant_id.clone(), + ) .await .switch() .attach_printable("Failed while encrypting Address while Update")?; Some( - db.update_address(address_id, update_address, &key_store) + db.update_address(key_manager_state, address_id, update_address, &key_store) .await .switch() .attach_printable(format!( @@ -399,6 +435,7 @@ pub async fn update_customer( let address = update_customer .get_domain_address( + &state, customer_address, &merchant_account.merchant_id, &customer.customer_id, @@ -409,7 +446,7 @@ pub async fn update_customer( .switch() .attach_printable("Failed while encrypting address")?; Some( - db.insert_address_for_customers(address, &key_store) + db.insert_address_for_customers(key_manager_state, address, &key_store) .await .switch() .attach_printable("Failed while inserting new address")?, @@ -419,47 +456,44 @@ pub async fn update_customer( } else { match &customer.address_id { Some(address_id) => Some( - db.find_address_by_address_id(address_id, &key_store) + db.find_address_by_address_id(key_manager_state, address_id, &key_store) .await .switch()?, ), None => None, } }; + let encrypted_data = types::batch_encrypt( + &(&state).into(), + CustomerRequestWithEmail::to_encryptable(CustomerRequestWithEmail { + name: update_customer.name.clone(), + email: update_customer.email.clone(), + phone: update_customer.phone.clone(), + }), + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + .await + .switch()?; + let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data) + .change_context(errors::CustomersErrorResponse::InternalServerError)?; let response = db .update_customer_by_customer_id_merchant_id( + key_manager_state, customer_id.to_owned(), merchant_account.merchant_id.to_owned(), customer, - async { - Ok(storage::CustomerUpdate::Update { - name: update_customer - .name - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - email: update_customer - .email - .async_lift(|inner| { - types::encrypt_optional(inner.map(|inner| inner.expose()), key) - }) - .await?, - phone: Box::new( - update_customer - .phone - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - ), - phone_country_code: update_customer.phone_country_code, - metadata: update_customer.metadata, - description: update_customer.description, - connector_customer: None, - address_id: address.clone().map(|addr| addr.address_id), - }) - } - .await - .switch() - .attach_printable("Failed while encrypting while updating customer")?, + storage::CustomerUpdate::Update { + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: Box::new(encryptable_customer.phone), + phone_country_code: update_customer.phone_country_code, + metadata: update_customer.metadata, + description: update_customer.description, + connector_customer: None, + address_id: address.clone().map(|addr| addr.address_id), + }, &key_store, merchant_account.storage_scheme, ) diff --git a/crates/router/src/core/disputes.rs b/crates/router/src/core/disputes.rs index 2d2d7bbcae..6a7f531f5a 100644 --- a/crates/router/src/core/disputes.rs +++ b/crates/router/src/core/disputes.rs @@ -89,6 +89,7 @@ pub async fn accept_dispute( )?; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &dispute.payment_id, &merchant_account.merchant_id, &key_store, @@ -202,6 +203,7 @@ pub async fn submit_evidence( .await?; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &dispute.payment_id, &merchant_account.merchant_id, &key_store, diff --git a/crates/router/src/core/encryption.rs b/crates/router/src/core/encryption.rs index 7f7945bfd1..efd7d3bfea 100644 --- a/crates/router/src/core/encryption.rs +++ b/crates/router/src/core/encryption.rs @@ -14,7 +14,7 @@ pub async fn transfer_encryption_key( ) -> errors::CustomResult { let db = &*state.store; let key_stores = db - .get_all_key_stores(&db.get_master_key().to_vec().into()) + .get_all_key_stores(&state.into(), &db.get_master_key().to_vec().into()) .await .change_context(errors::ApiErrorResponse::InternalServerError)?; send_request_to_key_service_for_merchant(state, key_stores).await diff --git a/crates/router/src/core/files/helpers.rs b/crates/router/src/core/files/helpers.rs index 3a56586560..ed95e7790e 100644 --- a/crates/router/src/core/files/helpers.rs +++ b/crates/router/src/core/files/helpers.rs @@ -261,6 +261,7 @@ pub async fn upload_and_get_provider_provider_file_id_profile_id( let payment_intent = state .store .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &dispute.payment_id, &merchant_account.merchant_id, key_store, diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index 331eb43021..8ddd0b7aa2 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -123,7 +123,7 @@ where pub async fn should_call_frm( merchant_account: &domain::MerchantAccount, payment_data: &payments::PaymentData, - db: &dyn StorageInterface, + state: &SessionState, key_store: domain::MerchantKeyStore, ) -> RouterResult<( bool, @@ -136,6 +136,7 @@ where { match merchant_account.frm_routing_algorithm.clone() { Some(frm_routing_algorithm_value) => { + let db = &*state.store; let frm_routing_algorithm_struct: FrmRoutingAlgorithm = frm_routing_algorithm_value .clone() .parse_value("FrmRoutingAlgorithm") @@ -157,6 +158,7 @@ where let merchant_connector_account_from_db_option = db .find_merchant_connector_account_by_profile_id_connector_name( + &state.into(), &profile_id, &frm_routing_algorithm_struct.data, &key_store, @@ -417,7 +419,7 @@ where let frm_data_updated = fraud_check_operation .to_update_tracker()? .update_tracker( - &*state.store, + state, &key_store, frm_data.clone(), payment_data, @@ -492,7 +494,7 @@ where let mut frm_data = fraud_check_operation .to_update_tracker()? .update_tracker( - &*state.store, + state, &key_store, frm_data.to_owned(), payment_data, @@ -527,7 +529,7 @@ where let updated_frm_data = fraud_check_operation .to_update_tracker()? .update_tracker( - &*state.store, + state, &key_store, frm_data.to_owned(), payment_data, @@ -547,7 +549,6 @@ where #[allow(clippy::too_many_arguments)] pub async fn call_frm_before_connector_call<'a, F, Req>( - db: &dyn StorageInterface, operation: &BoxedOperation<'_, F, Req>, merchant_account: &domain::MerchantAccount, payment_data: &mut payments::PaymentData, @@ -562,13 +563,13 @@ where F: Send + Clone, { let (is_frm_enabled, frm_routing_algorithm, frm_connector_label, frm_configs) = - should_call_frm(merchant_account, payment_data, db, key_store.clone()).await?; + should_call_frm(merchant_account, payment_data, state, key_store.clone()).await?; if let Some((frm_routing_algorithm_val, profile_id)) = frm_routing_algorithm.zip(frm_connector_label) { if let Some(frm_configs) = frm_configs.clone() { let mut updated_frm_info = Box::pin(make_frm_data_and_fraud_check_operation( - db, + &*state.store, state, merchant_account, payment_data.to_owned(), @@ -655,6 +656,7 @@ pub async fn frm_fulfillment_core( let db = &*state.clone().store; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &req.payment_id.clone(), &merchant_account.merchant_id, &key_store, diff --git a/crates/router/src/core/fraud_check/operation.rs b/crates/router/src/core/fraud_check/operation.rs index 8c6f8b7bf5..2d159d2e22 100644 --- a/crates/router/src/core/fraud_check/operation.rs +++ b/crates/router/src/core/fraud_check/operation.rs @@ -14,7 +14,6 @@ use crate::{ errors::{self, RouterResult}, payments, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, types::{domain, fraud_check::FrmRouterData}, }; @@ -101,7 +100,7 @@ pub trait Domain: Send + Sync { pub trait UpdateTracker: Send { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, frm_data: D, payment_data: &mut payments::PaymentData, diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs index 7c97eee8b4..698eb43385 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs @@ -19,7 +19,6 @@ use crate::{ }, payments, }, - db::StorageInterface, errors, routes::app::ReqState, services::{self, api}, @@ -329,13 +328,14 @@ impl Domain for FraudCheckPost { impl UpdateTracker for FraudCheckPost { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, mut frm_data: FrmData, payment_data: &mut payments::PaymentData, frm_suggestion: Option, frm_router_data: FrmRouterData, ) -> RouterResult { + let db = &*state.store; let frm_check_update = match frm_router_data.response { FrmResponse::Sale(response) => match response { Err(err) => Some(FraudCheckUpdate::ErrorUpdate { @@ -515,6 +515,7 @@ impl UpdateTracker for FraudCheckPost { payment_data.payment_intent = db .update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), PaymentIntentUpdate::RejectUpdate { status: payment_intent_status, diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs index c153acc754..6a0bd78130 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs @@ -16,7 +16,6 @@ use crate::{ }, payments, }, - db::StorageInterface, errors, routes::app::ReqState, types::{ @@ -218,7 +217,7 @@ impl Domain for FraudCheckPre { impl UpdateTracker for FraudCheckPre { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + state: &SessionState, _key_store: &domain::MerchantKeyStore, mut frm_data: FrmData, payment_data: &mut payments::PaymentData, @@ -337,6 +336,7 @@ impl UpdateTracker for FraudCheckPre { }), }; + let db = &*state.store; frm_data.fraud_check = match frm_check_update { Some(fraud_check_update) => db .update_fraud_check_response_with_attempt_id( diff --git a/crates/router/src/core/locker_migration.rs b/crates/router/src/core/locker_migration.rs index 24ba06bcf3..22b3b29e03 100644 --- a/crates/router/src/core/locker_migration.rs +++ b/crates/router/src/core/locker_migration.rs @@ -17,10 +17,11 @@ pub async fn rust_locker_migration( merchant_id: &str, ) -> CustomResult, errors::ApiErrorResponse> { let db = state.store.as_ref(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -28,13 +29,13 @@ pub async fn rust_locker_migration( .change_context(errors::ApiErrorResponse::InternalServerError)?; let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound) .change_context(errors::ApiErrorResponse::InternalServerError)?; let domain_customers = db - .list_customers_by_merchant_id(merchant_id, &key_store) + .list_customers_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .change_context(errors::ApiErrorResponse::InternalServerError)?; diff --git a/crates/router/src/core/mandate/helpers.rs b/crates/router/src/core/mandate/helpers.rs index 2bb981d03e..a4eb18371f 100644 --- a/crates/router/src/core/mandate/helpers.rs +++ b/crates/router/src/core/mandate/helpers.rs @@ -21,6 +21,7 @@ pub async fn get_profile_id_for_mandate( let pi = state .store .find_payment_intent_by_payment_id_merchant_id( + &state.into(), payment_id, &merchant_account.merchant_id, key_store, diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 49e4885dc9..bf50405ba8 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -59,6 +59,7 @@ pub async fn initiate_payment_link_flow( let db = &*state.store; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &payment_id, &merchant_id, &key_store, @@ -497,6 +498,7 @@ pub async fn get_payment_link_status( let db = &*state.store; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &payment_id, &merchant_id, &key_store, diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index b9e21b616c..86cb1fc1b1 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -266,6 +266,7 @@ pub async fn render_pm_collect_link( // Fetch customer let customer = db .find_customer_by_customer_id_merchant_id( + &(&state).into(), &customer_id, &req.merchant_id, &key_store, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 468bc0dbfc..431826fbdb 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -23,11 +23,15 @@ use common_enums::enums::MerchantStorageScheme; use common_utils::{ consts, crypto::Encryptable, + encryption::Encryption, ext_traits::{AsyncExt, Encode, StringExt, ValueExt}, generate_id, id_type, - types::MinorUnit, + types::{ + keymanager::{Identifier, KeyManagerState}, + MinorUnit, + }, }; -use diesel_models::{business_profile::BusinessProfile, encryption::Encryption, payment_method}; +use diesel_models::{business_profile::BusinessProfile, payment_method}; use domain::CustomerUpdate; use error_stack::{report, ResultExt}; use euclid::{ @@ -77,7 +81,7 @@ use crate::{ #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn create_payment_method( - db: &dyn db::StorageInterface, + state: &routes::SessionState, req: &api::PaymentMethodCreate, customer_id: &id_type::CustomerId, payment_method_id: &str, @@ -94,8 +98,10 @@ pub async fn create_payment_method( payment_method_billing_address: Option, card_scheme: Option, ) -> errors::CustomResult { + let db = &*state.store; let customer = db .find_customer_by_customer_id_merchant_id( + &state.into(), customer_id, merchant_id, key_store, @@ -153,7 +159,7 @@ pub async fn create_payment_method( if customer.default_payment_method_id.is_none() && req.payment_method.is_some() { let _ = set_default_payment_method( - db, + state, merchant_id.to_string(), key_store.clone(), customer_id, @@ -197,7 +203,7 @@ pub fn store_default_payment_method( } #[instrument(skip_all)] pub async fn get_or_insert_payment_method( - db: &dyn db::StorageInterface, + state: &routes::SessionState, req: api::PaymentMethodCreate, resp: &mut api::PaymentMethodResponse, merchant_account: &domain::MerchantAccount, @@ -206,6 +212,7 @@ pub async fn get_or_insert_payment_method( ) -> errors::RouterResult { let mut payment_method_id = resp.payment_method_id.clone(); let mut locker_id = None; + let db = &*state.store; let payment_method = { let existing_pm_by_pmid = db .find_payment_method(&payment_method_id, merchant_account.storage_scheme) @@ -240,7 +247,7 @@ pub async fn get_or_insert_payment_method( Err(err) => { if err.current_context().is_db_not_found() { insert_payment_method( - db, + state, resp, &req, key_store, @@ -278,7 +285,7 @@ pub async fn migrate_payment_method( if let Some(connector_mandate_details) = &req.connector_mandate_details { helpers::validate_merchant_connector_ids_in_connector_mandate_details( - &*state.store, + &state, key_store, connector_mandate_details, merchant_id, @@ -294,7 +301,7 @@ pub async fn migrate_payment_method( &req, ); get_client_secret_or_add_payment_method( - state, + &state, payment_method_create_request, merchant_account, key_store, @@ -338,7 +345,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( let payment_method_billing_address: Option>> = req .billing .clone() - .async_map(|billing| create_encrypted_data(key_store, billing)) + .async_map(|billing| create_encrypted_data(&state, key_store, billing)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -346,6 +353,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( let customer = db .find_customer_by_customer_id_merchant_id( + &(&state).into(), &customer_id, &merchant_id, key_store, @@ -475,7 +483,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( let payment_method_data_encrypted: Option>> = payment_method_card_details - .async_map(|card_details| create_encrypted_data(key_store, card_details)) + .async_map(|card_details| create_encrypted_data(&state, key_store, card_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -535,7 +543,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( if customer.default_payment_method_id.is_none() && req.payment_method.is_some() { let _ = set_default_payment_method( - &*state.store, + &state, merchant_id.to_string(), key_store.clone(), &customer_id, @@ -572,12 +580,11 @@ pub fn get_card_bin_and_last4_digits_for_masked_card( #[instrument(skip_all)] pub async fn get_client_secret_or_add_payment_method( - state: routes::SessionState, + state: &routes::SessionState, req: api::PaymentMethodCreate, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> errors::RouterResponse { - let db = &*state.store; let merchant_id = &merchant_account.merchant_id; let customer_id = req.customer_id.clone().get_required_value("customer_id")?; @@ -589,7 +596,7 @@ pub async fn get_client_secret_or_add_payment_method( let payment_method_billing_address: Option>> = req .billing .clone() - .async_map(|billing| create_encrypted_data(key_store, billing)) + .async_map(|billing| create_encrypted_data(state, key_store, billing)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -608,7 +615,7 @@ pub async fn get_client_secret_or_add_payment_method( let payment_method_id = generate_id(consts::ID_LENGTH, "pm"); let res = create_payment_method( - db, + state, &req, &customer_id, payment_method_id.as_str(), @@ -629,7 +636,7 @@ pub async fn get_client_secret_or_add_payment_method( if res.status == enums::PaymentMethodStatus::AwaitingData { add_payment_method_status_update_task( - db, + &*state.store, &res, enums::PaymentMethodStatus::AwaitingData, enums::PaymentMethodStatus::Inactive, @@ -708,6 +715,7 @@ pub async fn add_payment_method_data( let customer_id = payment_method.customer_id.clone(); let customer = db .find_customer_by_customer_id_merchant_id( + &(&state).into(), &customer_id, &merchant_account.merchant_id, &key_store, @@ -754,7 +762,7 @@ pub async fn add_payment_method_data( .attach_printable("Failed to add payment method in db")?; get_or_insert_payment_method( - db, + &state, req.clone(), &mut pm_resp, &merchant_account, @@ -795,6 +803,7 @@ pub async fn add_payment_method_data( let pm_data_encrypted: Encryptable> = create_encrypted_data( + &state, &key_store, PaymentMethodsData::Card(updated_card), ) @@ -822,7 +831,7 @@ pub async fn add_payment_method_data( if customer.default_payment_method_id.is_none() { let _ = set_default_payment_method( - db, + &state, merchant_account.merchant_id.clone(), key_store.clone(), &customer_id, @@ -864,7 +873,7 @@ pub async fn add_payment_method_data( #[instrument(skip_all)] pub async fn add_payment_method( - state: routes::SessionState, + state: &routes::SessionState, req: api::PaymentMethodCreate, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -877,7 +886,7 @@ pub async fn add_payment_method( let payment_method_billing_address: Option>> = req .billing .clone() - .async_map(|billing| create_encrypted_data(key_store, billing)) + .async_map(|billing| create_encrypted_data(state, key_store, billing)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -894,7 +903,7 @@ pub async fn add_payment_method( #[cfg(feature = "payouts")] api_enums::PaymentMethod::BankTransfer => match req.bank_transfer.clone() { Some(bank) => add_bank_to_locker( - &state, + state, req.clone(), merchant_account, key_store, @@ -923,7 +932,7 @@ pub async fn add_payment_method( &card_details.card_exp_year, )?; Box::pin(add_card_to_locker( - &state, + state, req.clone(), &card_details, &customer_id, @@ -953,7 +962,7 @@ pub async fn add_payment_method( Some(duplication_check) => match duplication_check { payment_methods::DataDuplicationCheck::Duplicated => { let existing_pm = get_or_insert_payment_method( - db, + state, req.clone(), &mut resp, merchant_account, @@ -967,7 +976,7 @@ pub async fn add_payment_method( payment_methods::DataDuplicationCheck::MetaDataChanged => { if let Some(card) = req.card.clone() { let existing_pm = get_or_insert_payment_method( - db, + state, req.clone(), &mut resp, merchant_account, @@ -979,7 +988,7 @@ pub async fn add_payment_method( let client_secret = existing_pm.client_secret.clone(); delete_card_from_locker( - &state, + state, &customer_id, merchant_id, existing_pm @@ -990,7 +999,7 @@ pub async fn add_payment_method( .await?; let add_card_resp = add_card_hs( - &state, + state, req.clone(), &card, &customer_id, @@ -1018,12 +1027,9 @@ pub async fn add_payment_method( .attach_printable("Failed while updating card metadata changes"))? }; - let existing_pm_data = get_card_details_without_locker_fallback( - &existing_pm, - key_store.key.peek(), - &state, - ) - .await?; + let existing_pm_data = + get_card_details_without_locker_fallback(&existing_pm, state, key_store) + .await?; let updated_card = Some(api::CardDetailFromLocker { scheme: existing_pm.scheme.clone(), @@ -1052,7 +1058,9 @@ pub async fn add_payment_method( }); let pm_data_encrypted: Option>> = updated_pmd - .async_map(|updated_pmd| create_encrypted_data(key_store, updated_pmd)) + .async_map(|updated_pmd| { + create_encrypted_data(state, key_store, updated_pmd) + }) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1087,7 +1095,7 @@ pub async fn add_payment_method( }; resp.payment_method_id = generate_id(consts::ID_LENGTH, "pm"); let pm = insert_payment_method( - db, + state, &resp, &req, key_store, @@ -1112,7 +1120,7 @@ pub async fn add_payment_method( #[allow(clippy::too_many_arguments)] pub async fn insert_payment_method( - db: &dyn db::StorageInterface, + state: &routes::SessionState, resp: &api::PaymentMethodResponse, req: &api::PaymentMethodCreate, key_store: &domain::MerchantKeyStore, @@ -1133,14 +1141,14 @@ pub async fn insert_payment_method( let pm_data_encrypted: Option>> = pm_card_details .clone() - .async_map(|pm_card| create_encrypted_data(key_store, pm_card)) + .async_map(|pm_card| create_encrypted_data(state, key_store, pm_card)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt payment method data")?; create_payment_method( - db, + state, req, customer_id, &resp.payment_method_id, @@ -1202,7 +1210,9 @@ pub async fn update_customer_payment_method( // Fetch the existing payment method data from db let existing_card_data = decrypt::( + &(&state).into(), pm.payment_method_data.clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key_store.key.get_inner().peek(), ) .await @@ -1327,7 +1337,7 @@ pub async fn update_customer_payment_method( .map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone()))); let pm_data_encrypted: Option>> = updated_pmd - .async_map(|updated_pmd| create_encrypted_data(&key_store, updated_pmd)) + .async_map(|updated_pmd| create_encrypted_data(&state, &key_store, updated_pmd)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1445,6 +1455,7 @@ pub async fn add_bank_to_locker( > { let key = key_store.key.get_inner().peek(); let payout_method_data = api::PayoutMethodData::Bank(bank.clone()); + let key_manager_state: KeyManagerState = state.into(); let enc_data = async { serde_json::to_value(payout_method_data.to_owned()) .map_err(|err| { @@ -1458,7 +1469,14 @@ pub async fn add_bank_to_locker( let secret: Secret = Secret::new(v.to_string()); secret }) - .async_lift(|inner| domain::types::encrypt_optional(inner, key)) + .async_lift(|inner| { + domain::types::encrypt_optional( + &key_manager_state, + inner, + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + }) .await } .await @@ -1654,6 +1672,7 @@ pub async fn add_card_hs( #[instrument(skip_all)] pub async fn decode_and_decrypt_locker_data( + state: &routes::SessionState, key_store: &domain::MerchantKeyStore, enc_card_data: String, ) -> errors::CustomResult, errors::VaultError> { @@ -1664,13 +1683,18 @@ pub async fn decode_and_decrypt_locker_data( .change_context(errors::VaultError::ResponseDeserializationFailed) .attach_printable("Failed to decode hex string into bytes")?; // Decrypt - decrypt(Some(Encryption::new(decoded_bytes.into())), key) - .await - .change_context(errors::VaultError::FetchPaymentMethodFailed)? - .map_or( - Err(report!(errors::VaultError::FetchPaymentMethodFailed)), - |d| Ok(d.into_inner()), - ) + decrypt( + &state.into(), + Some(Encryption::new(decoded_bytes.into())), + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + .await + .change_context(errors::VaultError::FetchPaymentMethodFailed)? + .map_or( + Err(report!(errors::VaultError::FetchPaymentMethodFailed)), + |d| Ok(d.into_inner()), + ) } #[instrument(skip_all)] @@ -1729,9 +1753,9 @@ pub async fn get_payment_method_from_hs_locker<'a>( .attach_printable( "Failed to retrieve field - enc_card_data from RetrieveCardRespPayload", )?; - decode_and_decrypt_locker_data(key_store, enc_card_data.peek().to_string()).await? + decode_and_decrypt_locker_data(state, key_store, enc_card_data.peek().to_string()).await? } else { - mock_get_payment_method(&*state.store, key_store, payment_method_reference) + mock_get_payment_method(state, key_store, payment_method_reference) .await? .payment_method .payment_method_data @@ -2036,16 +2060,17 @@ pub async fn mock_get_card<'a>( #[instrument(skip_all)] pub async fn mock_get_payment_method<'a>( - db: &dyn db::StorageInterface, + state: &routes::SessionState, key_store: &domain::MerchantKeyStore, card_id: &'a str, ) -> errors::CustomResult { + let db = &*state.store; let locker_mock_up = db .find_locker_by_card_id(card_id) .await .change_context(errors::VaultError::FetchPaymentMethodFailed)?; let dec_data = if let Some(e) = locker_mock_up.enc_card_data { - decode_and_decrypt_locker_data(key_store, e).await + decode_and_decrypt_locker_data(state, key_store, e).await } else { Err(report!(errors::VaultError::FetchPaymentMethodFailed)) }?; @@ -2187,14 +2212,14 @@ pub async fn list_payment_methods( ) -> errors::RouterResponse { let db = &*state.store; let pm_config_mapping = &state.conf.pm_filters; - + let key_manager_state = &(&state).into(); let payment_intent = if let Some(cs) = &req.client_secret { if cs.starts_with("pm_") { validate_payment_method_and_client_secret(cs, db, &merchant_account).await?; None } else { helpers::verify_payment_intent_time_and_client_secret( - db, + &state, &merchant_account, &key_store, req.client_secret.clone(), @@ -2209,7 +2234,7 @@ pub async fn list_payment_methods( .as_ref() .async_map(|pi| async { helpers::get_address_by_id( - db, + &state, pi.shipping_address_id.clone(), &key_store, &pi.payment_id, @@ -2226,7 +2251,7 @@ pub async fn list_payment_methods( .as_ref() .async_map(|pi| async { helpers::get_address_by_id( - db, + &state, pi.billing_address_id.clone(), &key_store, &pi.payment_id, @@ -2246,6 +2271,7 @@ pub async fn list_payment_methods( .as_ref() .async_and_then(|cust| async { db.find_customer_by_customer_id_merchant_id( + key_manager_state, cust, &pi.merchant_id, &key_store, @@ -2293,6 +2319,7 @@ pub async fn list_payment_methods( let all_mcas = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( + key_manager_state, &merchant_account.merchant_id, false, &key_store, @@ -3273,6 +3300,7 @@ pub async fn call_surcharge_decision_management( let _ = state .store .update_payment_intent( + &(&state).into(), payment_intent, storage::PaymentIntentUpdate::SurchargeApplicableUpdate { surcharge_applicable: true, @@ -3322,6 +3350,7 @@ pub async fn call_surcharge_decision_management_for_saved_card( let _ = state .store .update_payment_intent( + &state.into(), payment_intent, storage::PaymentIntentUpdate::SurchargeApplicableUpdate { surcharge_applicable: true, @@ -3586,7 +3615,6 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed( customer_id: Option<&id_type::CustomerId>, ephemeral_api_key: Option<&str>, ) -> errors::RouterResponse { - let db = state.store.as_ref(); let limit = req.clone().and_then(|pml_req| pml_req.limit); let auth_cust = if let Some(key) = ephemeral_api_key { @@ -3617,7 +3645,7 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed( let cloned_secret = req.and_then(|r| r.client_secret.as_ref().cloned()); let payment_intent: Option = helpers::verify_payment_intent_time_and_client_secret( - db, + &state, &merchant_account, &key_store, cloned_secret, @@ -3671,6 +3699,7 @@ pub async fn list_customer_payment_method( let customer = db .find_customer_by_customer_id_merchant_id( + &state.into(), customer_id, &merchant_account.merchant_id, &key_store, @@ -3679,8 +3708,6 @@ pub async fn list_customer_payment_method( .await .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; - let key = key_store.key.get_inner().peek(); - let is_requires_cvv = db .find_config_by_key_unwrap_or( format!("{}_requires_cvv", merchant_account.merchant_id).as_str(), @@ -3741,7 +3768,8 @@ pub async fn list_customer_payment_method( let payment_method_retrieval_context = match payment_method { enums::PaymentMethod::Card => { - let card_details = get_card_details_with_locker_fallback(&pm, key, state).await?; + let card_details = + get_card_details_with_locker_fallback(&pm, state, &key_store).await?; if card_details.is_some() { PaymentMethodListContext { @@ -3761,12 +3789,13 @@ pub async fn list_customer_payment_method( enums::PaymentMethod::BankDebit => { // Retrieve the pm_auth connector details so that it can be tokenized - let bank_account_token_data = get_bank_account_connector_details(&pm, key) - .await - .unwrap_or_else(|error| { - logger::error!(?error); - None - }); + let bank_account_token_data = + get_bank_account_connector_details(state, &pm, &key_store) + .await + .unwrap_or_else(|error| { + logger::error!(?error); + None + }); if let Some(data) = bank_account_token_data { let token_data = PaymentTokenData::AuthBankDebit(data); @@ -3822,7 +3851,7 @@ pub async fn list_customer_payment_method( // Retrieve the masked bank details to be sent as a response let bank_details = if payment_method == enums::PaymentMethod::BankDebit { - get_masked_bank_details(&pm, key) + get_masked_bank_details(state, &pm, &key_store) .await .unwrap_or_else(|error| { logger::error!(?error); @@ -3833,8 +3862,9 @@ pub async fn list_customer_payment_method( }; let payment_method_billing = decrypt_generic_data::( + state, pm.payment_method_billing_address, - key, + &key_store, ) .await .attach_printable("unable to decrypt payment method billing address details")?; @@ -3993,6 +4023,7 @@ pub async fn get_mca_status( let mcas = state .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), merchant_id, true, key_store, @@ -4021,17 +4052,21 @@ pub async fn get_mca_status( Ok(false) } pub async fn decrypt_generic_data( + state: &routes::SessionState, data: Option, - key: &[u8], + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult> where T: serde::de::DeserializeOwned, { - let decrypted_data = decrypt::(data, key) - .await - .change_context(errors::StorageError::DecryptionError) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to decrypt data")?; + let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let decrypted_data = + decrypt::(&state.into(), data, identifier, key) + .await + .change_context(errors::StorageError::DecryptionError) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to decrypt data")?; decrypted_data .map(|decrypted_data| decrypted_data.into_inner().expose()) @@ -4043,22 +4078,28 @@ where pub async fn get_card_details_with_locker_fallback( pm: &payment_method::PaymentMethod, - key: &[u8], state: &routes::SessionState, + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult> { - let card_decrypted = - decrypt::(pm.payment_method_data.clone(), key) - .await - .change_context(errors::StorageError::DecryptionError) - .attach_printable("unable to decrypt card details") - .ok() - .flatten() - .map(|x| x.into_inner().expose()) - .and_then(|v| serde_json::from_value::(v).ok()) - .and_then(|pmd| match pmd { - PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), - _ => None, - }); + let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let card_decrypted = decrypt::( + &state.into(), + pm.payment_method_data.clone(), + identifier, + key, + ) + .await + .change_context(errors::StorageError::DecryptionError) + .attach_printable("unable to decrypt card details") + .ok() + .flatten() + .map(|x| x.into_inner().expose()) + .and_then(|v| serde_json::from_value::(v).ok()) + .and_then(|pmd| match pmd { + PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + _ => None, + }); Ok(if let Some(mut crd) = card_decrypted { crd.scheme.clone_from(&pm.scheme); @@ -4073,22 +4114,28 @@ pub async fn get_card_details_with_locker_fallback( pub async fn get_card_details_without_locker_fallback( pm: &payment_method::PaymentMethod, - key: &[u8], state: &routes::SessionState, + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult { - let card_decrypted = - decrypt::(pm.payment_method_data.clone(), key) - .await - .change_context(errors::StorageError::DecryptionError) - .attach_printable("unable to decrypt card details") - .ok() - .flatten() - .map(|x| x.into_inner().expose()) - .and_then(|v| serde_json::from_value::(v).ok()) - .and_then(|pmd| match pmd { - PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), - _ => None, - }); + let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let card_decrypted = decrypt::( + &state.into(), + pm.payment_method_data.clone(), + identifier, + key, + ) + .await + .change_context(errors::StorageError::DecryptionError) + .attach_printable("unable to decrypt card details") + .ok() + .flatten() + .map(|x| x.into_inner().expose()) + .and_then(|v| serde_json::from_value::(v).ok()) + .and_then(|pmd| match pmd { + PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + _ => None, + }); Ok(if let Some(mut crd) = card_decrypted { crd.scheme.clone_from(&pm.scheme); @@ -4141,25 +4188,32 @@ pub async fn get_lookup_key_from_locker( } async fn get_masked_bank_details( + state: &routes::SessionState, pm: &payment_method::PaymentMethod, - key: &[u8], + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult> { - let payment_method_data = - decrypt::(pm.payment_method_data.clone(), key) - .await - .change_context(errors::StorageError::DecryptionError) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to decrypt bank details")? - .map(|x| x.into_inner().expose()) - .map( - |v| -> Result> { - v.parse_value::("PaymentMethodsData") - .change_context(errors::StorageError::DeserializationFailed) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize Payment Method Auth config") - }, - ) - .transpose()?; + let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let payment_method_data = decrypt::( + &state.into(), + pm.payment_method_data.clone(), + identifier, + key, + ) + .await + .change_context(errors::StorageError::DecryptionError) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to decrypt bank details")? + .map(|x| x.into_inner().expose()) + .map( + |v| -> Result> { + v.parse_value::("PaymentMethodsData") + .change_context(errors::StorageError::DeserializationFailed) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to deserialize Payment Method Auth config") + }, + ) + .transpose()?; match payment_method_data { Some(pmd) => match pmd { @@ -4174,25 +4228,32 @@ async fn get_masked_bank_details( } async fn get_bank_account_connector_details( + state: &routes::SessionState, pm: &payment_method::PaymentMethod, - key: &[u8], + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult> { - let payment_method_data = - decrypt::(pm.payment_method_data.clone(), key) - .await - .change_context(errors::StorageError::DecryptionError) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to decrypt bank details")? - .map(|x| x.into_inner().expose()) - .map( - |v| -> Result> { - v.parse_value::("PaymentMethodsData") - .change_context(errors::StorageError::DeserializationFailed) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize Payment Method Auth config") - }, - ) - .transpose()?; + let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let payment_method_data = decrypt::( + &state.into(), + pm.payment_method_data.clone(), + identifier, + key, + ) + .await + .change_context(errors::StorageError::DecryptionError) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to decrypt bank details")? + .map(|x| x.into_inner().expose()) + .map( + |v| -> Result> { + v.parse_value::("PaymentMethodsData") + .change_context(errors::StorageError::DeserializationFailed) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to deserialize Payment Method Auth config") + }, + ) + .transpose()?; match payment_method_data { Some(pmd) => match pmd { @@ -4229,17 +4290,20 @@ async fn get_bank_account_connector_details( } } pub async fn set_default_payment_method( - db: &dyn db::StorageInterface, + state: &routes::SessionState, merchant_id: String, key_store: domain::MerchantKeyStore, customer_id: &id_type::CustomerId, payment_method_id: String, storage_scheme: MerchantStorageScheme, ) -> errors::RouterResponse { + let db = &*state.store; + let key_manager_state = &state.into(); // check for the customer // TODO: customer need not be checked again here, this function can take an optional customer and check for existence of customer based on the optional value let customer = db .find_customer_by_customer_id_merchant_id( + key_manager_state, customer_id, &merchant_id, &key_store, @@ -4283,6 +4347,7 @@ pub async fn set_default_payment_method( // update the db with the default payment method id let updated_customer_details = db .update_customer_by_customer_id_merchant_id( + key_manager_state, customer_id.to_owned(), merchant_id.to_owned(), customer, @@ -4470,7 +4535,6 @@ pub async fn retrieve_payment_method( .await .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?; - let key = key_store.key.peek(); let card = if pm.payment_method == Some(enums::PaymentMethod::Card) { let card_detail = if state.conf.locker.locker_enabled { let card = get_card_from_locker( @@ -4486,7 +4550,7 @@ pub async fn retrieve_payment_method( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while getting card details from locker")? } else { - get_card_details_without_locker_fallback(&pm, key, &state).await? + get_card_details_without_locker_fallback(&pm, &state, &key_store).await? }; Some(card_detail) } else { @@ -4521,6 +4585,7 @@ pub async fn delete_payment_method( key_store: domain::MerchantKeyStore, ) -> errors::RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key = db .find_payment_method( pm_id.payment_method_id.as_str(), @@ -4531,6 +4596,7 @@ pub async fn delete_payment_method( let customer = db .find_customer_by_customer_id_merchant_id( + key_manager_state, &key.customer_id, &merchant_account.merchant_id, &key_store, @@ -4570,6 +4636,7 @@ pub async fn delete_payment_method( }; db.update_customer_by_customer_id_merchant_id( + key_manager_state, key.customer_id, key.merchant_id, customer, @@ -4591,6 +4658,7 @@ pub async fn delete_payment_method( } pub async fn create_encrypted_data( + state: &routes::SessionState, key_store: &domain::MerchantKeyStore, data: T, ) -> Result>, error_stack::Report> @@ -4598,6 +4666,8 @@ where T: Debug + serde::Serialize, { let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let key_manager_state: KeyManagerState = state.into(); let encoded_data = Encode::encode_to_value(&data) .change_context(errors::StorageError::SerializationFailed) @@ -4605,10 +4675,11 @@ where let secret_data = Secret::<_, masking::WithType>::new(encoded_data); - let encrypted_data = domain::types::encrypt(secret_data, key) - .await - .change_context(errors::StorageError::EncryptionError) - .attach_printable("Unable to encrypt data")?; + let encrypted_data = + domain::types::encrypt(&key_manager_state, secret_data, identifier.clone(), key) + .await + .change_context(errors::StorageError::EncryptionError) + .attach_printable("Unable to encrypt data")?; Ok(encrypted_data) } diff --git a/crates/router/src/core/payment_methods/validator.rs b/crates/router/src/core/payment_methods/validator.rs index be34d3db38..23470fb3ef 100644 --- a/crates/router/src/core/payment_methods/validator.rs +++ b/crates/router/src/core/payment_methods/validator.rs @@ -27,6 +27,7 @@ pub async fn validate_request_and_initiate_payment_method_collect_link( let merchant_id = merchant_account.merchant_id.clone(); match db .find_customer_by_customer_id_merchant_id( + &state.into(), &customer_id, &merchant_id, key_store, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 5cad096bd3..9c420c1e44 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -164,7 +164,7 @@ where let (operation, customer) = operation .to_domain()? .get_or_create_customer_details( - &*state.store, + state, &mut payment_data, customer_details, &key_store, @@ -208,8 +208,6 @@ where // Fetch and check FRM configs #[cfg(feature = "frm")] let mut frm_info = None; - #[cfg(feature = "frm")] - let db = &*state.store; #[allow(unused_variables, unused_mut)] let mut should_continue_transaction: bool = true; #[cfg(feature = "frm")] @@ -217,7 +215,6 @@ where #[cfg(feature = "frm")] let frm_configs = if state.conf.frm.enabled { Box::pin(frm_core::call_frm_before_connector_call( - db, &operation, &merchant_account, &mut payment_data, @@ -1203,6 +1200,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { let payment_intent = state .store .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, &merchant_id, &merchant_key_store, @@ -2883,7 +2881,7 @@ pub async fn list_payments( let merchant_id = &merchant.merchant_id; let db = state.store.as_ref(); let payment_intents = helpers::filter_by_constraints( - db, + &state, &constraints, merchant_id, &key_store, @@ -2955,6 +2953,7 @@ pub async fn apply_filters_on_payments( let db = state.store.as_ref(); let list: Vec<(storage::PaymentIntent, storage::PaymentAttempt)> = db .get_filtered_payment_intents_attempt( + &(&state).into(), &merchant.merchant_id, &constraints.clone().into(), &merchant_key_store, @@ -3008,6 +3007,7 @@ pub async fn get_filters_for_payments( let db = state.store.as_ref(); let pi = db .filter_payment_intents_by_time_range_constraints( + &(&state).into(), &merchant.merchant_id, &time_range, &merchant_key_store, @@ -4021,6 +4021,7 @@ pub async fn payment_external_authentication( let payment_id = req.payment_id; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &payment_id, merchant_id, &key_store, @@ -4054,6 +4055,7 @@ pub async fn payment_external_authentication( state .store .find_customer_by_customer_id_merchant_id( + &(&state).into(), customer_id, &merchant_account.merchant_id, &key_store, @@ -4076,7 +4078,7 @@ pub async fn payment_external_authentication( let currency = payment_attempt.currency.get_required_value("currency")?; let amount = payment_attempt.get_total_amount(); let shipping_address = helpers::create_or_find_address_for_payment_by_request( - db, + &state, None, payment_intent.shipping_address_id.as_deref(), merchant_id, @@ -4087,7 +4089,7 @@ pub async fn payment_external_authentication( ) .await?; let billing_address = helpers::create_or_find_address_for_payment_by_request( - db, + &state, None, payment_intent.billing_address_id.as_deref(), merchant_id, @@ -4259,9 +4261,11 @@ pub async fn payments_manual_update( error_message, error_reason, } = req; + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -4270,7 +4274,7 @@ pub async fn payments_manual_update( .attach_printable("Error while fetching the key store by merchant_id")?; let merchant_account = state .store - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound) .attach_printable("Error while fetching the merchant_account by merchant_id")?; @@ -4290,6 +4294,7 @@ pub async fn payments_manual_update( let payment_intent = state .store .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &payment_id, &merchant_account.merchant_id, &key_store, @@ -4345,6 +4350,7 @@ pub async fn payments_manual_update( state .store .update_payment_intent( + &(&state).into(), payment_intent, payment_intent_update, &key_store, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 3629457d5a..ecf8ff0c40 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1,8 +1,9 @@ use std::{borrow::Cow, str::FromStr}; use api_models::{ + customers::CustomerRequestWithEmail, mandates::RecurringDetails, - payments::{CardToken, GetPaymentMethodType, RequestSurchargeDetails}, + payments::{AddressDetailsWithPhone, CardToken, GetPaymentMethodType, RequestSurchargeDetails}, }; use base64::Engine; use common_enums::ConnectorType; @@ -10,7 +11,10 @@ use common_utils::{ crypto::Encryptable, ext_traits::{AsyncExt, ByteSliceExt, Encode, ValueExt}, fp_utils, generate_id, id_type, pii, - types::MinorUnit, + types::{ + keymanager::{Identifier, KeyManagerState, ToEncryptable}, + MinorUnit, + }, }; use diesel_models::enums::{self}; // TODO : Evaluate all the helper functions () @@ -139,7 +143,7 @@ pub fn filter_mca_based_on_connector_type( #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn create_or_update_address_for_payment_by_request( - db: &dyn StorageInterface, + session_state: &SessionState, req_address: Option<&api::Address>, address_id: Option<&str>, merchant_id: &str, @@ -149,87 +153,54 @@ pub async fn create_or_update_address_for_payment_by_request( storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult, errors::ApiErrorResponse> { let key = merchant_key_store.key.get_inner().peek(); - + let db = &session_state.store; + let key_manager_state = &session_state.into(); Ok(match address_id { Some(id) => match req_address { Some(address) => { - let address_update = async { - Ok::<_, error_stack::Report>( - storage::AddressUpdate::Update { - city: address - .address - .as_ref() - .and_then(|value| value.city.clone()), - country: address.address.as_ref().and_then(|value| value.country), - line1: address - .address - .as_ref() - .and_then(|value| value.line1.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - line2: address - .address - .as_ref() - .and_then(|value| value.line2.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - line3: address - .address - .as_ref() - .and_then(|value| value.line3.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - state: address - .address - .as_ref() - .and_then(|value| value.state.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - zip: address - .address - .as_ref() - .and_then(|value| value.zip.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - first_name: address - .address - .as_ref() - .and_then(|value| value.first_name.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - last_name: address - .address - .as_ref() - .and_then(|value| value.last_name.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - phone_number: address - .phone - .as_ref() - .and_then(|value| value.number.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - country_code: address - .phone - .as_ref() - .and_then(|value| value.country_code.clone()), - updated_by: storage_scheme.to_string(), - email: address - .email - .as_ref() - .cloned() - .async_lift(|inner| { - types::encrypt_optional(inner.map(|inner| inner.expose()), key) - }) - .await?, - }, - ) - } + let encrypted_data = types::batch_encrypt( + &session_state.into(), + AddressDetailsWithPhone::to_encryptable(AddressDetailsWithPhone { + address: address.address.clone(), + phone_number: address + .phone + .as_ref() + .and_then(|phone| phone.number.clone()), + email: address.email.clone(), + }), + Identifier::Merchant(merchant_key_store.merchant_id.clone()), + key, + ) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while encrypting address")?; + let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while encrypting address")?; + let address_update = storage::AddressUpdate::Update { + city: address + .address + .as_ref() + .and_then(|value| value.city.clone()), + country: address.address.as_ref().and_then(|value| value.country), + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + state: encryptable_address.state, + zip: encryptable_address.zip, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, + phone_number: encryptable_address.phone_number, + country_code: address + .phone + .as_ref() + .and_then(|value| value.country_code.clone()), + updated_by: storage_scheme.to_string(), + email: encryptable_address.email, + }; let address = db .find_address_by_merchant_id_payment_id_address_id( + key_manager_state, merchant_id, payment_id, id, @@ -241,6 +212,7 @@ pub async fn create_or_update_address_for_payment_by_request( .attach_printable("Error while fetching address")?; Some( db.update_address_for_payments( + key_manager_state, address, address_update, payment_id.to_string(), @@ -254,6 +226,7 @@ pub async fn create_or_update_address_for_payment_by_request( } None => Some( db.find_address_by_merchant_id_payment_id_address_id( + key_manager_state, merchant_id, payment_id, id, @@ -268,10 +241,11 @@ pub async fn create_or_update_address_for_payment_by_request( }, None => match req_address { Some(address) => { - let address = get_domain_address(address, merchant_id, key, storage_scheme) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed while encrypting address while insert")?; + let address = + get_domain_address(session_state, address, merchant_id, key, storage_scheme) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while encrypting address while insert")?; let payment_address = domain::PaymentAddress { address, @@ -281,6 +255,7 @@ pub async fn create_or_update_address_for_payment_by_request( Some( db.insert_address_for_payments( + key_manager_state, payment_id, payment_address, merchant_key_store, @@ -301,7 +276,7 @@ pub async fn create_or_update_address_for_payment_by_request( #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn create_or_find_address_for_payment_by_request( - db: &dyn StorageInterface, + state: &SessionState, req_address: Option<&api::Address>, address_id: Option<&str>, merchant_id: &str, @@ -311,10 +286,12 @@ pub async fn create_or_find_address_for_payment_by_request( storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult, errors::ApiErrorResponse> { let key = merchant_key_store.key.get_inner().peek(); - + let db = &state.store; + let key_manager_state = &state.into(); Ok(match address_id { Some(id) => Some( db.find_address_by_merchant_id_payment_id_address_id( + key_manager_state, merchant_id, payment_id, id, @@ -329,7 +306,7 @@ pub async fn create_or_find_address_for_payment_by_request( None => match req_address { Some(address) => { // generate a new address here - let address = get_domain_address(address, merchant_id, key, storage_scheme) + let address = get_domain_address(state, address, merchant_id, key, storage_scheme) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while encrypting address while insert")?; @@ -342,6 +319,7 @@ pub async fn create_or_find_address_for_payment_by_request( Some( db.insert_address_for_payments( + key_manager_state, payment_id, payment_address, merchant_key_store, @@ -359,70 +337,56 @@ pub async fn create_or_find_address_for_payment_by_request( } pub async fn get_domain_address( + session_state: &SessionState, address: &api_models::payments::Address, merchant_id: &str, key: &[u8], storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult { async { - let address_details = address.address.as_ref(); + let address_details = &address.address.as_ref(); + let encrypted_data = types::batch_encrypt( + &session_state.into(), + AddressDetailsWithPhone::to_encryptable(AddressDetailsWithPhone { + address: address_details.cloned(), + phone_number: address + .phone + .as_ref() + .and_then(|phone| phone.number.clone()), + email: address.email.clone(), + }), + Identifier::Merchant(merchant_id.to_string()), + key, + ) + .await?; + let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data) + .change_context(common_utils::errors::CryptoError::EncodingFailed)?; Ok(domain::Address { id: None, - phone_number: address - .phone - .as_ref() - .and_then(|a| a.number.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, + phone_number: encryptable_address.phone_number, country_code: address.phone.as_ref().and_then(|a| a.country_code.clone()), merchant_id: merchant_id.to_string(), address_id: generate_id(consts::ID_LENGTH, "add"), city: address_details.and_then(|address_details| address_details.city.clone()), country: address_details.and_then(|address_details| address_details.country), - line1: address_details - .and_then(|address_details| address_details.line1.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - line2: address_details - .and_then(|address_details| address_details.line2.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - line3: address_details - .and_then(|address_details| address_details.line3.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - state: address_details - .and_then(|address_details| address_details.state.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + state: encryptable_address.state, created_at: common_utils::date_time::now(), - first_name: address_details - .and_then(|address_details| address_details.first_name.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - last_name: address_details - .and_then(|address_details| address_details.last_name.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, modified_at: common_utils::date_time::now(), - zip: address_details - .and_then(|address_details| address_details.zip.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, + zip: encryptable_address.zip, updated_by: storage_scheme.to_string(), - email: address - .email - .as_ref() - .cloned() - .async_lift(|inner| types::encrypt_optional(inner.map(|inner| inner.expose()), key)) - .await?, + email: encryptable_address.email, }) } .await } pub async fn get_address_by_id( - db: &dyn StorageInterface, + state: &SessionState, address_id: Option, merchant_key_store: &domain::MerchantKeyStore, payment_id: &str, @@ -431,17 +395,21 @@ pub async fn get_address_by_id( ) -> CustomResult, errors::ApiErrorResponse> { match address_id { None => Ok(None), - Some(address_id) => Ok(db - .find_address_by_merchant_id_payment_id_address_id( - merchant_id, - payment_id, - &address_id, - merchant_key_store, - storage_scheme, - ) - .await - .map(|payment_address| payment_address.address) - .ok()), + Some(address_id) => { + let db = &*state.store; + Ok(db + .find_address_by_merchant_id_payment_id_address_id( + &state.into(), + merchant_id, + payment_id, + &address_id, + merchant_key_store, + storage_scheme, + ) + .await + .map(|payment_address| payment_address.address) + .ok()) + } } } @@ -688,12 +656,13 @@ pub async fn get_token_for_recurring_mandate( ) .await .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?; - + let key_manager_state: KeyManagerState = state.into(); let original_payment_intent = mandate .original_payment_id .as_ref() .async_map(|payment_id| async { db.find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, payment_id, &mandate.merchant_id, merchant_key_store, @@ -1429,7 +1398,7 @@ pub(crate) async fn get_payment_method_create_request( } pub async fn get_customer_from_details( - db: &dyn StorageInterface, + state: &SessionState, customer_id: Option, merchant_id: &str, payment_data: &mut PaymentData, @@ -1439,8 +1408,10 @@ pub async fn get_customer_from_details( match customer_id { None => Ok(None), Some(customer_id) => { + let db = &*state.store; let customer = db .find_customer_optional_by_customer_id_merchant_id( + &state.into(), &customer_id, merchant_id, merchant_key_store, @@ -1549,8 +1520,8 @@ pub async fn get_connector_default( #[instrument(skip_all)] #[allow(clippy::type_complexity)] pub async fn create_customer_if_not_exist<'a, F: Clone, R>( + state: &SessionState, operation: BoxedOperation<'a, F, R>, - db: &dyn StorageInterface, payment_data: &mut PaymentData, req: Option, merchant_id: &str, @@ -1613,7 +1584,7 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( payment_data.payment_intent.customer_details = raw_customer_details .clone() - .async_map(|customer_details| create_encrypted_data(key_store, customer_details)) + .async_map(|customer_details| create_encrypted_data(state, key_store, customer_details)) .await .transpose() .change_context(errors::StorageError::EncryptionError) @@ -1622,18 +1593,36 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( let customer_id = request_customer_details .customer_id .or(payment_data.payment_intent.customer_id.clone()); - + let db = &*state.store; + let key_manager_state = &state.into(); let optional_customer = match customer_id { Some(customer_id) => { let customer_data = db .find_customer_optional_by_customer_id_merchant_id( + key_manager_state, &customer_id, merchant_id, key_store, storage_scheme, ) .await?; - + let key = key_store.key.get_inner().peek(); + let encrypted_data = types::batch_encrypt( + key_manager_state, + CustomerRequestWithEmail::to_encryptable(CustomerRequestWithEmail { + name: request_customer_details.name.clone(), + email: request_customer_details.email.clone(), + phone: request_customer_details.phone.clone(), + }), + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + .await + .change_context(errors::StorageError::SerializationFailed) + .attach_printable("Failed while encrypting Customer while Update")?; + let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data) + .change_context(errors::StorageError::SerializationFailed) + .attach_printable("Failed while encrypting Customer while Update")?; Some(match customer_data { Some(c) => { // Update the customer data if new data is passed in the request @@ -1642,44 +1631,19 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( | request_customer_details.phone.is_some() | request_customer_details.phone_country_code.is_some() { - let key = key_store.key.get_inner().peek(); - let customer_update = async { - Ok::<_, error_stack::Report>( - Update { - name: request_customer_details - .name - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - email: request_customer_details - .email - .clone() - .async_lift(|inner| { - types::encrypt_optional( - inner.map(|inner| inner.expose()), - key, - ) - }) - .await?, - phone: Box::new( - request_customer_details - .phone - .clone() - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - ), - phone_country_code: request_customer_details.phone_country_code, - description: None, - connector_customer: None, - metadata: None, - address_id: None, - }, - ) - } - .await - .change_context(errors::StorageError::SerializationFailed) - .attach_printable("Failed while encrypting Customer while Update")?; + let customer_update = Update { + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: Box::new(encryptable_customer.phone), + phone_country_code: request_customer_details.phone_country_code, + description: None, + connector_customer: None, + metadata: None, + address_id: None, + }; db.update_customer_by_customer_id_merchant_id( + key_manager_state, customer_id, merchant_id.to_string(), c, @@ -1693,51 +1657,25 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( } } None => { - let new_customer = async { - let key = key_store.key.get_inner().peek(); - Ok::<_, error_stack::Report>( - domain::Customer { - customer_id, - merchant_id: merchant_id.to_string(), - name: request_customer_details - .name - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - email: request_customer_details - .email - .clone() - .async_lift(|inner| { - types::encrypt_optional( - inner.map(|inner| inner.expose()), - key, - ) - }) - .await?, - phone: request_customer_details - .phone - .clone() - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - phone_country_code: request_customer_details - .phone_country_code - .clone(), - description: None, - created_at: common_utils::date_time::now(), - id: None, - metadata: None, - modified_at: common_utils::date_time::now(), - connector_customer: None, - address_id: None, - default_payment_method_id: None, - updated_by: None, - }, - ) - } - .await - .change_context(errors::StorageError::SerializationFailed) - .attach_printable("Failed while encrypting Customer while insert")?; + let new_customer = domain::Customer { + customer_id, + merchant_id: merchant_id.to_string(), + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: encryptable_customer.phone, + phone_country_code: request_customer_details.phone_country_code.clone(), + description: None, + created_at: common_utils::date_time::now(), + id: None, + metadata: None, + modified_at: common_utils::date_time::now(), + connector_customer: None, + address_id: None, + default_payment_method_id: None, + updated_by: None, + }; metrics::CUSTOMER_CREATED.add(&metrics::CONTEXT, 1, &[]); - db.insert_customer(new_customer, key_store, storage_scheme) + db.insert_customer(key_manager_state, new_customer, key_store, storage_scheme) .await } }) @@ -1746,6 +1684,7 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( None => None, Some(customer_id) => db .find_customer_optional_by_customer_id_merchant_id( + key_manager_state, customer_id, merchant_id, key_store, @@ -2533,14 +2472,16 @@ where #[cfg(feature = "olap")] pub(super) async fn filter_by_constraints( - db: &dyn StorageInterface, + state: &SessionState, constraints: &api::PaymentListConstraints, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult, errors::DataStorageError> { + let db = &*state.store; let result = db .filter_payment_intent_by_constraints( + &(state).into(), merchant_id, &constraints.clone().into(), key_store, @@ -2957,17 +2898,19 @@ pub(crate) fn validate_pm_or_token_given( // A function to perform database lookup and then verify the client secret pub async fn verify_payment_intent_time_and_client_secret( - db: &dyn StorageInterface, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, client_secret: Option, ) -> error_stack::Result, errors::ApiErrorResponse> { + let db = &*state.store; client_secret .async_map(|cs| async move { let payment_id = get_payment_id_from_client_secret(&cs)?; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, &merchant_account.merchant_id, key_store, @@ -3371,6 +3314,7 @@ pub async fn get_merchant_connector_account( merchant_connector_id: Option<&String>, ) -> RouterResult { let db = &*state.store; + let key_manager_state = &state.into(); match creds_identifier { Some(creds_identifier) => { let key = format!("mcd_{merchant_id}_{creds_identifier}"); @@ -3445,6 +3389,7 @@ pub async fn get_merchant_connector_account( None => { if let Some(merchant_connector_id) = merchant_connector_id { db.find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, merchant_id, merchant_connector_id, key_store, @@ -3457,6 +3402,7 @@ pub async fn get_merchant_connector_account( ) } else { db.find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, profile_id, connector_name, key_store, @@ -3737,13 +3683,14 @@ impl AttemptType { request: &api::PaymentsRequest, fetched_payment_intent: PaymentIntent, fetched_payment_attempt: PaymentAttempt, - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, storage_scheme: storage::enums::MerchantStorageScheme, ) -> RouterResult<(PaymentIntent, PaymentAttempt)> { match self { Self::SameOld => Ok((fetched_payment_intent, fetched_payment_attempt)), Self::New => { + let db = &*state.store; let new_attempt_count = fetched_payment_intent.attempt_count + 1; let new_payment_attempt = db .insert_payment_attempt( @@ -3766,6 +3713,7 @@ impl AttemptType { let updated_payment_intent = db .update_payment_intent( + &state.into(), fetched_payment_intent, storage::PaymentIntentUpdate::StatusAndAttemptUpdate { status: payment_intent_status_fsm( @@ -4159,6 +4107,7 @@ pub fn is_apple_pay_simplified_flow( } pub async fn get_encrypted_apple_pay_connector_wallets_details( + state: &SessionState, key_store: &domain::MerchantKeyStore, connector_metadata: &Option>, ) -> RouterResult>>> { @@ -4179,10 +4128,15 @@ pub async fn get_encrypted_apple_pay_connector_wallets_details( }) .transpose()? .map(masking::Secret::new); - + let key_manager_state: KeyManagerState = state.into(); let encrypted_connector_apple_pay_details = connector_apple_pay_details .async_lift(|wallets_details| { - types::encrypt_optional(wallets_details, key_store.key.get_inner().peek()) + types::encrypt_optional( + &key_manager_state, + wallets_details, + Identifier::Merchant(key_store.merchant_id.clone()), + key_store.key.get_inner().peek(), + ) }) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -4266,6 +4220,7 @@ where let merchant_connector_account_list = state .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), merchant_account.merchant_id.as_str(), false, key_store, @@ -5126,13 +5081,15 @@ pub async fn override_setup_future_usage_to_on_session( } pub async fn validate_merchant_connector_ids_in_connector_mandate_details( - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, connector_mandate_details: &api_models::payment_methods::PaymentsMandateReference, merchant_id: &str, ) -> CustomResult<(), errors::ApiErrorResponse> { + let db = &*state.store; let merchant_connector_account_list = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), merchant_id, true, key_store, diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index be54ead0ac..70cc77aa53 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -28,7 +28,6 @@ pub use self::{ use super::{helpers, CustomerDetails, PaymentData}; use crate::{ core::errors::{self, CustomResult, RouterResult}, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -117,7 +116,7 @@ pub trait Domain: Send + Sync { /// This will fetch customer details, (this operation is flow specific) async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, merchant_key_store: &domain::MerchantKeyStore, @@ -259,7 +258,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, _request: Option, merchant_key_store: &domain::MerchantKeyStore, @@ -274,7 +273,7 @@ where Ok(( Box::new(self), helpers::get_customer_from_details( - db, + state, payment_data.payment_intent.customer_id.clone(), &merchant_key_store.merchant_id, payment_data, @@ -334,7 +333,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, _request: Option, merchant_key_store: &domain::MerchantKeyStore, @@ -349,7 +348,7 @@ where Ok(( Box::new(self), helpers::get_customer_from_details( - db, + state, payment_data.payment_intent.customer_id.clone(), &merchant_key_store.merchant_id, payment_data, @@ -408,7 +407,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, _request: Option, merchant_key_store: &domain::MerchantKeyStore, @@ -423,7 +422,7 @@ where Ok(( Box::new(self), helpers::get_customer_from_details( - db, + state, payment_data.payment_intent.customer_id.clone(), &merchant_key_store.merchant_id, payment_data, @@ -483,7 +482,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - _db: &dyn StorageInterface, + _state: &SessionState, _payment_data: &mut PaymentData, _request: Option, _merchant_key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 172c5943c0..5b18512fe2 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -53,6 +53,7 @@ impl GetTracker, api::PaymentsCaptureRequest> payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -97,7 +98,7 @@ impl GetTracker, api::PaymentsCaptureRequest> amount = payment_attempt.get_total_amount().into(); let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -107,7 +108,7 @@ impl GetTracker, api::PaymentsCaptureRequest> .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -117,7 +118,7 @@ impl GetTracker, api::PaymentsCaptureRequest> .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -199,7 +200,7 @@ impl UpdateTracker, api::PaymentsCaptureRequest> for #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &'b SessionState, + state: &'b SessionState, _req_state: ReqState, mut payment_data: PaymentData, _customer: Option, @@ -224,9 +225,10 @@ impl UpdateTracker, api::PaymentsCaptureRequest> for merchant_decision: Some(api_models::enums::MerchantDecision::Approved.to_string()), updated_by: storage_scheme.to_string(), }; - payment_data.payment_intent = db + payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent, intent_status_update, key_store, @@ -234,7 +236,8 @@ impl UpdateTracker, api::PaymentsCaptureRequest> for ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; - db.store + state + .store .update_payment_attempt_with_attempt_id( payment_data.payment_attempt.clone(), storage::PaymentAttemptUpdate::StatusUpdate { diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index 94591d44e4..a8a2756a58 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -51,6 +51,7 @@ impl GetTracker, api::PaymentsCancelRequest> let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -82,7 +83,7 @@ impl GetTracker, api::PaymentsCancelRequest> .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -92,7 +93,7 @@ impl GetTracker, api::PaymentsCancelRequest> .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -102,7 +103,7 @@ impl GetTracker, api::PaymentsCancelRequest> .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -212,7 +213,7 @@ impl UpdateTracker, api::PaymentsCancelRequest> for #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &'b SessionState, + state: &'b SessionState, req_state: ReqState, mut payment_data: PaymentData, _customer: Option, @@ -242,9 +243,10 @@ impl UpdateTracker, api::PaymentsCancelRequest> for }; if let Some(payment_intent_update) = intent_status_update { - payment_data.payment_intent = db + payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent, payment_intent_update, key_store, @@ -254,7 +256,8 @@ impl UpdateTracker, api::PaymentsCancelRequest> for .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; } - db.store + state + .store .update_payment_attempt_with_attempt_id( payment_data.payment_attempt.clone(), storage::PaymentAttemptUpdate::VoidUpdate { diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index f6e6dee14a..03a3d031e1 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -54,6 +54,7 @@ impl GetTracker, api::PaymentsCaptu payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -135,7 +136,7 @@ impl GetTracker, api::PaymentsCaptu amount = payment_attempt.get_total_amount().into(); let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -145,7 +146,7 @@ impl GetTracker, api::PaymentsCaptu .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -155,7 +156,7 @@ impl GetTracker, api::PaymentsCaptu .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index 9c48df1dd5..c687539275 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -17,7 +17,6 @@ use crate::{ }, utils as core_utils, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -56,6 +55,7 @@ impl GetTracker, api::PaymentsRequest> for Co payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -200,7 +200,7 @@ impl GetTracker, api::PaymentsRequest> for Co )?; let shipping_address = helpers::create_or_update_address_for_payment_by_request( - db, + state, request.shipping.as_ref(), payment_intent.shipping_address_id.clone().as_deref(), merchant_id.as_ref(), @@ -216,7 +216,7 @@ impl GetTracker, api::PaymentsRequest> for Co .map(|shipping_address| shipping_address.address_id.clone()); let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -226,7 +226,7 @@ impl GetTracker, api::PaymentsRequest> for Co .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -364,7 +364,7 @@ impl Domain for CompleteAuthorize { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -377,8 +377,8 @@ impl Domain for CompleteAuthorize { errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -478,6 +478,7 @@ impl UpdateTracker, api::PaymentsRequest> for Comple let updated_payment_intent = db .update_payment_intent( + &state.into(), payment_intent, payment_intent_update, key_store, diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 2955b0c385..addd2f3067 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -7,7 +7,10 @@ use api_models::{ payments::{AdditionalPaymentData, ExtendedCardInfo}, }; use async_trait::async_trait; -use common_utils::ext_traits::{AsyncExt, Encode, StringExt, ValueExt}; +use common_utils::{ + ext_traits::{AsyncExt, Encode, StringExt, ValueExt}, + types::keymanager::Identifier, +}; use error_stack::{report, ResultExt}; use futures::FutureExt; use hyperswitch_domain_models::payments::payment_intent::PaymentIntentUpdateFields; @@ -30,7 +33,6 @@ use crate::{ }, utils as core_utils, }, - db::StorageInterface, events::audit_events::{AuditEvent, AuditEventType}, routes::{app::ReqState, SessionState}, services, @@ -74,6 +76,7 @@ impl GetTracker, api::PaymentsRequest> for Pa // Parallel calls - level 0 let mut payment_intent = store .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, m_merchant_id.as_str(), key_store, @@ -181,13 +184,13 @@ impl GetTracker, api::PaymentsRequest> for Pa let m_payment_intent_payment_id = payment_intent.payment_id.clone(); let m_customer_details_customer_id = customer_details.customer_id.clone(); let m_payment_intent_customer_id = payment_intent.customer_id.clone(); - let store = state.clone().store; let m_key_store = key_store.clone(); + let session_state = state.clone(); let shipping_address_fut = tokio::spawn( async move { helpers::create_or_update_address_for_payment_by_request( - store.as_ref(), + &session_state, m_request_shipping.as_ref(), m_payment_intent_shipping_address_id.as_deref(), m_merchant_id.as_str(), @@ -209,13 +212,13 @@ impl GetTracker, api::PaymentsRequest> for Pa let m_payment_intent_customer_id = payment_intent.customer_id.clone(); let m_payment_intent_billing_address_id = payment_intent.billing_address_id.clone(); let m_payment_intent_payment_id = payment_intent.payment_id.clone(); - let store = state.clone().store; let m_key_store = key_store.clone(); + let session_state = state.clone(); let billing_address_fut = tokio::spawn( async move { helpers::create_or_update_address_for_payment_by_request( - store.as_ref(), + &session_state, m_request_billing.as_ref(), m_payment_intent_billing_address_id.as_deref(), m_merchant_id.as_ref(), @@ -306,7 +309,7 @@ impl GetTracker, api::PaymentsRequest> for Pa request, payment_intent, payment_attempt, - &*state.store, + state, key_store, storage_scheme, ) @@ -467,8 +470,6 @@ impl GetTracker, api::PaymentsRequest> for Pa .in_current_span(), ); - let store = state.clone().store; - let n_payment_method_billing_address_id = payment_attempt.payment_method_billing_address_id.clone(); let n_request_payment_method_billing_address = request @@ -480,11 +481,12 @@ impl GetTracker, api::PaymentsRequest> for Pa let m_key_store = key_store.clone(); let m_customer_details_customer_id = customer_details.customer_id.clone(); let m_merchant_id = merchant_id.clone(); + let session_state = state.clone(); let payment_method_billing_future = tokio::spawn( async move { helpers::create_or_update_address_for_payment_by_request( - store.as_ref(), + &session_state, n_request_payment_method_billing_address.as_ref(), n_payment_method_billing_address_id.as_deref(), m_merchant_id.as_str(), @@ -688,7 +690,7 @@ impl Domain for PaymentConfirm { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -701,8 +703,8 @@ impl Domain for PaymentConfirm { errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -1077,12 +1079,15 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to encode additional pm data")?; + let key_manager_state = &state.into(); let encode_additional_pm_to_value = if let Some(ref pm) = payment_data.payment_method_info { let key = key_store.key.get_inner().peek(); let card_detail_from_locker: Option = decrypt::( + key_manager_state, pm.payment_method_data.clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key, ) .await @@ -1243,7 +1248,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let billing_address = payment_data.address.get_payment_billing(); let billing_details = billing_address - .async_map(|billing_details| create_encrypted_data(key_store, billing_details)) + .async_map(|billing_details| create_encrypted_data(state, key_store, billing_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1251,7 +1256,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let shipping_address = payment_data.address.get_shipping(); let shipping_details = shipping_address - .async_map(|shipping_details| create_encrypted_data(key_store, shipping_details)) + .async_map(|shipping_details| create_encrypted_data(state, key_store, shipping_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1273,10 +1278,12 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let m_storage_scheme = storage_scheme.to_string(); let session_expiry = m_payment_data_payment_intent.session_expiry; let m_key_store = key_store.clone(); + let key_manager_state = state.into(); let payment_intent_fut = tokio::spawn( async move { m_db.update_payment_intent( + &key_manager_state, m_payment_data_payment_intent, storage::PaymentIntentUpdate::Update(Box::new(PaymentIntentUpdateFields { amount: payment_data.payment_intent.amount, @@ -1320,10 +1327,13 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let m_customer_merchant_id = customer.merchant_id.to_owned(); let m_key_store = key_store.clone(); let m_updated_customer = updated_customer.clone(); - let m_db = state.clone().store; + let session_state = state.clone(); + let m_db = session_state.store.clone(); + let key_manager_state = state.into(); tokio::spawn( async move { m_db.update_customer_by_customer_id_merchant_id( + &key_manager_state, m_customer_customer_id, m_customer_merchant_id, customer, diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 18dc2c5f39..929f404eae 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -6,7 +6,7 @@ use api_models::{ use async_trait::async_trait; use common_utils::{ ext_traits::{AsyncExt, Encode, ValueExt}, - types::MinorUnit, + types::{keymanager::Identifier, MinorUnit}, }; use diesel_models::{ephemeral_key, PaymentMethod}; use error_stack::{self, ResultExt}; @@ -32,7 +32,10 @@ use crate::{ utils as core_utils, }, db::StorageInterface, - routes::{app::ReqState, SessionState}, + routes::{ + app::{ReqState, SessionStateInfo}, + SessionState, + }, services, types::{ api::{self, PaymentIdTypeExt}, @@ -143,7 +146,7 @@ impl GetTracker, api::PaymentsRequest> for Pa let customer_details = helpers::get_customer_details_from_request(request); let shipping_address = helpers::create_or_find_address_for_payment_by_request( - db, + state, request.shipping.as_ref(), None, merchant_id, @@ -155,7 +158,7 @@ impl GetTracker, api::PaymentsRequest> for Pa .await?; let billing_address = helpers::create_or_find_address_for_payment_by_request( - db, + state, request.billing.as_ref(), None, merchant_id, @@ -168,7 +171,7 @@ impl GetTracker, api::PaymentsRequest> for Pa let payment_method_billing_address = helpers::create_or_find_address_for_payment_by_request( - db, + state, request .payment_method_data .as_ref() @@ -286,7 +289,12 @@ impl GetTracker, api::PaymentsRequest> for Pa .await?; payment_intent = db - .insert_payment_intent(payment_intent_new, merchant_key_store, storage_scheme) + .insert_payment_intent( + &state.into(), + payment_intent_new, + merchant_key_store, + storage_scheme, + ) .await .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment { payment_id: payment_id.clone(), @@ -475,7 +483,7 @@ impl Domain for PaymentCreate { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -488,8 +496,8 @@ impl Domain for PaymentCreate { errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -638,12 +646,15 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let raw_customer_details = customer .map(|customer| CustomerData::try_from(customer.clone())) .transpose()?; + let session_state = state.session_state(); // Updation of Customer Details for the cases where both customer_id and specific customer // details are provided in Payment Create Request let customer_details = raw_customer_details .clone() - .async_map(|customer_details| create_encrypted_data(key_store, customer_details)) + .async_map(|customer_details| { + create_encrypted_data(&session_state, key_store, customer_details) + }) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -652,6 +663,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent, storage::PaymentIntentUpdate::PaymentCreateUpdate { return_url: None, @@ -836,7 +848,9 @@ impl PaymentCreate { .as_ref() .async_map(|pm_info| async { decrypt::( + &state.into(), pm_info.payment_method_data.clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key_store.key.get_inner().peek(), ) .await @@ -983,7 +997,7 @@ impl PaymentCreate { #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] async fn make_payment_intent( - _state: &SessionState, + state: &SessionState, payment_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -1057,7 +1071,7 @@ impl PaymentCreate { let billing_details = request .billing .clone() - .async_map(|billing_details| create_encrypted_data(key_store, billing_details)) + .async_map(|billing_details| create_encrypted_data(state, key_store, billing_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1068,7 +1082,7 @@ impl PaymentCreate { let shipping_details = request .shipping .clone() - .async_map(|shipping_details| create_encrypted_data(key_store, shipping_details)) + .async_map(|shipping_details| create_encrypted_data(state, key_store, shipping_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1093,7 +1107,7 @@ impl PaymentCreate { // Encrypting our Customer Details to be stored in Payment Intent let customer_details = raw_customer_details - .async_map(|customer_details| create_encrypted_data(key_store, customer_details)) + .async_map(|customer_details| create_encrypted_data(state, key_store, customer_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index a3a8130a9f..a83079a6a4 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -48,6 +48,7 @@ impl GetTracker, PaymentsCancelRequest> for P let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -79,7 +80,7 @@ impl GetTracker, PaymentsCancelRequest> for P .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -89,7 +90,7 @@ impl GetTracker, PaymentsCancelRequest> for P .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -99,7 +100,7 @@ impl GetTracker, PaymentsCancelRequest> for P .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -234,6 +235,7 @@ impl UpdateTracker, PaymentsCancelRequest> for Payme payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent, intent_status_update, key_store, diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index fc075f2979..808b2ae618 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -2,7 +2,10 @@ use std::collections::HashMap; use async_trait::async_trait; use common_enums::AuthorizationStatus; -use common_utils::{ext_traits::Encode, types::MinorUnit}; +use common_utils::{ + ext_traits::Encode, + types::{keymanager::KeyManagerState, MinorUnit}, +}; use error_stack::{report, ResultExt}; use futures::FutureExt; use hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt; @@ -265,7 +268,7 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu { async fn update_tracker<'b>( &'b self, - db: &'b SessionState, + state: &'b SessionState, _payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData< @@ -317,7 +320,7 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu }; //payment_attempt update if let Some(payment_attempt_update) = option_payment_attempt_update { - payment_data.payment_attempt = db + payment_data.payment_attempt = state .store .update_payment_attempt_with_attempt_id( payment_data.payment_attempt.clone(), @@ -329,9 +332,10 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu } // payment_intent update if let Some(payment_intent_update) = option_payment_intent_update { - payment_data.payment_intent = db + payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), payment_intent_update, key_store, @@ -370,7 +374,8 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu "missing authorization_id in incremental_authorization_details in payment_data", ), )?; - db.store + state + .store .update_authorization_by_merchant_id_authorization_id( router_data.merchant_id.clone(), authorization_id, @@ -380,7 +385,7 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu .to_not_found_response(errors::ApiErrorResponse::InternalServerError) .attach_printable("failed while updating authorization")?; //Fetch all the authorizations of the payment and send in incremental authorization response - let authorizations = db + let authorizations = state .store .find_all_authorizations_by_merchant_id_payment_id( &router_data.merchant_id, @@ -1255,9 +1260,11 @@ async fn payment_response_update_tracker( let m_key_store = key_store.clone(); let m_payment_data_payment_intent = payment_data.payment_intent.clone(); let m_payment_intent_update = payment_intent_update.clone(); + let key_manager_state: KeyManagerState = state.into(); let payment_intent_fut = tokio::spawn( async move { m_db.update_payment_intent( + &key_manager_state, m_payment_data_payment_intent, m_payment_intent_update, &m_key_store, diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 503a5c5f2b..00f599d409 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -13,7 +13,6 @@ use crate::{ errors::{self, RouterResult, StorageErrorExt}, payments::{self, helpers, operations, PaymentData}, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -53,6 +52,7 @@ impl GetTracker, api::PaymentsSessionRequest> let mut payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -89,7 +89,7 @@ impl GetTracker, api::PaymentsSessionRequest> let amount = payment_attempt.get_total_amount().into(); let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -99,7 +99,7 @@ impl GetTracker, api::PaymentsSessionRequest> .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -109,7 +109,7 @@ impl GetTracker, api::PaymentsSessionRequest> .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -244,6 +244,7 @@ impl UpdateTracker, api::PaymentsSessionRequest> for Some(metadata) => state .store .update_payment_intent( + &state.into(), payment_data.payment_intent, storage::PaymentIntentUpdate::MetadataUpdate { metadata, @@ -295,7 +296,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -308,8 +309,8 @@ where errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -358,6 +359,7 @@ where let all_connector_accounts = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), &merchant_account.merchant_id, false, key_store, diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index c4096c6ea5..98befe122c 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -12,7 +12,6 @@ use crate::{ errors::{self, CustomResult, RouterResult, StorageErrorExt}, payments::{helpers, operations, CustomerDetails, PaymentAddress, PaymentData}, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -51,6 +50,7 @@ impl GetTracker, api::PaymentsStartRequest> f payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -86,7 +86,7 @@ impl GetTracker, api::PaymentsStartRequest> f amount = payment_attempt.get_total_amount().into(); let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -96,7 +96,7 @@ impl GetTracker, api::PaymentsStartRequest> f .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -106,7 +106,7 @@ impl GetTracker, api::PaymentsStartRequest> f .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -268,7 +268,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -281,8 +281,8 @@ where errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 5f20ccdf27..fff096d9b4 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use api_models::enums::FrmSuggestion; use async_trait::async_trait; -use common_utils::ext_traits::AsyncExt; +use common_utils::{ext_traits::AsyncExt, types::keymanager::KeyManagerState}; use error_stack::ResultExt; use router_derive::PaymentOperation; use router_env::{instrument, logger, tracing}; @@ -16,7 +16,6 @@ use crate::{ PaymentData, }, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -58,7 +57,7 @@ impl Domain for PaymentStatus { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -71,8 +70,8 @@ impl Domain for PaymentStatus { errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -197,7 +196,7 @@ impl GetTracker, api::PaymentsRetrieveRequest payment_id, merchant_account, key_store, - &*state.store, + state, request, self, merchant_account.storage_scheme, @@ -214,7 +213,7 @@ async fn get_tracker_for_sync< payment_id: &api::PaymentIdType, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - db: &dyn StorageInterface, + state: &SessionState, request: &api::PaymentsRetrieveRequest, operation: Op, storage_scheme: enums::MerchantStorageScheme, @@ -222,7 +221,7 @@ async fn get_tracker_for_sync< let (payment_intent, mut payment_attempt, currency, amount); (payment_intent, payment_attempt) = get_payment_intent_payment_attempt( - db, + state, payment_id, &merchant_account.merchant_id, key_store, @@ -238,7 +237,7 @@ async fn get_tracker_for_sync< amount = payment_attempt.get_total_amount().into(); let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id.clone(), @@ -247,7 +246,7 @@ async fn get_tracker_for_sync< ) .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id.clone(), @@ -257,7 +256,7 @@ async fn get_tracker_for_sync< .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id.clone(), @@ -267,7 +266,7 @@ async fn get_tracker_for_sync< .await?; payment_attempt.encoded_data.clone_from(&request.param); - + let db = &*state.store; let attempts = match request.expand_attempts { Some(true) => { Some(db @@ -510,18 +509,21 @@ impl ValidateRequest for Payme } pub async fn get_payment_intent_payment_attempt( - db: &dyn StorageInterface, + state: &SessionState, payment_id: &api::PaymentIdType, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult<(storage::PaymentIntent, storage::PaymentAttempt)> { + let key_manager_state: KeyManagerState = state.into(); + let db = &*state.store; let get_pi_pa = || async { let (pi, pa); match payment_id { api_models::payments::PaymentIdType::PaymentIntentId(ref id) => { pi = db .find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, id, merchant_id, key_store, @@ -547,6 +549,7 @@ pub async fn get_payment_intent_payment_attempt( .await?; pi = db .find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, pa.payment_id.as_str(), merchant_id, key_store, @@ -560,6 +563,7 @@ pub async fn get_payment_intent_payment_attempt( .await?; pi = db .find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, pa.payment_id.as_str(), merchant_id, key_store, @@ -578,6 +582,7 @@ pub async fn get_payment_intent_payment_attempt( pi = db .find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, pa.payment_id.as_str(), merchant_id, key_store, diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index 98006e574c..f95ec0d014 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -24,7 +24,6 @@ use crate::{ payments::{self, helpers, operations, CustomerDetails, PaymentAddress, PaymentData}, utils as core_utils, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -64,6 +63,7 @@ impl GetTracker, api::PaymentsRequest> for Pa payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -193,7 +193,7 @@ impl GetTracker, api::PaymentsRequest> for Pa } let shipping_address = helpers::create_or_update_address_for_payment_by_request( - db, + state, request.shipping.as_ref(), payment_intent.shipping_address_id.as_deref(), merchant_id, @@ -207,7 +207,7 @@ impl GetTracker, api::PaymentsRequest> for Pa ) .await?; let billing_address = helpers::create_or_update_address_for_payment_by_request( - db, + state, request.billing.as_ref(), payment_intent.billing_address_id.as_deref(), merchant_id, @@ -222,7 +222,7 @@ impl GetTracker, api::PaymentsRequest> for Pa .await?; let payment_method_billing = helpers::create_or_update_address_for_payment_by_request( - db, + state, request .payment_method_data .as_ref() @@ -491,7 +491,7 @@ impl Domain for PaymentUpdate { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -504,8 +504,8 @@ impl Domain for PaymentUpdate { errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -715,7 +715,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let billing_details = payment_data .address .get_payment_billing() - .async_map(|billing_details| create_encrypted_data(key_store, billing_details)) + .async_map(|billing_details| create_encrypted_data(state, key_store, billing_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -724,7 +724,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let shipping_details = payment_data .address .get_shipping() - .async_map(|shipping_details| create_encrypted_data(key_store, shipping_details)) + .async_map(|shipping_details| create_encrypted_data(state, key_store, shipping_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -741,6 +741,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), storage::PaymentIntentUpdate::Update(Box::new(PaymentIntentUpdateFields { amount: payment_data.amount.into(), diff --git a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs index 61a629df62..20c2e051b6 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -16,10 +16,7 @@ use crate::{ PaymentAddress, }, }, - routes::{ - app::{ReqState, StorageInterface}, - SessionState, - }, + routes::{app::ReqState, SessionState}, services, types::{ api::{self, PaymentIdTypeExt}, @@ -59,6 +56,7 @@ impl let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -179,7 +177,7 @@ impl UpdateTracker, PaymentsIncrementalAut #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &'b SessionState, + state: &'b SessionState, _req_state: ReqState, mut payment_data: payments::PaymentData, _customer: Option, @@ -224,7 +222,7 @@ impl UpdateTracker, PaymentsIncrementalAut connector_authorization_id: None, previously_authorized_amount: payment_data.payment_intent.amount, }; - let authorization = db + let authorization = state .store .insert_authorization(authorization_new.clone()) .await @@ -236,9 +234,10 @@ impl UpdateTracker, PaymentsIncrementalAut }) .attach_printable("failed while inserting new authorization")?; // Update authorization_count in payment_intent - payment_data.payment_intent = db + payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), storage::PaymentIntentUpdate::AuthorizationCountUpdate { authorization_count: new_authorization_count, @@ -295,7 +294,7 @@ impl Domain #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - _db: &dyn StorageInterface, + _state: &SessionState, _payment_data: &mut payments::PaymentData, _request: Option, _merchant_key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index bf5ae5aa63..99cb6aaec6 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -469,6 +469,7 @@ where payment_data.payment_intent = db .update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), storage::PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { active_attempt_id: payment_data.payment_attempt.attempt_id.clone(), diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 0ff53c63b4..ff9f6ddbf3 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -528,6 +528,7 @@ pub async fn refresh_cgraph_cache<'a>( let mut merchant_connector_accounts = state .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), &key_store.merchant_id, false, key_store, diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index b77bdb7515..4016336713 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -8,7 +8,7 @@ use common_utils::{ id_type, pii, }; use error_stack::{report, ResultExt}; -use masking::{ExposeInterface, PeekInterface, Secret}; +use masking::{ExposeInterface, Secret}; use router_env::{instrument, metrics::add_attributes, tracing}; use super::helpers; @@ -214,7 +214,7 @@ where let pm_data_encrypted: Option>> = pm_card_details - .async_map(|pm_card| create_encrypted_data(key_store, pm_card)) + .async_map(|pm_card| create_encrypted_data(state, key_store, pm_card)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -223,7 +223,7 @@ where let encrypted_payment_method_billing_address: Option< Encryptable>, > = payment_method_billing_address - .async_map(|address| create_encrypted_data(key_store, address.clone())) + .async_map(|address| create_encrypted_data(state, key_store, address.clone())) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -310,7 +310,7 @@ where let pm_metadata = create_payment_method_metadata(None, connector_token)?; payment_methods::cards::create_payment_method( - db, + state, &payment_method_create_request, &customer_id, &resp.payment_method_id, @@ -407,7 +407,7 @@ where Err(err) => { if err.current_context().is_db_not_found() { payment_methods::cards::insert_payment_method( - db, + state, &resp, &payment_method_create_request.clone(), key_store, @@ -479,10 +479,8 @@ where ))? }; - let existing_pm_data = payment_methods::cards::get_card_details_without_locker_fallback( - &existing_pm, - key_store.key.peek(), - state, + let existing_pm_data = payment_methods::cards::get_card_details_without_locker_fallback(&existing_pm,state, + key_store, ) .await?; @@ -518,7 +516,7 @@ where let pm_data_encrypted: Option< Encryptable>, > = updated_pmd - .async_map(|pmd| create_encrypted_data(key_store, pmd)) + .async_map(|pmd| create_encrypted_data(state, key_store, pmd)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -601,7 +599,7 @@ where resp.payment_method_id = generate_id(consts::ID_LENGTH, "pm"); payment_methods::cards::create_payment_method( - db, + state, &payment_method_create_request, &customer_id, &resp.payment_method_id, diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index 3de8e50549..78ff9900f6 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -122,6 +122,7 @@ pub async fn initiate_payout_link( // Fetch customer let customer = db .find_customer_by_customer_id_merchant_id( + &(&state).into(), &customer_id, &req.merchant_id, &key_store, @@ -263,10 +264,12 @@ pub async fn filter_payout_methods( key_store: &domain::MerchantKeyStore, payout: &hyperswitch_domain_models::payouts::payouts::Payouts, ) -> errors::RouterResult> { - let db: &dyn StorageInterface = &*state.store; + let db = &*state.store; + let key_manager_state = &state.into(); //Fetch all merchant connector accounts let all_mcas = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( + key_manager_state, &merchant_account.merchant_id, false, key_store, @@ -282,7 +285,7 @@ pub async fn filter_payout_methods( common_enums::ConnectorType::PayoutProcessor, ); let address = db - .find_address_by_address_id(&payout.address_id.clone(), key_store) + .find_address_by_address_id(key_manager_state, &payout.address_id.clone(), key_store) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable_lazy(|| { diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index b2c883c381..6db3c1edf4 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -732,6 +732,7 @@ pub async fn payouts_list_core( Ok(payout_attempt) => { match db .find_customer_by_customer_id_merchant_id( + &(&state).into(), &payouts.customer_id, merchant_id, &key_store, @@ -822,7 +823,14 @@ pub async fn payouts_filtered_list_core( .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?; let data: Vec = join_all(list.into_iter().map(|(p, pa, c)| async { - match domain::Customer::convert_back(c, &key_store.key).await { + match domain::Customer::convert_back( + &(&state).into(), + c, + &key_store.key, + key_store.merchant_id.clone(), + ) + .await + { Ok(domain_cust) => Some((p, pa, domain_cust)), Err(err) => { logger::warn!( @@ -1084,6 +1092,7 @@ pub async fn create_recipient( { payout_data.customer_details = Some( db.update_customer_by_customer_id_merchant_id( + &state.into(), customer_id, merchant_id, customer, @@ -2184,7 +2193,7 @@ pub async fn payout_create_db_entries( // Get or create address let billing_address = payment_helpers::create_or_find_address_for_payment_by_request( - db, + state, req.billing.as_ref(), None, merchant_id, @@ -2345,7 +2354,7 @@ pub async fn make_payout_data( .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?; let billing_address = payment_helpers::create_or_find_address_for_payment_by_request( - db, + state, None, Some(&payouts.address_id.to_owned()), merchant_id, @@ -2358,6 +2367,7 @@ pub async fn make_payout_data( let customer_details = db .find_customer_optional_by_customer_id_merchant_id( + &state.into(), &payouts.customer_id.to_owned(), merchant_id, key_store, diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 3ab1714320..6cda7b2843 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -1,13 +1,17 @@ -use api_models::{enums, payment_methods::Card, payouts}; +use api_models::{customers::CustomerRequestWithEmail, enums, payment_methods::Card, payouts}; use common_utils::{ + encryption::Encryption, errors::CustomResult, ext_traits::{AsyncExt, StringExt}, fp_utils, generate_customer_id_of_default_length, id_type, - types::MinorUnit, + types::{ + keymanager::{Identifier, KeyManagerState, ToEncryptable}, + MinorUnit, + }, }; -use diesel_models::encryption::Encryption; use error_stack::{report, ResultExt}; -use masking::{ExposeInterface, PeekInterface, Secret}; +use hyperswitch_domain_models::type_encryption::batch_encrypt; +use masking::{PeekInterface, Secret}; use router_env::logger; use super::PayoutData; @@ -233,6 +237,7 @@ pub async fn save_payout_data_to_locker( } _ => { let key = key_store.key.get_inner().peek(); + let key_manager_state: KeyManagerState = state.into(); let enc_data = async { serde_json::to_value(payout_method_data.to_owned()) .change_context(errors::ApiErrorResponse::InternalServerError) @@ -242,7 +247,14 @@ pub async fn save_payout_data_to_locker( let secret: Secret = Secret::new(v.to_string()); secret }) - .async_lift(|inner| domain_types::encrypt_optional(inner, key)) + .async_lift(|inner| { + domain_types::encrypt_optional( + &key_manager_state, + inner, + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + }) .await } .await @@ -453,7 +465,7 @@ pub async fn save_payout_data_to_locker( }); ( Some( - cards::create_encrypted_data(key_store, pm_data) + cards::create_encrypted_data(state, key_store, pm_data) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt customer details")?, @@ -489,7 +501,7 @@ pub async fn save_payout_data_to_locker( if should_insert_in_pm_table { let payment_method_id = common_utils::generate_id(crate::consts::ID_LENGTH, "pm"); cards::create_payment_method( - db, + state, &new_payment_method, &payout_attempt.customer_id, &payment_method_id, @@ -601,9 +613,11 @@ pub async fn get_or_create_customer_details( let merchant_id = &merchant_account.merchant_id; let key = key_store.key.get_inner().peek(); + let key_manager_state = &state.into(); match db .find_customer_optional_by_customer_id_merchant_id( + key_manager_state, &customer_id, merchant_id, key_store, @@ -614,21 +628,28 @@ pub async fn get_or_create_customer_details( { Some(customer) => Ok(Some(customer)), None => { + let encrypted_data = batch_encrypt( + &state.into(), + CustomerRequestWithEmail::to_encryptable(CustomerRequestWithEmail { + name: customer_details.name.clone(), + email: customer_details.email.clone(), + phone: customer_details.phone.clone(), + }), + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + let encryptable_customer = + CustomerRequestWithEmail::from_encryptable(encrypted_data) + .change_context(errors::ApiErrorResponse::InternalServerError)?; + let customer = domain::Customer { customer_id, merchant_id: merchant_id.to_string(), - name: domain_types::encrypt_optional(customer_details.name.to_owned(), key) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?, - email: domain_types::encrypt_optional( - customer_details.email.to_owned().map(|e| e.expose()), - key, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?, - phone: domain_types::encrypt_optional(customer_details.phone.to_owned(), key) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?, + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: encryptable_customer.phone, description: None, phone_country_code: customer_details.phone_country_code.to_owned(), metadata: None, @@ -642,9 +663,14 @@ pub async fn get_or_create_customer_details( }; Ok(Some( - db.insert_customer(customer, key_store, merchant_account.storage_scheme) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?, + db.insert_customer( + key_manager_state, + customer, + key_store, + merchant_account.storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?, )) } } diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index 3434ac3e9c..32e5ef308c 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -15,7 +15,7 @@ use common_utils::{ crypto::{HmacSha256, SignMessage}, ext_traits::AsyncExt, generate_id, - types::{self as util_types, AmountConvertor}, + types::{self as util_types, keymanager::Identifier, AmountConvertor}, }; use error_stack::ResultExt; use helpers::PaymentAuthConnectorDataExt; @@ -110,7 +110,7 @@ pub async fn create_link_token( > = connector.connector.get_connector_integration(); let payment_intent = oss_helpers::verify_payment_intent_time_and_client_secret( - &*state.store, + &state, &merchant_account, &key_store, payload.client_secret, @@ -121,7 +121,7 @@ pub async fn create_link_token( .as_ref() .async_map(|pi| async { oss_helpers::get_address_by_id( - &*state.store, + &state, pi.billing_address_id.clone(), &key_store, &pi.payment_id, @@ -139,6 +139,7 @@ pub async fn create_link_token( let merchant_connector_account = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + &(&state).into(), merchant_account.merchant_id.as_str(), &selected_config.mca_id, &key_store, @@ -234,6 +235,7 @@ pub async fn exchange_token_core( let merchant_connector_account = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + &(&state).into(), merchant_account.merchant_id.as_str(), &config.mca_id, &key_store, @@ -294,6 +296,7 @@ async fn store_bank_details_in_payment_methods( let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &payload.payment_id, &merchant_account.merchant_id, &key_store, @@ -326,7 +329,9 @@ async fn store_bank_details_in_payment_methods( for pm in payment_methods { if pm.payment_method == Some(enums::PaymentMethod::BankDebit) { let bank_details_pm_data = decrypt::( + &(&state).into(), pm.payment_method_data.clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key, ) .await @@ -433,10 +438,11 @@ async fn store_bank_details_in_payment_methods( ); let payment_method_data = payment_methods::PaymentMethodsData::BankDetails(pmd); - let encrypted_data = cards::create_encrypted_data(&key_store, payment_method_data) - .await - .change_context(ApiErrorResponse::InternalServerError) - .attach_printable("Unable to encrypt customer details")?; + let encrypted_data = + cards::create_encrypted_data(&state, &key_store, payment_method_data) + .await + .change_context(ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt customer details")?; let pm_update = storage::PaymentMethodUpdate::PaymentMethodDataUpdate { payment_method_data: Some(encrypted_data.into()), @@ -446,7 +452,7 @@ async fn store_bank_details_in_payment_methods( } else { let payment_method_data = payment_methods::PaymentMethodsData::BankDetails(pmd); let encrypted_data = - cards::create_encrypted_data(&key_store, Some(payment_method_data)) + cards::create_encrypted_data(&state, &key_store, Some(payment_method_data)) .await .change_context(ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt customer details")?; @@ -701,14 +707,19 @@ pub async fn retrieve_payment_method_from_auth_service( let connector = PaymentAuthConnectorData::get_connector_by_name( auth_token.connector_details.connector.as_str(), )?; - + let key_manager_state = &state.into(); let merchant_account = db - .find_merchant_account_by_merchant_id(&payment_intent.merchant_id, key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &payment_intent.merchant_id, + key_store, + ) .await .to_not_found_response(ApiErrorResponse::MerchantAccountNotFound)?; let mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &payment_intent.merchant_id, &auth_token.connector_details.mca_id, key_store, @@ -770,7 +781,7 @@ pub async fn retrieve_payment_method_from_auth_service( } let address = oss_helpers::get_address_by_id( - &*state.store, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index 5b74eefb9a..374c37dc21 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -57,6 +57,7 @@ pub async fn refund_create_core( payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &req.payment_id, merchant_id, &key_store, @@ -415,6 +416,7 @@ pub async fn refund_retrieve_core( let payment_id = refund.payment_id.as_str(); payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), payment_id, merchant_id, &key_store, @@ -924,9 +926,11 @@ pub async fn refund_manual_update( state: SessionState, req: api_models::refunds::RefundManualUpdateRequest, ) -> RouterResponse { + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &req.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -935,7 +939,7 @@ pub async fn refund_manual_update( .attach_printable("Error while fetching the key store by merchant_id")?; let merchant_account = state .store - .find_merchant_account_by_merchant_id(&req.merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &req.merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound) .attach_printable("Error while fetching the merchant_account by merchant_id")?; @@ -1125,6 +1129,7 @@ pub async fn sync_refund_with_gateway_workflow( state: &SessionState, refund_tracker: &storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { + let key_manager_state = &state.into(); let refund_core = serde_json::from_value::(refund_tracker.tracking_data.clone()) .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1138,6 +1143,7 @@ pub async fn sync_refund_with_gateway_workflow( let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &refund_core.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -1145,7 +1151,11 @@ pub async fn sync_refund_with_gateway_workflow( let merchant_account = state .store - .find_merchant_account_by_merchant_id(&refund_core.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &refund_core.merchant_id, + &key_store, + ) .await?; let response = Box::pin(refund_retrieve_core( @@ -1220,17 +1230,22 @@ pub async fn trigger_refund_execute_workflow( refund_tracker.tracking_data ) })?; - + let key_manager_state = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &refund_core.merchant_id, &state.store.get_master_key().to_vec().into(), ) .await?; let merchant_account = db - .find_merchant_account_by_merchant_id(&refund_core.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &refund_core.merchant_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1245,7 +1260,11 @@ pub async fn trigger_refund_execute_workflow( match (&refund.sent_to_gateway, &refund.refund_status) { (false, enums::RefundStatus::Pending) => { let merchant_account = db - .find_merchant_account_by_merchant_id(&refund.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &refund.merchant_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1261,6 +1280,7 @@ pub async fn trigger_refund_execute_workflow( let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(state.into()), &payment_attempt.payment_id, &refund.merchant_id, &key_store, diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 5f10de56e4..b17217737b 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -120,7 +120,7 @@ pub async fn create_routing_config( .await?; helpers::validate_connectors_in_routing_config( - db, + &state, &key_store, &merchant_account.merchant_id, &profile_id, diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index d99177aa55..707ed41d95 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -15,6 +15,7 @@ use storage_impl::redis::cache; use crate::{ core::errors::{self, RouterResult}, db::StorageInterface, + routes::SessionState, types::{domain, storage}, utils::StringExt, }; @@ -186,7 +187,7 @@ pub async fn update_routing_algorithm( /// This will help make one of all configured algorithms to be in active state for a particular /// merchant pub async fn update_merchant_active_algorithm_ref( - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, config_key: cache::CacheKind<'_>, algorithm_id: routing_types::RoutingAlgorithmRef, @@ -218,8 +219,9 @@ pub async fn update_merchant_active_algorithm_ref( payment_link_config: None, pm_collect_link_config: None, }; - + let db = &*state.store; db.update_specific_fields_in_merchant( + &state.into(), &key_store.merchant_id, merchant_account_update, key_store, @@ -300,14 +302,16 @@ pub async fn update_business_profile_active_algorithm_ref( } pub async fn validate_connectors_in_routing_config( - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, merchant_id: &str, profile_id: &str, routing_algorithm: &routing_types::RoutingAlgorithm, ) -> RouterResult<()> { - let all_mcas = db + let all_mcas = &*state + .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), merchant_id, true, key_store, diff --git a/crates/router/src/core/surcharge_decision_config.rs b/crates/router/src/core/surcharge_decision_config.rs index b35d7c5ad2..55ea4ba16f 100644 --- a/crates/router/src/core/surcharge_decision_config.rs +++ b/crates/router/src/core/surcharge_decision_config.rs @@ -91,7 +91,7 @@ pub async fn upsert_surcharge_decision_config( algo_id.update_surcharge_config_id(key.clone()); let config_key = cache::CacheKind::Surcharge(key.into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update routing algorithm ref")?; @@ -128,7 +128,7 @@ pub async fn upsert_surcharge_decision_config( algo_id.update_surcharge_config_id(key.clone()); let config_key = cache::CacheKind::Surcharge(key.clone().into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update routing algorithm ref")?; @@ -158,7 +158,7 @@ pub async fn delete_surcharge_decision_config( .unwrap_or_default(); algo_id.surcharge_config_algo_id = None; let config_key = cache::CacheKind::Surcharge(key.clone().into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update deleted algorithm ref")?; diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 461ac85536..be33ccc69e 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -4,6 +4,7 @@ use api_models::{ payments::RedirectionResponse, user::{self as user_api, InviteMultipleUserResponse}, }; +use common_utils::types::keymanager::Identifier; #[cfg(feature = "email")] use diesel_models::user_role::UserRoleUpdate; use diesel_models::{ @@ -1104,9 +1105,11 @@ pub async fn create_internal_user( state: SessionState, request: user_api::CreateInternalUserRequest, ) -> UserResponse<()> { + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, consts::user_role::INTERNAL_USER_MERCHANT_ID, &state.store.get_master_key().to_vec().into(), ) @@ -1122,6 +1125,7 @@ pub async fn create_internal_user( let internal_merchant = state .store .find_merchant_account_by_merchant_id( + key_manager_state, consts::user_role::INTERNAL_USER_MERCHANT_ID, &key_store, ) @@ -1176,7 +1180,7 @@ pub async fn switch_merchant_id( } let user = user_from_token.get_user_from_db(&state).await?; - + let key_manager_state = &(&state).into(); let role_info = roles::RoleInfo::from_role_id( &state, &user_from_token.role_id, @@ -1190,6 +1194,7 @@ pub async fn switch_merchant_id( let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, request.merchant_id.as_str(), &state.store.get_master_key().to_vec().into(), ) @@ -1204,7 +1209,11 @@ pub async fn switch_merchant_id( let org_id = state .store - .find_merchant_account_by_merchant_id(request.merchant_id.as_str(), &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + request.merchant_id.as_str(), + &key_store, + ) .await .map_err(|e| { if e.current_context().is_db_not_found() { @@ -1306,6 +1315,7 @@ pub async fn list_merchants_for_user( let merchant_accounts = state .store .list_multiple_merchant_accounts( + &(&state).into(), user_roles .iter() .map(|role| role.merchant_id.clone()) @@ -1852,7 +1862,9 @@ pub async fn update_totp( totp_secret: Some( // TODO: Impl conversion trait for User and move this there domain::types::encrypt::( + &(&state).into(), totp.get_secret_base32().into(), + Identifier::User(key_store.user_id.clone()), key_store.key.peek(), ) .await @@ -1918,7 +1930,10 @@ pub async fn transfer_user_key_store_keymanager( let db = &state.global_store; let key_stores = db - .get_all_user_key_store(&state.store.get_master_key().to_vec().into()) + .get_all_user_key_store( + &(&state).into(), + &state.store.get_master_key().to_vec().into(), + ) .await .change_context(UserErrors::InternalServerError)?; @@ -2056,10 +2071,12 @@ pub async fn create_user_authentication_method( ) .change_context(UserErrors::InternalServerError) .attach_printable("Failed to decode DEK")?; - + let id = uuid::Uuid::new_v4().to_string(); let (private_config, public_config) = utils::user::construct_public_and_private_db_configs( + &state, &req.auth_method, &user_auth_encryption_key, + id.clone(), ) .await?; @@ -2103,7 +2120,7 @@ pub async fn create_user_authentication_method( state .store .insert_user_authentication_method(UserAuthenticationMethodNew { - id: uuid::Uuid::new_v4().to_string(), + id, auth_id, owner_id: req.owner_id, owner_type: req.owner_type, @@ -2137,8 +2154,10 @@ pub async fn update_user_authentication_method( .attach_printable("Failed to decode DEK")?; let (private_config, public_config) = utils::user::construct_public_and_private_db_configs( + &state, &req.auth_method, &user_auth_encryption_key, + req.id.clone(), ) .await?; @@ -2212,9 +2231,12 @@ pub async fn get_sso_auth_url( .await .to_not_found_response(UserErrors::InvalidUserAuthMethodOperation)?; - let open_id_private_config = - utils::user::decrypt_oidc_private_config(&state, user_authentication_method.private_config) - .await?; + let open_id_private_config = utils::user::decrypt_oidc_private_config( + &state, + user_authentication_method.private_config, + request.id.clone(), + ) + .await?; let open_id_public_config = serde_json::from_value::( user_authentication_method @@ -2264,9 +2286,12 @@ pub async fn sso_sign( .await .change_context(UserErrors::InternalServerError)?; - let open_id_private_config = - utils::user::decrypt_oidc_private_config(&state, user_authentication_method.private_config) - .await?; + let open_id_private_config = utils::user::decrypt_oidc_private_config( + &state, + user_authentication_method.private_config, + authentication_method_id, + ) + .await?; let open_id_public_config = serde_json::from_value::( user_authentication_method diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 3648984629..b279d852d4 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -613,6 +613,7 @@ pub async fn backfill_metadata( let key_store = state .store .get_merchant_key_store_by_merchant_id( + &state.into(), &user.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -713,6 +714,7 @@ pub async fn get_merchant_connector_account_by_name( state .store .find_merchant_connector_account_by_merchant_id_connector_name( + &state.into(), merchant_id, connector_name, key_store, diff --git a/crates/router/src/core/user/sample_data.rs b/crates/router/src/core/user/sample_data.rs index e1ccb3b033..b679753b56 100644 --- a/crates/router/src/core/user/sample_data.rs +++ b/crates/router/src/core/user/sample_data.rs @@ -25,6 +25,7 @@ pub async fn generate_sample_data_for_user( let key_store = state .store .get_merchant_key_store_by_merchant_id( + &(&state).into(), &user_from_token.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -50,7 +51,7 @@ pub async fn generate_sample_data_for_user( state .store - .insert_payment_intents_batch_for_sample_data(payment_intents, &key_store) + .insert_payment_intents_batch_for_sample_data(&(&state).into(), payment_intents, &key_store) .await .switch()?; state @@ -74,10 +75,11 @@ pub async fn delete_sample_data_for_user( _req_state: ReqState, ) -> SampleDataApiResponse<()> { let merchant_id_del = user_from_token.merchant_id.as_str(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &user_from_token.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -87,7 +89,7 @@ pub async fn delete_sample_data_for_user( state .store - .delete_payment_intents_for_sample_data(merchant_id_del, &key_store) + .delete_payment_intents_for_sample_data(key_manager_state, merchant_id_del, &key_store) .await .switch()?; state diff --git a/crates/router/src/core/verification.rs b/crates/router/src/core/verification.rs index b6e71b2828..65ac568832 100644 --- a/crates/router/src/core/verification.rs +++ b/crates/router/src/core/verification.rs @@ -91,13 +91,19 @@ pub async fn get_verified_apple_domains_with_mid_mca_id( errors::ApiErrorResponse, > { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)?; let verified_domains = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, merchant_id.as_str(), merchant_connector_id.as_str(), &key_store, diff --git a/crates/router/src/core/verification/utils.rs b/crates/router/src/core/verification/utils.rs index bfbc1cb8b4..a59789b280 100644 --- a/crates/router/src/core/verification/utils.rs +++ b/crates/router/src/core/verification/utils.rs @@ -15,9 +15,11 @@ pub async fn check_existence_and_add_domain_to_db( merchant_connector_id: String, domain_from_req: Vec, ) -> CustomResult, errors::ApiErrorResponse> { + let key_manager_state = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -27,6 +29,7 @@ pub async fn check_existence_and_add_domain_to_db( let merchant_connector_account = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &merchant_id, &merchant_connector_id, &key_store, @@ -66,6 +69,7 @@ pub async fn check_existence_and_add_domain_to_db( state .store .update_merchant_connector_account( + key_manager_state, merchant_connector_account, updated_mca.into(), &key_store, diff --git a/crates/router/src/core/webhooks/incoming.rs b/crates/router/src/core/webhooks/incoming.rs index 9eb74dc2df..5c5346ce13 100644 --- a/crates/router/src/core/webhooks/incoming.rs +++ b/crates/router/src/core/webhooks/incoming.rs @@ -258,7 +258,7 @@ async fn incoming_webhooks_core( Some(merchant_connector_account) => merchant_connector_account, None => { helper_utils::get_mca_from_object_reference_id( - &*state.clone().store, + &state, object_ref_id.clone(), &merchant_account, &connector_name, @@ -1675,6 +1675,7 @@ async fn fetch_optional_mca_and_connector( if connector_name_or_mca_id.starts_with("mca_") { let mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + &state.into(), &merchant_account.merchant_id, connector_name_or_mca_id, key_store, diff --git a/crates/router/src/core/webhooks/outgoing.rs b/crates/router/src/core/webhooks/outgoing.rs index c40b4ae886..ed2b41dc64 100644 --- a/crates/router/src/core/webhooks/outgoing.rs +++ b/crates/router/src/core/webhooks/outgoing.rs @@ -4,7 +4,7 @@ use api_models::{ webhook_events::{OutgoingWebhookRequestContent, OutgoingWebhookResponseContent}, webhooks, }; -use common_utils::{ext_traits::Encode, request::RequestContent}; +use common_utils::{ext_traits::Encode, request::RequestContent, types::keymanager::Identifier}; use diesel_models::process_tracker::business_status; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::type_encryption::decrypt; @@ -87,6 +87,7 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( }; let request_content = get_outgoing_webhook_request( + &state, &merchant_account, outgoing_webhook, &business_profile, @@ -97,7 +98,7 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( .attach_printable("Failed to construct outgoing webhook request content")?; let event_metadata = storage::EventMetadata::foreign_from((&content, &primary_object_id)); - + let key_manager_state = &(&state).into(); let new_event = domain::Event { event_id: event_id.clone(), event_type, @@ -113,11 +114,13 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( initial_attempt_id: Some(event_id.clone()), request: Some( domain_types::encrypt( + key_manager_state, request_content .encode_to_string_of_json() .change_context(errors::ApiErrorResponse::WebhookProcessingFailure) .attach_printable("Failed to encode outgoing webhook request content") .map(Secret::new)?, + Identifier::Merchant(merchant_key_store.merchant_id.clone()), merchant_key_store.key.get_inner().peek(), ) .await @@ -131,7 +134,7 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( let event_insert_result = state .store - .insert_event(new_event, merchant_key_store) + .insert_event(key_manager_state, new_event, merchant_key_store) .await; let event = match event_insert_result { @@ -552,6 +555,7 @@ fn get_webhook_url_from_business_profile( } pub(crate) async fn get_outgoing_webhook_request( + state: &SessionState, merchant_account: &domain::MerchantAccount, outgoing_webhook: api::OutgoingWebhook, business_profile: &diesel_models::business_profile::BusinessProfile, @@ -559,6 +563,7 @@ pub(crate) async fn get_outgoing_webhook_request( ) -> CustomResult { #[inline] async fn get_outgoing_webhook_request_inner( + state: &SessionState, outgoing_webhook: api::OutgoingWebhook, business_profile: &diesel_models::business_profile::BusinessProfile, key_store: &domain::MerchantKeyStore, @@ -571,9 +576,11 @@ pub(crate) async fn get_outgoing_webhook_request( let transformed_outgoing_webhook = WebhookType::from(outgoing_webhook); let payment_response_hash_key = business_profile.payment_response_hash_key.clone(); let custom_headers = decrypt::( + &state.into(), business_profile .outgoing_webhook_custom_http_headers .clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key_store.key.get_inner().peek(), ) .await @@ -614,6 +621,7 @@ pub(crate) async fn get_outgoing_webhook_request( #[cfg(feature = "stripe")] Some(api_models::enums::Connector::Stripe) => { get_outgoing_webhook_request_inner::( + state, outgoing_webhook, business_profile, key_store, @@ -622,6 +630,7 @@ pub(crate) async fn get_outgoing_webhook_request( } _ => { get_outgoing_webhook_request_inner::( + state, outgoing_webhook, business_profile, key_store, @@ -645,7 +654,7 @@ async fn update_event_if_client_error( error_message: String, ) -> CustomResult { let is_webhook_notified = false; - + let key_manager_state = &(&state).into(); let response_to_store = OutgoingWebhookResponseContent { body: None, headers: None, @@ -657,12 +666,14 @@ async fn update_event_if_client_error( is_webhook_notified, response: Some( domain_types::encrypt( + key_manager_state, response_to_store .encode_to_string_of_json() .change_context( errors::WebhooksFlowError::OutgoingWebhookResponseEncodingFailed, ) .map(Secret::new)?, + Identifier::Merchant(merchant_key_store.merchant_id.clone()), merchant_key_store.key.get_inner().peek(), ) .await @@ -674,6 +685,7 @@ async fn update_event_if_client_error( state .store .update_event_by_merchant_id_event_id( + key_manager_state, merchant_id, event_id, event_update, @@ -733,7 +745,7 @@ async fn update_event_in_storage( ) -> CustomResult { let status_code = response.status(); let is_webhook_notified = status_code.is_success(); - + let key_manager_state = &(&state).into(); let response_headers = response .headers() .iter() @@ -772,12 +784,14 @@ async fn update_event_in_storage( is_webhook_notified, response: Some( domain_types::encrypt( + key_manager_state, response_to_store .encode_to_string_of_json() .change_context( errors::WebhooksFlowError::OutgoingWebhookResponseEncodingFailed, ) .map(Secret::new)?, + Identifier::Merchant(merchant_key_store.merchant_id.clone()), merchant_key_store.key.get_inner().peek(), ) .await @@ -788,6 +802,7 @@ async fn update_event_in_storage( state .store .update_event_by_merchant_id_event_id( + key_manager_state, merchant_id, event_id, event_update, diff --git a/crates/router/src/core/webhooks/webhook_events.rs b/crates/router/src/core/webhooks/webhook_events.rs index 2a16ccce0b..19118ad639 100644 --- a/crates/router/src/core/webhooks/webhook_events.rs +++ b/crates/router/src/core/webhooks/webhook_events.rs @@ -28,7 +28,7 @@ pub async fn list_initial_delivery_attempts( api::webhook_events::EventListConstraintsInternal::foreign_try_from(constraints)?; let store = state.store.as_ref(); - + let key_manager_state = &(&state).into(); let (account, key_store) = determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?; @@ -36,14 +36,14 @@ pub async fn list_initial_delivery_attempts( api_models::webhook_events::EventListConstraintsInternal::ObjectIdFilter { object_id } => { match account { MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account) => store - .list_initial_events_by_merchant_id_primary_object_id( + .list_initial_events_by_merchant_id_primary_object_id(key_manager_state, &merchant_account.merchant_id, &object_id, &key_store, ) .await, MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => store - .list_initial_events_by_profile_id_primary_object_id( + .list_initial_events_by_profile_id_primary_object_id(key_manager_state, &business_profile.profile_id, &object_id, &key_store, @@ -73,7 +73,7 @@ pub async fn list_initial_delivery_attempts( match account { MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account) => store - .list_initial_events_by_merchant_id_constraints( + .list_initial_events_by_merchant_id_constraints(key_manager_state, &merchant_account.merchant_id, created_after, created_before, @@ -83,7 +83,7 @@ pub async fn list_initial_delivery_attempts( ) .await, MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => store - .list_initial_events_by_profile_id_constraints( + .list_initial_events_by_profile_id_constraints(key_manager_state, &business_profile.profile_id, created_after, created_before, @@ -116,11 +116,12 @@ pub async fn list_delivery_attempts( let (account, key_store) = determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?; - + let key_manager_state = &(&state).into(); let events = match account { MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account) => { store .list_events_by_merchant_id_initial_attempt_id( + key_manager_state, &merchant_account.merchant_id, &initial_attempt_id, &key_store, @@ -130,6 +131,7 @@ pub async fn list_delivery_attempts( MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => { store .list_events_by_profile_id_initial_attempt_id( + key_manager_state, &business_profile.profile_id, &initial_attempt_id, &key_store, @@ -162,12 +164,17 @@ pub async fn retry_delivery_attempt( event_id: String, ) -> RouterResponse { let store = state.store.as_ref(); - + let key_manager_state = &(&state).into(); let (account, key_store) = determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?; let event_to_retry = store - .find_event_by_merchant_id_event_id(&key_store.merchant_id, &event_id, &key_store) + .find_event_by_merchant_id_event_id( + key_manager_state, + &key_store.merchant_id, + &event_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::EventNotFound)?; @@ -216,7 +223,7 @@ pub async fn retry_delivery_attempt( }; let event = store - .insert_event(new_event, &key_store) + .insert_event(key_manager_state, new_event, &key_store) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to insert event")?; @@ -245,7 +252,12 @@ pub async fn retry_delivery_attempt( .await; let updated_event = store - .find_event_by_merchant_id_event_id(&key_store.merchant_id, &new_event_id, &key_store) + .find_event_by_merchant_id_event_id( + key_manager_state, + &key_store.merchant_id, + &new_event_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::EventNotFound)?; @@ -259,8 +271,10 @@ async fn determine_identifier_and_get_key_store( merchant_id_or_profile_id: String, ) -> errors::RouterResult<(MerchantAccountOrBusinessProfile, domain::MerchantKeyStore)> { let store = state.store.as_ref(); + let key_manager_state = &(&state).into(); match store .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id_or_profile_id, &store.get_master_key().to_vec().into(), ) @@ -271,7 +285,11 @@ async fn determine_identifier_and_get_key_store( // Find a merchant account having `merchant_id` = `merchant_id_or_profile_id`. Ok(key_store) => { let merchant_account = store - .find_merchant_account_by_merchant_id(&merchant_id_or_profile_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &merchant_id_or_profile_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -301,6 +319,7 @@ async fn determine_identifier_and_get_key_store( let key_store = store .get_merchant_key_store_by_merchant_id( + key_manager_state, &business_profile.merchant_id, &store.get_master_key().to_vec().into(), ) diff --git a/crates/router/src/db/address.rs b/crates/router/src/db/address.rs index f210019b4d..04a8f673a9 100644 --- a/crates/router/src/db/address.rs +++ b/crates/router/src/db/address.rs @@ -1,4 +1,4 @@ -use common_utils::id_type; +use common_utils::{id_type, types::keymanager::KeyManagerState}; use diesel_models::{address::AddressUpdateInternal, enums::MerchantStorageScheme}; use error_stack::ResultExt; @@ -22,6 +22,7 @@ where { async fn update_address( &self, + state: &KeyManagerState, address_id: String, address: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -29,6 +30,7 @@ where async fn update_address_for_payments( &self, + state: &KeyManagerState, this: domain::PaymentAddress, address: domain::AddressUpdate, payment_id: String, @@ -38,12 +40,14 @@ where async fn find_address_by_address_id( &self, + state: &KeyManagerState, address_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult; async fn insert_address_for_payments( &self, + state: &KeyManagerState, payment_id: &str, address: domain::PaymentAddress, key_store: &domain::MerchantKeyStore, @@ -52,12 +56,14 @@ where async fn insert_address_for_customers( &self, + state: &KeyManagerState, address: domain::CustomerAddress, key_store: &domain::MerchantKeyStore, ) -> CustomResult; async fn find_address_by_merchant_id_payment_id_address_id( &self, + state: &KeyManagerState, merchant_id: &str, payment_id: &str, address_id: &str, @@ -67,6 +73,7 @@ where async fn update_address_by_merchant_id_customer_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, address: storage_types::AddressUpdate, @@ -76,7 +83,7 @@ where #[cfg(not(feature = "kv_store"))] mod storage { - use common_utils::{ext_traits::AsyncExt, id_type}; + use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; @@ -98,6 +105,7 @@ mod storage { #[instrument(skip_all)] async fn find_address_by_address_id( &self, + state: &KeyManagerState, address_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -107,7 +115,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -117,6 +129,7 @@ mod storage { #[instrument(skip_all)] async fn find_address_by_merchant_id_payment_id_address_id( &self, + state: &KeyManagerState, merchant_id: &str, payment_id: &str, address_id: &str, @@ -134,7 +147,7 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) }) @@ -144,6 +157,7 @@ mod storage { #[instrument(skip_all)] async fn update_address( &self, + state: &KeyManagerState, address_id: String, address: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -154,7 +168,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -164,6 +182,7 @@ mod storage { #[instrument(skip_all)] async fn update_address_for_payments( &self, + state: &KeyManagerState, this: domain::PaymentAddress, address_update: domain::AddressUpdate, _payment_id: String, @@ -180,7 +199,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -190,6 +213,7 @@ mod storage { #[instrument(skip_all)] async fn insert_address_for_payments( &self, + state: &KeyManagerState, _payment_id: &str, address: domain::PaymentAddress, key_store: &domain::MerchantKeyStore, @@ -205,7 +229,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -215,6 +243,7 @@ mod storage { #[instrument(skip_all)] async fn insert_address_for_customers( &self, + state: &KeyManagerState, address: domain::CustomerAddress, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -228,7 +257,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -238,6 +271,7 @@ mod storage { #[instrument(skip_all)] async fn update_address_by_merchant_id_customer_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, address: storage_types::AddressUpdate, @@ -257,7 +291,7 @@ mod storage { for address in addresses.into_iter() { output.push( address - .convert(key_store.key.get_inner()) + .convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError)?, ) @@ -271,7 +305,7 @@ mod storage { #[cfg(feature = "kv_store")] mod storage { - use common_utils::{ext_traits::AsyncExt, id_type}; + use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use diesel_models::{enums::MerchantStorageScheme, AddressUpdateInternal}; use error_stack::{report, ResultExt}; use redis_interface::HsetnxReply; @@ -299,6 +333,7 @@ mod storage { #[instrument(skip_all)] async fn find_address_by_address_id( &self, + state: &KeyManagerState, address_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -308,7 +343,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -318,6 +357,7 @@ mod storage { #[instrument(skip_all)] async fn find_address_by_merchant_id_payment_id_address_id( &self, + state: &KeyManagerState, merchant_id: &str, payment_id: &str, address_id: &str, @@ -362,7 +402,11 @@ mod storage { } }?; address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -370,6 +414,7 @@ mod storage { #[instrument(skip_all)] async fn update_address( &self, + state: &KeyManagerState, address_id: String, address: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -380,7 +425,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -390,6 +439,7 @@ mod storage { #[instrument(skip_all)] async fn update_address_for_payments( &self, + state: &KeyManagerState, this: domain::PaymentAddress, address_update: domain::AddressUpdate, payment_id: String, @@ -420,7 +470,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -457,7 +511,11 @@ mod storage { .change_context(errors::StorageError::KVError)?; updated_address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -467,6 +525,7 @@ mod storage { #[instrument(skip_all)] async fn insert_address_for_payments( &self, + state: &KeyManagerState, payment_id: &str, address: domain::PaymentAddress, key_store: &domain::MerchantKeyStore, @@ -493,7 +552,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -553,7 +616,11 @@ mod storage { } .into()), Ok(HsetnxReply::KeySet) => Ok(created_address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?), Err(er) => Err(er).change_context(errors::StorageError::KVError), @@ -565,6 +632,7 @@ mod storage { #[instrument(skip_all)] async fn insert_address_for_customers( &self, + state: &KeyManagerState, address: domain::CustomerAddress, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -578,7 +646,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -588,6 +660,7 @@ mod storage { #[instrument(skip_all)] async fn update_address_by_merchant_id_customer_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, address: storage_types::AddressUpdate, @@ -607,7 +680,11 @@ mod storage { for address in addresses.into_iter() { output.push( address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ) @@ -623,6 +700,7 @@ mod storage { impl AddressInterface for MockDb { async fn find_address_by_address_id( &self, + state: &KeyManagerState, address_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -635,7 +713,11 @@ impl AddressInterface for MockDb { { Some(address) => address .clone() - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError), None => { @@ -648,6 +730,7 @@ impl AddressInterface for MockDb { async fn find_address_by_merchant_id_payment_id_address_id( &self, + state: &KeyManagerState, _merchant_id: &str, _payment_id: &str, address_id: &str, @@ -663,7 +746,11 @@ impl AddressInterface for MockDb { { Some(address) => address .clone() - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError), None => { @@ -676,6 +763,7 @@ impl AddressInterface for MockDb { async fn update_address( &self, + state: &KeyManagerState, address_id: String, address_update: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -694,7 +782,11 @@ impl AddressInterface for MockDb { }); match updated_addr { Some(address_updated) => address_updated - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError), None => Err(errors::StorageError::ValueNotFound( @@ -706,6 +798,7 @@ impl AddressInterface for MockDb { async fn update_address_for_payments( &self, + state: &KeyManagerState, this: domain::PaymentAddress, address_update: domain::AddressUpdate, _payment_id: String, @@ -726,7 +819,11 @@ impl AddressInterface for MockDb { }); match updated_addr { Some(address_updated) => address_updated - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError), None => Err(errors::StorageError::ValueNotFound( @@ -738,6 +835,7 @@ impl AddressInterface for MockDb { async fn insert_address_for_payments( &self, + state: &KeyManagerState, _payment_id: &str, address_new: domain::PaymentAddress, key_store: &domain::MerchantKeyStore, @@ -752,13 +850,18 @@ impl AddressInterface for MockDb { addresses.push(address.clone()); address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } async fn insert_address_for_customers( &self, + state: &KeyManagerState, address_new: domain::CustomerAddress, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -771,13 +874,18 @@ impl AddressInterface for MockDb { addresses.push(address.clone()); address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } async fn update_address_by_merchant_id_customer_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, address_update: storage_types::AddressUpdate, @@ -801,7 +909,11 @@ impl AddressInterface for MockDb { match updated_addr { Some(address) => { let address: domain::Address = address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; Ok(vec![address]) diff --git a/crates/router/src/db/customers.rs b/crates/router/src/db/customers.rs index cad3d2969f..1bfda65852 100644 --- a/crates/router/src/db/customers.rs +++ b/crates/router/src/db/customers.rs @@ -1,4 +1,4 @@ -use common_utils::{ext_traits::AsyncExt, id_type}; +use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use error_stack::ResultExt; use futures::future::try_join_all; use router_env::{instrument, tracing}; @@ -29,14 +29,17 @@ where async fn find_customer_optional_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult, errors::StorageError>; + #[allow(clippy::too_many_arguments)] async fn update_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: id_type::CustomerId, merchant_id: String, customer: domain::Customer, @@ -47,6 +50,7 @@ where async fn find_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -55,12 +59,14 @@ where async fn list_customers_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError>; async fn insert_customer( &self, + state: &KeyManagerState, customer_data: domain::Customer, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -69,7 +75,7 @@ where #[cfg(feature = "kv_store")] mod storage { - use common_utils::{ext_traits::AsyncExt, id_type}; + use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use diesel_models::kv; use error_stack::{report, ResultExt}; use futures::future::try_join_all; @@ -103,6 +109,7 @@ mod storage { // check customer not found in kv and fallback to db async fn find_customer_optional_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -149,9 +156,13 @@ mod storage { let maybe_result = maybe_customer .async_map(|c| async { - c.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + c.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await .transpose()?; @@ -167,6 +178,7 @@ mod storage { #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: id_type::CustomerId, merchant_id: String, customer: domain::Customer, @@ -236,7 +248,11 @@ mod storage { }; updated_object? - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -244,6 +260,7 @@ mod storage { #[instrument(skip_all)] async fn find_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -287,7 +304,11 @@ mod storage { }?; let result: domain::Customer = customer - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; //.await @@ -303,6 +324,7 @@ mod storage { #[instrument(skip_all)] async fn list_customers_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { @@ -316,7 +338,11 @@ mod storage { let customers = try_join_all(encrypted_customers.into_iter().map( |encrypted_customer| async { encrypted_customer - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }, @@ -329,6 +355,7 @@ mod storage { #[instrument(skip_all)] async fn insert_customer( &self, + state: &KeyManagerState, customer_data: domain::Customer, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -394,7 +421,11 @@ mod storage { }?; create_customer - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -419,7 +450,7 @@ mod storage { #[cfg(not(feature = "kv_store"))] mod storage { - use common_utils::{ext_traits::AsyncExt, id_type}; + use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use error_stack::{report, ResultExt}; use futures::future::try_join_all; use masking::PeekInterface; @@ -447,6 +478,7 @@ mod storage { #[instrument(skip_all)] async fn find_customer_optional_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -462,7 +494,7 @@ mod storage { .await .map_err(|error| report!(errors::StorageError::from(error)))? .async_map(|c| async { - c.convert(key_store.key.get_inner()) + c.convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) }) @@ -483,6 +515,7 @@ mod storage { #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: id_type::CustomerId, merchant_id: String, _customer: domain::Customer, @@ -494,13 +527,13 @@ mod storage { storage_types::Customer::update_by_customer_id_merchant_id( &conn, customer_id, - merchant_id, + merchant_id.clone(), customer_update.into(), ) .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|c| async { - c.convert(key_store.key.get_inner()) + c.convert(state, key_store.key.get_inner(), merchant_id) .await .change_context(errors::StorageError::DecryptionError) }) @@ -510,6 +543,7 @@ mod storage { #[instrument(skip_all)] async fn find_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -525,7 +559,7 @@ mod storage { .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|c| async { - c.convert(key_store.key.get_inner()) + c.convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) }) @@ -541,6 +575,7 @@ mod storage { #[instrument(skip_all)] async fn list_customers_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { @@ -554,7 +589,7 @@ mod storage { let customers = try_join_all(encrypted_customers.into_iter().map( |encrypted_customer| async { encrypted_customer - .convert(key_store.key.get_inner()) + .convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) }, @@ -567,6 +602,7 @@ mod storage { #[instrument(skip_all)] async fn insert_customer( &self, + state: &KeyManagerState, customer_data: domain::Customer, key_store: &domain::MerchantKeyStore, _storage_scheme: MerchantStorageScheme, @@ -580,9 +616,13 @@ mod storage { .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|c| async { - c.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + c.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await } @@ -610,6 +650,7 @@ impl CustomerInterface for MockDb { #[allow(clippy::panic)] async fn find_customer_optional_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -624,9 +665,13 @@ impl CustomerInterface for MockDb { .cloned(); customer .async_map(|c| async { - c.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + c.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await .transpose() @@ -634,6 +679,7 @@ impl CustomerInterface for MockDb { async fn list_customers_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { @@ -646,7 +692,11 @@ impl CustomerInterface for MockDb { .map(|customer| async { customer .to_owned() - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }), @@ -659,6 +709,7 @@ impl CustomerInterface for MockDb { #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, + _state: &KeyManagerState, _customer_id: id_type::CustomerId, _merchant_id: String, _customer: domain::Customer, @@ -672,6 +723,7 @@ impl CustomerInterface for MockDb { async fn find_customer_by_customer_id_merchant_id( &self, + _state: &KeyManagerState, _customer_id: &id_type::CustomerId, _merchant_id: &str, _key_store: &domain::MerchantKeyStore, @@ -684,6 +736,7 @@ impl CustomerInterface for MockDb { #[allow(clippy::panic)] async fn insert_customer( &self, + state: &KeyManagerState, customer_data: domain::Customer, key_store: &domain::MerchantKeyStore, _storage_scheme: MerchantStorageScheme, @@ -697,7 +750,11 @@ impl CustomerInterface for MockDb { customers.push(customer.clone()); customer - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } diff --git a/crates/router/src/db/events.rs b/crates/router/src/db/events.rs index aba11f9598..2fd8a09f17 100644 --- a/crates/router/src/db/events.rs +++ b/crates/router/src/db/events.rs @@ -1,4 +1,4 @@ -use common_utils::ext_traits::AsyncExt; +use common_utils::{ext_traits::AsyncExt, types::keymanager::KeyManagerState}; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; @@ -23,12 +23,14 @@ where { async fn insert_event( &self, + state: &KeyManagerState, event: domain::Event, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult; async fn find_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -36,13 +38,16 @@ where async fn list_initial_events_by_merchant_id_primary_object_id( &self, + state: &KeyManagerState, merchant_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError>; + #[allow(clippy::too_many_arguments)] async fn list_initial_events_by_merchant_id_constraints( &self, + state: &KeyManagerState, merchant_id: &str, created_after: Option, created_before: Option, @@ -53,6 +58,7 @@ where async fn list_events_by_merchant_id_initial_attempt_id( &self, + state: &KeyManagerState, merchant_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -60,13 +66,16 @@ where async fn list_initial_events_by_profile_id_primary_object_id( &self, + state: &KeyManagerState, profile_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError>; + #[allow(clippy::too_many_arguments)] async fn list_initial_events_by_profile_id_constraints( &self, + state: &KeyManagerState, profile_id: &str, created_after: Option, created_before: Option, @@ -77,6 +86,7 @@ where async fn list_events_by_profile_id_initial_attempt_id( &self, + state: &KeyManagerState, profile_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -84,6 +94,7 @@ where async fn update_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, event: domain::EventUpdate, @@ -96,6 +107,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn insert_event( &self, + state: &KeyManagerState, event: domain::Event, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -107,7 +119,11 @@ impl EventInterface for Store { .insert(&conn) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -115,6 +131,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn find_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -123,7 +140,11 @@ impl EventInterface for Store { storage::Event::find_by_merchant_id_event_id(&conn, merchant_id, event_id) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -131,6 +152,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_initial_events_by_merchant_id_primary_object_id( &self, + state: &KeyManagerState, merchant_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -148,7 +170,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -161,6 +187,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_initial_events_by_merchant_id_constraints( &self, + state: &KeyManagerState, merchant_id: &str, created_after: Option, created_before: Option, @@ -184,7 +211,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -197,6 +228,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_events_by_merchant_id_initial_attempt_id( &self, + state: &KeyManagerState, merchant_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -214,7 +246,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -227,6 +263,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_initial_events_by_profile_id_primary_object_id( &self, + state: &KeyManagerState, profile_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -244,7 +281,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -257,6 +298,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_initial_events_by_profile_id_constraints( &self, + state: &KeyManagerState, profile_id: &str, created_after: Option, created_before: Option, @@ -280,7 +322,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -293,6 +339,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_events_by_profile_id_initial_attempt_id( &self, + state: &KeyManagerState, profile_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -306,7 +353,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -319,6 +370,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn update_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, event: domain::EventUpdate, @@ -328,7 +380,11 @@ impl EventInterface for Store { storage::Event::update_by_merchant_id_event_id(&conn, merchant_id, event_id, event.into()) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -338,6 +394,7 @@ impl EventInterface for Store { impl EventInterface for MockDb { async fn insert_event( &self, + state: &KeyManagerState, event: domain::Event, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -350,13 +407,18 @@ impl EventInterface for MockDb { locked_events.push(stored_event.clone()); stored_event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } async fn find_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -370,7 +432,11 @@ impl EventInterface for MockDb { .cloned() .async_map(|event| async { event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -386,6 +452,7 @@ impl EventInterface for MockDb { async fn list_initial_events_by_merchant_id_primary_object_id( &self, + state: &KeyManagerState, merchant_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -405,7 +472,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -416,6 +487,7 @@ impl EventInterface for MockDb { async fn list_initial_events_by_merchant_id_constraints( &self, + state: &KeyManagerState, merchant_id: &str, created_after: Option, created_before: Option, @@ -470,7 +542,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -481,6 +557,7 @@ impl EventInterface for MockDb { async fn list_events_by_merchant_id_initial_attempt_id( &self, + state: &KeyManagerState, merchant_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -498,7 +575,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -509,6 +590,7 @@ impl EventInterface for MockDb { async fn list_initial_events_by_profile_id_primary_object_id( &self, + state: &KeyManagerState, profile_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -528,7 +610,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -539,6 +625,7 @@ impl EventInterface for MockDb { async fn list_initial_events_by_profile_id_constraints( &self, + state: &KeyManagerState, profile_id: &str, created_after: Option, created_before: Option, @@ -593,7 +680,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -604,6 +695,7 @@ impl EventInterface for MockDb { async fn list_events_by_profile_id_initial_attempt_id( &self, + state: &KeyManagerState, profile_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -621,7 +713,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -632,6 +728,7 @@ impl EventInterface for MockDb { async fn update_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, event: domain::EventUpdate, @@ -657,7 +754,11 @@ impl EventInterface for MockDb { event_to_update .clone() - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -665,6 +766,9 @@ impl EventInterface for MockDb { #[cfg(test)] mod tests { + use std::sync::Arc; + + use common_utils::types::keymanager::Identifier; use diesel_models::{enums, events::EventMetadata}; use time::macros::datetime; @@ -673,6 +777,10 @@ mod tests { events::EventInterface, merchant_key_store::MerchantKeyStoreInterface, MasterKeyInterface, MockDb, }, + routes::{ + self, + app::{settings::Settings, StorageImpl}, + }, services, types::domain, }; @@ -685,17 +793,31 @@ mod tests { .await .expect("Failed to create Mock store"); let event_id = "test_event_id"; + let (tx, _) = tokio::sync::oneshot::channel(); + let app_state = Box::pin(routes::AppState::with_storage( + Settings::default(), + StorageImpl::PostgresqlTest, + tx, + Box::new(services::MockApiClient), + )) + .await; + let state = &Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); let merchant_id = "merchant1"; let business_profile_id = "profile1"; let payment_id = "test_payment_id"; - + let key_manager_state = &state.into(); let master_key = mockdb.get_master_key(); mockdb .insert_merchant_key_store( + key_manager_state, domain::MerchantKeyStore { merchant_id: merchant_id.into(), key: domain::types::encrypt( + key_manager_state, services::generate_aes256_key().unwrap().to_vec().into(), + Identifier::Merchant(merchant_id.to_string()), master_key, ) .await @@ -707,12 +829,17 @@ mod tests { .await .unwrap(); let merchant_key_store = mockdb - .get_merchant_key_store_by_merchant_id(merchant_id, &master_key.to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &master_key.to_vec().into(), + ) .await .unwrap(); let event1 = mockdb .insert_event( + key_manager_state, domain::Event { event_id: event_id.into(), event_type: enums::EventType::PaymentSucceeded, @@ -742,6 +869,7 @@ mod tests { let updated_event = mockdb .update_event_by_merchant_id_event_id( + key_manager_state, merchant_id, event_id, domain::EventUpdate::UpdateResponse { diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index ce0821c632..ed2357c1a1 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use common_enums::enums::MerchantStorageScheme; -use common_utils::{errors::CustomResult, id_type, pii}; +use common_utils::{errors::CustomResult, id_type, pii, types::keymanager::KeyManagerState}; use diesel_models::{ enums, enums::ProcessTrackerStatus, @@ -102,27 +102,30 @@ impl KafkaStore { impl AddressInterface for KafkaStore { async fn find_address_by_address_id( &self, + state: &KeyManagerState, address_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .find_address_by_address_id(address_id, key_store) + .find_address_by_address_id(state, address_id, key_store) .await } async fn update_address( &self, + state: &KeyManagerState, address_id: String, address: storage::AddressUpdate, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .update_address(address_id, address, key_store) + .update_address(state, address_id, address, key_store) .await } async fn update_address_for_payments( &self, + state: &KeyManagerState, this: domain::PaymentAddress, address: domain::AddressUpdate, payment_id: String, @@ -130,24 +133,33 @@ impl AddressInterface for KafkaStore { storage_scheme: MerchantStorageScheme, ) -> CustomResult { self.diesel_store - .update_address_for_payments(this, address, payment_id, key_store, storage_scheme) + .update_address_for_payments( + state, + this, + address, + payment_id, + key_store, + storage_scheme, + ) .await } async fn insert_address_for_payments( &self, + state: &KeyManagerState, payment_id: &str, address: domain::PaymentAddress, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult { self.diesel_store - .insert_address_for_payments(payment_id, address, key_store, storage_scheme) + .insert_address_for_payments(state, payment_id, address, key_store, storage_scheme) .await } async fn find_address_by_merchant_id_payment_id_address_id( &self, + state: &KeyManagerState, merchant_id: &str, payment_id: &str, address_id: &str, @@ -156,6 +168,7 @@ impl AddressInterface for KafkaStore { ) -> CustomResult { self.diesel_store .find_address_by_merchant_id_payment_id_address_id( + state, merchant_id, payment_id, address_id, @@ -167,23 +180,31 @@ impl AddressInterface for KafkaStore { async fn insert_address_for_customers( &self, + state: &KeyManagerState, address: domain::CustomerAddress, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .insert_address_for_customers(address, key_store) + .insert_address_for_customers(state, address, key_store) .await } async fn update_address_by_merchant_id_customer_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, address: storage::AddressUpdate, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store - .update_address_by_merchant_id_customer_id(customer_id, merchant_id, address, key_store) + .update_address_by_merchant_id_customer_id( + state, + customer_id, + merchant_id, + address, + key_store, + ) .await } } @@ -332,6 +353,7 @@ impl CustomerInterface for KafkaStore { async fn find_customer_optional_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -339,6 +361,7 @@ impl CustomerInterface for KafkaStore { ) -> CustomResult, errors::StorageError> { self.diesel_store .find_customer_optional_by_customer_id_merchant_id( + state, customer_id, merchant_id, key_store, @@ -349,6 +372,7 @@ impl CustomerInterface for KafkaStore { async fn update_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: id_type::CustomerId, merchant_id: String, customer: domain::Customer, @@ -358,6 +382,7 @@ impl CustomerInterface for KafkaStore { ) -> CustomResult { self.diesel_store .update_customer_by_customer_id_merchant_id( + state, customer_id, merchant_id, customer, @@ -370,16 +395,18 @@ impl CustomerInterface for KafkaStore { async fn list_customers_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store - .list_customers_by_merchant_id(merchant_id, key_store) + .list_customers_by_merchant_id(state, merchant_id, key_store) .await } async fn find_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -387,6 +414,7 @@ impl CustomerInterface for KafkaStore { ) -> CustomResult { self.diesel_store .find_customer_by_customer_id_merchant_id( + state, customer_id, merchant_id, key_store, @@ -397,12 +425,13 @@ impl CustomerInterface for KafkaStore { async fn insert_customer( &self, + state: &KeyManagerState, customer_data: domain::Customer, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult { self.diesel_store - .insert_customer(customer_data, key_store, storage_scheme) + .insert_customer(state, customer_data, key_store, storage_scheme) .await } } @@ -519,33 +548,37 @@ impl EphemeralKeyInterface for KafkaStore { impl EventInterface for KafkaStore { async fn insert_event( &self, + state: &KeyManagerState, event: domain::Event, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .insert_event(event, merchant_key_store) + .insert_event(state, event, merchant_key_store) .await } async fn find_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .find_event_by_merchant_id_event_id(merchant_id, event_id, merchant_key_store) + .find_event_by_merchant_id_event_id(state, merchant_id, event_id, merchant_key_store) .await } async fn list_initial_events_by_merchant_id_primary_object_id( &self, + state: &KeyManagerState, merchant_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .list_initial_events_by_merchant_id_primary_object_id( + state, merchant_id, primary_object_id, merchant_key_store, @@ -555,6 +588,7 @@ impl EventInterface for KafkaStore { async fn list_initial_events_by_merchant_id_constraints( &self, + state: &KeyManagerState, merchant_id: &str, created_after: Option, created_before: Option, @@ -564,6 +598,7 @@ impl EventInterface for KafkaStore { ) -> CustomResult, errors::StorageError> { self.diesel_store .list_initial_events_by_merchant_id_constraints( + state, merchant_id, created_after, created_before, @@ -576,12 +611,14 @@ impl EventInterface for KafkaStore { async fn list_events_by_merchant_id_initial_attempt_id( &self, + state: &KeyManagerState, merchant_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .list_events_by_merchant_id_initial_attempt_id( + state, merchant_id, initial_attempt_id, merchant_key_store, @@ -591,12 +628,14 @@ impl EventInterface for KafkaStore { async fn list_initial_events_by_profile_id_primary_object_id( &self, + state: &KeyManagerState, profile_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .list_initial_events_by_profile_id_primary_object_id( + state, profile_id, primary_object_id, merchant_key_store, @@ -606,6 +645,7 @@ impl EventInterface for KafkaStore { async fn list_initial_events_by_profile_id_constraints( &self, + state: &KeyManagerState, profile_id: &str, created_after: Option, created_before: Option, @@ -615,6 +655,7 @@ impl EventInterface for KafkaStore { ) -> CustomResult, errors::StorageError> { self.diesel_store .list_initial_events_by_profile_id_constraints( + state, profile_id, created_after, created_before, @@ -627,12 +668,14 @@ impl EventInterface for KafkaStore { async fn list_events_by_profile_id_initial_attempt_id( &self, + state: &KeyManagerState, profile_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .list_events_by_profile_id_initial_attempt_id( + state, profile_id, initial_attempt_id, merchant_key_store, @@ -642,13 +685,20 @@ impl EventInterface for KafkaStore { async fn update_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, event: domain::EventUpdate, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .update_event_by_merchant_id_event_id(merchant_id, event_id, event, merchant_key_store) + .update_event_by_merchant_id_event_id( + state, + merchant_id, + event_id, + event, + merchant_key_store, + ) .await } } @@ -790,43 +840,47 @@ impl PaymentLinkInterface for KafkaStore { impl MerchantAccountInterface for KafkaStore { async fn insert_merchant( &self, + state: &KeyManagerState, merchant_account: domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .insert_merchant(merchant_account, key_store) + .insert_merchant(state, merchant_account, key_store) .await } async fn find_merchant_account_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .find_merchant_account_by_merchant_id(merchant_id, key_store) + .find_merchant_account_by_merchant_id(state, merchant_id, key_store) .await } async fn update_merchant( &self, + state: &KeyManagerState, this: domain::MerchantAccount, merchant_account: storage::MerchantAccountUpdate, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .update_merchant(this, merchant_account, key_store) + .update_merchant(state, this, merchant_account, key_store) .await } async fn update_specific_fields_in_merchant( &self, + state: &KeyManagerState, merchant_id: &str, merchant_account: storage::MerchantAccountUpdate, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .update_specific_fields_in_merchant(merchant_id, merchant_account, key_store) + .update_specific_fields_in_merchant(state, merchant_id, merchant_account, key_store) .await } @@ -841,20 +895,22 @@ impl MerchantAccountInterface for KafkaStore { async fn find_merchant_account_by_publishable_key( &self, + state: &KeyManagerState, publishable_key: &str, ) -> CustomResult { self.diesel_store - .find_merchant_account_by_publishable_key(publishable_key) + .find_merchant_account_by_publishable_key(state, publishable_key) .await } #[cfg(feature = "olap")] async fn list_merchant_accounts_by_organization_id( &self, + state: &KeyManagerState, organization_id: &str, ) -> CustomResult, errors::StorageError> { self.diesel_store - .list_merchant_accounts_by_organization_id(organization_id) + .list_merchant_accounts_by_organization_id(state, organization_id) .await } @@ -870,10 +926,11 @@ impl MerchantAccountInterface for KafkaStore { #[cfg(feature = "olap")] async fn list_multiple_merchant_accounts( &self, + state: &KeyManagerState, merchant_ids: Vec, ) -> CustomResult, errors::StorageError> { self.diesel_store - .list_multiple_merchant_accounts(merchant_ids) + .list_multiple_merchant_accounts(state, merchant_ids) .await } } @@ -957,12 +1014,14 @@ impl MerchantConnectorAccountInterface for KafkaStore { } async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, + state: &KeyManagerState, merchant_id: &str, connector: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store .find_merchant_connector_account_by_merchant_id_connector_label( + state, merchant_id, connector, key_store, @@ -972,12 +1031,14 @@ impl MerchantConnectorAccountInterface for KafkaStore { async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, + state: &KeyManagerState, merchant_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .find_merchant_connector_account_by_merchant_id_connector_name( + state, merchant_id, connector_name, key_store, @@ -987,12 +1048,14 @@ impl MerchantConnectorAccountInterface for KafkaStore { async fn find_merchant_connector_account_by_profile_id_connector_name( &self, + state: &KeyManagerState, profile_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store .find_merchant_connector_account_by_profile_id_connector_name( + state, profile_id, connector_name, key_store, @@ -1002,22 +1065,25 @@ impl MerchantConnectorAccountInterface for KafkaStore { async fn insert_merchant_connector_account( &self, + state: &KeyManagerState, t: domain::MerchantConnectorAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .insert_merchant_connector_account(t, key_store) + .insert_merchant_connector_account(state, t, key_store) .await } async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_connector_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + state, merchant_id, merchant_connector_id, key_store, @@ -1027,12 +1093,14 @@ impl MerchantConnectorAccountInterface for KafkaStore { async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, + state: &KeyManagerState, merchant_id: &str, get_disabled: bool, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + state, merchant_id, get_disabled, key_store, @@ -1042,12 +1110,13 @@ impl MerchantConnectorAccountInterface for KafkaStore { async fn update_merchant_connector_account( &self, + state: &KeyManagerState, this: domain::MerchantConnectorAccount, merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .update_merchant_connector_account(this, merchant_connector_account, key_store) + .update_merchant_connector_account(state, this, merchant_connector_account, key_store) .await } @@ -1326,6 +1395,7 @@ impl PaymentAttemptInterface for KafkaStore { impl PaymentIntentInterface for KafkaStore { async fn update_payment_intent( &self, + state: &KeyManagerState, this: storage::PaymentIntent, payment_intent: storage::PaymentIntentUpdate, key_store: &domain::MerchantKeyStore, @@ -1333,7 +1403,13 @@ impl PaymentIntentInterface for KafkaStore { ) -> CustomResult { let intent = self .diesel_store - .update_payment_intent(this.clone(), payment_intent, key_store, storage_scheme) + .update_payment_intent( + state, + this.clone(), + payment_intent, + key_store, + storage_scheme, + ) .await?; if let Err(er) = self @@ -1349,6 +1425,7 @@ impl PaymentIntentInterface for KafkaStore { async fn insert_payment_intent( &self, + state: &KeyManagerState, new: storage::PaymentIntent, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -1356,7 +1433,7 @@ impl PaymentIntentInterface for KafkaStore { logger::debug!("Inserting PaymentIntent Via KafkaStore"); let intent = self .diesel_store - .insert_payment_intent(new, key_store, storage_scheme) + .insert_payment_intent(state, new, key_store, storage_scheme) .await?; if let Err(er) = self @@ -1372,6 +1449,7 @@ impl PaymentIntentInterface for KafkaStore { async fn find_payment_intent_by_payment_id_merchant_id( &self, + state: &KeyManagerState, payment_id: &str, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -1379,6 +1457,7 @@ impl PaymentIntentInterface for KafkaStore { ) -> CustomResult { self.diesel_store .find_payment_intent_by_payment_id_merchant_id( + state, payment_id, merchant_id, key_store, @@ -1390,19 +1469,27 @@ impl PaymentIntentInterface for KafkaStore { #[cfg(feature = "olap")] async fn filter_payment_intent_by_constraints( &self, + state: &KeyManagerState, merchant_id: &str, filters: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult, errors::DataStorageError> { self.diesel_store - .filter_payment_intent_by_constraints(merchant_id, filters, key_store, storage_scheme) + .filter_payment_intent_by_constraints( + state, + merchant_id, + filters, + key_store, + storage_scheme, + ) .await } #[cfg(feature = "olap")] async fn filter_payment_intents_by_time_range_constraints( &self, + state: &KeyManagerState, merchant_id: &str, time_range: &api_models::payments::TimeRange, key_store: &domain::MerchantKeyStore, @@ -1410,6 +1497,7 @@ impl PaymentIntentInterface for KafkaStore { ) -> CustomResult, errors::DataStorageError> { self.diesel_store .filter_payment_intents_by_time_range_constraints( + state, merchant_id, time_range, key_store, @@ -1421,6 +1509,7 @@ impl PaymentIntentInterface for KafkaStore { #[cfg(feature = "olap")] async fn get_filtered_payment_intents_attempt( &self, + state: &KeyManagerState, merchant_id: &str, constraints: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, key_store: &domain::MerchantKeyStore, @@ -1434,6 +1523,7 @@ impl PaymentIntentInterface for KafkaStore { > { self.diesel_store .get_filtered_payment_intents_attempt( + state, merchant_id, constraints, key_store, @@ -2059,21 +2149,23 @@ impl RefundInterface for KafkaStore { impl MerchantKeyStoreInterface for KafkaStore { async fn insert_merchant_key_store( &self, + state: &KeyManagerState, merchant_key_store: domain::MerchantKeyStore, key: &Secret>, ) -> CustomResult { self.diesel_store - .insert_merchant_key_store(merchant_key_store, key) + .insert_merchant_key_store(state, merchant_key_store, key) .await } async fn get_merchant_key_store_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key: &Secret>, ) -> CustomResult { self.diesel_store - .get_merchant_key_store_by_merchant_id(merchant_id, key) + .get_merchant_key_store_by_merchant_id(state, merchant_id, key) .await } @@ -2089,18 +2181,20 @@ impl MerchantKeyStoreInterface for KafkaStore { #[cfg(feature = "olap")] async fn list_multiple_key_stores( &self, + state: &KeyManagerState, merchant_ids: Vec, key: &Secret>, ) -> CustomResult, errors::StorageError> { self.diesel_store - .list_multiple_key_stores(merchant_ids, key) + .list_multiple_key_stores(state, merchant_ids, key) .await } async fn get_all_key_stores( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { - self.diesel_store.get_all_key_stores(key).await + self.diesel_store.get_all_key_stores(state, key).await } } @@ -2592,6 +2686,7 @@ impl DashboardMetadataInterface for KafkaStore { impl BatchSampleDataInterface for KafkaStore { async fn insert_payment_intents_batch_for_sample_data( &self, + state: &KeyManagerState, batch: Vec, key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore, ) -> CustomResult< @@ -2600,7 +2695,7 @@ impl BatchSampleDataInterface for KafkaStore { > { let payment_intents_list = self .diesel_store - .insert_payment_intents_batch_for_sample_data(batch, key_store) + .insert_payment_intents_batch_for_sample_data(state, batch, key_store) .await?; for payment_intent in payment_intents_list.iter() { @@ -2654,6 +2749,7 @@ impl BatchSampleDataInterface for KafkaStore { async fn delete_payment_intents_for_sample_data( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore, ) -> CustomResult< @@ -2662,7 +2758,7 @@ impl BatchSampleDataInterface for KafkaStore { > { let payment_intents_list = self .diesel_store - .delete_payment_intents_for_sample_data(merchant_id, key_store) + .delete_payment_intents_for_sample_data(state, merchant_id, key_store) .await?; for payment_intent in payment_intents_list.iter() { @@ -2947,29 +3043,32 @@ impl GenericLinkInterface for KafkaStore { impl UserKeyStoreInterface for KafkaStore { async fn insert_user_key_store( &self, + state: &KeyManagerState, user_key_store: domain::UserKeyStore, key: &Secret>, ) -> CustomResult { self.diesel_store - .insert_user_key_store(user_key_store, key) + .insert_user_key_store(state, user_key_store, key) .await } async fn get_user_key_store_by_user_id( &self, + state: &KeyManagerState, user_id: &str, key: &Secret>, ) -> CustomResult { self.diesel_store - .get_user_key_store_by_user_id(user_id, key) + .get_user_key_store_by_user_id(state, user_id, key) .await } async fn get_all_user_key_store( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { - self.diesel_store.get_all_user_key_store(key).await + self.diesel_store.get_all_user_key_store(state, key).await } } diff --git a/crates/router/src/db/merchant_account.rs b/crates/router/src/db/merchant_account.rs index ff3830b238..eadf00c872 100644 --- a/crates/router/src/db/merchant_account.rs +++ b/crates/router/src/db/merchant_account.rs @@ -1,7 +1,7 @@ #[cfg(feature = "olap")] use std::collections::HashMap; -use common_utils::ext_traits::AsyncExt; +use common_utils::{ext_traits::AsyncExt, types::keymanager::KeyManagerState}; use diesel_models::MerchantAccountUpdateInternal; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; @@ -31,12 +31,14 @@ where { async fn insert_merchant( &self, + state: &KeyManagerState, merchant_account: domain::MerchantAccount, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult; async fn find_merchant_account_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult; @@ -48,6 +50,7 @@ where async fn update_merchant( &self, + state: &KeyManagerState, this: domain::MerchantAccount, merchant_account: storage::MerchantAccountUpdate, merchant_key_store: &domain::MerchantKeyStore, @@ -55,6 +58,7 @@ where async fn update_specific_fields_in_merchant( &self, + state: &KeyManagerState, merchant_id: &str, merchant_account: storage::MerchantAccountUpdate, merchant_key_store: &domain::MerchantKeyStore, @@ -62,12 +66,14 @@ where async fn find_merchant_account_by_publishable_key( &self, + state: &KeyManagerState, publishable_key: &str, ) -> CustomResult; #[cfg(feature = "olap")] async fn list_merchant_accounts_by_organization_id( &self, + state: &KeyManagerState, organization_id: &str, ) -> CustomResult, errors::StorageError>; @@ -79,6 +85,7 @@ where #[cfg(feature = "olap")] async fn list_multiple_merchant_accounts( &self, + state: &KeyManagerState, merchant_ids: Vec, ) -> CustomResult, errors::StorageError>; } @@ -88,6 +95,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn insert_merchant( &self, + state: &KeyManagerState, merchant_account: domain::MerchantAccount, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -99,7 +107,11 @@ impl MerchantAccountInterface for Store { .insert(&conn) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -107,6 +119,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_account_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -121,7 +134,11 @@ impl MerchantAccountInterface for Store { { fetch_func() .await? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_id.to_string(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -130,7 +147,11 @@ impl MerchantAccountInterface for Store { { cache::get_or_populate_in_memory(self, merchant_id, fetch_func, &ACCOUNTS_CACHE) .await? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -139,6 +160,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn update_merchant( &self, + state: &KeyManagerState, this: domain::MerchantAccount, merchant_account: storage::MerchantAccountUpdate, merchant_key_store: &domain::MerchantKeyStore, @@ -157,7 +179,11 @@ impl MerchantAccountInterface for Store { publish_and_redact_merchant_account_cache(self, &updated_merchant_account).await?; } updated_merchant_account - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -165,6 +191,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn update_specific_fields_in_merchant( &self, + state: &KeyManagerState, merchant_id: &str, merchant_account: storage::MerchantAccountUpdate, merchant_key_store: &domain::MerchantKeyStore, @@ -183,7 +210,11 @@ impl MerchantAccountInterface for Store { publish_and_redact_merchant_account_cache(self, &updated_merchant_account).await?; } updated_merchant_account - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -191,6 +222,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_account_by_publishable_key( &self, + state: &KeyManagerState, publishable_key: &str, ) -> CustomResult { let fetch_by_pub_key_func = || async { @@ -219,6 +251,7 @@ impl MerchantAccountInterface for Store { } let key_store = self .get_merchant_key_store_by_merchant_id( + state, &merchant_account.merchant_id, &self.get_master_key().to_vec().into(), ) @@ -226,7 +259,11 @@ impl MerchantAccountInterface for Store { Ok(authentication::AuthenticationData { merchant_account: merchant_account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, @@ -238,6 +275,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn list_merchant_accounts_by_organization_id( &self, + state: &KeyManagerState, organization_id: &str, ) -> CustomResult, errors::StorageError> { use futures::future::try_join_all; @@ -253,6 +291,7 @@ impl MerchantAccountInterface for Store { let merchant_key_stores = try_join_all(encrypted_merchant_accounts.iter().map(|merchant_account| { self.get_merchant_key_store_by_merchant_id( + state, &merchant_account.merchant_id, &db_master_key, ) @@ -265,7 +304,11 @@ impl MerchantAccountInterface for Store { .zip(merchant_key_stores.iter()) .map(|(merchant_account, key_store)| async { merchant_account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }), @@ -314,6 +357,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn list_multiple_merchant_accounts( &self, + state: &KeyManagerState, merchant_ids: Vec, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; @@ -327,6 +371,7 @@ impl MerchantAccountInterface for Store { let merchant_key_stores = self .list_multiple_key_stores( + state, encrypted_merchant_accounts .iter() .map(|merchant_account| &merchant_account.merchant_id) @@ -351,7 +396,11 @@ impl MerchantAccountInterface for Store { )), )?; merchant_account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }, @@ -399,6 +448,7 @@ impl MerchantAccountInterface for MockDb { #[allow(clippy::panic)] async fn insert_merchant( &self, + state: &KeyManagerState, mut merchant_account: domain::MerchantAccount, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -412,7 +462,11 @@ impl MerchantAccountInterface for MockDb { accounts.push(account.clone()); account - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -420,6 +474,7 @@ impl MerchantAccountInterface for MockDb { #[allow(clippy::panic)] async fn find_merchant_account_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -429,9 +484,13 @@ impl MerchantAccountInterface for MockDb { .find(|account| account.merchant_id == merchant_id) .cloned() .async_map(|a| async { - a.convert(merchant_key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + a.convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await .transpose()?; @@ -445,6 +504,7 @@ impl MerchantAccountInterface for MockDb { async fn update_merchant( &self, + _state: &KeyManagerState, _this: domain::MerchantAccount, _merchant_account: storage::MerchantAccountUpdate, _merchant_key_store: &domain::MerchantKeyStore, @@ -455,6 +515,7 @@ impl MerchantAccountInterface for MockDb { async fn update_specific_fields_in_merchant( &self, + _state: &KeyManagerState, _merchant_id: &str, _merchant_account: storage::MerchantAccountUpdate, _merchant_key_store: &domain::MerchantKeyStore, @@ -465,6 +526,7 @@ impl MerchantAccountInterface for MockDb { async fn find_merchant_account_by_publishable_key( &self, + _state: &KeyManagerState, _publishable_key: &str, ) -> CustomResult { // [#172]: Implement function for `MockDb` @@ -489,6 +551,7 @@ impl MerchantAccountInterface for MockDb { #[cfg(feature = "olap")] async fn list_merchant_accounts_by_organization_id( &self, + _state: &KeyManagerState, _organization_id: &str, ) -> CustomResult, errors::StorageError> { Err(errors::StorageError::MockDbError)? @@ -497,6 +560,7 @@ impl MerchantAccountInterface for MockDb { #[cfg(feature = "olap")] async fn list_multiple_merchant_accounts( &self, + _state: &KeyManagerState, _merchant_ids: Vec, ) -> CustomResult, errors::StorageError> { Err(errors::StorageError::MockDbError)? diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index b9becb90dc..078980f918 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -1,6 +1,9 @@ use async_bb8_diesel::AsyncConnection; -use common_utils::ext_traits::{AsyncExt, ByteSliceExt, Encode}; -use diesel_models::encryption::Encryption; +use common_utils::{ + encryption::Encryption, + ext_traits::{AsyncExt, ByteSliceExt, Encode}, + types::keymanager::KeyManagerState, +}; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; #[cfg(feature = "accounts_cache")] @@ -121,6 +124,7 @@ where { async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, + state: &KeyManagerState, merchant_id: &str, connector_label: &str, key_store: &domain::MerchantKeyStore, @@ -128,6 +132,7 @@ where async fn find_merchant_connector_account_by_profile_id_connector_name( &self, + state: &KeyManagerState, profile_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -135,6 +140,7 @@ where async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, + state: &KeyManagerState, merchant_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -142,12 +148,14 @@ where async fn insert_merchant_connector_account( &self, + state: &KeyManagerState, t: domain::MerchantConnectorAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult; async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_connector_id: &str, key_store: &domain::MerchantKeyStore, @@ -155,6 +163,7 @@ where async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, + state: &KeyManagerState, merchant_id: &str, get_disabled: bool, key_store: &domain::MerchantKeyStore, @@ -162,6 +171,7 @@ where async fn update_merchant_connector_account( &self, + state: &KeyManagerState, this: domain::MerchantConnectorAccount, merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal, key_store: &domain::MerchantKeyStore, @@ -187,6 +197,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, + state: &KeyManagerState, merchant_id: &str, connector_label: &str, key_store: &domain::MerchantKeyStore, @@ -206,7 +217,7 @@ impl MerchantConnectorAccountInterface for Store { { find_call() .await? - .convert(key_store.key.get_inner()) + .convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DeserializationFailed) } @@ -221,9 +232,13 @@ impl MerchantConnectorAccountInterface for Store { ) .await .async_and_then(|item| async { - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await } @@ -232,6 +247,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_connector_account_by_profile_id_connector_name( &self, + state: &KeyManagerState, profile_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -251,7 +267,11 @@ impl MerchantConnectorAccountInterface for Store { { find_call() .await? - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DeserializationFailed) } @@ -266,9 +286,13 @@ impl MerchantConnectorAccountInterface for Store { ) .await .async_and_then(|item| async { - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await } @@ -277,6 +301,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, + state: &KeyManagerState, merchant_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -293,9 +318,13 @@ impl MerchantConnectorAccountInterface for Store { let mut output = Vec::with_capacity(items.len()); for item in items.into_iter() { output.push( - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError)?, + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError)?, ) } Ok(output) @@ -306,6 +335,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_connector_id: &str, key_store: &domain::MerchantKeyStore, @@ -325,7 +355,11 @@ impl MerchantConnectorAccountInterface for Store { { find_call() .await? - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -339,7 +373,11 @@ impl MerchantConnectorAccountInterface for Store { &cache::ACCOUNTS_CACHE, ) .await? - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -348,6 +386,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn insert_merchant_connector_account( &self, + state: &KeyManagerState, t: domain::MerchantConnectorAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -359,9 +398,13 @@ impl MerchantConnectorAccountInterface for Store { .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|item| async { - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await } @@ -369,6 +412,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, + state: &KeyManagerState, merchant_id: &str, get_disabled: bool, key_store: &domain::MerchantKeyStore, @@ -381,9 +425,13 @@ impl MerchantConnectorAccountInterface for Store { let mut output = Vec::with_capacity(items.len()); for item in items.into_iter() { output.push( - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError)?, + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError)?, ) } Ok(output) @@ -492,6 +540,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn update_merchant_connector_account( &self, + state: &KeyManagerState, this: domain::MerchantConnectorAccount, merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal, key_store: &domain::MerchantKeyStore, @@ -516,9 +565,13 @@ impl MerchantConnectorAccountInterface for Store { .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|item| async { - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await }; @@ -629,6 +682,7 @@ impl MerchantConnectorAccountInterface for MockDb { } async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, + state: &KeyManagerState, merchant_id: &str, connector: &str, key_store: &domain::MerchantKeyStore, @@ -645,7 +699,11 @@ impl MerchantConnectorAccountInterface for MockDb { .cloned() .async_map(|account| async { account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -663,6 +721,7 @@ impl MerchantConnectorAccountInterface for MockDb { async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, + state: &KeyManagerState, merchant_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -681,7 +740,11 @@ impl MerchantConnectorAccountInterface for MockDb { for account in accounts.into_iter() { output.push( account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ) @@ -691,6 +754,7 @@ impl MerchantConnectorAccountInterface for MockDb { async fn find_merchant_connector_account_by_profile_id_connector_name( &self, + state: &KeyManagerState, profile_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -709,7 +773,11 @@ impl MerchantConnectorAccountInterface for MockDb { match maybe_mca { Some(mca) => mca .to_owned() - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError), None => Err(errors::StorageError::ValueNotFound( @@ -721,6 +789,7 @@ impl MerchantConnectorAccountInterface for MockDb { async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_connector_id: &str, key_store: &domain::MerchantKeyStore, @@ -737,7 +806,11 @@ impl MerchantConnectorAccountInterface for MockDb { .cloned() .async_map(|account| async { account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -755,6 +828,7 @@ impl MerchantConnectorAccountInterface for MockDb { async fn insert_merchant_connector_account( &self, + state: &KeyManagerState, t: domain::MerchantConnectorAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -788,13 +862,18 @@ impl MerchantConnectorAccountInterface for MockDb { }; accounts.push(account.clone()); account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, + state: &KeyManagerState, merchant_id: &str, get_disabled: bool, key_store: &domain::MerchantKeyStore, @@ -818,7 +897,11 @@ impl MerchantConnectorAccountInterface for MockDb { for account in accounts.into_iter() { output.push( account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ) @@ -828,6 +911,7 @@ impl MerchantConnectorAccountInterface for MockDb { async fn update_merchant_connector_account( &self, + state: &KeyManagerState, this: domain::MerchantConnectorAccount, merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal, key_store: &domain::MerchantKeyStore, @@ -846,7 +930,11 @@ impl MerchantConnectorAccountInterface for MockDb { }) .async_map(|account| async { account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -890,8 +978,10 @@ impl MerchantConnectorAccountInterface for MockDb { #[cfg(feature = "accounts_cache")] #[cfg(test)] mod merchant_connector_account_cache_tests { + use std::sync::Arc; + use api_models::enums::CountryAlpha2; - use common_utils::date_time; + use common_utils::{date_time, types::keymanager::Identifier}; use diesel_models::enums::ConnectorType; use error_stack::ResultExt; use masking::PeekInterface; @@ -901,6 +991,7 @@ mod merchant_connector_account_cache_tests { pub_sub::PubSubInterface, }; use time::macros::datetime; + use tokio::sync::oneshot; use crate::{ core::errors, @@ -908,6 +999,10 @@ mod merchant_connector_account_cache_tests { merchant_connector_account::MerchantConnectorAccountInterface, merchant_key_store::MerchantKeyStoreInterface, MasterKeyInterface, MockDb, }, + routes::{ + self, + app::{settings::Settings, StorageImpl}, + }, services, types::{ domain::{self, behaviour::Conversion}, @@ -918,6 +1013,19 @@ mod merchant_connector_account_cache_tests { #[allow(clippy::unwrap_used)] #[tokio::test] async fn test_connector_profile_id_cache() { + let conf = Settings::new().unwrap(); + let tx: oneshot::Sender<()> = oneshot::channel().0; + + let app_state = Box::pin(routes::AppState::with_storage( + conf, + StorageImpl::PostgresqlTest, + tx, + Box::new(services::MockApiClient), + )) + .await; + let state = &Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); #[allow(clippy::expect_used)] let db = MockDb::new(&redis_interface::RedisSettings::default()) .await @@ -934,12 +1042,15 @@ mod merchant_connector_account_cache_tests { let connector_label = "stripe_USA"; let merchant_connector_id = "simple_merchant_connector_id"; let profile_id = "pro_max_ultra"; - + let key_manager_state = &state.into(); db.insert_merchant_key_store( + key_manager_state, domain::MerchantKeyStore { merchant_id: merchant_id.into(), key: domain::types::encrypt( + key_manager_state, services::generate_aes256_key().unwrap().to_vec().into(), + Identifier::Merchant(merchant_id.to_string()), master_key, ) .await @@ -952,7 +1063,11 @@ mod merchant_connector_account_cache_tests { .unwrap(); let merchant_key = db - .get_merchant_key_store_by_merchant_id(merchant_id, &master_key.to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &master_key.to_vec().into(), + ) .await .unwrap(); @@ -961,7 +1076,9 @@ mod merchant_connector_account_cache_tests { merchant_id: merchant_id.to_string(), connector_name: "stripe".to_string(), connector_account_details: domain::types::encrypt( + key_manager_state, serde_json::Value::default().into(), + Identifier::Merchant(merchant_key.merchant_id.clone()), merchant_key.key.get_inner().peek(), ) .await @@ -986,7 +1103,9 @@ mod merchant_connector_account_cache_tests { status: common_enums::ConnectorStatus::Inactive, connector_wallets_details: Some( domain::types::encrypt( + key_manager_state, serde_json::Value::default().into(), + Identifier::Merchant(merchant_key.merchant_id.clone()), merchant_key.key.get_inner().peek(), ) .await @@ -995,13 +1114,14 @@ mod merchant_connector_account_cache_tests { additional_merchant_data: None, }; - db.insert_merchant_connector_account(mca.clone(), &merchant_key) + db.insert_merchant_connector_account(key_manager_state, mca.clone(), &merchant_key) .await .unwrap(); let find_call = || async { Conversion::convert( db.find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, profile_id, &mca.connector_name, &merchant_key, diff --git a/crates/router/src/db/merchant_key_store.rs b/crates/router/src/db/merchant_key_store.rs index 42a862e2d3..1cc12796bc 100644 --- a/crates/router/src/db/merchant_key_store.rs +++ b/crates/router/src/db/merchant_key_store.rs @@ -1,3 +1,4 @@ +use common_utils::types::keymanager::KeyManagerState; use error_stack::{report, ResultExt}; use masking::Secret; use router_env::{instrument, tracing}; @@ -19,12 +20,14 @@ use crate::{ pub trait MerchantKeyStoreInterface { async fn insert_merchant_key_store( &self, + state: &KeyManagerState, merchant_key_store: domain::MerchantKeyStore, key: &Secret>, ) -> CustomResult; async fn get_merchant_key_store_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key: &Secret>, ) -> CustomResult; @@ -37,12 +40,14 @@ pub trait MerchantKeyStoreInterface { #[cfg(feature = "olap")] async fn list_multiple_key_stores( &self, + state: &KeyManagerState, merchant_ids: Vec, key: &Secret>, ) -> CustomResult, errors::StorageError>; async fn get_all_key_stores( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError>; } @@ -52,10 +57,12 @@ impl MerchantKeyStoreInterface for Store { #[instrument(skip_all)] async fn insert_merchant_key_store( &self, + state: &KeyManagerState, merchant_key_store: domain::MerchantKeyStore, key: &Secret>, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; + let merchant_id = merchant_key_store.merchant_id.clone(); merchant_key_store .construct_new() .await @@ -63,7 +70,7 @@ impl MerchantKeyStoreInterface for Store { .insert(&conn) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(key) + .convert(state, key, merchant_id) .await .change_context(errors::StorageError::DecryptionError) } @@ -71,6 +78,7 @@ impl MerchantKeyStoreInterface for Store { #[instrument(skip_all)] async fn get_merchant_key_store_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key: &Secret>, ) -> CustomResult { @@ -89,7 +97,7 @@ impl MerchantKeyStoreInterface for Store { { fetch_func() .await? - .convert(key) + .convert(state, key, merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) } @@ -104,7 +112,7 @@ impl MerchantKeyStoreInterface for Store { &ACCOUNTS_CACHE, ) .await? - .convert(key) + .convert(state, key, merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) } @@ -146,6 +154,7 @@ impl MerchantKeyStoreInterface for Store { #[instrument(skip_all)] async fn list_multiple_key_stores( &self, + state: &KeyManagerState, merchant_ids: Vec, key: &Secret>, ) -> CustomResult, errors::StorageError> { @@ -161,8 +170,9 @@ impl MerchantKeyStoreInterface for Store { }; futures::future::try_join_all(fetch_func().await?.into_iter().map(|key_store| async { + let merchant_id = key_store.merchant_id.clone(); key_store - .convert(key) + .convert(state, key, merchant_id) .await .change_context(errors::StorageError::DecryptionError) })) @@ -171,6 +181,7 @@ impl MerchantKeyStoreInterface for Store { async fn get_all_key_stores( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; @@ -181,8 +192,9 @@ impl MerchantKeyStoreInterface for Store { }; futures::future::try_join_all(fetch_func().await?.into_iter().map(|key_store| async { + let merchant_id = key_store.merchant_id.clone(); key_store - .convert(key) + .convert(state, key, merchant_id) .await .change_context(errors::StorageError::DecryptionError) })) @@ -194,6 +206,7 @@ impl MerchantKeyStoreInterface for Store { impl MerchantKeyStoreInterface for MockDb { async fn insert_merchant_key_store( &self, + state: &KeyManagerState, merchant_key_store: domain::MerchantKeyStore, key: &Secret>, ) -> CustomResult { @@ -213,15 +226,16 @@ impl MerchantKeyStoreInterface for MockDb { .await .change_context(errors::StorageError::MockDbError)?; locked_merchant_key_store.push(merchant_key.clone()); - + let merchant_id = merchant_key.merchant_id.clone(); merchant_key - .convert(key) + .convert(state, key, merchant_id) .await .change_context(errors::StorageError::DecryptionError) } async fn get_merchant_key_store_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key: &Secret>, ) -> CustomResult { @@ -234,7 +248,7 @@ impl MerchantKeyStoreInterface for MockDb { .ok_or(errors::StorageError::ValueNotFound(String::from( "merchant_key_store", )))? - .convert(key) + .convert(state, key, merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) } @@ -258,6 +272,7 @@ impl MerchantKeyStoreInterface for MockDb { #[cfg(feature = "olap")] async fn list_multiple_key_stores( &self, + state: &KeyManagerState, merchant_ids: Vec, key: &Secret>, ) -> CustomResult, errors::StorageError> { @@ -269,7 +284,7 @@ impl MerchantKeyStoreInterface for MockDb { .map(|merchant_key| async { merchant_key .to_owned() - .convert(key) + .convert(state, key, merchant_key.merchant_id.clone()) .await .change_context(errors::StorageError::DecryptionError) }), @@ -278,6 +293,7 @@ impl MerchantKeyStoreInterface for MockDb { } async fn get_all_key_stores( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { let merchant_key_stores = self.merchant_key_store.lock().await; @@ -285,7 +301,7 @@ impl MerchantKeyStoreInterface for MockDb { futures::future::try_join_all(merchant_key_stores.iter().map(|merchant_key| async { merchant_key .to_owned() - .convert(key) + .convert(state, key, merchant_key.merchant_id.clone()) .await .change_context(errors::StorageError::DecryptionError) })) @@ -295,30 +311,54 @@ impl MerchantKeyStoreInterface for MockDb { #[cfg(test)] mod tests { + use std::sync::Arc; + + use common_utils::types::keymanager::Identifier; use time::macros::datetime; + use tokio::sync::oneshot; use crate::{ db::{merchant_key_store::MerchantKeyStoreInterface, MasterKeyInterface, MockDb}, + routes::{ + self, + app::{settings::Settings, StorageImpl}, + }, services, - types::domain::{self}, + types::domain, }; - #[allow(clippy::unwrap_used)] + #[allow(clippy::unwrap_used, clippy::expect_used)] #[tokio::test] async fn test_mock_db_merchant_key_store_interface() { + let conf = Settings::new().expect("invalid settings"); + let tx: oneshot::Sender<()> = oneshot::channel().0; + let app_state = Box::pin(routes::AppState::with_storage( + conf, + StorageImpl::PostgresqlTest, + tx, + Box::new(services::MockApiClient), + )) + .await; + let state = &Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); #[allow(clippy::expect_used)] let mock_db = MockDb::new(&redis_interface::RedisSettings::default()) .await .expect("Failed to create mock DB"); let master_key = mock_db.get_master_key(); let merchant_id = "merchant1"; - + let identifier = Identifier::Merchant(merchant_id.to_string()); + let key_manager_state = &state.into(); let merchant_key1 = mock_db .insert_merchant_key_store( + key_manager_state, domain::MerchantKeyStore { merchant_id: merchant_id.into(), key: domain::types::encrypt( + key_manager_state, services::generate_aes256_key().unwrap().to_vec().into(), + identifier.clone(), master_key, ) .await @@ -331,7 +371,11 @@ mod tests { .unwrap(); let found_merchant_key1 = mock_db - .get_merchant_key_store_by_merchant_id(merchant_id, &master_key.to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &master_key.to_vec().into(), + ) .await .unwrap(); @@ -340,10 +384,13 @@ mod tests { let insert_duplicate_merchant_key1_result = mock_db .insert_merchant_key_store( + key_manager_state, domain::MerchantKeyStore { merchant_id: merchant_id.into(), key: domain::types::encrypt( + key_manager_state, services::generate_aes256_key().unwrap().to_vec().into(), + identifier.clone(), master_key, ) .await @@ -356,12 +403,20 @@ mod tests { assert!(insert_duplicate_merchant_key1_result.is_err()); let find_non_existent_merchant_key_result = mock_db - .get_merchant_key_store_by_merchant_id("non_existent", &master_key.to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + "non_existent", + &master_key.to_vec().into(), + ) .await; assert!(find_non_existent_merchant_key_result.is_err()); let find_merchant_key_with_incorrect_master_key_result = mock_db - .get_merchant_key_store_by_merchant_id(merchant_id, &vec![0; 32].into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &vec![0; 32].into(), + ) .await; assert!(find_merchant_key_with_incorrect_master_key_result.is_err()); } diff --git a/crates/router/src/db/user/sample_data.rs b/crates/router/src/db/user/sample_data.rs index 5a23666c7b..cabf62b850 100644 --- a/crates/router/src/db/user/sample_data.rs +++ b/crates/router/src/db/user/sample_data.rs @@ -1,3 +1,4 @@ +use common_utils::types::keymanager::KeyManagerState; use diesel_models::{ errors::DatabaseError, query::user::sample_data as sample_data_queries, @@ -20,6 +21,7 @@ use crate::{connection::pg_connection_write, core::errors::CustomResult, service pub trait BatchSampleDataInterface { async fn insert_payment_intents_batch_for_sample_data( &self, + state: &KeyManagerState, batch: Vec, key_store: &MerchantKeyStore, ) -> CustomResult, StorageError>; @@ -36,6 +38,7 @@ pub trait BatchSampleDataInterface { async fn delete_payment_intents_for_sample_data( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &MerchantKeyStore, ) -> CustomResult, StorageError>; @@ -55,6 +58,7 @@ pub trait BatchSampleDataInterface { impl BatchSampleDataInterface for Store { async fn insert_payment_intents_batch_for_sample_data( &self, + state: &KeyManagerState, batch: Vec, key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { @@ -68,13 +72,17 @@ impl BatchSampleDataInterface for Store { .change_context(StorageError::EncryptionError) })) .await?; - sample_data_queries::insert_payment_intents(&conn, new_intents) .await .map_err(diesel_error_to_data_error) .map(|v| { try_join_all(v.into_iter().map(|payment_intent| { - PaymentIntent::convert_back(payment_intent, key_store.key.get_inner()) + PaymentIntent::convert_back( + state, + payment_intent, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) })) .map(|join_result| join_result.change_context(StorageError::DecryptionError)) })? @@ -111,6 +119,7 @@ impl BatchSampleDataInterface for Store { async fn delete_payment_intents_for_sample_data( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { @@ -122,7 +131,12 @@ impl BatchSampleDataInterface for Store { .map_err(diesel_error_to_data_error) .map(|v| { try_join_all(v.into_iter().map(|payment_intent| { - PaymentIntent::convert_back(payment_intent, key_store.key.get_inner()) + PaymentIntent::convert_back( + state, + payment_intent, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) })) .map(|join_result| join_result.change_context(StorageError::DecryptionError)) })? @@ -162,6 +176,7 @@ impl BatchSampleDataInterface for Store { impl BatchSampleDataInterface for storage_impl::MockDb { async fn insert_payment_intents_batch_for_sample_data( &self, + _state: &KeyManagerState, _batch: Vec, _key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { @@ -184,6 +199,7 @@ impl BatchSampleDataInterface for storage_impl::MockDb { async fn delete_payment_intents_for_sample_data( &self, + _state: &KeyManagerState, _merchant_id: &str, _key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { diff --git a/crates/router/src/db/user_key_store.rs b/crates/router/src/db/user_key_store.rs index c7e48037e9..e7dbe531c6 100644 --- a/crates/router/src/db/user_key_store.rs +++ b/crates/router/src/db/user_key_store.rs @@ -1,4 +1,4 @@ -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, types::keymanager::KeyManagerState}; use error_stack::{report, ResultExt}; use masking::Secret; use router_env::{instrument, tracing}; @@ -18,18 +18,21 @@ use crate::{ pub trait UserKeyStoreInterface { async fn insert_user_key_store( &self, + state: &KeyManagerState, user_key_store: domain::UserKeyStore, key: &Secret>, ) -> CustomResult; async fn get_user_key_store_by_user_id( &self, + state: &KeyManagerState, user_id: &str, key: &Secret>, ) -> CustomResult; async fn get_all_user_key_store( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError>; } @@ -39,10 +42,12 @@ impl UserKeyStoreInterface for Store { #[instrument(skip_all)] async fn insert_user_key_store( &self, + state: &KeyManagerState, user_key_store: domain::UserKeyStore, key: &Secret>, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; + let user_id = user_key_store.user_id.clone(); user_key_store .construct_new() .await @@ -50,7 +55,7 @@ impl UserKeyStoreInterface for Store { .insert(&conn) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(key) + .convert(state, key, user_id) .await .change_context(errors::StorageError::DecryptionError) } @@ -58,6 +63,7 @@ impl UserKeyStoreInterface for Store { #[instrument(skip_all)] async fn get_user_key_store_by_user_id( &self, + state: &KeyManagerState, user_id: &str, key: &Secret>, ) -> CustomResult { @@ -66,13 +72,14 @@ impl UserKeyStoreInterface for Store { diesel_models::user_key_store::UserKeyStore::find_by_user_id(&conn, user_id) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(key) + .convert(state, key, user_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) } async fn get_all_user_key_store( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; @@ -84,8 +91,9 @@ impl UserKeyStoreInterface for Store { }; futures::future::try_join_all(fetch_func().await?.into_iter().map(|key_store| async { + let user_id = key_store.user_id.clone(); key_store - .convert(key) + .convert(state, key, user_id) .await .change_context(errors::StorageError::DecryptionError) })) @@ -98,6 +106,7 @@ impl UserKeyStoreInterface for MockDb { #[instrument(skip_all)] async fn insert_user_key_store( &self, + state: &KeyManagerState, user_key_store: domain::UserKeyStore, key: &Secret>, ) -> CustomResult { @@ -117,23 +126,25 @@ impl UserKeyStoreInterface for MockDb { .await .change_context(errors::StorageError::MockDbError)?; locked_user_key_store.push(user_key_store.clone()); - + let user_id = user_key_store.user_id.clone(); user_key_store - .convert(key) + .convert(state, key, user_id) .await .change_context(errors::StorageError::DecryptionError) } async fn get_all_user_key_store( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { let user_key_store = self.user_key_store.lock().await; futures::future::try_join_all(user_key_store.iter().map(|user_key| async { + let user_id = user_key.user_id.clone(); user_key .to_owned() - .convert(key) + .convert(state, key, user_id) .await .change_context(errors::StorageError::DecryptionError) })) @@ -143,6 +154,7 @@ impl UserKeyStoreInterface for MockDb { #[instrument(skip_all)] async fn get_user_key_store_by_user_id( &self, + state: &KeyManagerState, user_id: &str, key: &Secret>, ) -> CustomResult { @@ -156,7 +168,7 @@ impl UserKeyStoreInterface for MockDb { "No user_key_store is found for user_id={}", user_id )))? - .convert(key) + .convert(state, key, user_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) } diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 38000f7b66..6405a3b847 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -193,7 +193,11 @@ pub async fn start_server(conf: settings::Settings) -> Applicatio let api_client = Box::new( services::ProxyClient::new( conf.proxy.clone(), - services::proxy_bypass_urls(&conf.locker, &conf.proxy.bypass_proxy_urls), + services::proxy_bypass_urls( + conf.key_manager.get_inner(), + &conf.locker, + &conf.proxy.bypass_proxy_urls, + ), ) .map_err(|error| { errors::ApplicationError::ApiClientError(error.current_context().clone()) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 0d7d042f7a..b733e76289 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -110,6 +110,7 @@ pub trait SessionStateInfo { fn event_handler(&self) -> EventsHandler; fn get_request_id(&self) -> Option; fn add_request_id(&mut self, request_id: RequestId); + fn session_state(&self) -> SessionState; } impl SessionStateInfo for SessionState { @@ -130,6 +131,9 @@ impl SessionStateInfo for SessionState { self.store.add_request_id(request_id.to_string()); self.request_id.replace(request_id); } + fn session_state(&self) -> SessionState { + self.clone() + } } #[derive(Clone)] pub struct AppState { diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 003cd71c00..25df0ef643 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -40,7 +40,7 @@ pub async fn create_payment_method_api( json_payload.into_inner(), |state, auth, req, _| async move { Box::pin(cards::get_client_secret_or_add_payment_method( - state, + &state, req, &auth.merchant_account, &auth.key_store, @@ -88,9 +88,11 @@ async fn get_merchant_account( state: &SessionState, merchant_id: &str, ) -> CustomResult<(MerchantKeyStore, domain::MerchantAccount), errors::ApiErrorResponse> { + let key_manager_state = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -99,7 +101,7 @@ async fn get_merchant_account( let merchant_account = state .store - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; Ok((key_store, merchant_account)) @@ -530,7 +532,7 @@ pub async fn default_payment_method_set_api( payload, |state, auth: auth::AuthenticationData, default_payment_method, _| async move { cards::set_default_payment_method( - &*state.clone().store, + &state, auth.merchant_account.merchant_id, auth.key_store, customer_id, diff --git a/crates/router/src/routes/recon.rs b/crates/router/src/routes/recon.rs index aabbd637ae..d8100e8d34 100644 --- a/crates/router/src/routes/recon.rs +++ b/crates/router/src/routes/recon.rs @@ -85,15 +85,17 @@ pub async fn send_recon_request( .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)? .merchant_id; + let key_manager_state = &(&state).into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id.as_str(), &db.get_master_key().to_vec().into(), ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id.as_str(), &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id.as_str(), &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -130,7 +132,12 @@ pub async fn send_recon_request( }; let response = db - .update_merchant(merchant_account, updated_merchant_account, &key_store) + .update_merchant( + key_manager_state, + merchant_account, + updated_merchant_account, + &key_store, + ) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable_lazy(|| { @@ -162,6 +169,7 @@ pub async fn recon_merchant_account_update( let key_store = db .get_merchant_key_store_by_merchant_id( + &(&state).into(), &req.merchant_id, &db.get_master_key().to_vec().into(), ) @@ -169,7 +177,7 @@ pub async fn recon_merchant_account_update( .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(&(&state).into(), merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -178,7 +186,12 @@ pub async fn recon_merchant_account_update( }; let response = db - .update_merchant(merchant_account, updated_merchant_account, &key_store) + .update_merchant( + &(&state).into(), + merchant_account, + updated_merchant_account, + &key_store, + ) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable_lazy(|| { diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 5dc46d3565..ea9ce17743 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -474,12 +474,19 @@ pub async fn send_request( let should_bypass_proxy = url .as_str() .starts_with(&state.conf.connectors.dummyconnector.base_url) - || proxy_bypass_urls(&state.conf.locker, &state.conf.proxy.bypass_proxy_urls) - .contains(&url.to_string()); + || proxy_bypass_urls( + state.conf.key_manager.get_inner(), + &state.conf.locker, + &state.conf.proxy.bypass_proxy_urls, + ) + .contains(&url.to_string()); #[cfg(not(feature = "dummy_connector"))] - let should_bypass_proxy = - proxy_bypass_urls(&state.conf.locker, &state.conf.proxy.bypass_proxy_urls) - .contains(&url.to_string()); + let should_bypass_proxy = proxy_bypass_urls( + &state.conf.key_manager.get_inner(), + &state.conf.locker, + &state.conf.proxy.bypass_proxy_urls, + ) + .contains(&url.to_string()); let client = client::create_client( &state.conf.proxy, should_bypass_proxy, diff --git a/crates/router/src/services/api/client.rs b/crates/router/src/services/api/client.rs index f3cb2f3f31..4dda8eab62 100644 --- a/crates/router/src/services/api/client.rs +++ b/crates/router/src/services/api/client.rs @@ -15,7 +15,7 @@ use crate::{ errors::{ApiClientError, CustomResult}, payments, }, - routes::SessionState, + routes::{app::settings::KeyManagerConfig, SessionState}, }; static NON_PROXIED_CLIENT: OnceCell = OnceCell::new(); @@ -110,7 +110,12 @@ pub fn create_client( } } -pub fn proxy_bypass_urls(locker: &Locker, config_whitelist: &[String]) -> Vec { +pub fn proxy_bypass_urls( + key_manager: &KeyManagerConfig, + locker: &Locker, + config_whitelist: &[String], +) -> Vec { + let key_manager_host = key_manager.url.to_owned(); let locker_host = locker.host.to_owned(); let locker_host_rs = locker.host_rs.to_owned(); @@ -126,8 +131,11 @@ pub fn proxy_bypass_urls(locker: &Locker, config_whitelist: &[String]) -> Vec RouterResult<(AuthenticationData, AuthenticationType)> { + let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() .get_merchant_key_store_by_merchant_id( + key_manager_state, self.0.as_ref(), &state.store().get_master_key().to_vec().into(), ) @@ -552,7 +561,7 @@ where let merchant = state .store() - .find_merchant_account_by_merchant_id(self.0.as_ref(), &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, self.0.as_ref(), &key_store) .await .map_err(|e| { if e.current_context().is_db_not_found() { @@ -590,10 +599,10 @@ where ) -> RouterResult<(AuthenticationData, AuthenticationType)> { let publishable_key = get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?; - + let key_manager_state = &(&state.session_state()).into(); state .store() - .find_merchant_account_by_publishable_key(publishable_key) + .find_merchant_account_by_publishable_key(key_manager_state, publishable_key) .await .map_err(|e| { if e.current_context().is_db_not_found() { @@ -818,10 +827,11 @@ where let permissions = authorization::get_permissions(state, &payload).await?; authorization::check_authorization(&self.0, &permissions)?; - + let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() .get_merchant_key_store_by_merchant_id( + key_manager_state, &payload.merchant_id, &state.store().get_master_key().to_vec().into(), ) @@ -831,7 +841,11 @@ where let merchant = state .store() - .find_merchant_account_by_merchant_id(&payload.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &key_store, + ) .await .change_context(errors::ApiErrorResponse::InvalidJwtToken)?; @@ -868,10 +882,11 @@ where let permissions = authorization::get_permissions(state, &payload).await?; authorization::check_authorization(&self.0, &permissions)?; - + let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() .get_merchant_key_store_by_merchant_id( + key_manager_state, &payload.merchant_id, &state.store().get_master_key().to_vec().into(), ) @@ -881,7 +896,11 @@ where let merchant = state .store() - .find_merchant_account_by_merchant_id(&payload.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &key_store, + ) .await .change_context(errors::ApiErrorResponse::InvalidJwtToken)?; @@ -963,10 +982,11 @@ where state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { let payload = parse_jwt_payload::(request_headers, state).await?; - + let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() .get_merchant_key_store_by_merchant_id( + key_manager_state, &payload.merchant_id, &state.store().get_master_key().to_vec().into(), ) @@ -976,7 +996,11 @@ where let merchant = state .store() - .find_merchant_account_by_merchant_id(&payload.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index 884e47c6fc..054888d1df 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -8,13 +8,17 @@ pub use api_models::admin::{ MerchantId, PaymentMethodsEnabled, ToggleAllKVRequest, ToggleAllKVResponse, ToggleKVRequest, ToggleKVResponse, WebhookDetails, }; -use common_utils::ext_traits::{AsyncExt, Encode, ValueExt}; +use common_utils::{ + ext_traits::{AsyncExt, Encode, ValueExt}, + types::keymanager::Identifier, +}; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{merchant_key_store::MerchantKeyStore, type_encryption::decrypt}; use masking::{ExposeInterface, PeekInterface, Secret}; use crate::{ core::{errors, payment_methods::cards::create_encrypted_data}, + routes::SessionState, types::{domain, storage, transformers::ForeignTryFrom}, }; @@ -84,11 +88,14 @@ impl ForeignTryFrom for MerchantAccountResponse { } pub async fn business_profile_response( + state: &SessionState, item: storage::business_profile::BusinessProfile, key_store: &MerchantKeyStore, ) -> Result> { let outgoing_webhook_custom_http_headers = decrypt::( + &state.into(), item.outgoing_webhook_custom_http_headers.clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key_store.key.get_inner().peek(), ) .await @@ -147,6 +154,7 @@ pub async fn business_profile_response( } pub async fn create_business_profile( + state: &SessionState, merchant_account: domain::MerchantAccount, request: BusinessProfileCreate, key_store: &MerchantKeyStore, @@ -188,7 +196,7 @@ pub async fn create_business_profile( .transpose()?; let outgoing_webhook_custom_http_headers = request .outgoing_webhook_custom_http_headers - .async_map(|headers| create_encrypted_data(key_store, headers)) + .async_map(|headers| create_encrypted_data(state, key_store, headers)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/types/api/mandates.rs b/crates/router/src/types/api/mandates.rs index 71b90ab41c..686f7f3ce4 100644 --- a/crates/router/src/types/api/mandates.rs +++ b/crates/router/src/types/api/mandates.rs @@ -2,7 +2,6 @@ use api_models::mandates; pub use api_models::mandates::{MandateId, MandateResponse, MandateRevokedResponse}; use common_utils::ext_traits::OptionExt; use error_stack::ResultExt; -use masking::PeekInterface; use serde::{Deserialize, Serialize}; use crate::{ @@ -73,8 +72,8 @@ impl MandateResponseExt for MandateResponse { } else { payment_methods::cards::get_card_details_without_locker_fallback( &payment_method, - key_store.key.get_inner().peek(), state, + &key_store, ) .await? }; diff --git a/crates/router/src/types/domain/address.rs b/crates/router/src/types/domain/address.rs index 0ed09a3501..e14ab8ea64 100644 --- a/crates/router/src/types/domain/address.rs +++ b/crates/router/src/types/domain/address.rs @@ -1,18 +1,19 @@ use async_trait::async_trait; use common_utils::{ - crypto, date_time, + crypto::{self, Encryptable}, + date_time, + encryption::Encryption, errors::{CustomResult, ValidationError}, id_type, + types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, }; -use diesel_models::{address::AddressUpdateInternal, encryption::Encryption, enums}; +use diesel_models::{address::AddressUpdateInternal, enums}; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; +use rustc_hash::FxHashMap; use time::{OffsetDateTime, PrimitiveDateTime}; -use super::{ - behaviour, - types::{self, AsyncLift}, -}; +use super::{behaviour, types}; #[derive(Clone, Debug, serde::Serialize)] pub struct Address { @@ -73,8 +74,10 @@ impl behaviour::Conversion for CustomerAddress { } async fn convert_back( + state: &KeyManagerState, other: Self::DstType, key: &Secret>, + key_store_ref_id: String, ) -> CustomResult { let customer_id = other @@ -84,7 +87,7 @@ impl behaviour::Conversion for CustomerAddress { field_name: "customer_id".to_string(), })?; - let address = Address::convert_back(other, key).await?; + let address = Address::convert_back(state, other, key, key_store_ref_id).await?; Ok(Self { address, @@ -117,8 +120,10 @@ impl behaviour::Conversion for PaymentAddress { } async fn convert_back( + state: &KeyManagerState, other: Self::DstType, key: &Secret>, + key_store_ref_id: String, ) -> CustomResult { let payment_id = other .payment_id @@ -129,7 +134,7 @@ impl behaviour::Conversion for PaymentAddress { let customer_id = other.customer_id.clone(); - let address = Address::convert_back(other, key).await?; + let address = Address::convert_back(state, other, key, key_store_ref_id).await?; Ok(Self { address, @@ -180,36 +185,45 @@ impl behaviour::Conversion for Address { } async fn convert_back( + state: &KeyManagerState, other: Self::DstType, key: &Secret>, + _key_store_ref_id: String, ) -> CustomResult { - async { - let inner_decrypt = |inner| types::decrypt(inner, key.peek()); - let inner_decrypt_email = |inner| types::decrypt(inner, key.peek()); - Ok::>(Self { - id: other.id, - address_id: other.address_id, - city: other.city, - country: other.country, - line1: other.line1.async_lift(inner_decrypt).await?, - line2: other.line2.async_lift(inner_decrypt).await?, - line3: other.line3.async_lift(inner_decrypt).await?, - state: other.state.async_lift(inner_decrypt).await?, - zip: other.zip.async_lift(inner_decrypt).await?, - first_name: other.first_name.async_lift(inner_decrypt).await?, - last_name: other.last_name.async_lift(inner_decrypt).await?, - phone_number: other.phone_number.async_lift(inner_decrypt).await?, - country_code: other.country_code, - created_at: other.created_at, - modified_at: other.modified_at, - updated_by: other.updated_by, - merchant_id: other.merchant_id, - email: other.email.async_lift(inner_decrypt_email).await?, - }) - } + let identifier = Identifier::Merchant(other.merchant_id.clone()); + let decrypted: FxHashMap>> = types::batch_decrypt( + state, + diesel_models::Address::to_encryptable(other.clone()), + identifier.clone(), + key.peek(), + ) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting".to_string(), + })?; + let encryptable_address = diesel_models::Address::from_encryptable(decrypted) + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting".to_string(), + })?; + Ok(Self { + id: other.id, + address_id: other.address_id, + city: other.city, + country: other.country, + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + state: encryptable_address.state, + zip: encryptable_address.zip, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, + phone_number: encryptable_address.phone_number, + country_code: other.country_code, + created_at: other.created_at, + modified_at: other.modified_at, + updated_by: other.updated_by, + merchant_id: other.merchant_id, + email: encryptable_address.email, }) } diff --git a/crates/router/src/types/domain/customer.rs b/crates/router/src/types/domain/customer.rs index c5b0b3701c..b7ed05e350 100644 --- a/crates/router/src/types/domain/customer.rs +++ b/crates/router/src/types/domain/customer.rs @@ -1,10 +1,16 @@ -use common_utils::{crypto, date_time, id_type, pii}; -use diesel_models::{customers::CustomerUpdateInternal, encryption::Encryption}; +use api_models::customers::CustomerRequestWithEncryption; +use common_utils::{ + crypto, date_time, + encryption::Encryption, + id_type, pii, + types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, +}; +use diesel_models::customers::CustomerUpdateInternal; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; use time::PrimitiveDateTime; -use super::types::{self, AsyncLift}; +use super::types; use crate::errors::{CustomResult, ValidationError}; #[derive(Clone, Debug)] @@ -53,36 +59,49 @@ impl super::behaviour::Conversion for Customer { } async fn convert_back( + state: &KeyManagerState, item: Self::DstType, key: &Secret>, + _key_store_ref_id: String, ) -> CustomResult where Self: Sized, { - async { - let inner_decrypt = |inner| types::decrypt(inner, key.peek()); - let inner_decrypt_email = |inner| types::decrypt(inner, key.peek()); - Ok::>(Self { - id: Some(item.id), - customer_id: item.customer_id, - merchant_id: item.merchant_id, - name: item.name.async_lift(inner_decrypt).await?, - email: item.email.async_lift(inner_decrypt_email).await?, - phone: item.phone.async_lift(inner_decrypt).await?, - phone_country_code: item.phone_country_code, - description: item.description, - created_at: item.created_at, - metadata: item.metadata, - modified_at: item.modified_at, - connector_customer: item.connector_customer, - address_id: item.address_id, - default_payment_method_id: item.default_payment_method_id, - updated_by: item.updated_by, - }) - } + let decrypted = types::batch_decrypt( + state, + CustomerRequestWithEncryption::to_encryptable(CustomerRequestWithEncryption { + name: item.name.clone(), + phone: item.phone.clone(), + email: item.email.clone(), + }), + Identifier::Merchant(item.merchant_id.clone()), + key.peek(), + ) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting customer data".to_string(), + })?; + let encryptable_customer = CustomerRequestWithEncryption::from_encryptable(decrypted) + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting customer data".to_string(), + })?; + + Ok(Self { + id: Some(item.id), + customer_id: item.customer_id, + merchant_id: item.merchant_id, + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: encryptable_customer.phone, + phone_country_code: item.phone_country_code, + description: item.description, + created_at: item.created_at, + metadata: item.metadata, + modified_at: item.modified_at, + connector_customer: item.connector_customer, + address_id: item.address_id, + default_payment_method_id: item.default_payment_method_id, + updated_by: item.updated_by, }) } diff --git a/crates/router/src/types/domain/event.rs b/crates/router/src/types/domain/event.rs index 74a6cfb47b..c5eca1ab5e 100644 --- a/crates/router/src/types/domain/event.rs +++ b/crates/router/src/types/domain/event.rs @@ -1,14 +1,18 @@ -use common_utils::crypto::OptionalEncryptableSecretString; +use common_utils::{ + crypto::OptionalEncryptableSecretString, + types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, +}; use diesel_models::{ enums::{EventClass, EventObjectType, EventType, WebhookDeliveryAttempt}, events::{EventMetadata, EventUpdateInternal}, + EventWithEncryption, }; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; use crate::{ errors::{CustomResult, ValidationError}, - types::domain::types::{self, AsyncLift}, + types::domain::types, }; #[derive(Clone, Debug)] @@ -80,41 +84,49 @@ impl super::behaviour::Conversion for Event { } async fn convert_back( + state: &KeyManagerState, item: Self::DstType, key: &Secret>, + key_store_ref_id: String, ) -> CustomResult where Self: Sized, { - async { - Ok::>(Self { - event_id: item.event_id, - event_type: item.event_type, - event_class: item.event_class, - is_webhook_notified: item.is_webhook_notified, - primary_object_id: item.primary_object_id, - primary_object_type: item.primary_object_type, - created_at: item.created_at, - merchant_id: item.merchant_id, - business_profile_id: item.business_profile_id, - primary_object_created_at: item.primary_object_created_at, - idempotent_event_id: item.idempotent_event_id, - initial_attempt_id: item.initial_attempt_id, - request: item - .request - .async_lift(|inner| types::decrypt(inner, key.peek())) - .await?, - response: item - .response - .async_lift(|inner| types::decrypt(inner, key.peek())) - .await?, - delivery_attempt: item.delivery_attempt, - metadata: item.metadata, - }) - } + let decrypted = types::batch_decrypt( + state, + EventWithEncryption::to_encryptable(EventWithEncryption { + request: item.request.clone(), + response: item.response.clone(), + }), + Identifier::Merchant(key_store_ref_id.clone()), + key.peek(), + ) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting event data".to_string(), + })?; + let encryptable_event = EventWithEncryption::from_encryptable(decrypted).change_context( + ValidationError::InvalidValue { + message: "Failed while decrypting event data".to_string(), + }, + )?; + Ok(Self { + event_id: item.event_id, + event_type: item.event_type, + event_class: item.event_class, + is_webhook_notified: item.is_webhook_notified, + primary_object_id: item.primary_object_id, + primary_object_type: item.primary_object_type, + created_at: item.created_at, + merchant_id: item.merchant_id, + business_profile_id: item.business_profile_id, + primary_object_created_at: item.primary_object_created_at, + idempotent_event_id: item.idempotent_event_id, + initial_attempt_id: item.initial_attempt_id, + request: encryptable_event.request, + response: encryptable_event.response, + delivery_attempt: item.delivery_attempt, + metadata: item.metadata, }) } diff --git a/crates/router/src/types/domain/merchant_connector_account.rs b/crates/router/src/types/domain/merchant_connector_account.rs index 2cf091eda1..6f654848f6 100644 --- a/crates/router/src/types/domain/merchant_connector_account.rs +++ b/crates/router/src/types/domain/merchant_connector_account.rs @@ -1,13 +1,12 @@ use common_utils::{ crypto::{Encryptable, GcmAes256}, date_time, + encryption::Encryption, errors::{CustomResult, ValidationError}, pii, + types::keymanager::{Identifier, KeyManagerState}, }; -use diesel_models::{ - encryption::Encryption, enums, - merchant_connector_account::MerchantConnectorAccountUpdateInternal, -}; +use diesel_models::{enums, merchant_connector_account::MerchantConnectorAccountUpdateInternal}; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -108,15 +107,20 @@ impl behaviour::Conversion for MerchantConnectorAccount { } async fn convert_back( + state: &KeyManagerState, other: Self::DstType, key: &Secret>, + _key_store_ref_id: String, ) -> CustomResult { + let identifier = Identifier::Merchant(other.merchant_id.clone()); Ok(Self { id: Some(other.id), merchant_id: other.merchant_id, connector_name: other.connector_name, - connector_account_details: Encryptable::decrypt( + connector_account_details: Encryptable::decrypt_via_api( + state, other.connector_account_details, + identifier.clone(), key.peek(), GcmAes256, ) @@ -145,7 +149,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { status: other.status, connector_wallets_details: other .connector_wallets_details - .async_lift(|inner| types::decrypt(inner, key.peek())) + .async_lift(|inner| types::decrypt(state, inner, identifier.clone(), key.peek())) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting connector wallets details".to_string(), diff --git a/crates/router/src/types/domain/types.rs b/crates/router/src/types/domain/types.rs index 151ae61f5f..53239cd27c 100644 --- a/crates/router/src/types/domain/types.rs +++ b/crates/router/src/types/domain/types.rs @@ -1,6 +1,7 @@ use common_utils::types::keymanager::KeyManagerState; pub use hyperswitch_domain_models::type_encryption::{ - decrypt, encrypt, encrypt_optional, AsyncLift, Lift, TypeEncryption, + batch_decrypt, batch_encrypt, decrypt, encrypt, encrypt_optional, AsyncLift, Lift, + TypeEncryption, }; impl From<&crate::SessionState> for KeyManagerState { diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index c4c67796d4..ec44103a8f 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -7,8 +7,11 @@ use common_enums::TokenPurpose; #[cfg(not(feature = "v2"))] use common_utils::id_type; #[cfg(feature = "keymanager_create")] -use common_utils::types::keymanager::{EncryptionCreateRequest, Identifier}; -use common_utils::{crypto::Encryptable, errors::CustomResult, new_type::MerchantName, pii}; +use common_utils::types::keymanager::EncryptionCreateRequest; +use common_utils::{ + crypto::Encryptable, errors::CustomResult, new_type::MerchantName, pii, + types::keymanager::Identifier, +}; use diesel_models::{ enums::{TotpStatus, UserStatus}, organization as diesel_org, @@ -368,6 +371,7 @@ impl NewUserMerchant { if state .store .get_merchant_key_store_by_merchant_id( + &(&state).into(), self.get_merchant_id().as_str(), &state.store.get_master_key().to_vec().into(), ) @@ -949,9 +953,14 @@ impl UserFromStorage { pub async fn get_or_create_key_store(&self, state: &SessionState) -> UserResult { let master_key = state.store.get_master_key(); + let key_manager_state = &state.into(); let key_store_result = state .global_store - .get_user_key_store_by_user_id(self.get_user_id(), &master_key.to_vec().into()) + .get_user_key_store_by_user_id( + key_manager_state, + self.get_user_id(), + &master_key.to_vec().into(), + ) .await; if let Ok(key_store) = key_store_result { @@ -968,16 +977,21 @@ impl UserFromStorage { let key_store = UserKeyStore { user_id: self.get_user_id().to_string(), - key: domain_types::encrypt(key.to_vec().into(), master_key) - .await - .change_context(UserErrors::InternalServerError)?, + key: domain_types::encrypt( + key_manager_state, + key.to_vec().into(), + Identifier::User(self.get_user_id().to_string()), + master_key, + ) + .await + .change_context(UserErrors::InternalServerError)?, created_at: common_utils::date_time::now(), }; #[cfg(feature = "keymanager_create")] { common_utils::keymanager::create_key_in_key_manager( - &state.into(), + key_manager_state, EncryptionCreateRequest { identifier: Identifier::User(key_store.user_id.clone()), }, @@ -988,7 +1002,7 @@ impl UserFromStorage { state .global_store - .insert_user_key_store(key_store, &master_key.to_vec().into()) + .insert_user_key_store(key_manager_state, key_store, &master_key.to_vec().into()) .await .change_context(UserErrors::InternalServerError) } else { @@ -1014,10 +1028,11 @@ impl UserFromStorage { if self.0.totp_secret.is_none() { return Ok(None); } - + let key_manager_state = &state.into(); let user_key_store = state .global_store .get_user_key_store_by_user_id( + key_manager_state, self.get_user_id(), &state.store.get_master_key().to_vec().into(), ) @@ -1025,7 +1040,9 @@ impl UserFromStorage { .change_context(UserErrors::InternalServerError)?; Ok(domain_types::decrypt::( + key_manager_state, self.0.totp_secret.clone(), + Identifier::User(user_key_store.user_id.clone()), user_key_store.key.peek(), ) .await @@ -1141,6 +1158,7 @@ impl SignInWithMultipleRolesStrategy { let merchant_accounts = state .store .list_multiple_merchant_accounts( + &state.into(), self.user_roles .iter() .map(|role| role.merchant_id.clone()) diff --git a/crates/router/src/types/domain/user_key_store.rs b/crates/router/src/types/domain/user_key_store.rs index 4c1427d58d..3a1a9a60e9 100644 --- a/crates/router/src/types/domain/user_key_store.rs +++ b/crates/router/src/types/domain/user_key_store.rs @@ -1,6 +1,7 @@ use common_utils::{ crypto::{Encryptable, GcmAes256}, date_time, + types::keymanager::{Identifier, KeyManagerState}, }; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -32,14 +33,17 @@ impl super::behaviour::Conversion for UserKeyStore { } async fn convert_back( + state: &KeyManagerState, item: Self::DstType, key: &Secret>, + _key_store_ref_id: String, ) -> CustomResult where Self: Sized, { + let identifier = Identifier::User(item.user_id.clone()); Ok(Self { - key: Encryptable::decrypt(item.key, key.peek(), GcmAes256) + key: Encryptable::decrypt_via_api(state, item.key, identifier, key.peek(), GcmAes256) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting customer data".to_string(), diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 64e2f534b7..a36aee4593 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -14,19 +14,25 @@ pub mod verify_connector; use std::fmt::Debug; -use api_models::{enums, payments, webhooks}; +use api_models::{ + enums, + payments::{self, AddressDetailsWithPhone}, + webhooks, +}; use base64::Engine; -use common_utils::id_type; pub use common_utils::{ crypto, ext_traits::{ByteSliceExt, BytesExt, Encode, StringExt, ValueExt}, fp_utils::when, validation::validate_email, }; +use common_utils::{ + id_type, + types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, +}; use error_stack::ResultExt; -use hyperswitch_domain_models::payments::PaymentIntent; +use hyperswitch_domain_models::{payments::PaymentIntent, type_encryption::batch_encrypt}; use image::Luma; -use masking::ExposeInterface; use nanoid::nanoid; use qrcode; use router_env::metrics::add_attributes; @@ -43,19 +49,10 @@ use crate::{ errors::{self, CustomResult, RouterResult, StorageErrorExt}, utils, webhooks as webhooks_core, }, - db::StorageInterface, logger, - routes::metrics, + routes::{metrics, SessionState}, services, - types::{ - self, - domain::{ - self, - types::{encrypt_optional, AsyncLift}, - }, - storage, - transformers::ForeignFrom, - }, + types::{self, domain, storage, transformers::ForeignFrom}, }; pub mod error_parser { @@ -194,14 +191,17 @@ impl QrImage { } pub async fn find_payment_intent_from_payment_id_type( - db: &dyn StorageInterface, + state: &SessionState, payment_id_type: payments::PaymentIdType, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { + let key_manager_state: KeyManagerState = state.into(); + let db = &*state.store; match payment_id_type { payments::PaymentIdType::PaymentIntentId(payment_id) => db .find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, &payment_id, &merchant_account.merchant_id, key_store, @@ -219,6 +219,7 @@ pub async fn find_payment_intent_from_payment_id_type( .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; db.find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, &attempt.payment_id, &merchant_account.merchant_id, key_store, @@ -237,6 +238,7 @@ pub async fn find_payment_intent_from_payment_id_type( .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; db.find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, &attempt.payment_id, &merchant_account.merchant_id, key_store, @@ -252,12 +254,13 @@ pub async fn find_payment_intent_from_payment_id_type( } pub async fn find_payment_intent_from_refund_id_type( - db: &dyn StorageInterface, + state: &SessionState, refund_id_type: webhooks::RefundIdType, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_name: &str, ) -> CustomResult { + let db = &*state.store; let refund = match refund_id_type { webhooks::RefundIdType::RefundId(id) => db .find_refund_by_merchant_id_refund_id( @@ -286,6 +289,7 @@ pub async fn find_payment_intent_from_refund_id_type( .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; db.find_payment_intent_by_payment_id_merchant_id( + &state.into(), &attempt.payment_id, &merchant_account.merchant_id, key_store, @@ -296,11 +300,12 @@ pub async fn find_payment_intent_from_refund_id_type( } pub async fn find_payment_intent_from_mandate_id_type( - db: &dyn StorageInterface, + state: &SessionState, mandate_id_type: webhooks::MandateIdType, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { + let db = &*state.store; let mandate = match mandate_id_type { webhooks::MandateIdType::MandateId(mandate_id) => db .find_mandate_by_merchant_id_mandate_id( @@ -320,6 +325,7 @@ pub async fn find_payment_intent_from_mandate_id_type( .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?, }; db.find_payment_intent_by_payment_id_merchant_id( + &state.into(), &mandate .original_payment_id .ok_or(errors::ApiErrorResponse::InternalServerError) @@ -333,11 +339,12 @@ pub async fn find_payment_intent_from_mandate_id_type( } pub async fn find_mca_from_authentication_id_type( - db: &dyn StorageInterface, + state: &SessionState, authentication_id_type: webhooks::AuthenticationIdType, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { + let db = &*state.store; let authentication = match authentication_id_type { webhooks::AuthenticationIdType::AuthenticationId(authentication_id) => db .find_authentication_by_merchant_id_authentication_id( @@ -356,6 +363,7 @@ pub async fn find_mca_from_authentication_id_type( } }; db.find_by_merchant_connector_account_merchant_id_merchant_connector_id( + &state.into(), &merchant_account.merchant_id, &authentication.merchant_connector_id, key_store, @@ -367,12 +375,13 @@ pub async fn find_mca_from_authentication_id_type( } pub async fn get_mca_from_payment_intent( - db: &dyn StorageInterface, + state: &SessionState, merchant_account: &domain::MerchantAccount, payment_intent: PaymentIntent, key_store: &domain::MerchantKeyStore, connector_name: &str, ) -> CustomResult { + let db = &*state.store; let payment_attempt = db .find_payment_attempt_by_attempt_id_merchant_id( &payment_intent.active_attempt.get_id(), @@ -381,10 +390,11 @@ pub async fn get_mca_from_payment_intent( ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; - + let key_manager_state = &state.into(); match payment_attempt.merchant_connector_id { Some(merchant_connector_id) => db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &merchant_account.merchant_id, &merchant_connector_id, key_store, @@ -410,6 +420,7 @@ pub async fn get_mca_from_payment_intent( }; db.find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, &profile_id, connector_name, key_store, @@ -426,12 +437,13 @@ pub async fn get_mca_from_payment_intent( #[cfg(feature = "payouts")] pub async fn get_mca_from_payout_attempt( - db: &dyn StorageInterface, + state: &SessionState, merchant_account: &domain::MerchantAccount, payout_id_type: webhooks::PayoutIdType, connector_name: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { + let db = &*state.store; let payout = match payout_id_type { webhooks::PayoutIdType::PayoutAttemptId(payout_attempt_id) => db .find_payout_attempt_by_merchant_id_payout_attempt_id( @@ -450,10 +462,11 @@ pub async fn get_mca_from_payout_attempt( .await .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?, }; - + let key_manager_state = &state.into(); match payout.merchant_connector_id { Some(merchant_connector_id) => db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &merchant_account.merchant_id, &merchant_connector_id, key_store, @@ -464,6 +477,7 @@ pub async fn get_mca_from_payout_attempt( }), None => db .find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, &payout.profile_id, connector_name, key_store, @@ -479,15 +493,17 @@ pub async fn get_mca_from_payout_attempt( } pub async fn get_mca_from_object_reference_id( - db: &dyn StorageInterface, + state: &SessionState, object_reference_id: webhooks::ObjectReferenceId, merchant_account: &domain::MerchantAccount, connector_name: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { + let db = &*state.store; match merchant_account.default_profile.as_ref() { Some(profile_id) => db .find_merchant_connector_account_by_profile_id_connector_name( + &state.into(), profile_id, connector_name, key_store, @@ -499,10 +515,10 @@ pub async fn get_mca_from_object_reference_id( _ => match object_reference_id { webhooks::ObjectReferenceId::PaymentId(payment_id_type) => { get_mca_from_payment_intent( - db, + state, merchant_account, find_payment_intent_from_payment_id_type( - db, + state, payment_id_type, merchant_account, key_store, @@ -515,10 +531,10 @@ pub async fn get_mca_from_object_reference_id( } webhooks::ObjectReferenceId::RefundId(refund_id_type) => { get_mca_from_payment_intent( - db, + state, merchant_account, find_payment_intent_from_refund_id_type( - db, + state, refund_id_type, merchant_account, key_store, @@ -532,10 +548,10 @@ pub async fn get_mca_from_object_reference_id( } webhooks::ObjectReferenceId::MandateId(mandate_id_type) => { get_mca_from_payment_intent( - db, + state, merchant_account, find_payment_intent_from_mandate_id_type( - db, + state, mandate_id_type, merchant_account, key_store, @@ -548,7 +564,7 @@ pub async fn get_mca_from_object_reference_id( } webhooks::ObjectReferenceId::ExternalAuthenticationID(authentication_id_type) => { find_mca_from_authentication_id_type( - db, + state, authentication_id_type, merchant_account, key_store, @@ -558,7 +574,7 @@ pub async fn get_mca_from_object_reference_id( #[cfg(feature = "payouts")] webhooks::ObjectReferenceId::PayoutId(payout_id_type) => { get_mca_from_payout_attempt( - db, + state, merchant_account, payout_id_type, connector_name, @@ -649,13 +665,16 @@ pub fn add_connector_http_status_code_metrics(option_status_code: Option) { pub trait CustomerAddress { async fn get_address_update( &self, + state: &SessionState, address_details: payments::AddressDetails, key: &[u8], storage_scheme: storage::enums::MerchantStorageScheme, + merchant_id: String, ) -> CustomResult; async fn get_domain_address( &self, + state: &SessionState, address_details: payments::AddressDetails, merchant_id: &str, customer_id: &id_type::CustomerId, @@ -668,126 +687,89 @@ pub trait CustomerAddress { impl CustomerAddress for api_models::customers::CustomerRequest { async fn get_address_update( &self, + state: &SessionState, address_details: payments::AddressDetails, key: &[u8], storage_scheme: storage::enums::MerchantStorageScheme, + merchant_id: String, ) -> CustomResult { - async { - Ok(storage::AddressUpdate::Update { - city: address_details.city, - country: address_details.country, - line1: address_details - .line1 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - line2: address_details - .line2 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - line3: address_details - .line3 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - zip: address_details - .zip - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - state: address_details - .state - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - first_name: address_details - .first_name - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - last_name: address_details - .last_name - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - phone_number: self - .phone - .clone() - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - country_code: self.phone_country_code.clone(), - updated_by: storage_scheme.to_string(), - email: self - .email - .as_ref() - .cloned() - .async_lift(|inner| encrypt_optional(inner.map(|inner| inner.expose()), key)) - .await?, - }) - } - .await + let encrypted_data = batch_encrypt( + &state.into(), + AddressDetailsWithPhone::to_encryptable(AddressDetailsWithPhone { + address: Some(address_details.clone()), + phone_number: self.phone.clone(), + email: self.email.clone(), + }), + Identifier::Merchant(merchant_id), + key, + ) + .await?; + let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data) + .change_context(common_utils::errors::CryptoError::EncodingFailed)?; + Ok(storage::AddressUpdate::Update { + city: address_details.city, + country: address_details.country, + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + zip: encryptable_address.zip, + state: encryptable_address.state, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, + phone_number: encryptable_address.phone_number, + country_code: self.phone_country_code.clone(), + updated_by: storage_scheme.to_string(), + email: encryptable_address.email, + }) } async fn get_domain_address( &self, + state: &SessionState, address_details: payments::AddressDetails, merchant_id: &str, customer_id: &id_type::CustomerId, key: &[u8], storage_scheme: storage::enums::MerchantStorageScheme, ) -> CustomResult { - async { - let address = domain::Address { - id: None, - city: address_details.city, - country: address_details.country, - line1: address_details - .line1 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - line2: address_details - .line2 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - line3: address_details - .line3 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - zip: address_details - .zip - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - state: address_details - .state - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - first_name: address_details - .first_name - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - last_name: address_details - .last_name - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - phone_number: self - .phone - .clone() - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - country_code: self.phone_country_code.clone(), - merchant_id: merchant_id.to_string(), - address_id: generate_id(consts::ID_LENGTH, "add"), - created_at: common_utils::date_time::now(), - modified_at: common_utils::date_time::now(), - updated_by: storage_scheme.to_string(), - email: self - .email - .as_ref() - .cloned() - .async_lift(|inner| encrypt_optional(inner.map(|inner| inner.expose()), key)) - .await?, - }; + let encrypted_data = batch_encrypt( + &state.into(), + AddressDetailsWithPhone::to_encryptable(AddressDetailsWithPhone { + address: Some(address_details.clone()), + phone_number: self.phone.clone(), + email: self.email.clone(), + }), + Identifier::Merchant(merchant_id.to_string()), + key, + ) + .await?; + let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data) + .change_context(common_utils::errors::CryptoError::EncodingFailed)?; + let address = domain::Address { + id: None, + city: address_details.city, + country: address_details.country, + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + zip: encryptable_address.zip, + state: encryptable_address.state, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, + phone_number: encryptable_address.phone_number, + country_code: self.phone_country_code.clone(), + merchant_id: merchant_id.to_string(), + address_id: generate_id(consts::ID_LENGTH, "add"), + created_at: common_utils::date_time::now(), + modified_at: common_utils::date_time::now(), + updated_by: storage_scheme.to_string(), + email: encryptable_address.email, + }; - Ok(domain::CustomerAddress { - address, - customer_id: customer_id.to_owned(), - }) - } - .await + Ok(domain::CustomerAddress { + address, + customer_id: customer_id.to_owned(), + }) } } @@ -911,7 +893,7 @@ pub async fn trigger_payments_webhook( key_store: &domain::MerchantKeyStore, payment_data: crate::core::payments::PaymentData, customer: Option, - state: &crate::routes::SessionState, + state: &SessionState, operation: Op, ) -> RouterResult<()> where diff --git a/crates/router/src/utils/connector_onboarding.rs b/crates/router/src/utils/connector_onboarding.rs index 15ad2eb895..0bc8e58fcc 100644 --- a/crates/router/src/utils/connector_onboarding.rs +++ b/crates/router/src/utils/connector_onboarding.rs @@ -45,9 +45,11 @@ pub async fn check_if_connector_exists( connector_id: &str, merchant_id: &str, ) -> RouterResult<()> { + let key_manager_state = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -57,6 +59,7 @@ pub async fn check_if_connector_exists( let _connector = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, merchant_id, connector_id, &key_store, diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index 3cd35886ff..9734122a26 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -2,8 +2,8 @@ use std::{collections::HashMap, sync::Arc}; use api_models::user as user_api; use common_enums::UserAuthType; -use common_utils::errors::CustomResult; -use diesel_models::{encryption::Encryption, enums::UserStatus, user_role::UserRole}; +use common_utils::{encryption::Encryption, errors::CustomResult, types::keymanager::Identifier}; +use diesel_models::{enums::UserStatus, user_role::UserRole}; use error_stack::ResultExt; use masking::{ExposeInterface, Secret}; use redis_interface::RedisConnectionPool; @@ -33,9 +33,11 @@ impl UserFromToken { &self, state: SessionState, ) -> UserResult { + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &self.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -49,7 +51,7 @@ impl UserFromToken { })?; let merchant_account = state .store - .find_merchant_account_by_merchant_id(&self.merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &self.merchant_id, &key_store) .await .map_err(|e| { if e.current_context().is_db_not_found() { @@ -218,8 +220,10 @@ impl ForeignFrom<&user_api::AuthConfig> for UserAuthType { } pub async fn construct_public_and_private_db_configs( + state: &SessionState, auth_config: &user_api::AuthConfig, encryption_key: &[u8], + id: String, ) -> UserResult<(Option, Option)> { match auth_config { user_api::AuthConfig::OpenIdConnect { @@ -231,7 +235,9 @@ pub async fn construct_public_and_private_db_configs( .attach_printable("Failed to convert auth config to json")?; let encrypted_config = domain::types::encrypt::( + &state.into(), private_config_value.into(), + Identifier::UserAuth(id), encryption_key, ) .await @@ -263,6 +269,7 @@ where pub async fn decrypt_oidc_private_config( state: &SessionState, encrypted_config: Option, + id: String, ) -> UserResult { let user_auth_key = hex::decode( state @@ -277,7 +284,9 @@ pub async fn decrypt_oidc_private_config( .attach_printable("Failed to decode DEK")?; let private_config = domain::types::decrypt::( + &state.into(), encrypted_config, + Identifier::UserAuth(id), &user_auth_key, ) .await diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index b817d3e1cd..0909f4ffbe 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -23,7 +23,7 @@ pub async fn generate_sample_data( ) -> SampleDataResult)>> { let merchant_id = merchant_id.to_string(); let sample_data_size: usize = req.record.unwrap_or(100); - + let key_manager_state = &state.into(); if !(10..=100).contains(&sample_data_size) { return Err(SampleDataError::InvalidRange.into()); } @@ -31,6 +31,7 @@ pub async fn generate_sample_data( let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id.as_str(), &state.store.get_master_key().to_vec().into(), ) @@ -39,7 +40,7 @@ pub async fn generate_sample_data( let merchant_from_db = state .store - .find_merchant_account_by_merchant_id(merchant_id.as_str(), &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id.as_str(), &key_store) .await .change_context::(SampleDataError::DataDoesNotExist)?; diff --git a/crates/router/src/workflows/api_key_expiry.rs b/crates/router/src/workflows/api_key_expiry.rs index dc39de8f93..e49a5f86c3 100644 --- a/crates/router/src/workflows/api_key_expiry.rs +++ b/crates/router/src/workflows/api_key_expiry.rs @@ -28,17 +28,22 @@ impl ProcessTrackerWorkflow for ApiKeyExpiryWorkflow { .tracking_data .clone() .parse_value("ApiKeyExpiryTrackingData")?; - + let key_manager_satte = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_satte, tracking_data.merchant_id.as_str(), &state.store.get_master_key().to_vec().into(), ) .await?; let merchant_account = db - .find_merchant_account_by_merchant_id(tracking_data.merchant_id.as_str(), &key_store) + .find_merchant_account_by_merchant_id( + key_manager_satte, + tracking_data.merchant_id.as_str(), + &key_store, + ) .await?; let email_id = merchant_account diff --git a/crates/router/src/workflows/attach_payout_account_workflow.rs b/crates/router/src/workflows/attach_payout_account_workflow.rs index eb60510ad7..7ff0faefa6 100644 --- a/crates/router/src/workflows/attach_payout_account_workflow.rs +++ b/crates/router/src/workflows/attach_payout_account_workflow.rs @@ -31,16 +31,17 @@ impl ProcessTrackerWorkflow for AttachPayoutAccountWorkflow { .merchant_id .clone() .get_required_value("merchant_id")?; - + let key_manager_state = &state.into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id.as_ref(), &db.get_master_key().to_vec().into(), ) .await?; let merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await?; let request = api::payouts::PayoutRequest::PayoutRetrieveRequest(tracking_data); diff --git a/crates/router/src/workflows/outgoing_webhook_retry.rs b/crates/router/src/workflows/outgoing_webhook_retry.rs index a8d5c28b64..d181aedd18 100644 --- a/crates/router/src/workflows/outgoing_webhook_retry.rs +++ b/crates/router/src/workflows/outgoing_webhook_retry.rs @@ -43,8 +43,10 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { .parse_value("OutgoingWebhookTrackingData")?; let db = &*state.store; + let key_manager_state = &state.into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, &tracking_data.merchant_id, &db.get_master_key().to_vec().into(), ) @@ -63,6 +65,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { let initial_event = match &tracking_data.initial_attempt_id { Some(initial_attempt_id) => { db.find_event_by_merchant_id_event_id( + key_manager_state, &business_profile.merchant_id, initial_attempt_id, &key_store, @@ -77,6 +80,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { tracking_data.primary_object_id, tracking_data.event_type ); db.find_event_by_merchant_id_event_id( + key_manager_state, &business_profile.merchant_id, &old_event_id, &key_store, @@ -106,7 +110,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { }; let event = db - .insert_event(new_event, &key_store) + .insert_event(key_manager_state, new_event, &key_store) .await .map_err(|error| { logger::error!(?error, "Failed to insert event in events table"); @@ -137,7 +141,11 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { // resource None => { let merchant_account = db - .find_merchant_account_by_merchant_id(&tracking_data.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &tracking_data.merchant_id, + &key_store, + ) .await?; // TODO: Add request state for the PT flows as well @@ -162,6 +170,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { }; let request_content = webhooks_core::get_outgoing_webhook_request( + state, &merchant_account, outgoing_webhook, &business_profile, diff --git a/crates/router/src/workflows/payment_method_status_update.rs b/crates/router/src/workflows/payment_method_status_update.rs index b8e57360d9..fa0631cc5c 100644 --- a/crates/router/src/workflows/payment_method_status_update.rs +++ b/crates/router/src/workflows/payment_method_status_update.rs @@ -30,17 +30,18 @@ impl ProcessTrackerWorkflow for PaymentMethodStatusUpdateWorkflow let prev_pm_status = tracking_data.prev_status; let curr_pm_status = tracking_data.curr_status; let merchant_id = tracking_data.merchant_id; - + let key_manager_state = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id.as_str(), &state.store.get_master_key().to_vec().into(), ) .await?; let merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await?; let payment_method = db diff --git a/crates/router/src/workflows/payment_sync.rs b/crates/router/src/workflows/payment_sync.rs index ef0fe07e9b..2bc7b183ec 100644 --- a/crates/router/src/workflows/payment_sync.rs +++ b/crates/router/src/workflows/payment_sync.rs @@ -38,9 +38,10 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { .tracking_data .clone() .parse_value("PaymentsRetrieveRequest")?; - + let key_manager_state = &state.into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, tracking_data .merchant_id .as_ref() @@ -51,6 +52,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { let merchant_account = db .find_merchant_account_by_merchant_id( + key_manager_state, tracking_data .merchant_id .as_ref() @@ -148,6 +150,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { payment_data.payment_intent = db .update_payment_intent( + &state.into(), payment_data.payment_intent, payment_intent_update, &key_store, diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index ed11f3b8b1..997ae51cf3 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -288,10 +288,11 @@ async fn payments_create_core() { let state = Arc::new(app_state) .get_session_state("public", || {}) .unwrap(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, "juspay_merchant", &state.store.get_master_key().to_vec().into(), ) @@ -300,7 +301,7 @@ async fn payments_create_core() { let merchant_account = state .store - .find_merchant_account_by_merchant_id("juspay_merchant", &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, "juspay_merchant", &key_store) .await .unwrap(); @@ -476,10 +477,11 @@ async fn payments_create_core_adyen_no_redirect() { let customer_id = format!("cust_{}", Uuid::new_v4()); let merchant_id = "arunraj".to_string(); let payment_id = "pay_mbabizu24mvu3mela5njyhpit10".to_string(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, "juspay_merchant", &state.store.get_master_key().to_vec().into(), ) @@ -488,7 +490,7 @@ async fn payments_create_core_adyen_no_redirect() { let merchant_account = state .store - .find_merchant_account_by_merchant_id("juspay_merchant", &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, "juspay_merchant", &key_store) .await .unwrap(); diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index 4c178e4122..fcf106a9ef 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -48,10 +48,11 @@ async fn payments_create_core() { let state = Arc::new(app_state) .get_session_state("public", || {}) .unwrap(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, "juspay_merchant", &state.store.get_master_key().to_vec().into(), ) @@ -60,7 +61,7 @@ async fn payments_create_core() { let merchant_account = state .store - .find_merchant_account_by_merchant_id("juspay_merchant", &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, "juspay_merchant", &key_store) .await .unwrap(); @@ -243,10 +244,11 @@ async fn payments_create_core_adyen_no_redirect() { let customer_id = format!("cust_{}", Uuid::new_v4()); let merchant_id = "arunraj".to_string(); let payment_id = "pay_mbabizu24mvu3mela5njyhpit10".to_string(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, "juspay_merchant", &state.store.get_master_key().to_vec().into(), ) @@ -255,7 +257,7 @@ async fn payments_create_core_adyen_no_redirect() { let merchant_account = state .store - .find_merchant_account_by_merchant_id("juspay_merchant", &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, "juspay_merchant", &key_store) .await .unwrap(); diff --git a/crates/storage_impl/src/mock_db/payment_intent.rs b/crates/storage_impl/src/mock_db/payment_intent.rs index bf3ce0baaa..3e183b94c2 100644 --- a/crates/storage_impl/src/mock_db/payment_intent.rs +++ b/crates/storage_impl/src/mock_db/payment_intent.rs @@ -1,4 +1,4 @@ -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, types::keymanager::KeyManagerState}; use diesel_models::enums as storage_enums; use error_stack::ResultExt; use hyperswitch_domain_models::{ @@ -19,6 +19,7 @@ impl PaymentIntentInterface for MockDb { #[cfg(feature = "olap")] async fn filter_payment_intent_by_constraints( &self, + _state: &KeyManagerState, _merchant_id: &str, _filters: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, _key_store: &MerchantKeyStore, @@ -30,6 +31,7 @@ impl PaymentIntentInterface for MockDb { #[cfg(feature = "olap")] async fn filter_payment_intents_by_time_range_constraints( &self, + _state: &KeyManagerState, _merchant_id: &str, _time_range: &api_models::payments::TimeRange, _key_store: &MerchantKeyStore, @@ -51,6 +53,7 @@ impl PaymentIntentInterface for MockDb { #[cfg(feature = "olap")] async fn get_filtered_payment_intents_attempt( &self, + _state: &KeyManagerState, _merchant_id: &str, _constraints: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, _key_store: &MerchantKeyStore, @@ -63,6 +66,7 @@ impl PaymentIntentInterface for MockDb { #[allow(clippy::panic)] async fn insert_payment_intent( &self, + _state: &KeyManagerState, new: PaymentIntent, _key_store: &MerchantKeyStore, _storage_scheme: storage_enums::MerchantStorageScheme, @@ -76,6 +80,7 @@ impl PaymentIntentInterface for MockDb { #[allow(clippy::unwrap_used)] async fn update_payment_intent( &self, + state: &KeyManagerState, this: PaymentIntent, update: PaymentIntentUpdate, key_store: &MerchantKeyStore, @@ -95,8 +100,10 @@ impl PaymentIntentInterface for MockDb { .change_context(StorageError::EncryptionError)?; *payment_intent = PaymentIntent::convert_back( + state, diesel_payment_intent_update.apply_changeset(diesel_payment_intent), key_store.key.get_inner(), + key_store.merchant_id.clone(), ) .await .change_context(StorageError::DecryptionError)?; @@ -108,6 +115,7 @@ impl PaymentIntentInterface for MockDb { #[allow(clippy::unwrap_used)] async fn find_payment_intent_by_payment_id_merchant_id( &self, + _state: &KeyManagerState, payment_id: &str, merchant_id: &str, _key_store: &MerchantKeyStore, diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index 62854cb261..ced94b5717 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -4,7 +4,10 @@ use api_models::payments::AmountFilter; use async_bb8_diesel::{AsyncConnection, AsyncRunQueryDsl}; #[cfg(feature = "olap")] use common_utils::errors::ReportSwitchExt; -use common_utils::ext_traits::{AsyncExt, Encode}; +use common_utils::{ + ext_traits::{AsyncExt, Encode}, + types::keymanager::KeyManagerState, +}; #[cfg(feature = "olap")] use diesel::{associations::HasTable, ExpressionMethods, JoinOnDsl, QueryDsl}; use diesel_models::{ @@ -53,6 +56,7 @@ use crate::{ impl PaymentIntentInterface for KVRouterStore { async fn insert_payment_intent( &self, + state: &KeyManagerState, payment_intent: PaymentIntent, merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -69,7 +73,12 @@ impl PaymentIntentInterface for KVRouterStore { match storage_scheme { MerchantStorageScheme::PostgresOnly => { self.router_store - .insert_payment_intent(payment_intent, merchant_key_store, storage_scheme) + .insert_payment_intent( + state, + payment_intent, + merchant_key_store, + storage_scheme, + ) .await } @@ -121,6 +130,7 @@ impl PaymentIntentInterface for KVRouterStore { #[instrument(skip_all)] async fn update_payment_intent( &self, + state: &KeyManagerState, this: PaymentIntent, payment_intent_update: PaymentIntentUpdate, merchant_key_store: &MerchantKeyStore, @@ -143,6 +153,7 @@ impl PaymentIntentInterface for KVRouterStore { MerchantStorageScheme::PostgresOnly => { self.router_store .update_payment_intent( + state, this, payment_intent_update, merchant_key_store, @@ -189,10 +200,14 @@ impl PaymentIntentInterface for KVRouterStore { .try_into_hset() .change_context(StorageError::KVError)?; - let payment_intent = - PaymentIntent::convert_back(diesel_intent, merchant_key_store.key.get_inner()) - .await - .change_context(StorageError::DecryptionError)?; + let payment_intent = PaymentIntent::convert_back( + state, + diesel_intent, + merchant_key_store.key.get_inner(), + merchant_id, + ) + .await + .change_context(StorageError::DecryptionError)?; Ok(payment_intent) } @@ -202,6 +217,7 @@ impl PaymentIntentInterface for KVRouterStore { #[instrument(skip_all)] async fn find_payment_intent_by_payment_id_merchant_id( &self, + state: &KeyManagerState, payment_id: &str, merchant_id: &str, merchant_key_store: &MerchantKeyStore, @@ -243,9 +259,14 @@ impl PaymentIntentInterface for KVRouterStore { } }?; - PaymentIntent::convert_back(diesel_payment_intent, merchant_key_store.key.get_inner()) - .await - .change_context(StorageError::DecryptionError) + PaymentIntent::convert_back( + state, + diesel_payment_intent, + merchant_key_store.key.get_inner(), + merchant_id.to_string(), + ) + .await + .change_context(StorageError::DecryptionError) } async fn get_active_payment_attempt( @@ -278,6 +299,7 @@ impl PaymentIntentInterface for KVRouterStore { #[cfg(feature = "olap")] async fn filter_payment_intent_by_constraints( &self, + state: &KeyManagerState, merchant_id: &str, filters: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -285,6 +307,7 @@ impl PaymentIntentInterface for KVRouterStore { ) -> error_stack::Result, StorageError> { self.router_store .filter_payment_intent_by_constraints( + state, merchant_id, filters, merchant_key_store, @@ -296,6 +319,7 @@ impl PaymentIntentInterface for KVRouterStore { #[cfg(feature = "olap")] async fn filter_payment_intents_by_time_range_constraints( &self, + state: &KeyManagerState, merchant_id: &str, time_range: &api_models::payments::TimeRange, merchant_key_store: &MerchantKeyStore, @@ -303,6 +327,7 @@ impl PaymentIntentInterface for KVRouterStore { ) -> error_stack::Result, StorageError> { self.router_store .filter_payment_intents_by_time_range_constraints( + state, merchant_id, time_range, merchant_key_store, @@ -314,6 +339,7 @@ impl PaymentIntentInterface for KVRouterStore { #[cfg(feature = "olap")] async fn get_filtered_payment_intents_attempt( &self, + state: &KeyManagerState, merchant_id: &str, filters: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -321,6 +347,7 @@ impl PaymentIntentInterface for KVRouterStore { ) -> error_stack::Result, StorageError> { self.router_store .get_filtered_payment_intents_attempt( + state, merchant_id, filters, merchant_key_store, @@ -351,6 +378,7 @@ impl PaymentIntentInterface for crate::RouterStore { #[instrument(skip_all)] async fn insert_payment_intent( &self, + state: &KeyManagerState, payment_intent: PaymentIntent, merchant_key_store: &MerchantKeyStore, _storage_scheme: MerchantStorageScheme, @@ -367,14 +395,20 @@ impl PaymentIntentInterface for crate::RouterStore { er.change_context(new_err) })?; - PaymentIntent::convert_back(diesel_payment_intent, merchant_key_store.key.get_inner()) - .await - .change_context(StorageError::DecryptionError) + PaymentIntent::convert_back( + state, + diesel_payment_intent, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) + .await + .change_context(StorageError::DecryptionError) } #[instrument(skip_all)] async fn update_payment_intent( &self, + state: &KeyManagerState, this: PaymentIntent, payment_intent: PaymentIntentUpdate, merchant_key_store: &MerchantKeyStore, @@ -394,14 +428,20 @@ impl PaymentIntentInterface for crate::RouterStore { er.change_context(new_err) })?; - PaymentIntent::convert_back(diesel_payment_intent, merchant_key_store.key.get_inner()) - .await - .change_context(StorageError::DecryptionError) + PaymentIntent::convert_back( + state, + diesel_payment_intent, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) + .await + .change_context(StorageError::DecryptionError) } #[instrument(skip_all)] async fn find_payment_intent_by_payment_id_merchant_id( &self, + state: &KeyManagerState, payment_id: &str, merchant_id: &str, merchant_key_store: &MerchantKeyStore, @@ -417,8 +457,10 @@ impl PaymentIntentInterface for crate::RouterStore { }) .async_and_then(|diesel_payment_intent| async { PaymentIntent::convert_back( + state, diesel_payment_intent, merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), ) .await .change_context(StorageError::DecryptionError) @@ -458,6 +500,7 @@ impl PaymentIntentInterface for crate::RouterStore { #[instrument(skip_all)] async fn filter_payment_intent_by_constraints( &self, + state: &KeyManagerState, merchant_id: &str, filters: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -498,6 +541,7 @@ impl PaymentIntentInterface for crate::RouterStore { // TODO: Fetch partial columns for this query since we only need some columns let starting_at = self .find_payment_intent_by_payment_id_merchant_id( + state, starting_after_id, merchant_id, merchant_key_store, @@ -516,6 +560,7 @@ impl PaymentIntentInterface for crate::RouterStore { // TODO: Fetch partial columns for this query since we only need some columns let ending_at = self .find_payment_intent_by_payment_id_merchant_id( + state, ending_before_id, merchant_id, merchant_key_store, @@ -560,8 +605,10 @@ impl PaymentIntentInterface for crate::RouterStore { .map(|payment_intents| { try_join_all(payment_intents.into_iter().map(|diesel_payment_intent| { PaymentIntent::convert_back( + state, diesel_payment_intent, merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), ) })) .map(|join_result| join_result.change_context(StorageError::DecryptionError)) @@ -579,6 +626,7 @@ impl PaymentIntentInterface for crate::RouterStore { #[instrument(skip_all)] async fn filter_payment_intents_by_time_range_constraints( &self, + state: &KeyManagerState, merchant_id: &str, time_range: &api_models::payments::TimeRange, merchant_key_store: &MerchantKeyStore, @@ -587,6 +635,7 @@ impl PaymentIntentInterface for crate::RouterStore { // TODO: Remove this redundant function let payment_filters = (*time_range).into(); self.filter_payment_intent_by_constraints( + state, merchant_id, &payment_filters, merchant_key_store, @@ -599,6 +648,7 @@ impl PaymentIntentInterface for crate::RouterStore { #[instrument(skip_all)] async fn get_filtered_payment_intents_attempt( &self, + state: &KeyManagerState, merchant_id: &str, constraints: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -640,6 +690,7 @@ impl PaymentIntentInterface for crate::RouterStore { // TODO: Fetch partial columns for this query since we only need some columns let starting_at = self .find_payment_intent_by_payment_id_merchant_id( + state, starting_after_id, merchant_id, merchant_key_store, @@ -658,6 +709,7 @@ impl PaymentIntentInterface for crate::RouterStore { // TODO: Fetch partial columns for this query since we only need some columns let ending_at = self .find_payment_intent_by_payment_id_merchant_id( + state, ending_before_id, merchant_id, merchant_key_store, @@ -745,13 +797,17 @@ impl PaymentIntentInterface for crate::RouterStore { .await .map(|results| { try_join_all(results.into_iter().map(|(pi, pa)| { - PaymentIntent::convert_back(pi, merchant_key_store.key.get_inner()).map( - |payment_intent| { - payment_intent.map(|payment_intent| { - (payment_intent, PaymentAttempt::from_storage_model(pa)) - }) - }, + PaymentIntent::convert_back( + state, + pi, + merchant_key_store.key.get_inner(), + merchant_id.to_string(), ) + .map(|payment_intent| { + payment_intent.map(|payment_intent| { + (payment_intent, PaymentAttempt::from_storage_model(pa)) + }) + }) })) .map(|join_result| join_result.change_context(StorageError::DecryptionError)) })