Skip to content

Commit

Permalink
feat(ch-app): Finished error-handling in keyring service and introduc…
Browse files Browse the repository at this point in the history
…es 'doc_type' feature
  • Loading branch information
schoenenberg committed Oct 16, 2023
1 parent 8965f5e commit 387498c
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 32 deletions.
4 changes: 4 additions & 0 deletions clearing-house-app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,7 @@ thiserror = "1.0.48"
serial_test = "2.0.0"
# Tempfile creation for testing
tempfile = "3.8.0"

[features]
# Enables the doc_type API
doc_type = []
4 changes: 1 addition & 3 deletions clearing-house-app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ async fn main() -> Result<(), anyhow::Error> {
let app_state = AppState::init(&conf).await?;

// Setup router
let app = axum::Router::new()
.merge(ports::logging_api::router())
.nest("/doctype", ports::doc_type_api::router())
let app = ports::router()
.with_state(app_state);

// Bind port and start server
Expand Down
17 changes: 17 additions & 0 deletions clearing-house-app/src/ports/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,27 @@
//! services. In this case, the logging service implements REST-API endpoints to provide access to
//! the logging service.
use axum::response::Response;
use crate::AppState;

#[cfg(doc_type)]
pub(crate) mod doc_type_api;
pub(crate) mod logging_api;

/// Router for the logging service and the doc_type service
#[cfg(doc_type)]
pub(crate) fn router() -> axum::routing::Router<AppState> {
axum::Router::new()
.merge(ports::logging_api::router())
.nest("/doctype", ports::doc_type_api::router());
}

/// Router for the logging service
#[cfg(not(doc_type))]
pub(crate) fn router() -> axum::routing::Router<AppState> {
axum::Router::new()
.merge(logging_api::router())
}

#[derive(Debug)]
pub(crate) enum ApiResponse<T: serde::Serialize> {
PreFlight(()),
Expand Down
13 changes: 8 additions & 5 deletions clearing-house-app/src/services/document_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ use crate::model::document::Document;
use crate::model::{parse_date, validate_and_sanitize_dates, SortingOrder};
use crate::services::keyring_service::KeyringService;
use crate::services::{DocumentReceipt, QueryResult};
use anyhow::anyhow;
use std::convert::TryFrom;
use std::sync::Arc;

/// Error type for DocumentService
#[derive(thiserror::Error, Debug)]
pub enum DocumentServiceError {
#[error("Document already exists!")]
Expand All @@ -26,13 +26,15 @@ pub enum DocumentServiceError {
#[error("Error while creating the chain hash!")]
ChainHashError,
#[error("Error while retrieving keys from keyring!")]
KeyringServiceError(#[from] anyhow::Error),
KeyringServiceError(#[from] crate::services::keyring_service::KeyringServiceError),
#[error("Invalid dates in query!")]
InvalidDates,
#[error("Document not found!")]
NotFound,
#[error("Key Ciphertext corrupted!")]
CorruptedCiphertext(#[from] hex::FromHexError),
#[error("Error while encrypting!")]
EncryptionError,
}

impl axum::response::IntoResponse for DocumentServiceError {
Expand All @@ -50,10 +52,11 @@ impl axum::response::IntoResponse for DocumentServiceError {
)
.into_response(),
Self::ChainHashError => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response(),
Self::KeyringServiceError(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
Self::KeyringServiceError(e) => e.into_response(),
Self::InvalidDates => (StatusCode::BAD_REQUEST, self.to_string()).into_response(),
Self::NotFound => (StatusCode::NOT_FOUND, self.to_string()).into_response(),
Self::CorruptedCiphertext(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
Self::EncryptionError => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response(),
}
}
}
Expand Down Expand Up @@ -111,7 +114,7 @@ impl DocumentService {
}
Err(e) => {
error!("Error while retrieving keys: {:?}", e);
Err(anyhow!("Error while retrieving keys!")) // InternalError
Err(DocumentServiceError::KeyringServiceError(e))
}
}?;

Expand All @@ -123,7 +126,7 @@ impl DocumentService {
}
Err(e) => {
error!("Error while encrypting: {:?}", e);
Err(anyhow!("Error while encrypting!")) // InternalError
Err(DocumentServiceError::EncryptionError)
}
}?;

Expand Down
75 changes: 55 additions & 20 deletions clearing-house-app/src/services/keyring_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,37 @@ use crate::crypto::restore_key_map;
use crate::db::key_store::KeyStore;
use crate::model::claims::ChClaims;
use crate::model::crypto::{KeyCtList, KeyMap, KeyMapListItem};
use crate::model::doc_type::DocumentType;
use anyhow::anyhow;

#[derive(Debug, thiserror::Error)]
pub enum KeyringServiceError {
#[error("Keymap generation error")]
KeymapGenerationFailed,
#[error("Keymap restoration error")]
KeymapRestorationFailed,
#[error("Document type not found")]
DocumentTypeNotFound,
#[error("Error during database operation: {description}: {source}")]
DatabaseError {
source: anyhow::Error,
description: String,
},
#[error("Error while decrypting keys")]
DecryptionError,
}

impl axum::response::IntoResponse for KeyringServiceError {
fn into_response(self) -> axum::response::Response {
use axum::http::StatusCode;
match self {
Self::KeymapGenerationFailed => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response(),
Self::KeymapRestorationFailed => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response(),
Self::DocumentTypeNotFound => (StatusCode::NOT_FOUND, self.to_string()).into_response(),
Self::DatabaseError { source, description } =>
(StatusCode::INTERNAL_SERVER_ERROR, format!("{}: {}", description, source)).into_response(),
Self::DecryptionError => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response(),
}
}
}

#[derive(Clone, Debug)]
pub struct KeyringService {
Expand All @@ -22,7 +51,7 @@ impl KeyringService {
ch_claims: ChClaims,
_pid: String,
dt_id: String,
) -> anyhow::Result<KeyMap> {
) -> Result<KeyMap, KeyringServiceError> {
trace!("generate_keys");
trace!("...user '{:?}'", &ch_claims.client_id);
match self.db.get_msk().await {
Expand All @@ -38,23 +67,23 @@ impl KeyringService {
}
Err(e) => {
error!("Error while generating key map: {}", e);
Err(anyhow!("Error while generating keys")) // InternalError
Err(KeyringServiceError::KeymapGenerationFailed)
}
}
}
Ok(None) => {
warn!("document type {} not found", &dt_id);
Err(anyhow!("Document type not found!")) // BadRequest
Err(KeyringServiceError::DocumentTypeNotFound)
}
Err(e) => {
warn!("Error while retrieving document type: {}", e);
Err(anyhow!("Error while retrieving document type")) // InternalError
Err(KeyringServiceError::DatabaseError {source: e, description: "Error while retrieving document type".to_string()})
}
}
}
Err(e) => {
error!("Error while retrieving master key: {}", e);
Err(anyhow!("Error while generating keys")) // InternalError
Err(KeyringServiceError::DatabaseError {source: e, description: "Error while retrieving master key".to_string()})
}
}
}
Expand All @@ -65,7 +94,7 @@ impl KeyringService {
ch_claims: ChClaims,
_pid: Option<String>,
key_cts: &KeyCtList,
) -> anyhow::Result<Vec<KeyMapListItem>> {
) -> Result<Vec<KeyMapListItem>, KeyringServiceError> {
trace!("decrypt_keys");
trace!("...user '{:?}'", &ch_claims.client_id);
debug!("number of cts to decrypt: {}", &key_cts.cts.len());
Expand Down Expand Up @@ -106,24 +135,24 @@ impl KeyringService {

// Currently, we don't tolerate errors while decrypting keys
if error_count > 0 {
Err(anyhow!("Error while decrypting keys")) // InternalError
Err(KeyringServiceError::DecryptionError)
} else {
Ok(key_maps)
}
}
Ok(None) => {
warn!("document type {} not found", &key_cts.dt);
Err(anyhow!("Document type not found!")) // BadRequest
Err(KeyringServiceError::DocumentTypeNotFound)
}
Err(e) => {
warn!("Error while retrieving document type: {}", e);
Err(anyhow!("Document type not found!")) // NotFound
Err(KeyringServiceError::DatabaseError {source: e, description: "Error while retrieving document type".to_string()})
}
}
}
Err(e) => {
error!("Error while retrieving master key: {}", e);
Err(anyhow!("Error while decrypting keys")) // InternalError
Err(KeyringServiceError::DecryptionError)
}
}
}
Expand All @@ -135,7 +164,7 @@ impl KeyringService {
keys_ct: String,
_pid: Option<String>,
dt_id: String,
) -> anyhow::Result<KeyMap> {
) -> Result<KeyMap, KeyringServiceError> {
trace!("decrypt_key_map");
trace!("...user '{:?}'", &ch_claims.client_id);
trace!("ct: {}", &keys_ct);
Expand All @@ -148,30 +177,30 @@ impl KeyringService {
// validate keys_ct input
let keys_ct = hex::decode(keys_ct).map_err(|e| {
error!("Error while decoding key ciphertext: {}", e);
anyhow!("Error while decrypting keys") // InternalError
KeyringServiceError::DecryptionError
})?;

match restore_key_map(key, dt, keys_ct) {
Ok(key_map) => Ok(key_map),
Err(e) => {
error!("Error while generating key map: {}", e);
Err(anyhow!("Error while restoring keys")) // InternalError
Err(KeyringServiceError::KeymapRestorationFailed)
}
}
}
Ok(None) => {
warn!("document type {} not found", &dt_id);
Err(anyhow!("Document type not found!")) // BadRequest
Err(KeyringServiceError::DocumentTypeNotFound)
}
Err(e) => {
warn!("Error while retrieving document type: {}", e);
Err(anyhow!("Document type not found!")) // NotFound
Err(KeyringServiceError::DatabaseError {source: e, description: "Error while retrieving document type".to_string()})
}
}
}
Err(e) => {
error!("Error while retrieving master key: {}", e);
Err(anyhow!("Error while decrypting keys")) // InternalError
Err(KeyringServiceError::DecryptionError)
}
}
}
Expand All @@ -182,10 +211,11 @@ impl KeyringService {
ch_claims: ChClaims,
pid: Option<String>,
cts: &KeyCtList,
) -> anyhow::Result<Vec<KeyMapListItem>> {
) -> Result<Vec<KeyMapListItem>, KeyringServiceError> {
self.decrypt_keys(ch_claims, pid, cts).await
}

