refactor(router): remove pii-encryption-script feature and use of timestamps for decryption (#1350)

This commit is contained in:
Sanchith Hegde
2023-06-05 18:18:00 +05:30
committed by GitHub
parent 5d8895c064
commit 9f2832f600
24 changed files with 52 additions and 585 deletions

View File

@ -16,8 +16,8 @@ kms = ["external_services/kms","dep:aws-config"]
email = ["external_services/email","dep:aws-config"]
basilisk = ["kms"]
stripe = ["dep:serde_qs"]
sandbox = ["kms", "stripe", "basilisk", "s3","email"]
production = ["kms", "stripe", "basilisk", "s3","pii-encryption-script","email"]
sandbox = ["kms", "stripe", "basilisk", "s3", "email"]
production = ["kms", "stripe", "basilisk", "s3", "email"]
olap = []
oltp = []
kv_store = []
@ -25,7 +25,6 @@ accounts_cache = []
openapi = ["olap", "oltp"]
vergen = ["router_env/vergen"]
multiple_mca = ["api_models/multiple_mca"]
pii-encryption-script = []
dummy_connector = ["api_models/dummy_connector"]
external_access_dc = ["dummy_connector"]
detailed_errors = ["api_models/detailed_errors", "error-stack/serde"]

View File

@ -36,24 +36,6 @@ async fn main() -> ApplicationResult<()> {
let _guard = logger::setup(&conf.log);
#[cfg(feature = "pii-encryption-script")]
{
let store =
router::services::Store::new(&conf, false, tokio::sync::oneshot::channel().0).await;
// ^-------- KMS decryption of the master key is a fallible and the server will panic in
// the above mentioned line
router::scripts::pii_encryption::test_2_step_encryption(&store).await;
#[allow(clippy::expect_used)]
router::scripts::pii_encryption::encrypt_merchant_account_fields(&store)
.await
.expect("Failed while encrypting merchant account");
crate::logger::error!("Done with everything");
}
logger::info!("Application started [{:?}] [{:?}]", conf.server, conf.log);
#[allow(clippy::expect_used)]

View File

@ -40,7 +40,6 @@ impl Default for super::settings::Secrets {
kms_encrypted_jwt_secret: "".into(),
#[cfg(feature = "kms")]
kms_encrypted_admin_api_key: "".into(),
migration_encryption_timestamp: 0,
}
}
}

View File

@ -37,8 +37,6 @@ pub enum Subcommand {
#[cfg(feature = "openapi")]
/// Generate the OpenAPI specification file from code.
GenerateOpenapiSpec,
#[cfg(feature = "pii-encryption-script")]
EncryptDatabase,
}
#[cfg(feature = "kms")]
@ -288,8 +286,6 @@ pub struct Secrets {
pub kms_encrypted_jwt_secret: String,
#[cfg(feature = "kms")]
pub kms_encrypted_admin_api_key: String,
pub migration_encryption_timestamp: i64,
}
#[derive(Debug, Deserialize, Clone)]

View File

@ -239,7 +239,7 @@ pub async fn delete_customer(
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while getting key for encryption")?;
let redacted_encrypted_value: Encryptable<masking::Secret<_>> =
Encryptable::encrypt(REDACTED.to_string().into(), &key, GcmAes256 {})
Encryptable::encrypt(REDACTED.to_string().into(), &key, GcmAes256)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
@ -278,7 +278,7 @@ 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 {})
Encryptable::encrypt(REDACTED.to_string().into(), &key, GcmAes256)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?,
),

View File

@ -74,16 +74,12 @@ pub trait StorageInterface:
pub trait MasterKeyInterface {
fn get_master_key(&self) -> &[u8];
fn get_migration_timestamp(&self) -> i64;
}
impl MasterKeyInterface for Store {
fn get_master_key(&self) -> &[u8] {
&self.master_key
}
fn get_migration_timestamp(&self) -> i64 {
self.migration_timestamp
}
}
/// Default dummy key for MockDb
@ -94,10 +90,6 @@ impl MasterKeyInterface for MockDb {
25, 26, 27, 28, 29, 30, 31, 32,
]
}
fn get_migration_timestamp(&self) -> i64 {
0
}
}
#[async_trait::async_trait]

