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

@ -101,7 +101,6 @@ admin_api_key = "test_admin" # admin API key for admin authentication. Only
kms_encrypted_admin_api_key = "" # Base64-encoded (KMS encrypted) ciphertext of the admin_api_key. Only applicable when KMS is enabled. kms_encrypted_admin_api_key = "" # Base64-encoded (KMS encrypted) ciphertext of the admin_api_key. Only applicable when KMS is enabled.
jwt_secret = "secret" # JWT secret used for user authentication. Only applicable when KMS is disabled. jwt_secret = "secret" # JWT secret used for user authentication. Only applicable when KMS is disabled.
kms_encrypted_jwt_secret = "" # Base64-encoded (KMS encrypted) ciphertext of the jwt_secret. Only applicable when KMS is enabled. kms_encrypted_jwt_secret = "" # Base64-encoded (KMS encrypted) ciphertext of the jwt_secret. Only applicable when KMS is enabled.
migration_encryption_timestamp = 0 # Timestamp to decide which entries are not encrypted in the database.
# Locker settings contain details for accessing a card locker, a # Locker settings contain details for accessing a card locker, a
# PCI Compliant storage entity which stores payment method information # PCI Compliant storage entity which stores payment method information

View File

@ -32,7 +32,6 @@ connection_timeout = 10
[secrets] [secrets]
admin_api_key = "test_admin" admin_api_key = "test_admin"
migration_encryption_timestamp = 1682425530
master_enc_key = "73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e13a" master_enc_key = "73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e13a"
[locker] [locker]

View File

@ -16,8 +16,8 @@ kms = ["external_services/kms","dep:aws-config"]
email = ["external_services/email","dep:aws-config"] email = ["external_services/email","dep:aws-config"]
basilisk = ["kms"] basilisk = ["kms"]
stripe = ["dep:serde_qs"] stripe = ["dep:serde_qs"]
sandbox = ["kms", "stripe", "basilisk", "s3","email"] sandbox = ["kms", "stripe", "basilisk", "s3", "email"]
production = ["kms", "stripe", "basilisk", "s3","pii-encryption-script","email"] production = ["kms", "stripe", "basilisk", "s3", "email"]
olap = [] olap = []
oltp = [] oltp = []
kv_store = [] kv_store = []
@ -25,7 +25,6 @@ accounts_cache = []
openapi = ["olap", "oltp"] openapi = ["olap", "oltp"]
vergen = ["router_env/vergen"] vergen = ["router_env/vergen"]
multiple_mca = ["api_models/multiple_mca"] multiple_mca = ["api_models/multiple_mca"]
pii-encryption-script = []
dummy_connector = ["api_models/dummy_connector"] dummy_connector = ["api_models/dummy_connector"]
external_access_dc = ["dummy_connector"] external_access_dc = ["dummy_connector"]
detailed_errors = ["api_models/detailed_errors", "error-stack/serde"] 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); 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); logger::info!("Application started [{:?}] [{:?}]", conf.server, conf.log);
#[allow(clippy::expect_used)] #[allow(clippy::expect_used)]

View File

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

View File

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

View File

