From 5b7faf556f6b4e384aa89c3c6c6a3b02deece022 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Tue, 3 Oct 2023 17:47:54 -0400 Subject: [PATCH] Handle file uploads with a validated session --- .gitignore | 1 + file-service/src/handlers.rs | 130 +++++++++++++++++++++++++++++++++-- file-service/src/main.rs | 83 ++-------------------- 3 files changed, 132 insertions(+), 82 deletions(-) diff --git a/.gitignore b/.gitignore index 4539abe..f8e5e3f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ result file-service/*.sqlite file-service/*.sqlite-shm file-service/*.sqlite-wal +file-service/var diff --git a/file-service/src/handlers.rs b/file-service/src/handlers.rs index b9004ee..afc0cbd 100644 --- a/file-service/src/handlers.rs +++ b/file-service/src/handlers.rs @@ -1,7 +1,12 @@ use build_html::Html; +use bytes::Buf; +use cookie::time::error::Format; +use file_service::WriteFileError; +use futures_util::StreamExt; use http::{Error, StatusCode}; use std::collections::HashMap; -use warp::http::Response; +use std::io::Read; +use warp::{filters::multipart::FormData, http::Response, multipart::Part}; use crate::{pages, App, AuthToken, FileId, FileInfo, ReadFileError, SessionToken}; @@ -97,11 +102,27 @@ pub async fn handle_auth( } } -pub async fn handle_upload(app: App, token: SessionToken) -> Result, Error> { +pub async fn handle_upload( + app: App, + token: SessionToken, + form: FormData, +) -> Result, Error> { match app.validate_session(token).await { - Ok(Some(_)) => Response::builder() - .status(StatusCode::NOT_IMPLEMENTED) - .body("".to_owned()), + Ok(Some(_)) => match process_file_upload(app, form).await { + Ok(_) => Response::builder() + .header("location", "/") + .status(StatusCode::SEE_OTHER) + .body("".to_owned()), + Err(UploadError::FilenameMissing) => Response::builder() + .status(StatusCode::BAD_REQUEST) + .body("filename is required for all files".to_owned()), + Err(UploadError::WriteFileError(err)) => Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(format!("could not write to the file system: {:?}", err)), + Err(UploadError::WarpError(err)) => Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(format!("error with the app framework: {:?}", err)), + }, _ => Response::builder() .status(StatusCode::UNAUTHORIZED) .body("".to_owned()), @@ -133,3 +154,102 @@ where }, } } + +async fn collect_multipart( + mut stream: warp::filters::multipart::FormData, +) -> Result, Option, Vec)>, warp::Error> { + let mut content: Vec<(Option, Option, Vec)> = Vec::new(); + + while let Some(part) = stream.next().await { + match part { + Ok(part) => content.push(collect_content(part).await.unwrap()), + Err(err) => return Err(err), + } + } + + Ok(content) +} + +async fn collect_content( + mut part: Part, +) -> Result<(Option, Option, Vec), String> { + let mut content: Vec = Vec::new(); + + while let Some(Ok(data)) = part.data().await { + let mut reader = data.reader(); + reader.read_to_end(&mut content).unwrap(); + } + + Ok(( + part.content_type().map(|s| s.to_owned()), + part.filename().map(|s| s.to_owned()), + content, + )) +} + +/* +async fn handle_upload( + form: warp::filters::multipart::FormData, + app: App, +) -> warp::http::Result> { + let files = collect_multipart(form).await; + match files { + Ok(files) => { + for (_, filename, content) in files { + match filename { + Some(filename) => { + app.add_file(filename, content).unwrap(); + } + None => { + return warp::http::Response::builder() + .status(StatusCode::BAD_REQUEST) + .body("".to_owned()) + } + } + } + } + Err(_err) => { + return warp::http::Response::builder() + .status(StatusCode::BAD_REQUEST) + .body("".to_owned()) + } + } + + // println!("file length: {:?}", files.map(|f| f.len())); + warp::http::Response::builder() + .header("location", "/") + .status(StatusCode::SEE_OTHER) + .body("".to_owned()) +} +*/ + +enum UploadError { + FilenameMissing, + WriteFileError(WriteFileError), + WarpError(warp::Error), +} + +impl From for UploadError { + fn from(err: WriteFileError) -> Self { + Self::WriteFileError(err) + } +} + +impl From for UploadError { + fn from(err: warp::Error) -> Self { + Self::WarpError(err) + } +} + +async fn process_file_upload(app: App, form: FormData) -> Result<(), UploadError> { + let files = collect_multipart(form).await?; + for (_, filename, content) in files { + match filename { + Some(filename) => { + app.add_file(filename, content).await?; + } + None => return Err(UploadError::FilenameMissing), + } + } + Ok(()) +} diff --git a/file-service/src/main.rs b/file-service/src/main.rs index 7cc8711..237abf8 100644 --- a/file-service/src/main.rs +++ b/file-service/src/main.rs @@ -42,75 +42,12 @@ async fn authenticate_user(app: App, auth_token: String) -> Result Result<(Option, Option, Vec), String> { - let mut content: Vec = Vec::new(); - - while let Some(Ok(data)) = part.data().await { - let mut reader = data.reader(); - reader.read_to_end(&mut content).unwrap(); - } - - Ok(( - part.content_type().map(|s| s.to_owned()), - part.filename().map(|s| s.to_owned()), - content, - )) -} */ /* -async fn collect_multipart( - mut stream: warp::filters::multipart::FormData, -) -> Result, Option, Vec)>, warp::Error> { - let mut content: Vec<(Option, Option, Vec)> = Vec::new(); - - while let Some(part) = stream.next().await { - match part { - Ok(part) => content.push(collect_content(part).await.unwrap()), - Err(err) => return Err(err), - } - } - - Ok(content) -} */ /* -async fn handle_upload( - form: warp::filters::multipart::FormData, - app: App, -) -> warp::http::Result> { - let files = collect_multipart(form).await; - match files { - Ok(files) => { - for (_, filename, content) in files { - match filename { - Some(filename) => { - app.add_file(filename, content).unwrap(); - } - None => { - return warp::http::Response::builder() - .status(StatusCode::BAD_REQUEST) - .body("".to_owned()) - } - } - } - } - Err(_err) => { - return warp::http::Response::builder() - .status(StatusCode::BAD_REQUEST) - .body("".to_owned()) - } - } - - // println!("file length: {:?}", files.map(|f| f.len())); - warp::http::Response::builder() - .header("location", "/") - .status(StatusCode::SEE_OTHER) - .body("".to_owned()) -} */ #[derive(Clone)] @@ -204,13 +141,6 @@ pub async fn main() { let app = App::new(authdb, store); - /* - let with_app = { - let app = app.clone(); - warp::any().map(move || app.clone()) - }; - */ - let log = warp::log("file_service"); let root = warp::path!() .and(warp::get()) @@ -228,6 +158,7 @@ pub async fn main() { .and(warp::post()) .and(with_app(app.clone())) .and(with_session()) + .and(warp::multipart::form()) .then(handle_upload); let thumbnail = warp::path!(String / "tn") @@ -243,13 +174,11 @@ pub async fn main() { .then(move |id, old_etags, app: App| file(app, id, old_etags)); let server = warp::serve( - root.or(auth).or(upload_via_form).with(log), /* - root.or(auth) - .or(thumbnail) - .or(file) - .or(upload_handler) - .with(log), - */ + root.or(auth) + .or(upload_via_form) + .or(thumbnail) + .or(file) + .with(log), ); server