View File

@ -2,7 +2,7 @@ use common_utils::ext_traits::AsyncExt;
use error_stack::{IntoReport, ResultExt};
use storage_models::address::AddressUpdateInternal;
use super::{MasterKeyInterface, MockDb, Store};
use super::{MockDb, Store};
use crate::{
connection,
core::errors::{self, CustomResult},
@ -58,7 +58,7 @@ impl AddressInterface for Store {
.async_and_then(|address| async {
let merchant_id = address.merchant_id.clone();
address
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -78,7 +78,7 @@ impl AddressInterface for Store {
.async_and_then(|address| async {
let merchant_id = address.merchant_id.clone();
address
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -101,7 +101,7 @@ impl AddressInterface for Store {
.async_and_then(|address| async {
let merchant_id = address.merchant_id.clone();
address
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -130,7 +130,7 @@ impl AddressInterface for Store {
let merchant_id = address.merchant_id.clone();
output.push(
address
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)?,
)
@ -158,7 +158,7 @@ impl AddressInterface for MockDb {
let merchant_id = address.merchant_id.clone();
address
.clone()
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}
@ -190,7 +190,7 @@ impl AddressInterface for MockDb {
Some(address_updated) => {
let merchant_id = address_updated.merchant_id.clone();
address_updated
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}
@ -217,7 +217,7 @@ impl AddressInterface for MockDb {
addresses.push(address.clone());
address
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}
@ -244,7 +244,7 @@ impl AddressInterface for MockDb {
}) {
Some(address) => {
let address: domain::Address = address
.convert(self, merchant_id, self.get_migration_timestamp())
.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)?;
Ok(vec![address])

View File

@ -2,7 +2,7 @@ use common_utils::ext_traits::AsyncExt;
use error_stack::{IntoReport, ResultExt};
use masking::PeekInterface;
use super::{MasterKeyInterface, MockDb, Store};
use super::{MockDb, Store};
use crate::{
connection,
core::{
@ -72,7 +72,7 @@ impl CustomerInterface for Store {
.map_err(Into::into)
.into_report()?
.async_map(|c| async {
c.convert(self, merchant_id, self.get_migration_timestamp())
c.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -108,7 +108,7 @@ impl CustomerInterface for Store {
.into_report()
.async_and_then(|c| async {
let merchant_id = c.merchant_id.clone();
c.convert(self, &merchant_id, self.get_migration_timestamp())
c.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -128,7 +128,7 @@ impl CustomerInterface for Store {
.into_report()
.async_and_then(|c| async {
let merchant_id = c.merchant_id.clone();
c.convert(self, &merchant_id, self.get_migration_timestamp())
c.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -156,7 +156,7 @@ impl CustomerInterface for Store {
.into_report()
.async_and_then(|c| async {
let merchant_id = c.merchant_id.clone();
c.convert(self, &merchant_id, self.get_migration_timestamp())
c.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -194,7 +194,7 @@ impl CustomerInterface for MockDb {
customer
.async_map(|c| async {
let merchant_id = c.merchant_id.clone();
c.convert(self, &merchant_id, self.get_migration_timestamp())
c.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -236,7 +236,7 @@ impl CustomerInterface for MockDb {
customers.push(customer.clone());
customer
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}

View File

@ -7,7 +7,6 @@ use crate::cache::{self, ACCOUNTS_CACHE};
use crate::{
connection,
core::errors::{self, CustomResult},
db::MasterKeyInterface,
types::{
domain::{
self,
@ -72,7 +71,7 @@ impl MerchantAccountInterface for Store {
.await
.map_err(Into::into)
.into_report()?
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}
@ -93,7 +92,7 @@ impl MerchantAccountInterface for Store {
{
fetch_func()
.await?
.convert(self, merchant_id, self.get_migration_timestamp())
.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}
@ -102,7 +101,7 @@ impl MerchantAccountInterface for Store {
{
super::cache::get_or_populate_in_memory(self, merchant_id, fetch_func, &ACCOUNTS_CACHE)
.await?
.convert(self, merchant_id, self.get_migration_timestamp())
.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}
@ -124,7 +123,7 @@ impl MerchantAccountInterface for Store {
.map_err(Into::into)
.into_report()
.async_and_then(|item| async {
item.convert(self, &_merchant_id, self.get_migration_timestamp())
item.convert(self, &_merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -163,7 +162,7 @@ impl MerchantAccountInterface for Store {
.map_err(Into::into)
.into_report()
.async_and_then(|item| async {
item.convert(self, merchant_id, self.get_migration_timestamp())
item.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -197,7 +196,7 @@ impl MerchantAccountInterface for Store {
.into_report()
.async_and_then(|item| async {
let merchant_id = item.merchant_id.clone();
item.convert(self, &merchant_id, self.get_migration_timestamp())
item.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -248,7 +247,7 @@ impl MerchantAccountInterface for MockDb {
accounts.push(account.clone());
account
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}
@ -264,7 +263,7 @@ impl MerchantAccountInterface for MockDb {
.find(|account| account.merchant_id == merchant_id)
.cloned()
.async_map(|a| async {
a.convert(self, merchant_id, self.get_migration_timestamp())
a.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})

View File

@ -7,7 +7,6 @@ use super::{MockDb, Store};
use crate::{
connection,
core::errors::{self, CustomResult},
db::MasterKeyInterface,
services::logger,
types::{
self,
@ -166,7 +165,7 @@ impl MerchantConnectorAccountInterface for Store {
.map_err(Into::into)
.into_report()
.async_and_then(|item| async {
item.convert(self, merchant_id, self.get_migration_timestamp())
item.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -194,7 +193,7 @@ impl MerchantConnectorAccountInterface for Store {
{
find_call()
.await?
.convert(self, merchant_id, self.get_migration_timestamp())
.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DeserializationFailed)
}
@ -203,7 +202,7 @@ impl MerchantConnectorAccountInterface for Store {
{
cache::get_or_populate_redis(self, merchant_connector_id, find_call)
.await?
.convert(self, merchant_id, self.get_migration_timestamp())
.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DeserializationFailed)
}
@ -223,7 +222,7 @@ impl MerchantConnectorAccountInterface for Store {
.into_report()
.async_and_then(|item| async {
let merchant_id = item.merchant_id.clone();
item.convert(self, &merchant_id, self.get_migration_timestamp())
item.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -244,7 +243,7 @@ impl MerchantConnectorAccountInterface for Store {
let mut output = Vec::with_capacity(items.len());
for item in items.into_iter() {
output.push(
item.convert(self, merchant_id, self.get_migration_timestamp())
item.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)?,
)
@ -271,7 +270,7 @@ impl MerchantConnectorAccountInterface for Store {
.into_report()
.async_and_then(|item| async {
let merchant_id = item.merchant_id.clone();
item.convert(self, &merchant_id, self.get_migration_timestamp())
item.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
})
@ -324,7 +323,7 @@ impl MerchantConnectorAccountInterface for MockDb {
.cloned()
.unwrap();
account
.convert(self, merchant_id, self.get_migration_timestamp())
.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}
@ -367,7 +366,7 @@ impl MerchantConnectorAccountInterface for MockDb {
};
accounts.push(account.clone());
account
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}

View File

@ -1,6 +1,5 @@
use error_stack::{IntoReport, ResultExt};
use super::MasterKeyInterface;
use crate::{
connection,
core::errors::{self, CustomResult},
@ -41,7 +40,7 @@ impl MerchantKeyStoreInterface for Store {
.await
.map_err(Into::into)
.into_report()?
.convert(self, &merchant_id, self.get_migration_timestamp())
.convert(self, &merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}
@ -57,7 +56,7 @@ impl MerchantKeyStoreInterface for Store {
.await
.map_err(Into::into)
.into_report()?
.convert(self, merchant_id, self.get_migration_timestamp())
.convert(self, merchant_id)
.await
.change_context(errors::StorageError::DecryptionError)
}

View File

@ -19,7 +19,6 @@ pub mod scheduler;
pub mod middleware;
#[cfg(feature = "openapi")]
pub mod openapi;
pub mod scripts;
pub mod services;
pub mod types;
pub mod utils;

View File

@ -1,2 +0,0 @@
#[cfg(feature = "pii-encryption-script")]
pub mod pii_encryption;

View File

@ -1,421 +0,0 @@
use async_bb8_diesel::AsyncConnection;
use common_utils::errors::CustomResult;
use diesel::{associations::HasTable, ExpressionMethods, Table};
use error_stack::{IntoReport, ResultExt};
use storage_models::{
address::Address,
customers::Customer,
merchant_account::MerchantAccount,
merchant_connector_account::MerchantConnectorAccount,
query::generics::generic_filter,
schema::{
address::dsl as ad_dsl, customers::dsl as cu_dsl, merchant_account::dsl as ma_dsl,
merchant_connector_account::dsl as mca_dsl,
},
};
use crate::{
connection,
core::errors,
db::{
merchant_account::MerchantAccountInterface, merchant_key_store::MerchantKeyStoreInterface,
MasterKeyInterface,
},
services::{self, Store},
types::{
domain::{
self,
behaviour::{Conversion, ReverseConversion},
merchant_key_store, types,
},
storage,
},
};
pub async fn create_merchant_key_store(
state: &Store,
merchant_id: &str,
key: Vec<u8>,
) -> CustomResult<(), errors::ApiErrorResponse> {
crate::logger::info!("Trying to create MerchantKeyStore for {}", merchant_id);
let master_key = state.get_master_key();
let key_store = merchant_key_store::MerchantKeyStore {
merchant_id: merchant_id.to_string(),
key: types::encrypt(key.into(), master_key)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to decrypt data from key store")?,
created_at: common_utils::date_time::now(),
};
match state.insert_merchant_key_store(key_store).await {
Ok(_) => Ok(()),
Err(err) => match err.current_context() {
errors::StorageError::DatabaseError(f) => match f.current_context() {
storage_models::errors::DatabaseError::UniqueViolation => Ok(()),
_ => Err(err.change_context(errors::ApiErrorResponse::InternalServerError)),
},
_ => Err(err.change_context(errors::ApiErrorResponse::InternalServerError)),
},
}
}
pub async fn encrypt_merchant_account_fields(
state: &Store,
) -> CustomResult<(), errors::ApiErrorResponse> {
let conn = connection::pg_connection_write(state)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
let merchants: Vec<MerchantAccount> = generic_filter::<
<MerchantAccount as HasTable>::Table,
_,
<<MerchantAccount as HasTable>::Table as Table>::PrimaryKey,
_,
>(
&conn,
ma_dsl::merchant_id.eq(ma_dsl::merchant_id),
None,
None,
None,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
for mer in merchants.iter() {
let key = services::generate_aes256_key()
.change_context(errors::ApiErrorResponse::InternalServerError)?;
create_merchant_key_store(state, &mer.merchant_id, key.to_vec()).await?;
}
let mut domain_merchants = Vec::with_capacity(merchants.len());
for mf in merchants.into_iter() {
let merchant_id = mf.merchant_id.clone();
let domain_merchant: domain::MerchantAccount = mf
.convert(state, &merchant_id, state.get_migration_timestamp())
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
domain_merchants.push(domain_merchant);
}
for m in domain_merchants {
let merchant_id = m.merchant_id.clone();
let updated_merchant_account = storage::MerchantAccountUpdate::Update {
merchant_name: m.merchant_name.clone(),
merchant_details: m.merchant_details.clone(),
return_url: None,
webhook_details: None,
sub_merchants_enabled: None,
parent_merchant_id: None,
primary_business_details: None,
enable_payment_response_hash: None,
payment_response_hash_key: None,
redirect_to_merchant_with_http_post: None,
routing_algorithm: None,
locker_id: None,
publishable_key: None,
metadata: None,
intent_fulfillment_time: None,
frm_routing_algorithm: None,
};
crate::logger::warn!("Started for {}", merchant_id);
state
.update_merchant(m, updated_merchant_account)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
encrypt_merchant_connector_account_fields(state, &merchant_id).await?;
encrypt_customer_fields(state, &merchant_id).await?;
encrypt_address_fields(state, &merchant_id).await?;
crate::logger::warn!("Done for {}", merchant_id);
}
Ok(())
}
pub async fn encrypt_merchant_connector_account_fields(
state: &Store,
merchant_id: &str,
) -> CustomResult<(), errors::ApiErrorResponse> {
crate::logger::warn!("Updating MerchantConnectorAccount for {}", merchant_id);
let conn = connection::pg_connection_write(state)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
let merchants: Vec<MerchantConnectorAccount> = generic_filter::<
<MerchantConnectorAccount as HasTable>::Table,
_,
<<MerchantConnectorAccount as HasTable>::Table as Table>::PrimaryKey,
_,
>(
&conn,
mca_dsl::merchant_id.eq(merchant_id.to_string()),
None,
None,
None,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
let mut domain_merchants = Vec::with_capacity(merchants.len());
for m in merchants.into_iter() {
let merchant_id = m.merchant_id.clone();
let domain_merchant: domain::MerchantConnectorAccount = m
.convert(state, &merchant_id, state.get_migration_timestamp())
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
domain_merchants.push(domain_merchant);
}
conn.transaction_async::<(), async_bb8_diesel::ConnectionError, _, _>(|conn| async move {
for m in domain_merchants {
let updated_merchant_connector_account =
storage::MerchantConnectorAccountUpdate::Update {
merchant_id: None,
connector_name: None,
connector_type: None,
frm_configs: None,
test_mode: None,
disabled: None,
merchant_connector_id: None,
payment_methods_enabled: None,
metadata: None,
connector_account_details: Some(m.connector_account_details.clone()),
};
Conversion::convert(m)
.await
.map_err(|_| {
async_bb8_diesel::ConnectionError::Query(
diesel::result::Error::QueryBuilderError(
"Error while decrypting MerchantConnectorAccount".into(),
),
)
})?
.update(&conn, updated_merchant_connector_account.into())
.await
.map_err(|_| {
async_bb8_diesel::ConnectionError::Query(
diesel::result::Error::QueryBuilderError(
"Error while updating MerchantConnectorAccount".into(),
),
)
})?;
}
Ok(())
})
.await
.into_report()
.change_context(errors::ApiErrorResponse::InternalServerError)?;
crate::logger::warn!(
"Done: Updating MerchantConnectorAccount for {}",
merchant_id
);
Ok(())
}
pub async fn encrypt_customer_fields(
state: &Store,
merchant_id: &str,
) -> CustomResult<(), errors::ApiErrorResponse> {
crate::logger::warn!("Updating Customer for {}", merchant_id);
let conn = connection::pg_connection_write(state)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
let merchants: Vec<Customer> = generic_filter::<
<Customer as HasTable>::Table,
_,
<<Customer as HasTable>::Table as Table>::PrimaryKey,
_,
>(
&conn,
cu_dsl::merchant_id.eq(merchant_id.to_string()),
None,
None,
None,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
let mut domain_merchants = Vec::with_capacity(merchants.len());
for m in merchants.into_iter() {
let merchant_id = m.merchant_id.clone();
let domain_merchant: domain::Customer = m
.convert(state, &merchant_id, state.get_migration_timestamp())
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
domain_merchants.push(domain_merchant);
}
conn.transaction_async::<(), async_bb8_diesel::ConnectionError, _, _>(|conn| async move {
for m in domain_merchants {
let update_customer = storage::CustomerUpdate::Update {
name: m.name.clone(),
email: m.email.clone(),
phone: m.phone.clone(),
description: None,
metadata: None,
phone_country_code: None,
connector_customer: None,
};
Customer::update_by_customer_id_merchant_id(
&conn,
m.customer_id.to_string(),
m.merchant_id.to_string(),
update_customer.into(),
)
.await
.map_err(|_| {
async_bb8_diesel::ConnectionError::Query(diesel::result::Error::QueryBuilderError(
"Error while updating Customer".into(),
))
})?;
}
Ok(())
})
.await
.into_report()
.change_context(errors::ApiErrorResponse::InternalServerError)?;
crate::logger::warn!("Done: Updating Customer for {}", merchant_id);
Ok(())
}
pub async fn encrypt_address_fields(
state: &Store,
merchant_id: &str,
) -> CustomResult<(), errors::ApiErrorResponse> {
crate::logger::warn!("Updating Address for {}", merchant_id);
let conn = connection::pg_connection_write(state)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
let merchants: Vec<Address> = generic_filter::<
<Address as HasTable>::Table,
_,
<<Address as HasTable>::Table as Table>::PrimaryKey,
_,
>(
&conn,
ad_dsl::merchant_id.eq(merchant_id.to_string()),
None,
None,
None,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
let mut domain_merchants = Vec::with_capacity(merchants.len());
for m in merchants.into_iter() {
let merchant_id = m.merchant_id.clone();
let domain_merchant: domain::Address = m
.convert(state, &merchant_id, state.get_migration_timestamp())
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
domain_merchants.push(domain_merchant);
}
conn.transaction_async::<(), async_bb8_diesel::ConnectionError, _, _>(|conn| async move {
for m in domain_merchants {
let update_address = storage::address::AddressUpdate::Update {
line1: m.line1.clone(),
line2: m.line2.clone(),
line3: m.line3.clone(),
state: m.state.clone(),
zip: m.zip.clone(),
first_name: m.first_name.clone(),
last_name: m.last_name.clone(),
phone_number: m.phone_number.clone(),
city: None,
country: None,
country_code: None,
};
Address::update_by_address_id(&conn, m.address_id, update_address.into())
.await
.map_err(|_| {
async_bb8_diesel::ConnectionError::Query(
diesel::result::Error::QueryBuilderError(
"Error while updating Address".into(),
),
)
})?;
}
Ok(())
})
.await
.into_report()
.change_context(errors::ApiErrorResponse::InternalServerError)?;
crate::logger::warn!("Done: Updating Address for {}", merchant_id);
Ok(())
}
///
/// # Panics
///
/// The functions runs at the start of the migration and tests, all the functional parts of
/// encryption.
///
#[allow(clippy::unwrap_used)]
pub async fn test_2_step_encryption(store: &Store) {
use masking::ExposeInterface;
let (encrypted_merchant_key, master_key) = {
let master_key = store.get_master_key();
let merchant_key: Vec<u8> = services::generate_aes256_key().unwrap().into();
let encrypted_merchant_key =
types::encrypt::<_, crate::pii::WithType>(merchant_key.into(), master_key)
.await
.unwrap()
.into_encrypted();
let encrypted_merchant_key =
storage_models::encryption::Encryption::new(encrypted_merchant_key);
(encrypted_merchant_key, master_key)
};
let dummy_data = "Hello, World!".to_string();
let encrypted_dummy_data = storage_models::encryption::Encryption::new(
types::encrypt::<_, crate::pii::WithType>(
masking::Secret::new(dummy_data.clone()),
&types::decrypt::<Vec<u8>, crate::pii::WithType>(
Some(encrypted_merchant_key.clone()),
master_key,
0,
0,
)
.await
.unwrap()
.unwrap()
.into_inner()
.expose(),
)
.await
.unwrap()
.into_encrypted(),
);
let dummy_data_returned = types::decrypt::<String, crate::pii::WithType>(
Some(encrypted_dummy_data),
&types::decrypt::<Vec<u8>, crate::pii::WithType>(
Some(encrypted_merchant_key),
master_key,
0,
0,
)
.await
.unwrap()
.unwrap()
.into_inner()
.expose(),
0,
0,
)
.await
.unwrap()
.unwrap()
.into_inner()
.expose();
assert!(dummy_data_returned == dummy_data)
}

View File

@ -119,7 +119,6 @@ pub struct Store {
#[cfg(feature = "kv_store")]
pub(crate) config: StoreConfig,
pub master_key: Vec<u8>,
pub migration_timestamp: i64,
}
#[cfg(feature = "kv_store")]
@ -183,7 +182,6 @@ impl Store {
drainer_num_partitions: config.drainer.num_partitions,
},
master_key: master_enc_key,
migration_timestamp: config.secrets.migration_encryption_timestamp,
}
}

View File

@ -74,7 +74,6 @@ impl behaviour::Conversion for Address {
other: Self::DstType,
db: &dyn StorageInterface,
merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<Self, ValidationError> {
let key = types::get_merchant_enc_key(db, merchant_id)
.await
@ -83,9 +82,7 @@ impl behaviour::Conversion for Address {
})?;
async {
let modified_at = other.modified_at.assume_utc().unix_timestamp();
let inner_decrypt =
|inner| types::decrypt(inner, &key, modified_at, migration_timestamp);
let inner_decrypt = |inner| types::decrypt(inner, &key);
Ok(Self {
id: Some(other.id),
address_id: other.address_id,

View File

@ -13,7 +13,6 @@ pub trait Conversion {
item: Self::DstType,
db: &dyn StorageInterface,
merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<Self, ValidationError>
where
Self: Sized;
@ -27,7 +26,6 @@ pub trait ReverseConversion<SrcType: Conversion> {
self,
db: &dyn StorageInterface,
merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<SrcType, ValidationError>;
}
@ -37,8 +35,7 @@ impl<T: Send, U: Conversion<DstType = T>> ReverseConversion<U> for T {
self,
db: &dyn StorageInterface,
merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<U, ValidationError> {
U::convert_back(self, db, merchant_id, migration_timestamp).await
U::convert_back(self, db, merchant_id).await
}
}

View File

@ -55,7 +55,6 @@ impl super::behaviour::Conversion for Customer {
item: Self::DstType,
db: &dyn StorageInterface,
merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<Self, ValidationError>
where
Self: Sized,
@ -66,12 +65,8 @@ impl super::behaviour::Conversion for Customer {
message: "Failed while getting key from key store".to_string(),
})?;
async {
let modified_at = item.modified_at.assume_utc().unix_timestamp();
let inner_decrypt =
|inner| types::decrypt(inner, &key, modified_at, migration_timestamp);
let inner_decrypt_email =
|inner| types::decrypt(inner, &key, modified_at, migration_timestamp);
let inner_decrypt = |inner| types::decrypt(inner, &key);
let inner_decrypt_email = |inner| types::decrypt(inner, &key);
Ok(Self {
id: Some(item.id),
customer_id: item.customer_id,

View File

@ -149,7 +149,6 @@ impl super::behaviour::Conversion for MerchantAccount {
item: Self::DstType,
db: &dyn StorageInterface,
merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<Self, ValidationError>
where
Self: Sized,
@ -160,7 +159,6 @@ impl super::behaviour::Conversion for MerchantAccount {
message: "Failed while getting key from key store".to_string(),
})?;
async {
let modified_at = item.modified_at.assume_utc().unix_timestamp();
Ok(Self {
id: Some(item.id),
merchant_id: item.merchant_id,
@ -170,15 +168,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| {
types::decrypt(inner, &key, modified_at, migration_timestamp)
})
.async_lift(|inner| types::decrypt(inner, &key))
.await?,
merchant_details: item
.merchant_details
.async_lift(|inner| {
types::decrypt(inner, &key, modified_at, migration_timestamp)
})
.async_lift(|inner| types::decrypt(inner, &key))
.await?,
webhook_details: item.webhook_details,
sub_merchants_enabled: item.sub_merchants_enabled,

View File

@ -89,14 +89,12 @@ impl behaviour::Conversion for MerchantConnectorAccount {
other: Self::DstType,
db: &dyn StorageInterface,
merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<Self, ValidationError> {
let key = types::get_merchant_enc_key(db, merchant_id)
.await
.change_context(ValidationError::InvalidValue {
message: "Error while getting key from keystore".to_string(),
})?;
let modified_at = other.modified_at.assume_utc().unix_timestamp();
Ok(Self {
id: Some(other.id),
@ -105,9 +103,7 @@ impl behaviour::Conversion for MerchantConnectorAccount {
connector_account_details: Encryptable::decrypt(
other.connector_account_details,
&key,
GcmAes256 {},
modified_at,
migration_timestamp,
GcmAes256,
)
.await
.change_context(ValidationError::InvalidValue {

View File

@ -36,14 +36,13 @@ impl super::behaviour::Conversion for MerchantKeyStore {
item: Self::DstType,
db: &dyn StorageInterface,
_merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<Self, ValidationError>
where
Self: Sized,
{
let key = &db.get_master_key();
Ok(Self {
key: Encryptable::decrypt(item.key, key, GcmAes256 {}, i64::MAX, migration_timestamp)
key: Encryptable::decrypt(item.key, key, GcmAes256)
.await
.change_context(ValidationError::InvalidValue {
message: "Failed while decrypting customer data".to_string(),

View File

@ -23,12 +23,11 @@ pub trait TypeEncryption<
key: &[u8],
crypt_algo: V,
) -> CustomResult<Self, errors::CryptoError>;
async fn decrypt(
encrypted_data: Encryption,
key: &[u8],
crypt_algo: V,
timestamp: i64,
migration_timestamp: i64,
) -> CustomResult<Self, errors::CryptoError>;
}
@ -54,22 +53,9 @@ impl<
encrypted_data: Encryption,
key: &[u8],
crypt_algo: V,
timestamp: i64,
migration_timestamp: i64,
) -> CustomResult<Self, errors::CryptoError> {
let encrypted = encrypted_data.into_inner();
let (data, encrypted) = if timestamp < migration_timestamp {
(
encrypted.clone(),
crypt_algo.encode_message(key, &encrypted)?,
)
} else {
(
crypt_algo.decode_message(key, encrypted.clone())?,
encrypted,
)
};
let data = crypt_algo.decode_message(key, encrypted.clone())?;
let value: String = std::str::from_utf8(&data)
.into_report()
@ -106,21 +92,9 @@ impl<
encrypted_data: Encryption,
key: &[u8],
crypt_algo: V,
timestamp: i64,
migration_timestamp: i64,
) -> CustomResult<Self, errors::CryptoError> {
let encrypted = encrypted_data.into_inner();
let (data, encrypted) = if timestamp < migration_timestamp {
(
encrypted.clone(),
crypt_algo.encode_message(key, &encrypted)?,
)
} else {
(
crypt_algo.decode_message(key, encrypted.clone())?,
encrypted,
)
};
let data = crypt_algo.decode_message(key, encrypted.clone())?;
let value: serde_json::Value = serde_json::from_slice(&data)
.into_report()
@ -152,22 +126,10 @@ impl<
encrypted_data: Encryption,
key: &[u8],
crypt_algo: V,
timestamp: i64,
migration_timestamp: i64,
) -> CustomResult<Self, errors::CryptoError> {
let encrypted = encrypted_data.into_inner();
let data = crypt_algo.decode_message(key, encrypted.clone())?;
let (data, encrypted) = if timestamp < migration_timestamp {
(
encrypted.clone(),
crypt_algo.encode_message(key, &encrypted)?,
)
} else {
(
crypt_algo.decode_message(key, encrypted.clone())?,
encrypted,
)
};
Ok(Self::new(data.into(), encrypted))
}
}
@ -241,7 +203,7 @@ where
crypto::Encryptable<Secret<E, S>>: TypeEncryption<E, crypto::GcmAes256, S>,
{
request::record_operation_time(
crypto::Encryptable::encrypt(inner, key, crypto::GcmAes256 {}),
crypto::Encryptable::encrypt(inner, key, crypto::GcmAes256),
&ENCRYPTION_TIME,
)
.await
@ -264,22 +226,12 @@ where
pub async fn decrypt<T: Clone, S: masking::Strategy<T>>(
inner: Option<Encryption>,
key: &[u8],
timestamp: i64,
migration_timestamp: i64,
) -> CustomResult<Option<crypto::Encryptable<Secret<T, S>>>, errors::CryptoError>
where
crypto::Encryptable<Secret<T, S>>: TypeEncryption<T, crypto::GcmAes256, S>,
{
request::record_operation_time(
inner.async_map(|item| {
crypto::Encryptable::decrypt(
item,
key,
crypto::GcmAes256 {},
timestamp,
migration_timestamp,
)
}),
inner.async_map(|item| crypto::Encryptable::decrypt(item, key, crypto::GcmAes256)),
&DECRYPTION_TIME,
)
.await