feat: add macro to generate ToEncryptable trait (#6313)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
Co-authored-by: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com>
This commit is contained in:
Kartikeya Hegde
2024-11-04 11:24:13 +05:30
committed by GitHub
parent adc5262f13
commit 19cf0f7437
19 changed files with 949 additions and 738 deletions

View File

@ -15,7 +15,7 @@ use diesel_models::configs;
use diesel_models::organization::OrganizationBridge;
use error_stack::{report, FutureExt, ResultExt};
use hyperswitch_domain_models::merchant_connector_account::{
McaFromRequest, McaFromRequestfromUpdate,
FromRequestEncryptableMerchantConnectorAccount, UpdateEncryptableMerchantConnectorAccount,
};
use masking::{ExposeInterface, PeekInterface, Secret};
use pm_auth::{connector::plaid::transformers::PlaidAuthType, types as pm_auth_types};
@ -2095,18 +2095,20 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect
let encrypted_data = domain_types::crypto_operation(
key_manager_state,
type_name!(domain::MerchantConnectorAccount),
domain_types::CryptoOperation::BatchEncrypt(McaFromRequestfromUpdate::to_encryptable(
McaFromRequestfromUpdate {
connector_account_details: self.connector_account_details,
connector_wallets_details:
helpers::get_connector_wallets_details_with_apple_pay_certificates(
&self.metadata,
&self.connector_wallets_details,
)
.await?,
additional_merchant_data: merchant_recipient_data.map(Secret::new),
},
)),
domain_types::CryptoOperation::BatchEncrypt(
UpdateEncryptableMerchantConnectorAccount::to_encryptable(
UpdateEncryptableMerchantConnectorAccount {
connector_account_details: self.connector_account_details,
connector_wallets_details:
helpers::get_connector_wallets_details_with_apple_pay_certificates(
&self.metadata,
&self.connector_wallets_details,
)
.await?,
additional_merchant_data: merchant_recipient_data.map(Secret::new),
},
),
),
km_types::Identifier::Merchant(key_store.merchant_id.clone()),
key_store.key.peek(),
)
@ -2115,9 +2117,10 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details".to_string())?;
let encrypted_data = McaFromRequestfromUpdate::from_encryptable(encrypted_data)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details")?;
let encrypted_data =
UpdateEncryptableMerchantConnectorAccount::from_encryptable(encrypted_data)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details")?;
Ok(storage::MerchantConnectorAccountUpdate::Update {
connector_type: Some(self.connector_type),
@ -2262,18 +2265,20 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect
let encrypted_data = domain_types::crypto_operation(
key_manager_state,
type_name!(domain::MerchantConnectorAccount),
domain_types::CryptoOperation::BatchEncrypt(McaFromRequestfromUpdate::to_encryptable(
McaFromRequestfromUpdate {
connector_account_details: self.connector_account_details,
connector_wallets_details:
helpers::get_connector_wallets_details_with_apple_pay_certificates(
&self.metadata,
&self.connector_wallets_details,
)
.await?,
additional_merchant_data: merchant_recipient_data.map(Secret::new),
},
)),
domain_types::CryptoOperation::BatchEncrypt(
UpdateEncryptableMerchantConnectorAccount::to_encryptable(
UpdateEncryptableMerchantConnectorAccount {
connector_account_details: self.connector_account_details,
connector_wallets_details:
helpers::get_connector_wallets_details_with_apple_pay_certificates(
&self.metadata,
&self.connector_wallets_details,
)
.await?,
additional_merchant_data: merchant_recipient_data.map(Secret::new),
},
),
),
km_types::Identifier::Merchant(key_store.merchant_id.clone()),
key_store.key.peek(),
)
@ -2282,9 +2287,10 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details".to_string())?;
let encrypted_data = McaFromRequestfromUpdate::from_encryptable(encrypted_data)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details")?;
let encrypted_data =
UpdateEncryptableMerchantConnectorAccount::from_encryptable(encrypted_data)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details")?;
Ok(storage::MerchantConnectorAccountUpdate::Update {
connector_type: Some(self.connector_type),
@ -2404,22 +2410,24 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate {
let encrypted_data = domain_types::crypto_operation(
key_manager_state,
type_name!(domain::MerchantConnectorAccount),
domain_types::CryptoOperation::BatchEncrypt(McaFromRequest::to_encryptable(
McaFromRequest {
connector_account_details: self.connector_account_details.ok_or(
errors::ApiErrorResponse::MissingRequiredField {
field_name: "connector_account_details",
},
)?,
connector_wallets_details:
helpers::get_connector_wallets_details_with_apple_pay_certificates(
&self.metadata,
&self.connector_wallets_details,
)
.await?,
additional_merchant_data: merchant_recipient_data.map(Secret::new),
},
)),
domain_types::CryptoOperation::BatchEncrypt(
FromRequestEncryptableMerchantConnectorAccount::to_encryptable(
FromRequestEncryptableMerchantConnectorAccount {
connector_account_details: self.connector_account_details.ok_or(
errors::ApiErrorResponse::MissingRequiredField {
field_name: "connector_account_details",
},
)?,
connector_wallets_details:
helpers::get_connector_wallets_details_with_apple_pay_certificates(
&self.metadata,
&self.connector_wallets_details,
)
.await?,
additional_merchant_data: merchant_recipient_data.map(Secret::new),
},
),
),
identifier.clone(),
key_store.key.peek(),
)
@ -2428,9 +2436,10 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate {
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details".to_string())?;
let encrypted_data = McaFromRequest::from_encryptable(encrypted_data)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details")?;
let encrypted_data =
FromRequestEncryptableMerchantConnectorAccount::from_encryptable(encrypted_data)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details")?;
Ok(domain::MerchantConnectorAccount {
merchant_id: business_profile.merchant_id.clone(),
@ -2573,22 +2582,24 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate {
let encrypted_data = domain_types::crypto_operation(
key_manager_state,
type_name!(domain::MerchantConnectorAccount),
domain_types::CryptoOperation::BatchEncrypt(McaFromRequest::to_encryptable(
McaFromRequest {
connector_account_details: self.connector_account_details.ok_or(
errors::ApiErrorResponse::MissingRequiredField {
field_name: "connector_account_details",
},
)?,
connector_wallets_details:
helpers::get_connector_wallets_details_with_apple_pay_certificates(
&self.metadata,
&self.connector_wallets_details,
)
.await?,
additional_merchant_data: merchant_recipient_data.map(Secret::new),
},
)),
domain_types::CryptoOperation::BatchEncrypt(
FromRequestEncryptableMerchantConnectorAccount::to_encryptable(
FromRequestEncryptableMerchantConnectorAccount {
connector_account_details: self.connector_account_details.ok_or(
errors::ApiErrorResponse::MissingRequiredField {
field_name: "connector_account_details",
},
)?,
connector_wallets_details:
helpers::get_connector_wallets_details_with_apple_pay_certificates(
&self.metadata,
&self.connector_wallets_details,
)
.await?,
additional_merchant_data: merchant_recipient_data.map(Secret::new),
},
),
),
identifier.clone(),
key_store.key.peek(),
)
@ -2597,9 +2608,10 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate {
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details".to_string())?;
let encrypted_data = McaFromRequest::from_encryptable(encrypted_data)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details")?;
let encrypted_data =
FromRequestEncryptableMerchantConnectorAccount::from_encryptable(encrypted_data)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while decrypting connector account details")?;
Ok(domain::MerchantConnectorAccount {
merchant_id: business_profile.merchant_id.clone(),

View File

@ -1,16 +1,15 @@
use api_models::customers::CustomerRequestWithEmail;
use common_utils::{
crypto::Encryptable,
errors::ReportSwitchExt,
ext_traits::{AsyncExt, OptionExt},
id_type, type_name,
id_type, pii, type_name,
types::{
keymanager::{Identifier, KeyManagerState, ToEncryptable},
Description,
},
};
use error_stack::{report, ResultExt};
use masking::{Secret, SwitchStrategy};
use masking::{ExposeInterface, Secret, SwitchStrategy};
use router_env::{instrument, tracing};
#[cfg(all(feature = "v2", feature = "customer_v2"))]
@ -145,13 +144,15 @@ impl CustomerCreateBridge for customers::CustomerRequest {
let encrypted_data = types::crypto_operation(
key_manager_state,
type_name!(domain::Customer),
types::CryptoOperation::BatchEncrypt(CustomerRequestWithEmail::to_encryptable(
CustomerRequestWithEmail {
name: self.name.clone(),
email: self.email.clone(),
phone: self.phone.clone(),
},
)),
types::CryptoOperation::BatchEncrypt(
domain::FromRequestEncryptableCustomer::to_encryptable(
domain::FromRequestEncryptableCustomer {
name: self.name.clone(),
email: self.email.clone().map(|a| a.expose().switch_strategy()),
phone: self.phone.clone(),
},
),
),
Identifier::Merchant(key_store.merchant_id.clone()),
key,
)
@ -160,8 +161,9 @@ impl CustomerCreateBridge for customers::CustomerRequest {
.switch()
.attach_printable("Failed while encrypting Customer")?;
let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data)
.change_context(errors::CustomersErrorResponse::InternalServerError)?;
let encryptable_customer =
domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
.change_context(errors::CustomersErrorResponse::InternalServerError)?;
Ok(domain::Customer {
customer_id: merchant_reference_id
@ -169,7 +171,13 @@ impl CustomerCreateBridge for customers::CustomerRequest {
.ok_or(errors::CustomersErrorResponse::InternalServerError)?,
merchant_id: merchant_id.to_owned(),
name: encryptable_customer.name,
email: encryptable_customer.email,
email: encryptable_customer.email.map(|email| {
let encryptable: Encryptable<Secret<String, pii::EmailStrategy>> = Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
phone: encryptable_customer.phone,
description: self.description.clone(),
phone_country_code: self.phone_country_code.clone(),
@ -234,13 +242,15 @@ impl CustomerCreateBridge for customers::CustomerRequest {
let encrypted_data = types::crypto_operation(
key_state,
type_name!(domain::Customer),
types::CryptoOperation::BatchEncrypt(CustomerRequestWithEmail::to_encryptable(
CustomerRequestWithEmail {
name: Some(self.name.clone()),
email: Some(self.email.clone()),
phone: self.phone.clone(),
},
)),
types::CryptoOperation::BatchEncrypt(
domain::FromRequestEncryptableCustomer::to_encryptable(
domain::FromRequestEncryptableCustomer {
name: Some(self.name.clone()),
email: Some(self.email.clone().expose().switch_strategy()),
phone: self.phone.clone(),
},
),
),
Identifier::Merchant(key_store.merchant_id.clone()),
key,
)
@ -249,15 +259,22 @@ impl CustomerCreateBridge for customers::CustomerRequest {
.switch()
.attach_printable("Failed while encrypting Customer")?;
let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data)
.change_context(errors::CustomersErrorResponse::InternalServerError)?;
let encryptable_customer =
domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
.change_context(errors::CustomersErrorResponse::InternalServerError)?;
Ok(domain::Customer {
id: common_utils::generate_time_ordered_id("cus"),
merchant_reference_id: merchant_reference_id.to_owned(),
merchant_id,
name: encryptable_customer.name,
email: encryptable_customer.email,
email: encryptable_customer.email.map(|email| {
let encryptable: Encryptable<Secret<String, pii::EmailStrategy>> = Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
phone: encryptable_customer.phone,
description: self.description.clone(),
phone_country_code: self.phone_country_code.clone(),
@ -1174,13 +1191,18 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
let encrypted_data = types::crypto_operation(
key_manager_state,
type_name!(domain::Customer),
types::CryptoOperation::BatchEncrypt(CustomerRequestWithEmail::to_encryptable(
CustomerRequestWithEmail {
name: self.name.clone(),
email: self.email.clone(),
phone: self.phone.clone(),
},
)),
types::CryptoOperation::BatchEncrypt(
domain::FromRequestEncryptableCustomer::to_encryptable(
domain::FromRequestEncryptableCustomer {
name: self.name.clone(),
email: self
.email
.as_ref()
.map(|a| a.clone().expose().switch_strategy()),
phone: self.phone.clone(),
},
),
),
Identifier::Merchant(key_store.merchant_id.clone()),
key,
)
@ -1188,8 +1210,9 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
.and_then(|val| val.try_into_batchoperation())
.switch()?;
let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data)
.change_context(errors::CustomersErrorResponse::InternalServerError)?;
let encryptable_customer =
domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
.change_context(errors::CustomersErrorResponse::InternalServerError)?;
let response = db
.update_customer_by_customer_id_merchant_id(
@ -1199,7 +1222,14 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
domain_customer.to_owned(),
storage::CustomerUpdate::Update {
name: encryptable_customer.name,
email: encryptable_customer.email,
email: encryptable_customer.email.map(|email| {
let encryptable: Encryptable<Secret<String, pii::EmailStrategy>> =
Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
phone: Box::new(encryptable_customer.phone),
phone_country_code: self.phone_country_code.clone(),
metadata: self.metadata.clone(),
@ -1266,13 +1296,18 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
let encrypted_data = types::crypto_operation(
key_manager_state,
type_name!(domain::Customer),
types::CryptoOperation::BatchEncrypt(CustomerRequestWithEmail::to_encryptable(
CustomerRequestWithEmail {
name: self.name.clone(),
email: self.email.clone(),
phone: self.phone.clone(),
},
)),
types::CryptoOperation::BatchEncrypt(
domain::FromRequestEncryptableCustomer::to_encryptable(
domain::FromRequestEncryptableCustomer {
name: self.name.clone(),
email: self
.email
.as_ref()
.map(|a| a.clone().expose().switch_strategy()),
phone: self.phone.clone(),
},
),
),
Identifier::Merchant(key_store.merchant_id.clone()),
key,
)
@ -1280,8 +1315,9 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
.and_then(|val| val.try_into_batchoperation())
.switch()?;
let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data)
.change_context(errors::CustomersErrorResponse::InternalServerError)?;
let encryptable_customer =
domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
.change_context(errors::CustomersErrorResponse::InternalServerError)?;
let response = db
.update_customer_by_global_id(
@ -1291,7 +1327,14 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
merchant_account.get_id(),
storage::CustomerUpdate::Update {
name: encryptable_customer.name,
email: Box::new(encryptable_customer.email),
email: Box::new(encryptable_customer.email.map(|email| {
let encryptable: Encryptable<Secret<String, pii::EmailStrategy>> =
Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
})),
phone: Box::new(encryptable_customer.phone),
phone_country_code: self.phone_country_code.clone(),
metadata: self.metadata.clone(),

View File

@ -1,12 +1,9 @@
use std::{borrow::Cow, str::FromStr};
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
use api_models::customers::CustomerRequestWithEmail;
use api_models::{
mandates::RecurringDetails,
payments::{
additional_info as payment_additional_types, AddressDetailsWithPhone, PaymentChargeRequest,
RequestSurchargeDetails,
additional_info as payment_additional_types, PaymentChargeRequest, RequestSurchargeDetails,
},
};
use base64::Engine;
@ -39,7 +36,7 @@ use hyperswitch_domain_models::{
};
use hyperswitch_interfaces::integrity::{CheckIntegrity, FlowIntegrity, GetIntegrityObject};
use josekit::jwe;
use masking::{ExposeInterface, PeekInterface};
use masking::{ExposeInterface, PeekInterface, SwitchStrategy};
use openssl::{
derive::Deriver,
pkey::PKey,
@ -126,16 +123,33 @@ pub async fn create_or_update_address_for_payment_by_request(
let encrypted_data = types::crypto_operation(
&session_state.into(),
type_name!(domain::Address),
types::CryptoOperation::BatchEncrypt(AddressDetailsWithPhone::to_encryptable(
AddressDetailsWithPhone {
address: address.address.clone(),
phone_number: address
.phone
.as_ref()
.and_then(|phone| phone.number.clone()),
email: address.email.clone(),
},
)),
types::CryptoOperation::BatchEncrypt(
domain::FromRequestEncryptableAddress::to_encryptable(
domain::FromRequestEncryptableAddress {
line1: address.address.as_ref().and_then(|a| a.line1.clone()),
line2: address.address.as_ref().and_then(|a| a.line2.clone()),
line3: address.address.as_ref().and_then(|a| a.line3.clone()),
state: address.address.as_ref().and_then(|a| a.state.clone()),
first_name: address
.address
.as_ref()
.and_then(|a| a.first_name.clone()),
last_name: address
.address
.as_ref()
.and_then(|a| a.last_name.clone()),
zip: address.address.as_ref().and_then(|a| a.zip.clone()),
phone_number: address
.phone
.as_ref()
.and_then(|phone| phone.number.clone()),
email: address
.email
.as_ref()
.map(|a| a.clone().expose().switch_strategy()),
},
),
),
Identifier::Merchant(merchant_key_store.merchant_id.clone()),
key,
)
@ -143,9 +157,10 @@ pub async fn create_or_update_address_for_payment_by_request(
.and_then(|val| val.try_into_batchoperation())
.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 encryptable_address =
domain::FromRequestEncryptableAddress::from_encryptable(encrypted_data)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while encrypting address")?;
let address_update = storage::AddressUpdate::Update {
city: address
.address
@ -165,7 +180,14 @@ pub async fn create_or_update_address_for_payment_by_request(
.as_ref()
.and_then(|value| value.country_code.clone()),
updated_by: storage_scheme.to_string(),
email: encryptable_address.email,
email: encryptable_address.email.map(|email| {
let encryptable: Encryptable<masking::Secret<String, pii::EmailStrategy>> =
Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
};
let address = db
.find_address_by_merchant_id_payment_id_address_id(
@ -317,23 +339,35 @@ pub async fn get_domain_address(
let encrypted_data = types::crypto_operation(
&session_state.into(),
type_name!(domain::Address),
types::CryptoOperation::BatchEncrypt(AddressDetailsWithPhone::to_encryptable(
AddressDetailsWithPhone {
address: address_details.cloned(),
phone_number: address
.phone
.as_ref()
.and_then(|phone| phone.number.clone()),
email: address.email.clone(),
},
)),
types::CryptoOperation::BatchEncrypt(
domain::FromRequestEncryptableAddress::to_encryptable(
domain::FromRequestEncryptableAddress {
line1: address.address.as_ref().and_then(|a| a.line1.clone()),
line2: address.address.as_ref().and_then(|a| a.line2.clone()),
line3: address.address.as_ref().and_then(|a| a.line3.clone()),
state: address.address.as_ref().and_then(|a| a.state.clone()),
first_name: address.address.as_ref().and_then(|a| a.first_name.clone()),
last_name: address.address.as_ref().and_then(|a| a.last_name.clone()),
zip: address.address.as_ref().and_then(|a| a.zip.clone()),
phone_number: address
.phone
.as_ref()
.and_then(|phone| phone.number.clone()),
email: address
.email
.as_ref()
.map(|a| a.clone().expose().switch_strategy()),
},
),
),
Identifier::Merchant(merchant_id.to_owned()),
key,
)
.await
.and_then(|val| val.try_into_batchoperation())?;
let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data)
.change_context(common_utils::errors::CryptoError::EncodingFailed)?;
let encryptable_address =
domain::FromRequestEncryptableAddress::from_encryptable(encrypted_data)
.change_context(common_utils::errors::CryptoError::EncodingFailed)?;
Ok(domain::Address {
phone_number: encryptable_address.phone_number,
country_code: address.phone.as_ref().and_then(|a| a.country_code.clone()),
@ -351,7 +385,14 @@ pub async fn get_domain_address(
modified_at: common_utils::date_time::now(),
zip: encryptable_address.zip,
updated_by: storage_scheme.to_string(),
email: encryptable_address.email,
email: encryptable_address.email.map(|email| {
let encryptable: Encryptable<masking::Secret<String, pii::EmailStrategy>> =
Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
})
}
.await
@ -1589,13 +1630,18 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R, D>(
let encrypted_data = types::crypto_operation(
key_manager_state,
type_name!(domain::Customer),
types::CryptoOperation::BatchEncrypt(CustomerRequestWithEmail::to_encryptable(
CustomerRequestWithEmail {
name: request_customer_details.name.clone(),
email: request_customer_details.email.clone(),
phone: request_customer_details.phone.clone(),
},
)),
types::CryptoOperation::BatchEncrypt(
domain::FromRequestEncryptableCustomer::to_encryptable(
domain::FromRequestEncryptableCustomer {
name: request_customer_details.name.clone(),
email: request_customer_details
.email
.as_ref()
.map(|e| e.clone().expose().switch_strategy()),
phone: request_customer_details.phone.clone(),
},
),
),
Identifier::Merchant(key_store.merchant_id.clone()),
key,
)
@ -1603,9 +1649,10 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R, D>(
.and_then(|val| val.try_into_batchoperation())
.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")?;
let encryptable_customer =
domain::FromRequestEncryptableCustomer::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
@ -1616,7 +1663,15 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R, D>(
{
let customer_update = Update {
name: encryptable_customer.name,
email: encryptable_customer.email,
email: encryptable_customer.email.map(|email| {
let encryptable: Encryptable<
masking::Secret<String, pii::EmailStrategy>,
> = Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
phone: Box::new(encryptable_customer.phone),
phone_country_code: request_customer_details.phone_country_code,
description: None,
@ -1644,7 +1699,15 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R, D>(
customer_id,
merchant_id: merchant_id.to_owned(),
name: encryptable_customer.name,
email: encryptable_customer.email,
email: encryptable_customer.email.map(|email| {
let encryptable: Encryptable<
masking::Secret<String, pii::EmailStrategy>,
> = Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
phone: encryptable_customer.phone,
phone_country_code: request_customer_details.phone_country_code.clone(),
description: None,

View File

@ -21,8 +21,8 @@ use error_stack::{self, ResultExt};
use hyperswitch_domain_models::{
mandates::{MandateData, MandateDetails},
payments::{
payment_attempt::PaymentAttempt,
payment_intent::{CustomerData, PaymentAddressFromRequest},
payment_attempt::PaymentAttempt, payment_intent::CustomerData,
FromRequestEncryptablePaymentIntent,
},
};
use masking::{ExposeInterface, PeekInterface, Secret};
@ -1390,11 +1390,13 @@ impl PaymentCreate {
&key_manager_state,
type_name!(storage::PaymentIntent),
domain::types::CryptoOperation::BatchEncrypt(
PaymentAddressFromRequest::to_encryptable(PaymentAddressFromRequest {
shipping: shipping_details_encoded,
billing: billing_details_encoded,
customer_details: customer_details_encoded,
}),
FromRequestEncryptablePaymentIntent::to_encryptable(
FromRequestEncryptablePaymentIntent {
shipping_details: shipping_details_encoded,
billing_details: billing_details_encoded,
customer_details: customer_details_encoded,
},
),
),
identifier.clone(),
key,
@ -1404,7 +1406,7 @@ impl PaymentCreate {
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to encrypt data")?;
let encrypted_data = PaymentAddressFromRequest::from_encryptable(encrypted_data)
let encrypted_data = FromRequestEncryptablePaymentIntent::from_encryptable(encrypted_data)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to encrypt the payment intent data")?;
@ -1457,10 +1459,10 @@ impl PaymentCreate {
.request_external_three_ds_authentication,
charges,
frm_metadata: request.frm_metadata.clone(),
billing_details: encrypted_data.billing,
billing_details: encrypted_data.billing_details,
customer_details: encrypted_data.customer_details,
merchant_order_reference_id: request.merchant_order_reference_id.clone(),
shipping_details: encrypted_data.shipping,
shipping_details: encrypted_data.shipping_details,
is_payment_processor_token_flow,
organization_id: merchant_account.organization_id.clone(),
shipping_cost: request.shipping_cost,

View File

@ -1,11 +1,10 @@
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
use api_models::customers::CustomerRequestWithEmail;
use api_models::{enums, payment_methods::Card, payouts};
use common_utils::{
crypto::Encryptable,
encryption::Encryption,
errors::CustomResult,
ext_traits::{AsyncExt, StringExt},
fp_utils, id_type, payout_method_utils as payout_additional, type_name,
fp_utils, id_type, payout_method_utils as payout_additional, pii, type_name,
types::{
keymanager::{Identifier, KeyManagerState},
MinorUnit, UnifiedCode, UnifiedMessage,
@ -15,7 +14,7 @@ use common_utils::{
use common_utils::{generate_customer_id_of_default_length, types::keymanager::ToEncryptable};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::type_encryption::{crypto_operation, CryptoOperation};
use masking::{PeekInterface, Secret};
use masking::{ExposeInterface, PeekInterface, Secret, SwitchStrategy};
use router_env::logger;
use super::PayoutData;
@ -696,13 +695,18 @@ pub(super) async fn get_or_create_customer_details(
let encrypted_data = crypto_operation(
&state.into(),
type_name!(domain::Customer),
CryptoOperation::BatchEncrypt(CustomerRequestWithEmail::to_encryptable(
CustomerRequestWithEmail {
name: customer_details.name.clone(),
email: customer_details.email.clone(),
phone: customer_details.phone.clone(),
},
)),
CryptoOperation::BatchEncrypt(
domain::FromRequestEncryptableCustomer::to_encryptable(
domain::FromRequestEncryptableCustomer {
name: customer_details.name.clone(),
email: customer_details
.email
.clone()
.map(|a| a.expose().switch_strategy()),
phone: customer_details.phone.clone(),
},
),
),
Identifier::Merchant(key_store.merchant_id.clone()),
key,
)
@ -711,7 +715,7 @@ pub(super) async fn get_or_create_customer_details(
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to encrypt customer")?;
let encryptable_customer =
CustomerRequestWithEmail::from_encryptable(encrypted_data)
domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to form EncryptableCustomer")?;
@ -719,7 +723,14 @@ pub(super) async fn get_or_create_customer_details(
customer_id: customer_id.clone(),
merchant_id: merchant_id.to_owned().clone(),
name: encryptable_customer.name,
email: encryptable_customer.email,
email: encryptable_customer.email.map(|email| {
let encryptable: Encryptable<Secret<String, pii::EmailStrategy>> =
Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
phone: encryptable_customer.phone,
description: None,
phone_country_code: customer_details.phone_country_code.to_owned(),

View File

@ -4,30 +4,38 @@ use common_utils::{
date_time,
encryption::Encryption,
errors::{CustomResult, ValidationError},
id_type, type_name,
id_type, pii, type_name,
types::keymanager::{Identifier, KeyManagerState, ToEncryptable},
};
use diesel_models::{address::AddressUpdateInternal, enums};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use masking::{PeekInterface, Secret, SwitchStrategy};
use rustc_hash::FxHashMap;
use time::{OffsetDateTime, PrimitiveDateTime};
use super::{behaviour, types};
#[derive(Clone, Debug, serde::Serialize)]
#[derive(Clone, Debug, serde::Serialize, router_derive::ToEncryption)]
pub struct Address {
pub address_id: String,
pub city: Option<String>,
pub country: Option<enums::CountryAlpha2>,
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,
#[encrypt]
pub line1: Option<Encryptable<Secret<String>>>,
#[encrypt]
pub line2: Option<Encryptable<Secret<String>>>,
#[encrypt]
pub line3: Option<Encryptable<Secret<String>>>,
#[encrypt]
pub state: Option<Encryptable<Secret<String>>>,
#[encrypt]
pub zip: Option<Encryptable<Secret<String>>>,
#[encrypt]
pub first_name: Option<Encryptable<Secret<String>>>,
#[encrypt]
pub last_name: Option<Encryptable<Secret<String>>>,
#[encrypt]
pub phone_number: Option<Encryptable<Secret<String>>>,
pub country_code: Option<String>,
#[serde(skip_serializing)]
#[serde(with = "custom_serde::iso8601")]
@ -37,7 +45,8 @@ pub struct Address {
pub modified_at: PrimitiveDateTime,
pub merchant_id: id_type::MerchantId,
pub updated_by: String,
pub email: crypto::OptionalEncryptableEmail,
#[encrypt]
pub email: Option<Encryptable<Secret<String, pii::EmailStrategy>>>,
}
/// Based on the flow, appropriate address has to be used
@ -191,8 +200,18 @@ impl behaviour::Conversion for Address {
let decrypted: FxHashMap<String, Encryptable<Secret<String>>> = types::crypto_operation(
state,
type_name!(Self::DstType),
types::CryptoOperation::BatchDecrypt(diesel_models::Address::to_encryptable(
other.clone(),
types::CryptoOperation::BatchDecrypt(EncryptedAddress::to_encryptable(
EncryptedAddress {
line1: other.line1,
line2: other.line2,
line3: other.line3,
state: other.state,
zip: other.zip,
first_name: other.first_name,
last_name: other.last_name,
phone_number: other.phone_number,
email: other.email,
},
)),
identifier.clone(),
key.peek(),
@ -202,10 +221,11 @@ impl behaviour::Conversion for Address {
.change_context(ValidationError::InvalidValue {
message: "Failed while decrypting".to_string(),
})?;
let encryptable_address = diesel_models::Address::from_encryptable(decrypted)
.change_context(ValidationError::InvalidValue {
let encryptable_address = EncryptedAddress::from_encryptable(decrypted).change_context(
ValidationError::InvalidValue {
message: "Failed while decrypting".to_string(),
})?;
},
)?;
Ok(Self {
address_id: other.address_id,
city: other.city,
@ -223,7 +243,13 @@ impl behaviour::Conversion for Address {
modified_at: other.modified_at,
updated_by: other.updated_by,
merchant_id: other.merchant_id,
email: encryptable_address.email,
email: encryptable_address.email.map(|email| {
let encryptable: Encryptable<Secret<String, pii::EmailStrategy>> = Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
})
}

View File

@ -1,22 +1,23 @@
use common_utils::{
crypto::OptionalEncryptableSecretString,
crypto::{Encryptable, OptionalEncryptableSecretString},
encryption::Encryption,
type_name,
types::keymanager::{KeyManagerState, ToEncryptable},
};
use diesel_models::{
enums::{EventClass, EventObjectType, EventType, WebhookDeliveryAttempt},
events::{EventMetadata, EventUpdateInternal},
EventWithEncryption,
};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use rustc_hash::FxHashMap;
use crate::{
errors::{CustomResult, ValidationError},
types::domain::types,
};
#[derive(Clone, Debug)]
#[derive(Clone, Debug, router_derive::ToEncryption)]
pub struct Event {
pub event_id: String,
pub event_type: EventType,
@ -30,8 +31,10 @@ pub struct Event {
pub primary_object_created_at: Option<time::PrimitiveDateTime>,
pub idempotent_event_id: Option<String>,
pub initial_attempt_id: Option<String>,
pub request: OptionalEncryptableSecretString,
pub response: OptionalEncryptableSecretString,
#[encrypt]
pub request: Option<Encryptable<Secret<String>>>,
#[encrypt]
pub response: Option<Encryptable<Secret<String>>>,
pub delivery_attempt: Option<WebhookDeliveryAttempt>,
pub metadata: Option<EventMetadata>,
}
@ -96,12 +99,10 @@ impl super::behaviour::Conversion for Event {
let decrypted = types::crypto_operation(
state,
type_name!(Self::DstType),
types::CryptoOperation::BatchDecrypt(EventWithEncryption::to_encryptable(
EventWithEncryption {
request: item.request.clone(),
response: item.response.clone(),
},
)),
types::CryptoOperation::BatchDecrypt(EncryptedEvent::to_encryptable(EncryptedEvent {
request: item.request.clone(),
response: item.response.clone(),
})),
key_manager_identifier,
key.peek(),
)
@ -110,7 +111,7 @@ impl super::behaviour::Conversion for Event {
.change_context(ValidationError::InvalidValue {
message: "Failed while decrypting event data".to_string(),
})?;
let encryptable_event = EventWithEncryption::from_encryptable(decrypted).change_context(
let encryptable_event = EncryptedEvent::from_encryptable(decrypted).change_context(
ValidationError::InvalidValue {
message: "Failed while decrypting event data".to_string(),
},

View File

@ -13,8 +13,6 @@ pub mod user_role;
pub mod verify_connector;
use std::fmt::Debug;
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
use api_models::payments::AddressDetailsWithPhone;
use api_models::{
enums,
payments::{self},
@ -22,10 +20,10 @@ use api_models::{
};
use common_utils::types::keymanager::KeyManagerState;
pub use common_utils::{
crypto,
crypto::{self, Encryptable},
ext_traits::{ByteSliceExt, BytesExt, Encode, StringExt, ValueExt},
fp_utils::when,
id_type,
id_type, pii,
validation::validate_email,
};
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
@ -38,6 +36,7 @@ pub use hyperswitch_connectors::utils::QrImage;
use hyperswitch_domain_models::payments::PaymentIntent;
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
use hyperswitch_domain_models::type_encryption::{crypto_operation, CryptoOperation};
use masking::{ExposeInterface, SwitchStrategy};
use nanoid::nanoid;
use router_env::metrics::add_attributes;
use serde::de::DeserializeOwned;
@ -779,20 +778,32 @@ impl CustomerAddress for api_models::customers::CustomerRequest {
let encrypted_data = crypto_operation(
&state.into(),
type_name!(storage::Address),
CryptoOperation::BatchEncrypt(AddressDetailsWithPhone::to_encryptable(
AddressDetailsWithPhone {
address: Some(address_details.clone()),
CryptoOperation::BatchEncrypt(domain::FromRequestEncryptableAddress::to_encryptable(
domain::FromRequestEncryptableAddress {
line1: address_details.line1.clone(),
line2: address_details.line2.clone(),
line3: address_details.line3.clone(),
state: address_details.state.clone(),
first_name: address_details.first_name.clone(),
last_name: address_details.last_name.clone(),
zip: address_details.zip.clone(),
phone_number: self.phone.clone(),
email: self.email.clone(),
email: self
.email
.as_ref()
.map(|a| a.clone().expose().switch_strategy()),
},
)),
Identifier::Merchant(merchant_id),
Identifier::Merchant(merchant_id.to_owned()),
key,
)
.await
.and_then(|val| val.try_into_batchoperation())?;
let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data)
.change_context(common_utils::errors::CryptoError::EncodingFailed)?;
let encryptable_address =
domain::FromRequestEncryptableAddress::from_encryptable(encrypted_data)
.change_context(common_utils::errors::CryptoError::EncodingFailed)?;
Ok(storage::AddressUpdate::Update {
city: address_details.city,
country: address_details.country,
@ -806,7 +817,14 @@ impl CustomerAddress for api_models::customers::CustomerRequest {
phone_number: encryptable_address.phone_number,
country_code: self.phone_country_code.clone(),
updated_by: storage_scheme.to_string(),
email: encryptable_address.email,
email: encryptable_address.email.map(|email| {
let encryptable: Encryptable<masking::Secret<String, pii::EmailStrategy>> =
Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
})
}
@ -822,11 +840,20 @@ impl CustomerAddress for api_models::customers::CustomerRequest {
let encrypted_data = crypto_operation(
&state.into(),
type_name!(storage::Address),
CryptoOperation::BatchEncrypt(AddressDetailsWithPhone::to_encryptable(
AddressDetailsWithPhone {
address: Some(address_details.clone()),
CryptoOperation::BatchEncrypt(domain::FromRequestEncryptableAddress::to_encryptable(
domain::FromRequestEncryptableAddress {
line1: address_details.line1.clone(),
line2: address_details.line2.clone(),
line3: address_details.line3.clone(),
state: address_details.state.clone(),
first_name: address_details.first_name.clone(),
last_name: address_details.last_name.clone(),
zip: address_details.zip.clone(),
phone_number: self.phone.clone(),
email: self.email.clone(),
email: self
.email
.as_ref()
.map(|a| a.clone().expose().switch_strategy()),
},
)),
Identifier::Merchant(merchant_id.to_owned()),
@ -834,8 +861,11 @@ impl CustomerAddress for api_models::customers::CustomerRequest {
)
.await
.and_then(|val| val.try_into_batchoperation())?;
let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data)
.change_context(common_utils::errors::CryptoError::EncodingFailed)?;
let encryptable_address =
domain::FromRequestEncryptableAddress::from_encryptable(encrypted_data)
.change_context(common_utils::errors::CryptoError::EncodingFailed)?;
let address = domain::Address {
city: address_details.city,
country: address_details.country,
@ -853,7 +883,14 @@ impl CustomerAddress for api_models::customers::CustomerRequest {
created_at: common_utils::date_time::now(),
modified_at: common_utils::date_time::now(),
updated_by: storage_scheme.to_string(),
email: encryptable_address.email,
email: encryptable_address.email.map(|email| {
let encryptable: Encryptable<masking::Secret<String, pii::EmailStrategy>> =
Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
};
Ok(domain::CustomerAddress {
@ -877,20 +914,31 @@ impl CustomerAddress for api_models::customers::CustomerUpdateRequest {
let encrypted_data = crypto_operation(
&state.into(),
type_name!(storage::Address),
CryptoOperation::BatchEncrypt(AddressDetailsWithPhone::to_encryptable(
AddressDetailsWithPhone {
address: Some(address_details.clone()),
CryptoOperation::BatchEncrypt(domain::FromRequestEncryptableAddress::to_encryptable(
domain::FromRequestEncryptableAddress {
line1: address_details.line1.clone(),
line2: address_details.line2.clone(),
line3: address_details.line3.clone(),
state: address_details.state.clone(),
first_name: address_details.first_name.clone(),
last_name: address_details.last_name.clone(),
zip: address_details.zip.clone(),
phone_number: self.phone.clone(),
email: self.email.clone(),
email: self
.email
.as_ref()
.map(|a| a.clone().expose().switch_strategy()),
},
)),
Identifier::Merchant(merchant_id),
Identifier::Merchant(merchant_id.to_owned()),
key,
)
.await
.and_then(|val| val.try_into_batchoperation())?;
let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data)
.change_context(common_utils::errors::CryptoError::EncodingFailed)?;
let encryptable_address =
domain::FromRequestEncryptableAddress::from_encryptable(encrypted_data)
.change_context(common_utils::errors::CryptoError::EncodingFailed)?;
Ok(storage::AddressUpdate::Update {
city: address_details.city,
country: address_details.country,
@ -904,7 +952,14 @@ impl CustomerAddress for api_models::customers::CustomerUpdateRequest {
phone_number: encryptable_address.phone_number,
country_code: self.phone_country_code.clone(),
updated_by: storage_scheme.to_string(),
email: encryptable_address.email,
email: encryptable_address.email.map(|email| {
let encryptable: Encryptable<masking::Secret<String, pii::EmailStrategy>> =
Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
})
}
@ -920,11 +975,20 @@ impl CustomerAddress for api_models::customers::CustomerUpdateRequest {
let encrypted_data = crypto_operation(
&state.into(),
type_name!(storage::Address),
CryptoOperation::BatchEncrypt(AddressDetailsWithPhone::to_encryptable(
AddressDetailsWithPhone {
address: Some(address_details.clone()),
CryptoOperation::BatchEncrypt(domain::FromRequestEncryptableAddress::to_encryptable(
domain::FromRequestEncryptableAddress {
line1: address_details.line1.clone(),
line2: address_details.line2.clone(),
line3: address_details.line3.clone(),
state: address_details.state.clone(),
first_name: address_details.first_name.clone(),
last_name: address_details.last_name.clone(),
zip: address_details.zip.clone(),
phone_number: self.phone.clone(),
email: self.email.clone(),
email: self
.email
.as_ref()
.map(|a| a.clone().expose().switch_strategy()),
},
)),
Identifier::Merchant(merchant_id.to_owned()),
@ -932,8 +996,10 @@ impl CustomerAddress for api_models::customers::CustomerUpdateRequest {
)
.await
.and_then(|val| val.try_into_batchoperation())?;
let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data)
.change_context(common_utils::errors::CryptoError::EncodingFailed)?;
let encryptable_address =
domain::FromRequestEncryptableAddress::from_encryptable(encrypted_data)
.change_context(common_utils::errors::CryptoError::EncodingFailed)?;
let address = domain::Address {
city: address_details.city,
country: address_details.country,
@ -951,7 +1017,14 @@ impl CustomerAddress for api_models::customers::CustomerUpdateRequest {
created_at: common_utils::date_time::now(),
modified_at: common_utils::date_time::now(),
updated_by: storage_scheme.to_string(),
email: encryptable_address.email,
email: encryptable_address.email.map(|email| {
let encryptable: Encryptable<masking::Secret<String, pii::EmailStrategy>> =
Encryptable::new(
email.clone().into_inner().switch_strategy(),
email.into_encrypted(),
);
encryptable
}),
};
Ok(domain::CustomerAddress {