diff --git a/core/src/lib.rs b/core/src/lib.rs index 87679cb..975a4b2 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -6,8 +6,8 @@ //! [code-size]: https://img.shields.io/github/languages/code-size/MedzikUser/HomeDisk?style=for-the-badge&color=c8df52&logo=github //! [ci]: https://img.shields.io/github/workflow/status/MedzikUser/HomeDisk/Rust/main?style=for-the-badge //! -//! [home-screenshot]: https://cdn.medzik.xyz/x4Glw7w.png -//! [login-screenshot]: https://cdn.medzik.xyz/KpwY4nb.png +//! [home-screenshot]: https://i.imgur.com/x4Glw7w.png +//! [login-screenshot]: https://i.imgur.com/KpwY4nb.png //! //! [![github]](https://github.com/MedzikUser/HomeDisk) //! [![docs-rs]](https://homedisk-doc.vercel.app) diff --git a/core/src/logger.rs b/core/src/logger.rs index 452e718..f96695d 100644 --- a/core/src/logger.rs +++ b/core/src/logger.rs @@ -1,10 +1,10 @@ use tracing::level_filters::LevelFilter; -/// Max Logger Level on debug build +// Max Logger Level on debug build #[cfg(debug_assertions)] const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG; -/// Max Logger Level on release build +// Max Logger Level on release build #[cfg(not(debug_assertions))] const MAX_LEVEL: LevelFilter = LevelFilter::INFO; diff --git a/server/src/auth/login.rs b/server/src/auth/login.rs index 472dff7..f745f2e 100644 --- a/server/src/auth/login.rs +++ b/server/src/auth/login.rs @@ -14,7 +14,7 @@ pub async fn handle( request: Result, JsonRejection>, ) -> Result, ServerError> { // validate json request - let request = validate_json::(request)?; + let request = validate_json(request)?; // create `User` type let user = User::new(&request.username, &request.password); @@ -42,5 +42,6 @@ pub async fn handle( }, }; + // send response Ok(Json(response)) } diff --git a/server/src/auth/mod.rs b/server/src/auth/mod.rs index e255e1b..7bd765e 100644 --- a/server/src/auth/mod.rs +++ b/server/src/auth/mod.rs @@ -2,7 +2,6 @@ mod login; mod register; mod whoami; -/// Handle `/api/auth/*` requests pub fn app() -> axum::Router { use axum::routing::{get, post}; diff --git a/server/src/auth/register.rs b/server/src/auth/register.rs index e451935..c75b6c9 100644 --- a/server/src/auth/register.rs +++ b/server/src/auth/register.rs @@ -16,7 +16,7 @@ pub async fn handle( request: Result, JsonRejection>, ) -> Result, ServerError> { // validate json request - let request = validate_json::(request)?; + let request = validate_json(request)?; // username must contain at least 4 characters if request.username.len() < 4 { @@ -33,10 +33,12 @@ pub async fn handle( return Err(ServerError::AuthError(AuthError::PasswordTooShort)); } + // create `User` type and hash password let user = User::new(&request.username, &request.password); + // create user in the database let response = match db.create_user(&user).await { - Ok(_) => { + Ok(_result) => { let token = create_token(&user, config.jwt.secret.as_bytes(), config.jwt.expires)?; Response::LoggedIn { @@ -56,14 +58,10 @@ pub async fn handle( }, }; - // create directory for user files - let user_dir = format!( - "{storage}/{username}", - storage = config.storage.path, - username = user.username, - ); - fs::create_dir_all(&user_dir) + // create directory for the user files + fs::create_dir_all(&format!("{}/{}", config.storage.path, user.username,)) .map_err(|e| ServerError::FsError(FsError::CreateDirectory(e.to_string())))?; + // send response Ok(Json(response)) } diff --git a/server/src/auth/whoami.rs b/server/src/auth/whoami.rs index e37a8db..c6a6f34 100644 --- a/server/src/auth/whoami.rs +++ b/server/src/auth/whoami.rs @@ -19,8 +19,8 @@ pub async fn handle( // search for a user in database let response = match db.find_user_by_id(&token.claims.sub).await { - Ok(res) => Response { - username: res.username, + Ok(user) => Response { + username: user.username, }, // error while searching for a user @@ -32,5 +32,6 @@ pub async fn handle( }, }; + // send response Ok(Json(response)) } diff --git a/server/src/error.rs b/server/src/error.rs index 3d333cb..9a3a0a3 100644 --- a/server/src/error.rs +++ b/server/src/error.rs @@ -15,21 +15,18 @@ pub enum Error { /// Custom Result pub type Result = std::result::Result; -/// axum::Error impl From for Error { fn from(err: axum::Error) -> Self { Error::Axum(err) } } -/// hyper::Error impl From for Error { fn from(err: hyper::Error) -> Self { Error::Hyper(err) } } -/// std::net::AddrParseError impl From for Error { fn from(err: std::net::AddrParseError) -> Self { Error::AddrParseError(err) diff --git a/server/src/fs/create_dir.rs b/server/src/fs/create_dir.rs index 8664292..2ddc2e8 100644 --- a/server/src/fs/create_dir.rs +++ b/server/src/fs/create_dir.rs @@ -9,9 +9,7 @@ use homedisk_types::{ fs::create_dir::Request, }; -use crate::middleware::{find_user, validate_json, validate_jwt}; - -use super::validate_path; +use crate::middleware::{find_user, validate_json, validate_jwt, validate_path}; pub async fn handle( Extension(db): Extension, @@ -20,7 +18,7 @@ pub async fn handle( request: Result, JsonRejection>, ) -> Result<(), ServerError> { // validate json request - let Json(request) = validate_json::(request)?; + let Json(request) = validate_json(request)?; // validate user token let token = validate_jwt(config.jwt.secret.as_bytes(), &token)?; @@ -42,6 +40,6 @@ pub async fn handle( fs::create_dir_all(path) .map_err(|err| ServerError::FsError(FsError::CreateDirectory(err.to_string())))?; - // send aan empty Response + // send an empty response Ok(()) } diff --git a/server/src/fs/delete.rs b/server/src/fs/delete.rs index 6a1be6e..e8f1815 100644 --- a/server/src/fs/delete.rs +++ b/server/src/fs/delete.rs @@ -9,9 +9,7 @@ use homedisk_types::{ fs::delete::Request, }; -use crate::middleware::{find_user, validate_jwt}; - -use super::validate_path; +use crate::middleware::{find_user, validate_jwt, validate_path}; pub async fn handle( Extension(db): Extension, @@ -53,6 +51,6 @@ pub async fn handle( .map_err(|err| ServerError::FsError(FsError::DeleteDirectory(err.to_string())))?; } - // send an empty Response + // send an empty response Ok(()) } diff --git a/server/src/fs/download.rs b/server/src/fs/download.rs index 9e5473a..74ed17e 100644 --- a/server/src/fs/download.rs +++ b/server/src/fs/download.rs @@ -7,9 +7,7 @@ use homedisk_types::errors::FsError; use homedisk_types::fs::upload::Pagination; use homedisk_types::{config::Config, errors::ServerError}; -use crate::middleware::{find_user, validate_jwt}; - -use super::validate_path; +use crate::middleware::{find_user, validate_jwt, validate_path}; pub async fn handle( Extension(db): Extension, @@ -37,6 +35,6 @@ pub async fn handle( let content = fs::read(path).map_err(|err| ServerError::FsError(FsError::ReadFile(err.to_string())))?; - // send file content in Response + // send file content in response Ok(content) } diff --git a/server/src/fs/list.rs b/server/src/fs/list.rs index 9361b11..0154fac 100644 --- a/server/src/fs/list.rs +++ b/server/src/fs/list.rs @@ -11,9 +11,7 @@ use homedisk_types::{ fs::list::{DirInfo, FileInfo, Request, Response}, }; -use crate::middleware::{find_user, validate_json, validate_jwt}; - -use super::validate_path; +use crate::middleware::{find_user, validate_json, validate_jwt, validate_path}; /// Get directory size on disk (size of all files in directory). fn dir_size(path: impl Into) -> io::Result { @@ -94,6 +92,8 @@ pub async fn handle( .get_appropriate_unit(true) .to_string(); + // TODO: fix modification time + // check how long it has been since the file was last modified let elapsed = metadata.modified().unwrap().elapsed().unwrap(); @@ -123,5 +123,6 @@ pub async fn handle( } } + // send response Ok(Json(Response { files, dirs })) } diff --git a/server/src/fs/mod.rs b/server/src/fs/mod.rs index 8462b84..14b0395 100644 --- a/server/src/fs/mod.rs +++ b/server/src/fs/mod.rs @@ -4,7 +4,6 @@ mod download; mod list; mod upload; -/// Handle `/api/fs/*` requests pub fn app() -> axum::Router { use axum::routing::{delete, get, post}; @@ -15,25 +14,3 @@ pub fn app() -> axum::Router { .route("/download", get(download::handle)) .route("/createdir", post(create_dir::handle)) } - -pub fn validate_path(path: &str) -> Result<(), homedisk_types::errors::ServerError> { - use homedisk_types::errors::{FsError, ServerError}; - - // `path` can't contain `..` - // to prevent attack attempts because by using a `..` you can access the previous folder - if path.contains("..") { - return Err(ServerError::FsError(FsError::ReadDirectory( - "the `path` must not contain `..`".to_string(), - ))); - } - - // `path` can't contain `~` - // to prevent attack attempts because `~` can get up a directory on `$HOME` - if path.contains('~') { - return Err(ServerError::FsError(FsError::ReadDirectory( - "the `path` must not contain `~`".to_string(), - ))); - } - - Ok(()) -} diff --git a/server/src/fs/upload.rs b/server/src/fs/upload.rs index 8211d55..d9dd326 100644 --- a/server/src/fs/upload.rs +++ b/server/src/fs/upload.rs @@ -14,9 +14,7 @@ use homedisk_types::{ fs::upload::Pagination, }; -use crate::middleware::{find_user, validate_jwt}; - -use super::validate_path; +use crate::middleware::{find_user, validate_jwt, validate_path}; pub async fn handle( Extension(db): Extension, @@ -76,6 +74,6 @@ pub async fn handle( .await .map_err(|err| ServerError::FsError(FsError::WriteFile(err.to_string())))?; - // send an empty Response + // send an empty response Ok(()) } diff --git a/server/src/middleware/mod.rs b/server/src/middleware/mod.rs index 3731f30..34c28d6 100644 --- a/server/src/middleware/mod.rs +++ b/server/src/middleware/mod.rs @@ -1,7 +1,9 @@ mod auth; mod jwt; mod validate_json; +mod validate_path; pub use auth::*; pub use jwt::*; pub use validate_json::*; +pub use validate_path::*; diff --git a/server/src/middleware/validate_json.rs b/server/src/middleware/validate_json.rs index 0d7af51..c19db64 100644 --- a/server/src/middleware/validate_json.rs +++ b/server/src/middleware/validate_json.rs @@ -2,9 +2,9 @@ use axum::{extract::rejection::JsonRejection, Json}; use homedisk_types::errors::ServerError; /// Validate json request -pub fn validate_json( - payload: Result, JsonRejection>, -) -> Result, ServerError> { +pub fn validate_json( + payload: Result, JsonRejection>, +) -> Result, ServerError> { match payload { // if success return payload Ok(payload) => Ok(payload), diff --git a/server/src/middleware/validate_path.rs b/server/src/middleware/validate_path.rs new file mode 100644 index 0000000..38007d5 --- /dev/null +++ b/server/src/middleware/validate_path.rs @@ -0,0 +1,40 @@ +use homedisk_types::errors::{FsError, ServerError}; + +/// Validate path param provided in the request +pub fn validate_path(path: &str) -> Result<(), ServerError> { + // `path` can't contain `..` + // to prevent attack attempts because by using a `..` you can access the previous folder + if path.contains("..") { + return Err(ServerError::FsError(FsError::ReadDirectory( + "the `path` can't contain `..`".to_string(), + ))); + } + + // `path` can't contain `~` + // to prevent attack attempts because `~` can get up a directory on `$HOME` + if path.contains('~') { + return Err(ServerError::FsError(FsError::ReadDirectory( + "the `path` can't not contain `~`".to_string(), + ))); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_validate_path() { + // Successfully + assert!(validate_path("Directory/path/to/test.png").is_ok()); + assert!(validate_path("/test.png").is_ok()); // `/` doesn't point to the system root + assert!(validate_path("./test.png").is_ok()); + + // Errors + assert!(validate_path("../../test.png").is_err()); + assert!(validate_path("../test.png").is_err()); + assert!(validate_path("~/test.png").is_err()); + } +}