#[cfg(doc_type)]
pub(crate) async fn create_doc_type(
&self,
doc_type: DocumentType,
Expand Down Expand Up @@ -213,6 +243,7 @@ impl KeyringService {
}
}

#[cfg(doc_type)]
pub(crate) async fn update_doc_type(
&self,
id: String,
Expand Down Expand Up @@ -240,6 +271,7 @@ impl KeyringService {
}
}

#[cfg(doc_type)]
pub(crate) async fn delete_doc_type(&self, id: String, pid: String) -> anyhow::Result<String> {
match self.db.delete_document_type(&id, &pid).await {
Ok(true) => Ok(String::from("Document type deleted!")), // NoContent
Expand All @@ -254,6 +286,7 @@ impl KeyringService {
}
}

#[cfg(doc_type)]
pub(crate) async fn get_doc_type(
&self,
id: String,
Expand All @@ -273,12 +306,14 @@ impl KeyringService {
}
}

#[cfg(doc_type)]

pub(crate) async fn get_doc_types(&self) -> anyhow::Result<Vec<DocumentType>> {
match self.db.get_all_document_types().await {
//TODO: would like to send "{}" instead of "null" when dt is not found
Ok(dt) => Ok(dt),
Err(e) => {
error!("Error while retrieving default doctypes: {:?}", e);
error!("Error while retrieving default doc_types: {:?}", e);
Err(anyhow!("Error while retrieving all document types")) // InternalError
}
}
Expand Down
6 changes: 2 additions & 4 deletions clearing-house-app/src/services/logging_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::model::{
};
use crate::services::document_service::DocumentService;

/// Error type for LoggingService
#[derive(Debug, thiserror::Error)]
pub enum LoggingServiceError {
#[error("Received empty payload, which cannot be logged!")]
Expand Down Expand Up @@ -373,10 +374,7 @@ impl LoggingService {
}
Err(e) => {
error!("Error while retrieving message: {:?}", e);
Err(LoggingServiceError::DatabaseError {
source: e,
description: format!("Error while retrieving messages for pid '{pid}'"),
})
Err(LoggingServiceError::DocumentServiceError(e))
}
}
}
Expand Down

0 comments on commit 387498c

Please sign in to comment.