Skip to content

Commit

Permalink
feat(router): add support v2 /add /retrieve /delete api handler
Browse files Browse the repository at this point in the history
  • Loading branch information
ShankarSinghC committed Oct 28, 2024
1 parent 98d6b20 commit 5c3dd38
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 132 deletions.
24 changes: 10 additions & 14 deletions src/routes/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,14 @@ pub async fn add_card(
.await?;

let (duplication_check, output) = match stored_data {
Some(locker) => {
let decrypted_locker_data =
crypto_operation::decrypt_data(&tenant_app_state, crypto_manager, locker)
.await?;
Some(mut locker) => {
crypto_operation::decrypt_data(&tenant_app_state, crypto_manager, &mut locker)
.await?;

let duplication_check = transformers::validate_card_metadata(
&decrypted_locker_data,
&request.data,
)?;
let duplication_check =
transformers::validate_card_metadata(&locker, &request.data)?;

(Some(duplication_check), decrypted_locker_data)
(Some(duplication_check), locker)
}
None => {
let encrypted_locker_data = crypto_operation::encrypt_data_and_insert_into_db(
Expand Down Expand Up @@ -197,7 +194,7 @@ pub async fn retrieve_card(
.find_by_entity_id(&tenant_app_state, request.merchant_id.clone())
.await?;

let locker = tenant_app_state
let mut locker = tenant_app_state
.db
.find_by_locker_id_merchant_id_customer_id(
request.card_reference.clone().into(),
Expand All @@ -206,10 +203,9 @@ pub async fn retrieve_card(
)
.await?;

let decrypted_locker_data =
crypto_operation::decrypt_data(&tenant_app_state, crypto_manager, locker).await?;
crypto_operation::decrypt_data(&tenant_app_state, crypto_manager, &mut locker).await?;

decrypted_locker_data
locker
.ttl
.map(|ttl| -> Result<(), error::ApiError> {
if utils::date_time::now() > ttl {
Expand All @@ -231,7 +227,7 @@ pub async fn retrieve_card(
})
.transpose()?;

Ok(Json(decrypted_locker_data.try_into()?))
Ok(Json(locker.try_into()?))
}

/// `/cards/fingerprint` handling the creation and retrieval of card fingerprint
Expand Down
41 changes: 34 additions & 7 deletions src/routes/data/crypto_operation.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use masking::ExposeInterface;

use crate::{
app::TenantAppState,
crypto::keymanager::CryptoOperationsManager,
error::{self, ContainerError, ResultContainerExt},
routes::data::types,
storage::{
types::{Encryptable, Locker, LockerNew},
storage_v2::{types::VaultNew, VaultInterface},
types::{Locker, LockerNew},
LockerInterface,
},
};
Expand Down Expand Up @@ -35,18 +38,42 @@ pub async fn encrypt_data_and_insert_into_db<'a>(
Ok(locker)
}

pub async fn decrypt_data(
pub async fn decrypt_data<T>(
tenant_app_state: &TenantAppState,
crypto_operator: Box<dyn CryptoOperationsManager>,
mut locker: Locker,
) -> Result<Locker, ContainerError<error::ApiError>> {
if let Some(encrypted_data) = locker.data.get_encrypted_inner_value() {
data: &mut T,
) -> Result<(), ContainerError<error::ApiError>>
where
T: types::SecretDataManager,
{
if let Some(encrypted_data) = data.get_encrypted_inner_value() {
let decrypted_data = crypto_operator
.decrypt_data(tenant_app_state, encrypted_data)
.await?;

locker.data = Encryptable::from_decrypted_data(decrypted_data)
data.set_decrypted_data(decrypted_data);
}
Ok(())
}

Ok(locker)
pub async fn encrypt_data_and_insert_into_db_v2(
tenant_app_state: &TenantAppState,
crypto_operator: Box<dyn CryptoOperationsManager>,
request: crate::routes::routes_v2::data::types::StoreDataRequest,
) -> Result<crate::storage::storage_v2::types::Vault, ContainerError<error::ApiError>> {
let data_to_be_encrypted = serde_json::to_vec(&request.data.clone().expose())
.change_error(error::ApiError::EncodingError)?;

let encrypted_data = crypto_operator
.encrypt_data(tenant_app_state, data_to_be_encrypted.into())
.await?;

let vault_new = VaultNew::new(request, encrypted_data.into());

let vault = tenant_app_state
.db
.insert_or_get_from_vault(vault_new)
.await?;

Ok(vault)
}
11 changes: 11 additions & 0 deletions src/routes/data/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ impl From<(Option<DataDuplicationCheck>, storage::types::Locker)>
}
}

impl From<storage::storage_v2::types::Vault>
for crate::routes::routes_v2::data::types::StoreDataResponse
{
fn from(value: storage::storage_v2::types::Vault) -> Self {
Self {
entity_id: value.entity_id,
vault_id: value.vault_id.expose(),
}
}
}

impl TryFrom<storage::types::Locker> for super::types::RetrieveCardResponse {
type Error = ContainerError<error::ApiError>;
fn try_from(value: storage::types::Locker) -> Result<Self, Self::Error> {
Expand Down
35 changes: 24 additions & 11 deletions src/routes/data/types.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
// #[derive(serde::Serialize, serde::Deserialize)]
// #[serde(rename_all = "camelCase")]
// pub struct Dedup {
// hash1: Option<String>,
// hash2: Option<String>,
// hash1_reference: Option<String>,
// hash2_reference: Option<String>,
// }
use masking::{Secret, StrongSecret};

use masking::Secret;

use crate::{error, storage, utils};
use crate::{
error,
storage::{
self,
types::{Encryptable, Locker},
},
utils,
};

#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Card {
Expand Down Expand Up @@ -180,3 +178,18 @@ impl Validation for StoreCardRequest {
}
}
}

pub trait SecretDataManager {
fn get_encrypted_inner_value(&self) -> Option<Secret<Vec<u8>>>;
fn set_decrypted_data(&mut self, decrypted_data: StrongSecret<Vec<u8>>);
}

impl SecretDataManager for Locker {
fn get_encrypted_inner_value(&self) -> Option<Secret<Vec<u8>>> {
self.data.get_encrypted_inner_value()
}

fn set_decrypted_data(&mut self, decrypted_data: StrongSecret<Vec<u8>>) {
self.data = Encryptable::from_decrypted_data(decrypted_data);
}
}
87 changes: 74 additions & 13 deletions src/routes/routes_v2/data.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,92 @@
use axum::Json;
use error_stack::ResultExt;
use masking::PeekInterface;
pub mod types;

use crate::{
crypto::keymanager::{self, KeyProvider},
custom_extractors::TenantStateResolver,
error::{self, ContainerError},
error::{self, ContainerError, ResultContainerExt},
routes::data::crypto_operation,
storage::storage_v2::VaultInterface,
utils,
};

pub async fn delete_data(
TenantStateResolver(_tenant_app_state): TenantStateResolver,
Json(_request): Json<types::DeleteDataRequest>,
TenantStateResolver(tenant_app_state): TenantStateResolver,
Json(request): Json<types::DeleteDataRequest>,
) -> Result<Json<types::DeleteDataResponse>, ContainerError<error::ApiError>> {
// need handle this once the key manger service is ready
todo!()
let _entity = keymanager::get_dek_manager()
.find_by_entity_id(&tenant_app_state, request.entity_id.clone())
.await?;

let _delete_status = tenant_app_state
.db
.delete_from_vault(request.vault_id.clone().into(), &request.entity_id)
.await?;
Ok(Json(types::DeleteDataResponse {
entity_id: request.entity_id,
vault_id: request.vault_id,
}))
}

pub async fn retrieve_data(
TenantStateResolver(_tenant_app_state): TenantStateResolver,
Json(_request): Json<types::RetrieveDataRequest>,
TenantStateResolver(tenant_app_state): TenantStateResolver,
Json(request): Json<types::RetrieveDataRequest>,
) -> Result<Json<types::RetrieveDataResponse>, ContainerError<error::ApiError>> {
// need handle this once the key manger service is ready
todo!()
let crypto_manager = keymanager::get_dek_manager()
.find_by_entity_id(&tenant_app_state, request.entity_id.clone())
.await?;

let mut vault = tenant_app_state
.db
.find_by_vault_id_entity_id(request.vault_id.clone().into(), &request.entity_id)
.await?;

crypto_operation::decrypt_data(&tenant_app_state, crypto_manager, &mut vault).await?;

vault
.expires_at
.map(|ttl| -> Result<(), error::ApiError> {
if utils::date_time::now() > ttl {
tokio::spawn(async move {
tenant_app_state
.db
.delete_from_vault(request.vault_id.into(), &request.entity_id)
.await
});

Err(error::ApiError::NotFoundError)
} else {
Ok(())
}
})
.transpose()?;
let decrypted_data = vault
.data
.get_decrypted_inner_value()
.ok_or(error::ApiError::UnknownError)
.attach_printable("Failed to decrypt the stored data")?;
let og = serde_json::from_slice(decrypted_data.peek().as_ref())
.change_error(error::ApiError::DecodingError)?;

Ok(Json(types::RetrieveDataResponse { data: og }))
}

pub async fn add_data(
TenantStateResolver(_tenant_app_state): TenantStateResolver,
Json(_request): Json<types::StoreDataRequest>,
TenantStateResolver(tenant_app_state): TenantStateResolver,
Json(request): Json<types::StoreDataRequest>,
) -> Result<Json<types::StoreDataResponse>, ContainerError<error::ApiError>> {
// need handle this once the key manger service is ready
todo!()
let crypto_manager = keymanager::get_dek_manager()
.find_or_create_entity(&tenant_app_state, request.entity_id.clone())
.await?;

let insert_data = crypto_operation::encrypt_data_and_insert_into_db_v2(
&tenant_app_state,
crypto_manager,
request,
)
.await?;

Ok(Json(types::StoreDataResponse::from(insert_data)))
}
19 changes: 16 additions & 3 deletions src/routes/routes_v2/data/types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use masking::Secret;
use masking::{Secret, StrongSecret};

use crate::routes::data::types::Ttl;
use crate::{
routes::data::types::{SecretDataManager, Ttl},
storage::{storage_v2::types::Vault, types::Encryptable},
};

#[derive(serde::Serialize, serde::Deserialize)]
pub struct DeleteDataRequest {
Expand All @@ -22,7 +25,7 @@ pub struct RetrieveDataRequest {

#[derive(serde::Serialize, serde::Deserialize)]
pub struct RetrieveDataResponse {
pub payload: Secret<serde_json::Value>,
pub data: Secret<serde_json::Value>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
Expand All @@ -38,3 +41,13 @@ pub struct StoreDataResponse {
pub entity_id: String,
pub vault_id: String,
}

impl SecretDataManager for Vault {
fn get_encrypted_inner_value(&self) -> Option<Secret<Vec<u8>>> {
self.data.get_encrypted_inner_value()
}

fn set_decrypted_data(&mut self, decrypted_data: StrongSecret<Vec<u8>>) {
self.data = Encryptable::from_decrypted_data(decrypted_data);
}
}
2 changes: 0 additions & 2 deletions src/storage/storage_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@ pub(crate) trait VaultInterface {
&self,
vault_id: Secret<String>,
entity_id: &str,
key: &Self::Algorithm,
) -> Result<types::Vault, ContainerError<Self::Error>>;

/// Insert data into vault table
async fn insert_or_get_from_vault(
&self,
new: types::VaultNew,
key: &Self::Algorithm,
) -> Result<types::Vault, ContainerError<Self::Error>>;

/// Delete data from the vault
Expand Down
16 changes: 5 additions & 11 deletions src/storage/storage_v2/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ use masking::{ExposeInterface, Secret};
use crate::{
crypto::encryption_manager::managers::aes::GcmAes256,
error::{self, ContainerError, ResultContainerExt},
storage::{
schema,
types::{StorageDecryption, StorageEncryption},
Storage,
},
storage::{schema, Storage},
};

use super::{types, VaultInterface};
Expand All @@ -24,7 +20,6 @@ impl VaultInterface for Storage {
&self,
vault_id: Secret<String>,
entity_id: &str,
key: &Self::Algorithm,
) -> Result<types::Vault, ContainerError<Self::Error>> {
let mut conn = self.get_conn().await?;

Expand All @@ -44,31 +39,30 @@ impl VaultInterface for Storage {
})
.map_err(error::ContainerError::from)
.map_err(From::from)
.and_then(|inner| Ok(inner.decrypt(key)?))
.map(|inner| inner.into())
}

async fn insert_or_get_from_vault(
&self,
new: types::VaultNew,
key: &Self::Algorithm,
) -> Result<types::Vault, ContainerError<Self::Error>> {
let mut conn = self.get_conn().await?;
let cloned_new = new.clone();

let query: Result<_, diesel::result::Error> =
diesel::insert_into(types::VaultInner::table())
.values(new.encrypt(key)?)
.values(new)
.get_result::<types::VaultInner>(&mut conn)
.await;

match query {
Ok(inner) => Ok(inner.decrypt(key)?),
Ok(inner) => Ok(inner.into()),
Err(error) => match error {
diesel::result::Error::DatabaseError(
diesel::result::DatabaseErrorKind::UniqueViolation,
_,
) => {
self.find_by_vault_id_entity_id(cloned_new.vault_id, &cloned_new.entity_id, key)
self.find_by_vault_id_entity_id(cloned_new.vault_id, &cloned_new.entity_id)
.await
}
error => Err(error).change_error(error::StorageError::InsertError)?,
Expand Down
Loading

0 comments on commit 5c3dd38

Please sign in to comment.