From ad7f9e0f90990214c2ab27cef72da7a5fc5ce73c Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Wed, 6 Aug 2025 15:54:05 +0200 Subject: [PATCH] s3 checks authed --- backend/windmill-api/src/s3_proxy.rs | 40 +++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/backend/windmill-api/src/s3_proxy.rs b/backend/windmill-api/src/s3_proxy.rs index 373a2ccb1a..5d4bdb7b1a 100644 --- a/backend/windmill-api/src/s3_proxy.rs +++ b/backend/windmill-api/src/s3_proxy.rs @@ -1,5 +1,7 @@ +use std::sync::Arc; + use axum::{ - extract::Path, + extract::{Path, Request}, response::{IntoResponse, Response}, routing::get, Extension, Router, @@ -11,7 +13,7 @@ use windmill_common::{ }; use crate::{ - auth::Tokened, + auth::AuthCache, db::{ApiAuthed, DB}, job_helpers_ee::{get_workspace_s3_resource, read_object_streamable}, }; @@ -25,7 +27,14 @@ pub fn workspaced_unauthed_service() -> Router { async fn get_object( Extension(db): Extension, Path((w_id, storage, object_key)): Path<(String, String, String)>, + Extension(auth_cache): Extension>, + req: Request, ) -> Result { + let token = get_token(&req)?; + let Some(authed) = auth_cache.get_authed(Some(w_id.clone()), token).await else { + return Err(Error::NotAuthorized("Invalid token".to_string())); + }; + let storage = if storage.is_empty() { None } else { @@ -33,7 +42,6 @@ async fn get_object( }; // temp values - let Tokened { token } = Tokened { token: "".to_string() }; let user_db: Option = None; let authed = ApiAuthed { email: "s3_proxy".to_string(), @@ -43,7 +51,7 @@ async fn get_object( }; let (_, s3_resource) = - get_workspace_s3_resource(&authed, &db, user_db, &token, &w_id, storage).await?; + get_workspace_s3_resource(&authed, &db, user_db, token, &w_id, storage).await?; let s3_resource = s3_resource.ok_or(Error::InternalErr( "No files storage resource defined at the workspace level".to_string(), ))?; @@ -53,3 +61,27 @@ async fn get_object( let stream_body = axum::body::Body::from_stream(stream); Ok(stream_body.into_response()) } + +fn get_token(req: &Request) -> Result<&str> { + get_header(&req, "Authorization") + .map_err(|e| Error::InternalErr(format!("Failed to get token: {}", e))) + .and_then(|token| { + if token.is_empty() { + Err(Error::InternalErr("Missing token".to_string())) + } else { + let token = token + .split_once(' ') + .map(|(_, t)| t.trim_start_matches("Credential=")) + .and_then(|t| t.split_once('/').map(|(t, _)| t)) + .ok_or_else(|| Error::InternalErr("Invalid S3 authorization".to_string()))?; + Ok(token) + } + }) +} + +fn get_header<'a>(req: &'a Request, header_name: &str) -> Result<&'a str> { + req.headers() + .get(header_name) + .and_then(|h| h.to_str().ok()) + .ok_or_else(|| Error::InternalErr(format!("{} missing in header", header_name))) +}