Improve testability and remove boxing
Some checks failed
Core Repos/ImageApi/pipeline/pr-master There was a failure building this commit
Some checks failed
Core Repos/ImageApi/pipeline/pr-master There was a failure building this commit
Leverage generics to remove the extra heap allocation for the response handlers using Dao's. Also moved some of the environment variables to app state to allow for easier testing.
This commit is contained in:
87
src/files.rs
87
src/files.rs
@@ -1,3 +1,4 @@
|
||||
use std::fmt::Debug;
|
||||
use std::fs::read_dir;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -5,17 +6,25 @@ use std::path::{Path, PathBuf};
|
||||
use ::anyhow;
|
||||
use anyhow::{anyhow, Context};
|
||||
|
||||
use actix_web::{web::Query, HttpResponse};
|
||||
use actix_web::{
|
||||
web::{self, Query},
|
||||
HttpResponse,
|
||||
};
|
||||
|
||||
use log::{debug, error};
|
||||
|
||||
use crate::data::{Claims, PhotosResponse, ThumbnailRequest};
|
||||
use crate::AppState;
|
||||
|
||||
use path_absolutize::*;
|
||||
|
||||
pub async fn list_photos(_: Claims, req: Query<ThumbnailRequest>) -> HttpResponse {
|
||||
pub async fn list_photos(
|
||||
_: Claims,
|
||||
req: Query<ThumbnailRequest>,
|
||||
app_state: web::Data<AppState>,
|
||||
) -> HttpResponse {
|
||||
let path = &req.path;
|
||||
if let Some(path) = is_valid_path(path) {
|
||||
if let Some(path) = is_valid_full_path(&PathBuf::from(&app_state.base_path), path) {
|
||||
debug!("Valid path: {:?}", path);
|
||||
let files = list_files(&path).unwrap_or_default();
|
||||
|
||||
@@ -31,9 +40,7 @@ pub async fn list_photos(_: Claims, req: Query<ThumbnailRequest>) -> HttpRespons
|
||||
)
|
||||
})
|
||||
.map(|path: &PathBuf| {
|
||||
let relative = path
|
||||
.strip_prefix(dotenv::var("BASE_PATH").unwrap())
|
||||
.unwrap();
|
||||
let relative = path.strip_prefix(&app_state.base_path).unwrap();
|
||||
relative.to_path_buf()
|
||||
})
|
||||
.map(|f| f.to_str().unwrap().to_string())
|
||||
@@ -43,9 +50,7 @@ pub async fn list_photos(_: Claims, req: Query<ThumbnailRequest>) -> HttpRespons
|
||||
.iter()
|
||||
.filter(|&f| f.metadata().map_or(false, |md| md.is_dir()))
|
||||
.map(|path: &PathBuf| {
|
||||
let relative = path
|
||||
.strip_prefix(dotenv::var("BASE_PATH").unwrap())
|
||||
.unwrap();
|
||||
let relative = path.strip_prefix(&app_state.base_path).unwrap();
|
||||
relative.to_path_buf()
|
||||
})
|
||||
.map(|f| f.to_str().unwrap().to_string())
|
||||
@@ -82,18 +87,13 @@ pub fn is_image_or_video(path: &Path) -> bool {
|
||||
|| extension == "nef"
|
||||
}
|
||||
|
||||
pub fn is_valid_path(path: &str) -> Option<PathBuf> {
|
||||
let base = PathBuf::from(dotenv::var("BASE_PATH").unwrap());
|
||||
|
||||
is_valid_full_path(&base, path)
|
||||
}
|
||||
|
||||
fn is_valid_full_path(base: &Path, path: &str) -> Option<PathBuf> {
|
||||
pub fn is_valid_full_path<P: AsRef<Path> + Debug>(base: &P, path: &str) -> Option<PathBuf> {
|
||||
debug!("Base: {:?}. Path: {}", base, path);
|
||||
|
||||
let path = PathBuf::from(path);
|
||||
let mut path = if path.is_relative() {
|
||||
let mut full_path = PathBuf::from(base);
|
||||
let mut full_path = PathBuf::new();
|
||||
full_path.push(base);
|
||||
full_path.push(&path);
|
||||
full_path
|
||||
} else {
|
||||
@@ -109,7 +109,10 @@ fn is_valid_full_path(base: &Path, path: &str) -> Option<PathBuf> {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_path_above_base_dir(base: &Path, full_path: &mut PathBuf) -> anyhow::Result<PathBuf> {
|
||||
fn is_path_above_base_dir<P: AsRef<Path> + Debug>(
|
||||
base: P,
|
||||
full_path: &mut PathBuf,
|
||||
) -> anyhow::Result<PathBuf> {
|
||||
full_path
|
||||
.absolutize()
|
||||
.with_context(|| format!("Unable to resolve absolute path: {:?}", full_path))
|
||||
@@ -135,15 +138,21 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
mod api {
|
||||
use actix_web::{web::Query, HttpResponse};
|
||||
use super::*;
|
||||
use actix::Actor;
|
||||
use actix_web::{
|
||||
web::{self, Query},
|
||||
HttpResponse,
|
||||
};
|
||||
|
||||
use super::list_photos;
|
||||
use crate::{
|
||||
data::{Claims, PhotosResponse, ThumbnailRequest},
|
||||
testhelpers::BodyReader,
|
||||
video::StreamActor,
|
||||
AppState,
|
||||
};
|
||||
|
||||
use std::fs;
|
||||
use std::{fs, sync::Arc};
|
||||
|
||||
fn setup() {
|
||||
let _ = env_logger::builder().is_test(true).try_init();
|
||||
@@ -160,7 +169,6 @@ mod tests {
|
||||
|
||||
let request: Query<ThumbnailRequest> = Query::from_query("path=").unwrap();
|
||||
|
||||
std::env::set_var("BASE_PATH", "/tmp");
|
||||
let mut temp_photo = std::env::temp_dir();
|
||||
let mut tmp = temp_photo.clone();
|
||||
|
||||
@@ -169,9 +177,17 @@ mod tests {
|
||||
|
||||
temp_photo.push("photo.jpg");
|
||||
|
||||
fs::File::create(temp_photo).unwrap();
|
||||
fs::File::create(temp_photo.clone()).unwrap();
|
||||
|
||||
let response: HttpResponse = list_photos(claims, request).await;
|
||||
let response: HttpResponse = list_photos(
|
||||
claims,
|
||||
request,
|
||||
web::Data::new(AppState::new(
|
||||
Arc::new(StreamActor {}.start()),
|
||||
String::from("/tmp"),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
let status = response.status();
|
||||
|
||||
let body: PhotosResponse = serde_json::from_str(&response.read_to_str()).unwrap();
|
||||
@@ -200,7 +216,15 @@ mod tests {
|
||||
|
||||
let request: Query<ThumbnailRequest> = Query::from_query("path=..").unwrap();
|
||||
|
||||
let response = list_photos(claims, request).await;
|
||||
let response = list_photos(
|
||||
claims,
|
||||
request,
|
||||
web::Data::new(AppState::new(
|
||||
Arc::new(StreamActor {}.start()),
|
||||
String::from("/tmp"),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status(), 400);
|
||||
}
|
||||
@@ -208,12 +232,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn directory_traversal_test() {
|
||||
assert_eq!(None, is_valid_path("../"));
|
||||
assert_eq!(None, is_valid_path(".."));
|
||||
assert_eq!(None, is_valid_path("fake/../../../"));
|
||||
assert_eq!(None, is_valid_path("../../../etc/passwd"));
|
||||
assert_eq!(None, is_valid_path("..//etc/passwd"));
|
||||
assert_eq!(None, is_valid_path("../../etc/passwd"));
|
||||
let base = env::temp_dir();
|
||||
assert_eq!(None, is_valid_full_path(&base, "../"));
|
||||
assert_eq!(None, is_valid_full_path(&base, ".."));
|
||||
assert_eq!(None, is_valid_full_path(&base, "fake/../../../"));
|
||||
assert_eq!(None, is_valid_full_path(&base, "../../../etc/passwd"));
|
||||
assert_eq!(None, is_valid_full_path(&base, "..//etc/passwd"));
|
||||
assert_eq!(None, is_valid_full_path(&base, "../../etc/passwd"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user