mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +08:00 
			
		
		
		
	refactor(router): remove pii-encryption-script feature and use of timestamps for decryption (#1350)
				
					
				
			This commit is contained in:
		| @ -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 | ||||||
|  | |||||||
| @ -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] | ||||||
|  | |||||||
| @ -17,7 +17,7 @@ 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"] | ||||||
|  | |||||||
| @ -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)] | ||||||
|  | |||||||
| @ -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, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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)] | ||||||
|  | |||||||
| @ -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)?, | ||||||
|         ), |         ), | ||||||
|  | |||||||
| @ -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] | ||||||
|  | |||||||
| @ -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]) | ||||||
|  | |||||||
| @ -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) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -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) | ||||||
|             }) |             }) | ||||||
|  | |||||||
| @ -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) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -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) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -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; | ||||||
|  | |||||||
| @ -1,2 +0,0 @@ | |||||||
| #[cfg(feature = "pii-encryption-script")] |  | ||||||
| pub mod pii_encryption; |  | ||||||
| @ -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) |  | ||||||
| } |  | ||||||
| @ -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, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | |||||||
| @ -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, | ||||||
|  | |||||||
| @ -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 | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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, | ||||||
|  | |||||||
| @ -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, | ||||||
|  | |||||||
| @ -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 { | ||||||
|  | |||||||
| @ -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(), | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Sanchith Hegde
					Sanchith Hegde