From 721fc171d0f801d13f564d11f43fb2bffcdf688d Mon Sep 17 00:00:00 2001 From: Dennis Nemec Date: Mon, 6 Oct 2025 00:27:27 +0200 Subject: [PATCH] Implemented GET and PATCH handler --- Dockerfile | 7 +- docker-compose.yaml | 2 +- src/api.rs | 176 ++++++++++++++++++++++++++++++++++++++++++++ src/gsd/dto.rs | 2 +- src/gsd/service.rs | 36 ++++++++- src/main.rs | 6 +- src/middleware.rs | 15 +--- 7 files changed, 222 insertions(+), 22 deletions(-) diff --git a/Dockerfile b/Dockerfile index b0be31b..2e9eb25 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,11 @@ COPY Cargo.toml Cargo.lock ./ # Copy source code COPY src ./src +# Install runtime dependencies (if needed, e.g., for SSL) +RUN apt-get update && \ + apt-get install -y --no-install-recommends ca-certificates libssl-dev pkg-config && \ + rm -rf /var/lib/apt/lists/* + # Build for release RUN cargo build --release @@ -18,7 +23,7 @@ FROM debian:bookworm-slim # Install runtime dependencies (if needed, e.g., for SSL) RUN apt-get update && \ - apt-get install -y --no-install-recommends ca-certificates && \ + apt-get install -y --no-install-recommends ca-certificates libssl-dev && \ rm -rf /var/lib/apt/lists/* # Create non-root user diff --git a/docker-compose.yaml b/docker-compose.yaml index f4dbf5c..391fd0a 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -23,7 +23,7 @@ services: dockerfile: Dockerfile container_name: rust-microservice ports: - - "3000:8080" + - "3000:3000" environment: - REDIS_URL=redis://redis:6379 - RUST_LOG=info diff --git a/src/api.rs b/src/api.rs index fe8d6e8..2ebc5d7 100644 --- a/src/api.rs +++ b/src/api.rs @@ -95,6 +95,182 @@ pub async fn handle_post( } } +pub async fn handle_get( + Extension(state): Extension>, + request: Request, +) -> impl IntoResponse { + let cloned_state = state.clone(); + let (mut parts, body) = request.into_parts(); + let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap(); + + let mut forwarded_request = cloned_state + .gsd_service + .forward_get_request(Request::from_parts( + parts.clone(), + Body::from(body_bytes.clone()), + )) + .await; + + if forwarded_request.is_err() { + error!( + "Failed to forward post: {:?}", + forwarded_request.err().unwrap() + ); + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + } + + + let content_type = forwarded_request.as_ref().unwrap().headers().get("Content-Type").unwrap().to_str().unwrap(); + if content_type == "application/json" { + let content_text = forwarded_request.unwrap().text().await; + if content_text.is_err() { + error!("Failed to read content text: {:?}", content_text.err()); + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + } + + let content = serde_json::from_str::(content_text.as_ref().unwrap().as_str()); + if content.is_err() { + error!("Failed to read content json: {:?}", content.err()); + error!("Content: {:?}", content_text); + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + } + + let content_unwrapped = content.unwrap(); + // Invalid session + if content_unwrapped.status.is_some() + && content_unwrapped.status.unwrap().internal_status == "201" + { + info!("Session invalid. Re-negotiate new session"); + + match cloned_state.gsd_service.get_session().await { + Ok(session) => { + set_and_log_session(&cloned_state, session.clone()).await; + + parts.headers.remove("sessionId"); + parts.headers.insert( + "sessionId", + HeaderValue::from_str(session.clone().as_str()).unwrap(), + ); + + forwarded_request = cloned_state + .gsd_service + .forward_get_request(Request::from_parts( + parts.clone(), + Body::from(body_bytes.clone()), + )) + .await; + + if let Err(e) = &forwarded_request { + error!("Redis: failed to forward patch: {:?}", e); + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + } + + forwarded_request + .unwrap() + .text() + .await + .unwrap() + .into_response() + } + Err(error) => { + error!("Error getting session: {:?}", error); + StatusCode::UNAUTHORIZED.into_response() + } + } + } else { + content_text.unwrap().into_response() + } + } else { + forwarded_request.unwrap().bytes().await.unwrap().into_response() + } +} + +pub async fn handle_patch( + Extension(state): Extension>, + request: Request, +) -> impl IntoResponse { + let cloned_state = state.clone(); + let (mut parts, body) = request.into_parts(); + let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap(); + + let mut forwarded_request = cloned_state + .gsd_service + .forward_patch_request(Request::from_parts( + parts.clone(), + Body::from(body_bytes.clone()), + )) + .await; + + if forwarded_request.is_err() { + error!( + "Failed to forward post: {:?}", + forwarded_request.err().unwrap() + ); + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + } + + let content_text = forwarded_request.unwrap().text().await; + if content_text.is_err() { + error!("Failed to read content text: {:?}", content_text.err()); + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + } + + let content = serde_json::from_str::(content_text.as_ref().unwrap().as_str()); + + if content.is_err() { + error!("Failed to read content json: {:?}", content.err()); + error!("Content: {:?}", content_text); + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + } + + let content_unwrapped = content.unwrap(); + // Invalid session + if content_unwrapped.status.is_some() + && content_unwrapped.status.unwrap().internal_status == "201" + { + info!("Session invalid. Re-negotiate new session"); + + match cloned_state.gsd_service.get_session().await { + Ok(session) => { + set_and_log_session(&cloned_state, session.clone()).await; + + parts.headers.remove("sessionId"); + parts.headers.insert( + "sessionId", + HeaderValue::from_str(session.clone().as_str()).unwrap(), + ); + + forwarded_request = cloned_state + .gsd_service + .forward_patch_request(Request::from_parts( + parts.clone(), + Body::from(body_bytes.clone()), + )) + .await; + + if let Err(e) = &forwarded_request { + error!("Redis: failed to forward post: {:?}", e); + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + } + + forwarded_request + .unwrap() + .text() + .await + .unwrap() + .into_response() + } + Err(error) => { + error!("Error getting session: {:?}", error); + StatusCode::UNAUTHORIZED.into_response() + } + } + } else { + content_text.unwrap().into_response() + } +} + + pub async fn userinfo(request: Request) -> impl IntoResponse { let access_token_string = &request.headers().get("authorization").unwrap().to_str().unwrap().to_string()[7..]; let user = decode_payload_unchecked::(access_token_string).unwrap(); diff --git a/src/gsd/dto.rs b/src/gsd/dto.rs index bb0e45b..3cc07da 100644 --- a/src/gsd/dto.rs +++ b/src/gsd/dto.rs @@ -16,7 +16,7 @@ pub struct GSDResponseDTO { #[derive(serde::Deserialize, serde::Serialize, Debug)] #[serde(rename_all = "camelCase")] pub struct GSDLoginResponseDataDTO { - pub session_id: String, + pub session_id: Option, } #[derive(serde::Deserialize, serde::Serialize, Debug)] diff --git a/src/gsd/service.rs b/src/gsd/service.rs index 6db0570..639d4cb 100644 --- a/src/gsd/service.rs +++ b/src/gsd/service.rs @@ -62,10 +62,10 @@ impl GSDService { match response_dto.data { Some(data) => { info!( - "Session: successfully obtained session with session id {}", - &data.session_id + "Session: successfully obtained session with session id {:?}", + &data.session_id.as_ref() ); - Ok(data.session_id.clone()) + Ok(data.session_id.unwrap()) } None => { error!("Session: failed to obtain session id. No session id in request found."); @@ -89,6 +89,36 @@ impl GSDService { .await .map_err(|e| GSDServiceError::RequestError(e.to_string())) } + + pub async fn forward_patch_request( + &self, + request: Request, + ) -> Result { + let (parts, body) = request.into_parts(); + + reqwest::Client::new() + .patch(format!("{}{}", self.host_url.clone(), parts.uri)) + .headers(parts.headers) + .body(axum::body::to_bytes(body, usize::MAX).await.unwrap()) + .send() + .await + .map_err(|e| GSDServiceError::RequestError(e.to_string())) + } + + pub async fn forward_get_request( + &self, + request: Request, + ) -> Result { + let (parts, body) = request.into_parts(); + + reqwest::Client::new() + .get(format!("{}{}", self.host_url.clone(), parts.uri)) + .headers(parts.headers) + .body(axum::body::to_bytes(body, usize::MAX).await.unwrap()) + .send() + .await + .map_err(|e| GSDServiceError::RequestError(e.to_string())) + } } impl From<&Config> for GSDService { diff --git a/src/main.rs b/src/main.rs index 07a067b..9d7d638 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ -use crate::api::{handle_post, userinfo}; +use crate::api::{handle_get, handle_patch, handle_post, userinfo}; use crate::config::load_config; use crate::middleware::AppState; use crate::repository::RedisRepository; use crate::util::initialize_logging; -use axum::routing::{get, post}; +use axum::routing::{get, patch, post}; use axum::{Extension, Router}; use axum_keycloak_auth::instance::{KeycloakAuthInstance, KeycloakConfig}; use axum_keycloak_auth::layer::KeycloakAuthLayer; @@ -53,6 +53,8 @@ async fn main() -> Result<(), Box> { let auth_router = auth::router(state.clone()); let proxy_router = Router::new() .route("/{*wildcard}", post(handle_post)) + .route("/{*wildcard}", get(handle_get)) + .route("/{*wildcard}", patch(handle_patch)) .route("/userinfo", get(userinfo)) .route_layer(Extension(state.clone())) .route_layer(axum::middleware::from_fn_with_state( diff --git a/src/middleware.rs b/src/middleware.rs index ec2e7be..f5bfab3 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -20,14 +20,6 @@ pub struct AppState { pub frontend_url: String, } -pub async fn auth_middleware( - State(_state): State>, - request: Request, - next: Next, -) -> Response { - next.run(request).await -} - /// Middleware to validate session and refresh tokens if needed pub async fn session_auth_middleware( jar: CookieJar, @@ -131,7 +123,6 @@ pub async fn gsd_decorate_header( next: Next, ) -> Response { let state_cloned = state.clone(); - info!("Gsd decorate header"); let session = state_cloned.repository.get_session().await; match session { @@ -172,10 +163,6 @@ pub async fn gsd_decorate_header( HeaderValue::from_str(state_cloned.config.gsd_app_key.as_str()).unwrap(), ); - let response = next.run(request).await; - - info!("Response: {:?}", response); - - response + next.run(request).await }