Skip to content

Commit

Permalink
feat(app): add 'static_process_owner' for processes (#117)
Browse files Browse the repository at this point in the history
* feat(app): add 'static_process_owner' for processes

* feat(app): Added new parameter to docs

* feat(app): Add test case for static_process_owner
  • Loading branch information
schoenenberg authored Jul 2, 2024
1 parent f522201 commit 33cd21a
Show file tree
Hide file tree
Showing 11 changed files with 504 additions and 438 deletions.
826 changes: 404 additions & 422 deletions clearing-house-app/Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions clearing-house-app/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ log_level = "INFO" # TRACE, DEBUG, INFO, WARN, ERROR
database_url= "postgres://my_user:my_password@localhost:5432/ch"
clear_db = true
signing_key = "keys/private_key.der" # Optional
static_process_owner = "MDS" # Optional
performance_tracing = false
7 changes: 7 additions & 0 deletions clearing-house-app/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub(crate) struct CHConfig {
pub(crate) log_level: Option<LogLevel>,
#[serde(default)]
pub(crate) signing_key: Option<String>,
#[serde(default)]
pub(crate) static_process_owner: Option<String>,
performance_tracing: Option<bool>,
}

Expand Down Expand Up @@ -105,16 +107,19 @@ mod test {
std::env::set_var("CH_APP_DATABASE_URL", "mongodb://localhost:27117");
std::env::set_var("CH_APP_CLEAR_DB", "true");
std::env::set_var("CH_APP_LOG_LEVEL", "INFO");
std::env::set_var("CH_APP_STATIC_PROCESS_OWNER", "ABC");

let conf = super::read_config(None);
assert_eq!(conf.database_url, "mongodb://localhost:27117");
assert!(conf.clear_db);
assert_eq!(conf.log_level, Some(super::LogLevel::Info));
assert_eq!(conf.static_process_owner, Some("ABC".to_string()));

// Cleanup
std::env::remove_var("CH_APP_DATABASE_URL");
std::env::remove_var("CH_APP_CLEAR_DB");
std::env::remove_var("CH_APP_LOG_LEVEL");
std::env::remove_var("CH_APP_STATIC_PROCESS_OWNER");
}

/// Test reading config from toml file
Expand All @@ -128,6 +133,7 @@ mod test {
let toml = r#"database_url = "mongodb://localhost:27019"
clear_db = true
log_level = "ERROR"
static_process_owner = "ABC"
"#;

// Write to file
Expand All @@ -140,5 +146,6 @@ log_level = "ERROR"
assert_eq!(conf.database_url, "mongodb://localhost:27019");
assert!(conf.clear_db);
assert_eq!(conf.log_level, Some(super::LogLevel::Error));
assert_eq!(conf.static_process_owner, Some("ABC".to_string()));
}
}
1 change: 1 addition & 0 deletions clearing-house-app/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ impl AppState {
let logging_service = Arc::new(services::logging_service::LoggingService::new(
process_store,
doc_service.clone(),
conf.static_process_owner.clone(),
));

let service_config = Arc::new(util::init_service_config(
Expand Down
2 changes: 1 addition & 1 deletion clearing-house-app/src/model/claims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ pub fn create_token<
) -> String {
let secret = env::var(ENV_SHARED_SECRET).unwrap_or_else(|_| panic!("Shared Secret not configured. Please configure environment variable {ENV_SHARED_SECRET}"));
let signing_secret = biscuit::jws::Secret::Bytes(secret.to_string().into_bytes());
let expiration_date = chrono::Utc::now() + chrono::Duration::minutes(5);
let expiration_date = chrono::Utc::now() + chrono::TimeDelta::try_minutes(5).expect("5 minutes is a valid time delta");

let claims = biscuit::ClaimsSet::<T> {
registered: biscuit::RegisteredClaims {
Expand Down
4 changes: 2 additions & 2 deletions clearing-house-app/src/model/ids/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,11 +378,11 @@ impl IdsQueryResult {
order: String,
documents: Vec<IdsMessage>,
) -> IdsQueryResult {
let date_from = chrono::NaiveDateTime::from_timestamp_opt(date_from, 0)
let date_from = chrono::DateTime::from_timestamp(date_from, 0)
.expect("Invalid date_from seconds")
.format("%Y-%m-%d %H:%M:%S")
.to_string();
let date_to = chrono::NaiveDateTime::from_timestamp_opt(date_to, 0)
let date_to = chrono::DateTime::from_timestamp(date_to, 0)
.expect("Invalid date_to seconds")
.format("%Y-%m-%d %H:%M:%S")
.to_string();
Expand Down
12 changes: 6 additions & 6 deletions clearing-house-app/src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ pub fn validate_and_sanitize_dates(
&now, &date_from, &date_to
);

let default_to_date = now.add(chrono::Duration::seconds(1));
let default_to_date = now.add(chrono::TimeDelta::try_seconds(1).expect("1 Second is a valid time delta"));
let default_from_date = default_to_date
.date()
.and_time(start_of_day())
- chrono::Duration::weeks(2);
- chrono::Duration::try_weeks(2).expect("2 weeks is a valid duration");

match (date_from, date_to) {
(Some(from), None) if from < now => Ok((from, default_to_date)),
Expand All @@ -98,17 +98,17 @@ mod test {
let date_now_midnight = date_now
.date()
.and_time(start_of_day());
let date_from = date_now_midnight - chrono::Duration::weeks(2);
let date_to = date_now_midnight - chrono::Duration::weeks(1);
let date_from = date_now_midnight - chrono::TimeDelta::try_weeks(2).expect("2 weeks is a valid duration");
let date_to = date_now_midnight - chrono::TimeDelta::try_weeks(1).expect("1 week is a valid duration");

// # Good cases
assert_eq!(
(date_from, date_now.add(chrono::Duration::seconds(1))),
(date_from, date_now.add(chrono::TimeDelta::try_seconds(1).expect("1 Second is a valid time delta"))),
super::validate_and_sanitize_dates(None, None, Some(date_now))
.expect("Should be valid")
);
assert_eq!(
(date_from, date_now.add(chrono::Duration::seconds(1))),
(date_from, date_now.add(chrono::TimeDelta::try_seconds(1).expect("1 Second is a valid time delta"))),
super::validate_and_sanitize_dates(Some(date_from), None, Some(date_now))
.expect("Should be valid")
);
Expand Down
4 changes: 2 additions & 2 deletions clearing-house-app/src/services/document_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ impl<T: DocumentStore> DocumentService<T> {
};

let mut result = QueryResult::new(
sanitized_date_from.timestamp(),
sanitized_date_to.timestamp(),
sanitized_date_from.and_utc().timestamp(),
sanitized_date_to.and_utc().timestamp(),
result_page,
result_size,
result_sort,
Expand Down
8 changes: 6 additions & 2 deletions clearing-house-app/src/services/logging_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ impl axum::response::IntoResponse for LoggingServiceError {
#[derive(Debug)]
pub(crate) struct LoggingService<T, S> {
db: T,
static_process_owner: Option<String>,
doc_api: Arc<DocumentService<S>>,
}

impl<T: ProcessStore, S: DocumentStore> LoggingService<T, S> {
pub fn new(db: T, doc_api: Arc<DocumentService<S>>) -> LoggingService<T, S> {
LoggingService { db, doc_api }
pub fn new(db: T, doc_api: Arc<DocumentService<S>>, static_process_owner: Option<String>) -> LoggingService<T, S> {
LoggingService { db, doc_api, static_process_owner }
}

pub async fn log(
Expand Down Expand Up @@ -172,6 +173,9 @@ impl<T: ProcessStore, S: DocumentStore> LoggingService<T, S> {

// validate payload
let mut owners = vec![user.clone()];
if let Some(static_process_owner) = &self.static_process_owner {
owners.push(static_process_owner.clone());
}
match m.payload {
Some(ref payload) if !payload.is_empty() => {
trace!("OwnerList: '{:#?}'", &payload);
Expand Down
73 changes: 71 additions & 2 deletions clearing-house-app/tests/create_process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ async fn log_message() {
std::env::set_var("SHARED_SECRET", "test");
std::env::set_var("CH_APP_LOG_LEVEL", "TRACE");
std::env::set_var("CH_APP_CLEAR_DB", "false");
std::env::set_var("CH_APP_STATIC_PROCESS_OWNER", "MDS_EDC_CONNECTOR");
std::env::set_var("CH_APP_DATABASE_URL", connection_string);

let app = clearing_house_app::app().await.unwrap();
Expand Down Expand Up @@ -217,17 +218,85 @@ async fn log_message() {

// Send log message
let log_response_unauth = app
.clone()
.oneshot(
Request::builder()
.uri(format!("/messages/log/{}", pid))
.method("POST")
.header("Content-Type", "application/json")
.header(SERVICE_HEADER, create_token("test", "test", &unauthorized_claims))
.header(
SERVICE_HEADER,
create_token("test", "test", &unauthorized_claims),
)
.body(serde_json::to_string(&log_msg).unwrap())
.unwrap(),
)
.await
.unwrap();

assert_eq!(log_response_unauth.status(), StatusCode::FORBIDDEN);
}

// ---------------------------------------------------------------------------------------------

// Send log message from static_process_owner
let static_process_owner_claims = ChClaims::new("MDS_EDC_CONNECTOR");

// Send log message
let log_response_unauth = app
.clone()
.oneshot(
Request::builder()
.uri(format!("/messages/log/{}", pid))
.method("POST")
.header("Content-Type", "application/json")
.header(
SERVICE_HEADER,
create_token("test", "test", &static_process_owner_claims),
)
.body(serde_json::to_string(&log_msg).unwrap())
.unwrap(),
)
.await
.unwrap();

assert_eq!(log_response_unauth.status(), StatusCode::CREATED);

// ---------------------------------------------------------------------------------------------

// Query as static_process_owner
let query_resp = app
.clone()
.oneshot(
Request::builder()
.uri(format!("/messages/query/{}", pid))
.method("POST")
.header("Content-Type", "application/json")
.header(
SERVICE_HEADER,
create_token("test", "test", &static_process_owner_claims),
)
.body(serde_json::to_string(&log_msg).unwrap())
.unwrap(),
)
.await
.unwrap();
assert_eq!(query_resp.status(), StatusCode::OK);

let body = axum::body::to_bytes(query_resp.into_body(), usize::MAX)
.await
.unwrap();
assert!(!body.is_empty());

let ids_message = serde_json::from_slice::<IdsQueryResult>(&body).unwrap();
println!("IDS Query Result: {:?}", ids_message);
let query_docs = ids_message.documents;

// Check the only document in the result
assert_eq!(query_docs.len(), 2);
let doc = query_docs
.first()
.expect("Document is there, just checked")
.to_owned();
assert_eq!(doc.payload.expect("Payload is there"), "test".to_string());
assert_eq!(doc.model_version, "test".to_string());
}
4 changes: 3 additions & 1 deletion docs/content/admin-guide/ch-app_installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ docker run -d \
-e CH_APP_DOCUMENT_DATABASE_URL='mongodb://mongohost:27017' \
-e CH_APP_CLEAR_DB='false' \
-e CH_APP_LOG_LEVEL='INFO' \
-e CH_APP_STATIC_PROCESS_OWNER='MDS_ID' \
-e SERVICE_ID_LOG='1' \
-e SHARED_SECRET='123' \
--name ch-app \
Expand All @@ -41,6 +42,7 @@ docker run -d \
-e CH_APP_DOCUMENT_DATABASE_URL='mongodb://mongohost:27017' \
-e CH_APP_CLEAR_DB='false' \
-e CH_APP_LOG_LEVEL='INFO' \
-e CH_APP_STATIC_PROCESS_OWNER='MDS_ID' \
-e SERVICE_ID_LOG='1' \
-e SHARED_SECRET='123' \
--name ch-app \
Expand All @@ -54,4 +56,4 @@ docker network rm testch

## Build

To build the ch-app yourself change into the `/clearing-house-app` directory and run `docker build -t ch-app:latest .`.
To build the ch-app yourself change into the `/clearing-house-app` directory and run `docker build -t ch-app:latest .`.

0 comments on commit 33cd21a

Please sign in to comment.