Skip to content

Commit 33298b3

Browse files
ArjunKarthikdracarys18hyperswitch-bot[bot]
authored
feat: encryption service integration to support batch encryption and decryption (#5164)
Co-authored-by: dracarys18 <[email protected]> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
1 parent c698921 commit 33298b3

File tree

127 files changed

+4239
-1378
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+4239
-1378
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/api_models/src/customers.rs

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
use common_utils::{crypto, custom_serde, id_type, pii};
2-
use masking::Secret;
1+
use common_utils::{
2+
crypto, custom_serde,
3+
encryption::Encryption,
4+
id_type,
5+
pii::{self, EmailStrategy},
6+
types::keymanager::ToEncryptable,
7+
};
8+
use euclid::dssa::graph::euclid_graph_prelude::FxHashMap;
9+
use masking::{ExposeInterface, Secret, SwitchStrategy};
310
use serde::{Deserialize, Serialize};
411
use utoipa::ToSchema;
512

@@ -40,6 +47,85 @@ pub struct CustomerRequest {
4047
pub metadata: Option<pii::SecretSerdeValue>,
4148
}
4249

50+
pub struct CustomerRequestWithEmail {
51+
pub name: Option<Secret<String>>,
52+
pub email: Option<pii::Email>,
53+
pub phone: Option<Secret<String>>,
54+
}
55+
56+
pub struct CustomerRequestWithEncryption {
57+
pub name: Option<Encryption>,
58+
pub phone: Option<Encryption>,
59+
pub email: Option<Encryption>,
60+
}
61+
62+
pub struct EncryptableCustomer {
63+
pub name: crypto::OptionalEncryptableName,
64+
pub phone: crypto::OptionalEncryptablePhone,
65+
pub email: crypto::OptionalEncryptableEmail,
66+
}
67+
68+
impl ToEncryptable<EncryptableCustomer, Secret<String>, Encryption>
69+
for CustomerRequestWithEncryption
70+
{
71+
fn to_encryptable(self) -> FxHashMap<String, Encryption> {
72+
let mut map = FxHashMap::with_capacity_and_hasher(3, Default::default());
73+
self.name.map(|x| map.insert("name".to_string(), x));
74+
self.phone.map(|x| map.insert("phone".to_string(), x));
75+
self.email.map(|x| map.insert("email".to_string(), x));
76+
map
77+
}
78+
79+
fn from_encryptable(
80+
mut hashmap: FxHashMap<String, crypto::Encryptable<Secret<String>>>,
81+
) -> common_utils::errors::CustomResult<EncryptableCustomer, common_utils::errors::ParsingError>
82+
{
83+
Ok(EncryptableCustomer {
84+
name: hashmap.remove("name"),
85+
phone: hashmap.remove("phone"),
86+
email: hashmap.remove("email").map(|email| {
87+
let encryptable: crypto::Encryptable<Secret<String, EmailStrategy>> =
88+
crypto::Encryptable::new(
89+
email.clone().into_inner().switch_strategy(),
90+
email.into_encrypted(),
91+
);
92+
encryptable
93+
}),
94+
})
95+
}
96+
}
97+
98+
impl ToEncryptable<EncryptableCustomer, Secret<String>, Secret<String>>
99+
for CustomerRequestWithEmail
100+
{
101+
fn to_encryptable(self) -> FxHashMap<String, Secret<String>> {
102+
let mut map = FxHashMap::with_capacity_and_hasher(3, Default::default());
103+
self.name.map(|x| map.insert("name".to_string(), x));
104+
self.phone.map(|x| map.insert("phone".to_string(), x));
105+
self.email
106+
.map(|x| map.insert("email".to_string(), x.expose().switch_strategy()));
107+
map
108+
}
109+
110+
fn from_encryptable(
111+
mut hashmap: FxHashMap<String, crypto::Encryptable<Secret<String>>>,
112+
) -> common_utils::errors::CustomResult<EncryptableCustomer, common_utils::errors::ParsingError>
113+
{
114+
Ok(EncryptableCustomer {
115+
name: hashmap.remove("name"),
116+
email: hashmap.remove("email").map(|email| {
117+
let encryptable: crypto::Encryptable<Secret<String, EmailStrategy>> =
118+
crypto::Encryptable::new(
119+
email.clone().into_inner().switch_strategy(),
120+
email.into_encrypted(),
121+
);
122+
encryptable
123+
}),
124+
phone: hashmap.remove("phone"),
125+
})
126+
}
127+
}
128+
43129
#[derive(Debug, Clone, Serialize, ToSchema)]
44130
pub struct CustomerResponse {
45131
/// The identifier for the customer object

crates/api_models/src/payments.rs

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ use common_utils::{
1111
ext_traits::{ConfigExt, Encode},
1212
hashing::HashedString,
1313
id_type,
14-
pii::{self, Email},
15-
types::{MinorUnit, StringMajorUnit},
14+
pii::{self, Email, EmailStrategy},
15+
types::{keymanager::ToEncryptable, MinorUnit, StringMajorUnit},
1616
};
17-
use masking::{PeekInterface, Secret, WithType};
17+
use euclid::dssa::graph::euclid_graph_prelude::FxHashMap;
18+
use masking::{ExposeInterface, PeekInterface, Secret, SwitchStrategy, WithType};
1819
use router_derive::Setter;
1920
use serde::{
2021
de::{self, Unexpected, Visitor},
@@ -3187,6 +3188,72 @@ impl AddressDetails {
31873188
}
31883189
}
31893190

3191+
pub struct AddressDetailsWithPhone {
3192+
pub address: Option<AddressDetails>,
3193+
pub phone_number: Option<Secret<String>>,
3194+
pub email: Option<Email>,
3195+
}
3196+
3197+
pub struct EncryptableAddressDetails {
3198+
pub line1: crypto::OptionalEncryptableSecretString,
3199+
pub line2: crypto::OptionalEncryptableSecretString,
3200+
pub line3: crypto::OptionalEncryptableSecretString,
3201+
pub state: crypto::OptionalEncryptableSecretString,
3202+
pub zip: crypto::OptionalEncryptableSecretString,
3203+
pub first_name: crypto::OptionalEncryptableSecretString,
3204+
pub last_name: crypto::OptionalEncryptableSecretString,
3205+
pub phone_number: crypto::OptionalEncryptableSecretString,
3206+
pub email: crypto::OptionalEncryptableEmail,
3207+
}
3208+
3209+
impl ToEncryptable<EncryptableAddressDetails, Secret<String>, Secret<String>>
3210+
for AddressDetailsWithPhone
3211+
{
3212+
fn from_encryptable(
3213+
mut hashmap: FxHashMap<String, crypto::Encryptable<Secret<String>>>,
3214+
) -> common_utils::errors::CustomResult<
3215+
EncryptableAddressDetails,
3216+
common_utils::errors::ParsingError,
3217+
> {
3218+
Ok(EncryptableAddressDetails {
3219+
line1: hashmap.remove("line1"),
3220+
line2: hashmap.remove("line2"),
3221+
line3: hashmap.remove("line3"),
3222+
state: hashmap.remove("state"),
3223+
zip: hashmap.remove("zip"),
3224+
first_name: hashmap.remove("first_name"),
3225+
last_name: hashmap.remove("last_name"),
3226+
phone_number: hashmap.remove("phone_number"),
3227+
email: hashmap.remove("email").map(|x| {
3228+
let inner: Secret<String, EmailStrategy> = x.clone().into_inner().switch_strategy();
3229+
crypto::Encryptable::new(inner, x.into_encrypted())
3230+
}),
3231+
})
3232+
}
3233+
3234+
fn to_encryptable(self) -> FxHashMap<String, Secret<String>> {
3235+
let mut map = FxHashMap::with_capacity_and_hasher(9, Default::default());
3236+
self.address.map(|address| {
3237+
address.line1.map(|x| map.insert("line1".to_string(), x));
3238+
address.line2.map(|x| map.insert("line2".to_string(), x));
3239+
address.line3.map(|x| map.insert("line3".to_string(), x));
3240+
address.state.map(|x| map.insert("state".to_string(), x));
3241+
address.zip.map(|x| map.insert("zip".to_string(), x));
3242+
address
3243+
.first_name
3244+
.map(|x| map.insert("first_name".to_string(), x));
3245+
address
3246+
.last_name
3247+
.map(|x| map.insert("last_name".to_string(), x));
3248+
});
3249+
self.email
3250+
.map(|x| map.insert("email".to_string(), x.expose().switch_strategy()));
3251+
self.phone_number
3252+
.map(|x| map.insert("phone_number".to_string(), x));
3253+
map
3254+
}
3255+
}
3256+
31903257
#[derive(Debug, Clone, Default, Eq, PartialEq, ToSchema, serde::Deserialize, serde::Serialize)]
31913258
pub struct PhoneDetails {
31923259
/// The contact number

crates/common_utils/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ license.workspace = true
1010
[features]
1111
keymanager = ["dep:router_env"]
1212
keymanager_mtls = ["reqwest/rustls-tls"]
13+
encryption_service = ["dep:router_env"]
1314
signals = ["dep:signal-hook-tokio", "dep:signal-hook", "dep:tokio", "dep:router_env", "dep:futures"]
1415
async_ext = ["dep:async-trait", "dep:futures"]
1516
logs = ["dep:router_env"]
1617
metrics = ["dep:router_env", "dep:futures"]
1718

1819
[dependencies]
1920
async-trait = { version = "0.1.79", optional = true }
21+
base64 = "0.22.0"
2022
blake3 = { version = "1.5.1", features = ["serde"] }
2123
bytes = "1.6.0"
2224
diesel = "2.1.5"

crates/common_utils/src/consts.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ pub const MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 64;
9999
/// Minimum allowed length for MerchantReferenceId
100100
pub const MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 1;
101101

102+
/// General purpose base64 engine
103+
pub const BASE64_ENGINE: base64::engine::GeneralPurpose = base64::engine::general_purpose::STANDARD;
102104
/// Regex for matching a domain
103105
/// Eg -
104106
/// http://www.example.com
Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,13 @@
1-
use common_utils::pii::EncryptionStrategy;
21
use diesel::{
32
backend::Backend,
43
deserialize::{self, FromSql, Queryable},
4+
expression::AsExpression,
55
serialize::ToSql,
6-
sql_types, AsExpression,
6+
sql_types,
77
};
88
use masking::Secret;
99

10-
#[derive(Debug, AsExpression, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
11-
#[diesel(sql_type = sql_types::Binary)]
12-
#[repr(transparent)]
13-
pub struct Encryption {
14-
inner: Secret<Vec<u8>, EncryptionStrategy>,
15-
}
16-
17-
impl<T: Clone> From<common_utils::crypto::Encryptable<T>> for Encryption {
18-
fn from(value: common_utils::crypto::Encryptable<T>) -> Self {
19-
Self::new(value.into_encrypted())
20-
}
21-
}
22-
23-
impl Encryption {
24-
pub fn new(item: Secret<Vec<u8>, EncryptionStrategy>) -> Self {
25-
Self { inner: item }
26-
}
27-
28-
#[inline]
29-
pub fn into_inner(self) -> Secret<Vec<u8>, EncryptionStrategy> {
30-
self.inner
31-
}
32-
33-
#[inline]
34-
pub fn get_inner(&self) -> &Secret<Vec<u8>, EncryptionStrategy> {
35-
&self.inner
36-
}
37-
}
10+
use crate::{crypto::Encryptable, pii::EncryptionStrategy};
3811

3912
impl<DB> FromSql<sql_types::Binary, DB> for Encryption
4013
where
@@ -69,3 +42,32 @@ where
6942
Ok(Self { inner: row })
7043
}
7144
}
45+
46+
#[derive(Debug, AsExpression, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
47+
#[diesel(sql_type = sql_types::Binary)]
48+
#[repr(transparent)]
49+
pub struct Encryption {
50+
inner: Secret<Vec<u8>, EncryptionStrategy>,
51+
}
52+
53+
impl<T: Clone> From<Encryptable<T>> for Encryption {
54+
fn from(value: Encryptable<T>) -> Self {
55+
Self::new(value.into_encrypted())
56+
}
57+
}
58+
59+
impl Encryption {
60+
pub fn new(item: Secret<Vec<u8>, EncryptionStrategy>) -> Self {
61+
Self { inner: item }
62+
}
63+
64+
#[inline]
65+
pub fn into_inner(self) -> Secret<Vec<u8>, EncryptionStrategy> {
66+
self.inner
67+
}
68+
69+
#[inline]
70+
pub fn get_inner(&self) -> &Secret<Vec<u8>, EncryptionStrategy> {
71+
&self.inner
72+
}
73+
}

0 commit comments

Comments
 (0)