1
0
mirror of https://github.com/CCC-MF/bwhc-kafka-rest-proxy.git synced 2025-04-19 19:16:51 +00:00

feat: check requests content type

This will reject DNPM:DIP requests with content type header
containing `application/json+v2` (and other content types).
This commit is contained in:
Paul-Christian Volkmer 2024-12-28 14:12:56 +01:00
parent e6af784ca3
commit e1b9ba1ee5

View File

@ -1,6 +1,6 @@
use axum::body::Body; use axum::body::Body;
use axum::http::header::AUTHORIZATION; use axum::http::header::{AUTHORIZATION, CONTENT_TYPE};
use axum::http::{Request, StatusCode}; use axum::http::{HeaderValue, Request, StatusCode};
use axum::middleware::{from_fn, Next}; use axum::middleware::{from_fn, Next};
use axum::response::{IntoResponse, Response}; use axum::response::{IntoResponse, Response};
use axum::routing::{delete, post}; use axum::routing::{delete, post};
@ -14,7 +14,7 @@ use tower_http::trace::TraceLayer;
use crate::cli::Cli; use crate::cli::Cli;
use crate::routes::{handle_delete, handle_post}; use crate::routes::{handle_delete, handle_post};
use crate::sender::MtbFileSender; use crate::sender::MtbFileSender;
use crate::AppResponse::{Accepted, InternalServerError, Unauthorized}; use crate::AppResponse::{Accepted, Unauthorized, UnsupportedContentType};
mod auth; mod auth;
mod cli; mod cli;
@ -31,20 +31,26 @@ enum AppResponse<'a> {
Accepted(&'a str), Accepted(&'a str),
Unauthorized, Unauthorized,
InternalServerError, InternalServerError,
UnsupportedContentType,
} }
#[allow(clippy::expect_used)] #[allow(clippy::expect_used)]
impl IntoResponse for AppResponse<'_> { impl IntoResponse for AppResponse<'_> {
fn into_response(self) -> Response { fn into_response(self) -> Response {
match self { match self {
UnsupportedContentType => (
StatusCode::UNSUPPORTED_MEDIA_TYPE,
"This application accepts bwHC data model version 1 with content type 'application/json'"
).into_response(),
_ => match self {
Accepted(request_id) => Response::builder() Accepted(request_id) => Response::builder()
.status(StatusCode::ACCEPTED) .status(StatusCode::ACCEPTED)
.header("X-Request-Id", request_id), .header("X-Request-Id", request_id),
Unauthorized => Response::builder().status(StatusCode::UNAUTHORIZED), Unauthorized => Response::builder().status(StatusCode::UNAUTHORIZED),
InternalServerError => Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR), _ => Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR),
}
.body(Body::empty()).expect("response built"),
} }
.body(Body::empty())
.expect("response built")
} }
} }
@ -67,6 +73,7 @@ async fn main() -> Result<(), ()> {
.route("/mtbfile", post(handle_post)) .route("/mtbfile", post(handle_post))
.route("/mtbfile/:patient_id", delete(handle_delete)) .route("/mtbfile/:patient_id", delete(handle_delete))
.layer(Extension(sender)) .layer(Extension(sender))
.layer(from_fn(check_content_type_header))
.layer(from_fn(check_basic_auth)); .layer(from_fn(check_basic_auth));
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -85,6 +92,17 @@ async fn main() -> Result<(), ()> {
Ok(()) Ok(())
} }
async fn check_content_type_header(request: Request<Body>, next: Next) -> Response {
match request
.headers()
.get(CONTENT_TYPE)
.map(HeaderValue::as_bytes)
{
Some(b"application/json" | b"application/json; charset=utf-8") => next.run(request).await,
_ => UnsupportedContentType.into_response(),
}
}
async fn check_basic_auth(request: Request<Body>, next: Next) -> Response { async fn check_basic_auth(request: Request<Body>, next: Next) -> Response {
if let Some(Ok(auth_header)) = request.headers().get(AUTHORIZATION).map(|x| x.to_str()) { if let Some(Ok(auth_header)) = request.headers().get(AUTHORIZATION).map(|x| x.to_str()) {
if auth::check_basic_auth(auth_header, &CONFIG.token) { if auth::check_basic_auth(auth_header, &CONFIG.token) {