diff --git a/.gitignore b/.gitignore index 79d0212442e26068f8e9d4d785a9f51b621619d5..7193ada6da59e29cc9120dbd4418200c29078bcb 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ public/**/*.js public/**/view/* public/standard/test_sitemap.xml public/standard/dyn_sitemap.xml -public/assets/uploads \ No newline at end of file +public/assets/uploads +testing_static \ No newline at end of file diff --git a/src/core/static_files.rs b/src/core/static_files.rs index 84ac310136eabd69eb89a2d4668ad007640d7759..561d400ce0da779f123b8f02c061312bd3e3abe7 100644 --- a/src/core/static_files.rs +++ b/src/core/static_files.rs @@ -1,5 +1,4 @@ use crate::env::Env; -use crate::AppState; use std::fs::create_dir_all; use std::path::Path; @@ -19,19 +18,40 @@ impl StaticFilesIndex { } } + pub fn get_public_dir(env: &Env) -> std::path::PathBuf { + match std::env::var("CONTEXT") { + Ok(v) => match &v[..] { + "testing" => StaticFilesIndex::create_if_missing_testing_static(), + _ => env.public_dir.clone(), + }, + Err(_) => env.public_dir.clone(), + } + } + + fn create_if_missing_testing_static() -> std::path::PathBuf { + let path = std::env::current_dir().unwrap().join("testing_static"); + if !path.exists() { + std::fs::create_dir_all(path.join("assets")).unwrap(); + } + + path + } + fn _push_path(path: &Path, files: &mut Vec<String>, strip_from: &Path) { let push_path = path.strip_prefix(strip_from).unwrap(); files.push(push_path.to_str().unwrap().to_owned()); } pub fn rebuild(&mut self, env: &Env) { - let root = env.public_dir.join("assets"); + // let root = env.public_dir.join("assets"); + let public_dir = StaticFilesIndex::get_public_dir(env); + let root = public_dir.join("assets"); self.0 = Vec::new(); - StaticFilesIndex::rec_read_dir(&root, &mut self.0, &env.public_dir); + StaticFilesIndex::rec_read_dir(&root, &mut self.0, &public_dir); } pub fn push_path(&mut self, path: &Path, env: &Env) { - let strip_from = env.public_dir.join("assets"); + let strip_from = StaticFilesIndex::get_public_dir(env); StaticFilesIndex::_push_path(path, &mut self.0, &strip_from); } } @@ -57,8 +77,8 @@ pub fn create_dir_if_missing(path: std::path::PathBuf) -> std::path::PathBuf { path } -pub fn get_uploads_dir(app_state: &AppState) -> std::path::PathBuf { - create_dir_if_missing(app_state.env.public_dir.join("assets/uploads")) +pub fn get_uploads_dir(public_dir: std::path::PathBuf) -> std::path::PathBuf { + create_dir_if_missing(public_dir.join("assets/uploads")) } pub fn dirname_from_type(upload_type: &UploadType) -> String { diff --git a/src/service/articles.rs b/src/service/articles.rs index b9e4ee4c4b96257b067fcd6414dc29f98412cccf..495033f18b60b182f22938b51cc67a3900af304e 100644 --- a/src/service/articles.rs +++ b/src/service/articles.rs @@ -364,7 +364,7 @@ mod test_articles { if path.exists() && path.is_dir() { let res = remove_dir_all(path); if let Err(e) = res { - return Err(format!("Error removing testing stativ views {}", e)); + return Err(format!("Error removing testing static views {}", e)); } } Ok(()) @@ -431,6 +431,7 @@ mod test_articles { // Static view tests let static_view_path = find_inserted.metadata.static_resource_path; assert!(static_view_path.is_some()); + let static_view_path = static_view_path.unwrap(); assert!(static_view_path.exists()); diff --git a/src/service/static_files.rs b/src/service/static_files.rs index b9c133b1602e858ba50f011265a7174661a74ad4..c3af8f01937bcf5628cf1332a8bc762ee235e765 100644 --- a/src/service/static_files.rs +++ b/src/service/static_files.rs @@ -29,12 +29,12 @@ async fn write_uploaded_file( filename: &String, upload_type: UploadType, ) -> Result<String, String> { - let uploads_dir = get_uploads_dir(app_state); + let uploads_dir = get_uploads_dir(StaticFilesIndex::get_public_dir(&app_state.env)); let sub_dir = dirname_from_type(&upload_type); let filepath = create_dir_if_missing(uploads_dir.join(&sub_dir)).join(&filename); match File::create(&filepath) { - Err(e) => Err(format!("Error creating file {:?}", e)), + Err(e) => Err(format!("Error creating file {:?} : {:?}", filepath, e)), Ok(mut f) => { // Field in turn is stream of *Bytes* object while let Some(chunk) = field.next().await { @@ -152,19 +152,52 @@ pub async fn post_files( #[cfg(test)] mod test_static_files { use super::*; - use crate::middleware::get_auth_cookie; + use crate::{ + core::static_files::StaticFilesIndex, + middleware::get_auth_cookie, + model::{AdminAuthCredentials, Administrator}, + }; use actix_web::{ http::{Method, StatusCode}, - test, App, + test, + web::Bytes, + App, }; + fn create_simple_request() -> Bytes { + Bytes::from( + "--abbc761f78ff4d7cb7573b5a23f96ef0\r\n\ + Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n\ + Content-Type: text/plain; charset=utf-8\r\nContent-Length: 4\r\n\r\n\ + test\r\n\ + --abbc761f78ff4d7cb7573b5a23f96ef0\r\n\ + Content-Disposition: form-data; name=\"file\"; filename=\"data.txt\"\r\n\ + Content-Type: text/plain; charset=utf-8\r\nContent-Length: 4\r\n\r\n\ + data\r\n\ + --abbc761f78ff4d7cb7573b5a23f96ef0--\r\n", + ) + } + + fn create_files_index(app_state: &AppState) -> Data<std::sync::Mutex<StaticFilesIndex>> { + let mut static_files_index = StaticFilesIndex(Vec::new()); + static_files_index.rebuild(&app_state.env); + Data::new(std::sync::Mutex::new(static_files_index)) + } + + fn clear_testing_static() { + std::fs::remove_dir_all(std::env::current_dir().unwrap().join("testing_static")).unwrap(); + } + #[tokio::test] async fn post_files_unauthenticated_should_be_unauthorized() { let app_state = AppState::for_test().await; + let static_files_index = create_files_index(&app_state); + let mut app = test::init_service( App::new() .app_data(Data::new(app_state.clone())) + .app_data(Data::clone(&static_files_index)) .app_data(Data::new(AuthenticatedAdminMiddleware::new( "kuadrado-admin-auth", ))) @@ -174,13 +207,94 @@ mod test_static_files { let req = test::TestRequest::with_uri("/post-files") .method(Method::POST) + .header( + "Content-Type", + "multipart/form-data; boundary=\"abbc761f78ff4d7cb7573b5a23f96ef0\"", + ) .cookie(get_auth_cookie( "wrong-cookie", app_state.encryption.random_ascii_lc_string(32), )) + .set_payload(create_simple_request()) .to_request(); let resp = test::call_service(&mut app, req).await; assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); } + + #[tokio::test] + async fn test_post_files() { + let app_state = AppState::for_test().await; + + let admin_user = Administrator::authenticated( + &app_state, + AdminAuthCredentials { + username: app_state.env.default_admin_username.to_owned(), + password: app_state.env.default_admin_password.to_owned(), + }, + ) + .await + .unwrap(); + + let static_files_index = create_files_index(&app_state); + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::clone(&static_files_index)) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .service(post_files), + ) + .await; + + let req = test::TestRequest::with_uri("/post-files") + .method(Method::POST) + .header( + "Content-Type", + "multipart/form-data; boundary=\"abbc761f78ff4d7cb7573b5a23f96ef0\"", + ) + .cookie(get_auth_cookie( + "kuadrado-admin-auth", + app_state + .encryption + .decrypt(&admin_user.auth_token.unwrap()) + .to_owned(), + )) + .set_payload(create_simple_request()) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + let status = resp.status(); + + assert_eq!(status, StatusCode::OK); + + let pathes: Vec<String> = test::read_body_json(resp).await; + let public_dir = StaticFilesIndex::get_public_dir(&app_state.env); + + let pathes_from_public = pathes + .iter() + .map(|p| { + std::path::Path::new(p) + .strip_prefix(&public_dir) + .unwrap() + .to_str() + .unwrap() + .to_owned() + }) + .collect::<Vec<String>>(); + + let index = static_files_index.lock().unwrap(); + + assert_eq!(pathes_from_public, index.0); + + let mut iter_pathes = pathes.iter(); + let f = std::fs::read_to_string(iter_pathes.next().unwrap()).unwrap(); + assert_eq!(f, "test"); + let f = std::fs::read_to_string(iter_pathes.next().unwrap()).unwrap(); + assert_eq!(f, "data"); + + clear_testing_static(); + } }