Implemented GET and PATCH handler
This commit is contained in:
@ -10,6 +10,11 @@ COPY Cargo.toml Cargo.lock ./
|
|||||||
# Copy source code
|
# Copy source code
|
||||||
COPY src ./src
|
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
|
# Build for release
|
||||||
RUN cargo build --release
|
RUN cargo build --release
|
||||||
|
|
||||||
@ -18,7 +23,7 @@ FROM debian:bookworm-slim
|
|||||||
|
|
||||||
# Install runtime dependencies (if needed, e.g., for SSL)
|
# Install runtime dependencies (if needed, e.g., for SSL)
|
||||||
RUN apt-get update && \
|
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/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Create non-root user
|
# Create non-root user
|
||||||
|
|||||||
@ -23,7 +23,7 @@ services:
|
|||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: rust-microservice
|
container_name: rust-microservice
|
||||||
ports:
|
ports:
|
||||||
- "3000:8080"
|
- "3000:3000"
|
||||||
environment:
|
environment:
|
||||||
- REDIS_URL=redis://redis:6379
|
- REDIS_URL=redis://redis:6379
|
||||||
- RUST_LOG=info
|
- RUST_LOG=info
|
||||||
|
|||||||
176
src/api.rs
176
src/api.rs
@ -95,6 +95,182 @@ pub async fn handle_post(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn handle_get(
|
||||||
|
Extension(state): Extension<Arc<AppState>>,
|
||||||
|
request: Request<Body>,
|
||||||
|
) -> 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::<GSDResponseDTO>(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<Arc<AppState>>,
|
||||||
|
request: Request<Body>,
|
||||||
|
) -> 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::<GSDResponseDTO>(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<Body>) -> impl IntoResponse {
|
pub async fn userinfo(request: Request<Body>) -> impl IntoResponse {
|
||||||
let access_token_string = &request.headers().get("authorization").unwrap().to_str().unwrap().to_string()[7..];
|
let access_token_string = &request.headers().get("authorization").unwrap().to_str().unwrap().to_string()[7..];
|
||||||
let user = decode_payload_unchecked::<User>(access_token_string).unwrap();
|
let user = decode_payload_unchecked::<User>(access_token_string).unwrap();
|
||||||
|
|||||||
@ -16,7 +16,7 @@ pub struct GSDResponseDTO {
|
|||||||
#[derive(serde::Deserialize, serde::Serialize, Debug)]
|
#[derive(serde::Deserialize, serde::Serialize, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GSDLoginResponseDataDTO {
|
pub struct GSDLoginResponseDataDTO {
|
||||||
pub session_id: String,
|
pub session_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize, Debug)]
|
#[derive(serde::Deserialize, serde::Serialize, Debug)]
|
||||||
|
|||||||
@ -62,10 +62,10 @@ impl GSDService {
|
|||||||
match response_dto.data {
|
match response_dto.data {
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
info!(
|
info!(
|
||||||
"Session: successfully obtained session with session id {}",
|
"Session: successfully obtained session with session id {:?}",
|
||||||
&data.session_id
|
&data.session_id.as_ref()
|
||||||
);
|
);
|
||||||
Ok(data.session_id.clone())
|
Ok(data.session_id.unwrap())
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
error!("Session: failed to obtain session id. No session id in request found.");
|
error!("Session: failed to obtain session id. No session id in request found.");
|
||||||
@ -89,6 +89,36 @@ impl GSDService {
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| GSDServiceError::RequestError(e.to_string()))
|
.map_err(|e| GSDServiceError::RequestError(e.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn forward_patch_request(
|
||||||
|
&self,
|
||||||
|
request: Request<Body>,
|
||||||
|
) -> Result<Response, GSDServiceError> {
|
||||||
|
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<Body>,
|
||||||
|
) -> Result<Response, GSDServiceError> {
|
||||||
|
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 {
|
impl From<&Config> for GSDService {
|
||||||
|
|||||||
@ -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::config::load_config;
|
||||||
use crate::middleware::AppState;
|
use crate::middleware::AppState;
|
||||||
use crate::repository::RedisRepository;
|
use crate::repository::RedisRepository;
|
||||||
use crate::util::initialize_logging;
|
use crate::util::initialize_logging;
|
||||||
use axum::routing::{get, post};
|
use axum::routing::{get, patch, post};
|
||||||
use axum::{Extension, Router};
|
use axum::{Extension, Router};
|
||||||
use axum_keycloak_auth::instance::{KeycloakAuthInstance, KeycloakConfig};
|
use axum_keycloak_auth::instance::{KeycloakAuthInstance, KeycloakConfig};
|
||||||
use axum_keycloak_auth::layer::KeycloakAuthLayer;
|
use axum_keycloak_auth::layer::KeycloakAuthLayer;
|
||||||
@ -53,6 +53,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let auth_router = auth::router(state.clone());
|
let auth_router = auth::router(state.clone());
|
||||||
let proxy_router = Router::new()
|
let proxy_router = Router::new()
|
||||||
.route("/{*wildcard}", post(handle_post))
|
.route("/{*wildcard}", post(handle_post))
|
||||||
|
.route("/{*wildcard}", get(handle_get))
|
||||||
|
.route("/{*wildcard}", patch(handle_patch))
|
||||||
.route("/userinfo", get(userinfo))
|
.route("/userinfo", get(userinfo))
|
||||||
.route_layer(Extension(state.clone()))
|
.route_layer(Extension(state.clone()))
|
||||||
.route_layer(axum::middleware::from_fn_with_state(
|
.route_layer(axum::middleware::from_fn_with_state(
|
||||||
|
|||||||
@ -20,14 +20,6 @@ pub struct AppState {
|
|||||||
pub frontend_url: String,
|
pub frontend_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn auth_middleware(
|
|
||||||
State(_state): State<Arc<AppState>>,
|
|
||||||
request: Request,
|
|
||||||
next: Next,
|
|
||||||
) -> Response {
|
|
||||||
next.run(request).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Middleware to validate session and refresh tokens if needed
|
/// Middleware to validate session and refresh tokens if needed
|
||||||
pub async fn session_auth_middleware(
|
pub async fn session_auth_middleware(
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
@ -131,7 +123,6 @@ pub async fn gsd_decorate_header(
|
|||||||
next: Next,
|
next: Next,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
let state_cloned = state.clone();
|
let state_cloned = state.clone();
|
||||||
info!("Gsd decorate header");
|
|
||||||
|
|
||||||
let session = state_cloned.repository.get_session().await;
|
let session = state_cloned.repository.get_session().await;
|
||||||
match session {
|
match session {
|
||||||
@ -172,10 +163,6 @@ pub async fn gsd_decorate_header(
|
|||||||
HeaderValue::from_str(state_cloned.config.gsd_app_key.as_str()).unwrap(),
|
HeaderValue::from_str(state_cloned.config.gsd_app_key.as_str()).unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = next.run(request).await;
|
next.run(request).await
|
||||||
|
|
||||||
info!("Response: {:?}", response);
|
|
||||||
|
|
||||||
response
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user