@ -239,7 +239,7 @@ pub async fn delete_customer(
.change_context(errors::ApiErrorResponse::InternalServerError) .change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while getting key for encryption")?; .attach_printable("Failed while getting key for encryption")?;
let redacted_encrypted_value: Encryptable<masking::Secret<_>> = let redacted_encrypted_value: Encryptable<masking::Secret<_>> =
Encryptable::encrypt(REDACTED.to_string().into(), &key, GcmAes256 {}) Encryptable::encrypt(REDACTED.to_string().into(), &key, GcmAes256)
.await .await
.change_context(errors::ApiErrorResponse::InternalServerError)?; .change_context(errors::ApiErrorResponse::InternalServerError)?;
@ -278,7 +278,7 @@ pub async fn delete_customer(
let updated_customer = storage::CustomerUpdate::Update { let updated_customer = storage::CustomerUpdate::Update {
name: Some(redacted_encrypted_value.clone()), name: Some(redacted_encrypted_value.clone()),
email: Some( email: Some(
Encryptable::encrypt(REDACTED.to_string().into(), &key, GcmAes256 {}) Encryptable::encrypt(REDACTED.to_string().into(), &key, GcmAes256)
.await .await
.change_context(errors::ApiErrorResponse::InternalServerError)?, .change_context(errors::ApiErrorResponse::InternalServerError)?,
), ),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,6 @@ pub mod scheduler;
pub mod middleware; pub mod middleware;
#[cfg(feature = "openapi")] #[cfg(feature = "openapi")]
pub mod openapi; pub mod openapi;
pub mod scripts;
pub mod services; pub mod services;
pub mod types; pub mod types;
pub mod utils; 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")] #[cfg(feature = "kv_store")]
pub(crate) config: StoreConfig, pub(crate) config: StoreConfig,
pub master_key: Vec<u8>, pub master_key: Vec<u8>,
pub migration_timestamp: i64,
} }
#[cfg(feature = "kv_store")] #[cfg(feature = "kv_store")]
@ -183,7 +182,6 @@ impl Store {
drainer_num_partitions: config.drainer.num_partitions, drainer_num_partitions: config.drainer.num_partitions,
}, },
master_key: master_enc_key, 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, other: Self::DstType,
db: &dyn StorageInterface, db: &dyn StorageInterface,
merchant_id: &str, merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<Self, ValidationError> { ) -> CustomResult<Self, ValidationError> {
let key = types::get_merchant_enc_key(db, merchant_id) let key = types::get_merchant_enc_key(db, merchant_id)
.await .await
@ -83,9 +82,7 @@ impl behaviour::Conversion for Address {
})?; })?;
async { async {
let modified_at = other.modified_at.assume_utc().unix_timestamp(); let inner_decrypt = |inner| types::decrypt(inner, &key);
let inner_decrypt =
|inner| types::decrypt(inner, &key, modified_at, migration_timestamp);
Ok(Self { Ok(Self {
id: Some(other.id), id: Some(other.id),
address_id: other.address_id, address_id: other.address_id,

View File

@ -13,7 +13,6 @@ pub trait Conversion {
item: Self::DstType, item: Self::DstType,
db: &dyn StorageInterface, db: &dyn StorageInterface,
merchant_id: &str, merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<Self, ValidationError> ) -> CustomResult<Self, ValidationError>
where where
Self: Sized; Self: Sized;
@ -27,7 +26,6 @@ pub trait ReverseConversion<SrcType: Conversion> {
self, self,
db: &dyn StorageInterface, db: &dyn StorageInterface,
merchant_id: &str, merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<SrcType, ValidationError>; ) -> CustomResult<SrcType, ValidationError>;
} }
@ -37,8 +35,7 @@ impl<T: Send, U: Conversion<DstType = T>> ReverseConversion<U> for T {
self, self,
db: &dyn StorageInterface, db: &dyn StorageInterface,
merchant_id: &str, merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<U, ValidationError> { ) -> 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, item: Self::DstType,
db: &dyn StorageInterface, db: &dyn StorageInterface,
merchant_id: &str, merchant_id: &str,
migration_timestamp: i64,
) -> CustomResult<Self, ValidationError> ) -> CustomResult<Self, ValidationError>
where where
Self: Sized, Self: Sized,
@ -66,12 +65,8 @@ impl super::behaviour::Conversion for Customer {
message: "Failed while getting key from key store".to_string(), message: "Failed while getting key from key store".to_string(),
})?; })?;
async { async {
let modified_at = item.modified_at.assume_utc().unix_timestamp(); let inner_decrypt = |inner| types::decrypt(inner, &key);
let inner_decrypt_email = |inner| types::decrypt(inner, &key);
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);
Ok(Self { Ok(Self {
id: Some(item.id), id: Some(item.id),
customer_id: item.customer_id, customer_id: item.customer_id,

View File

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

View File

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

View File

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

View File

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