This commit is contained in:
MedzikUser 2022-06-11 10:19:47 +02:00
parent 6b0ce6f057
commit bcdd5ffa4c
No known key found for this signature in database
GPG Key ID: A5FAC1E185C112DB
28 changed files with 123 additions and 108 deletions

16
Cargo.lock generated
View File

@ -416,9 +416,9 @@ dependencies = [
[[package]]
name = "flume"
version = "0.10.12"
version = "0.10.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843c03199d0c0ca54bc1ea90ac0d507274c28abcc4f691ae8b4eaa375087c76a"
checksum = "1ceeb589a3157cac0ab8cc585feb749bd2cea5cb55a6ee802ad72d9fd38303da"
dependencies = [
"futures-core",
"futures-sink",
@ -631,7 +631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "homedisk-core"
name = "homedisk"
version = "0.0.0"
dependencies = [
"anyhow",
@ -1740,9 +1740,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
[[package]]
name = "tracing"
version = "0.1.34"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09"
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
dependencies = [
"cfg-if",
"log",
@ -1752,11 +1752,11 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.26"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f"
checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921"
dependencies = [
"lazy_static",
"once_cell",
]
[[package]]

View File

@ -3,7 +3,7 @@
# HTTP host
host = "0.0.0.0"
# HTTP port
port = 8080
port = 8080
# Cors domains
cors = [
"127.0.0.1:8000",

View File

@ -1,5 +1,5 @@
[package]
name = "homedisk-core"
name = "homedisk"
authors = ["MedzikUser <nivua1fn@duck.com>"]
homepage = "https://github.com/HomeDisk/cloud"
repository = "https://github.com/HomeDisk/cloud"
@ -10,7 +10,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "cloud"
name = "homedisk"
path = "./src/main.rs"
[dependencies]

View File

@ -26,18 +26,20 @@ pub async fn handle(
// create user token
let token = create_token(&user, config.jwt.secret.as_bytes(), config.jwt.expires)?;
// Reponse user token
Response::LoggedIn {
access_token: token,
}
}
// error while searching for a user
Err(err) => {
return match err {
// user not found
Error::UserNotFound => Err(ServerError::AuthError(AuthError::UserNotFound)),
_ => Err(ServerError::AuthError(AuthError::UnknownError(
err.to_string(),
))),
}
// other error
_ => Err(ServerError::AuthError(AuthError::Other(err.to_string()))),
};
}
};

View File

@ -45,14 +45,15 @@ pub async fn handle(
}
}
Err(e) => {
if e.to_string().contains("UNIQUE constraint failed") {
// error while searching for a user
Err(err) => {
// user already exists
if err.to_string().contains("UNIQUE constraint failed") {
return Err(ServerError::AuthError(AuthError::UserAlreadyExists));
}
return Err(ServerError::AuthError(AuthError::UnknownError(
e.to_string(),
)));
// other error
return Err(ServerError::AuthError(AuthError::Other(err.to_string())));
}
};

View File

@ -24,13 +24,12 @@ pub async fn handle(
username: res.username,
},
// error while searching for a user
Err(err) => match err {
// user not found
Error::UserNotFound => return Err(ServerError::AuthError(AuthError::UserNotFound)),
_ => {
return Err(ServerError::AuthError(AuthError::UnknownError(
err.to_string(),
)))
}
// other error
_ => return Err(ServerError::AuthError(AuthError::Other(err.to_string()))),
},
};

View File

@ -3,7 +3,7 @@ use std::fs;
use axum::{extract::rejection::JsonRejection, Extension, Json};
use axum_auth::AuthBearer;
use homedisk_database::Database;
use homedisk_types::fs::create_dir::{Request, Response};
use homedisk_types::fs::create_dir::Request;
use homedisk_types::{
config::Config,
errors::{FsError, ServerError},
@ -18,7 +18,7 @@ pub async fn handle(
Extension(config): Extension<Config>,
AuthBearer(token): AuthBearer,
request: Result<Json<Request>, JsonRejection>,
) -> Result<Json<Response>, ServerError> {
) -> Result<(), ServerError> {
// validate json request
let Json(request) = validate_json::<Request>(request)?;
@ -38,9 +38,10 @@ pub async fn handle(
req_dir = request.path
);
// create dirs
// create directories
fs::create_dir_all(path)
.map_err(|err| ServerError::FsError(FsError::CreateDirectory(err.to_string())))?;
Ok(Json(Response { created: true }))
// send a blank Response
Ok(())
}

View File

@ -45,13 +45,16 @@ pub async fn handle(
// delete file
if path.is_file() {
fs::remove_file(&path)
// return error
.map_err(|err| ServerError::FsError(FsError::DeleteFile(err.to_string())))?;
}
// delete directory
else if path.is_dir() {
fs::remove_dir(&path)
// return error
.map_err(|err| ServerError::FsError(FsError::DeleteDirectory(err.to_string())))?;
}
// send a blank Response
Ok(())
}

View File

@ -36,5 +36,6 @@ pub async fn handle(
// read file content
let content = fs::read(path).unwrap();
// send file content in Response
Ok(content)
}

View File

@ -59,19 +59,19 @@ pub async fn handle(
// get paths from dir
let paths = fs::read_dir(&path)
.map_err(|err| ServerError::FsError(FsError::ReadDir(err.to_string())))?;
.map_err(|err| ServerError::FsError(FsError::ReadDirectory(err.to_string())))?;
let mut files = vec![];
let mut dirs = vec![];
for f in paths {
// handle Error
let f = f.map_err(|err| ServerError::FsError(FsError::UnknownError(err.to_string())))?;
let f = f.map_err(|err| ServerError::FsError(FsError::Other(err.to_string())))?;
// get path metadata
let metadata = f
.metadata()
.map_err(|err| ServerError::FsError(FsError::UnknownError(err.to_string())))?;
.map_err(|err| ServerError::FsError(FsError::Other(err.to_string())))?;
// get name of the path
let name = f.path().display().to_string().replace(&path, "");
@ -80,7 +80,7 @@ pub async fn handle(
if metadata.is_dir() {
let size = Byte::from_bytes(
dir_size(f.path().display().to_string())
.map_err(|err| ServerError::FsError(FsError::UnknownError(err.to_string())))?
.map_err(|err| ServerError::FsError(FsError::Other(err.to_string())))?
.into(),
)
.get_appropriate_unit(true)

View File

@ -21,7 +21,7 @@ pub fn validate_path(path: &str) -> Result<(), homedisk_types::errors::ServerErr
// `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::ReadDir(
return Err(ServerError::FsError(FsError::ReadDirectory(
"the `path` must not contain `..`".to_string(),
)));
}
@ -29,7 +29,7 @@ pub fn validate_path(path: &str) -> Result<(), homedisk_types::errors::ServerErr
// `path` can't contain `~`
// to prevent attack attempts because `~` can get up a directory on `$HOME`
if path.contains('~') {
return Err(ServerError::FsError(FsError::ReadDir(
return Err(ServerError::FsError(FsError::ReadDirectory(
"the `path` must not contain `~`".to_string(),
)));
}

View File

@ -2,14 +2,14 @@ use std::io::Write;
use std::{fs, path::Path};
use axum::extract::{Multipart, Query};
use axum::{Extension, Json};
use axum::Extension;
use axum_auth::AuthBearer;
use futures::TryStreamExt;
use homedisk_database::Database;
use homedisk_types::{
config::Config,
errors::{FsError, ServerError},
fs::upload::{Pagination, Response},
fs::upload::Pagination,
};
use crate::fs::validate_path;
@ -22,7 +22,7 @@ pub async fn handle(
AuthBearer(token): AuthBearer,
mut multipart: Multipart,
query: Query<Pagination>,
) -> Result<Json<Response>, ServerError> {
) -> Result<(), ServerError> {
// validate user token
let token = validate_jwt(config.jwt.secret.as_bytes(), &token)?;
@ -47,7 +47,7 @@ pub async fn handle(
// create a directory where the file will be placed
// e.g. path ==> `/secret/files/images/screenshot.png`
// directories up to `/home/homedisk/{username}/secret/files/images/` will be created
// directories up to `{storage dir}/{username}/secret/files/images/` will be created
match file_path.parent() {
Some(prefix) => fs::create_dir_all(&prefix)
.map_err(|err| ServerError::FsError(FsError::CreateFile(err.to_string())))?,
@ -75,5 +75,6 @@ pub async fn handle(
.await
.map_err(|err| ServerError::FsError(FsError::WriteFile(err.to_string())))?;
Ok(Json(Response { uploaded: true }))
// send a blank Response
Ok(())
}

View File

@ -5,7 +5,9 @@ use rust_utilities::crypto::jsonwebtoken::{Claims, Token};
/// Validate user token
pub fn validate_jwt(secret: &[u8], token: &str) -> Result<TokenData<Claims>, ServerError> {
match Token::decode(secret, token.to_string()) {
// if success return claims
Ok(claims) => Ok(claims),
// invalid token
Err(_) => Err(ServerError::AuthError(AuthError::InvalidToken)),
}
}
@ -18,6 +20,7 @@ mod tests {
use super::validate_jwt;
/// Test a token validation
#[test]
fn validate_token() {
let secret = b"secret";

View File

@ -15,14 +15,16 @@ pub fn create_token(user: &User, secret: &[u8], expires: i64) -> Result<String,
/// Search for a user
pub async fn find_user(db: Database, user_id: String) -> Result<User, ServerError> {
match db.find_user_by_id(user_id).await {
// if success return user
Ok(user) => Ok(user),
// errors
Err(err) => match err {
// user not found
homedisk_database::Error::UserNotFound => {
Err(ServerError::AuthError(AuthError::UserNotFound))
}
_ => Err(ServerError::AuthError(AuthError::UnknownError(
err.to_string(),
))),
// other error
_ => Err(ServerError::AuthError(AuthError::Other(err.to_string()))),
},
}
}
@ -33,6 +35,7 @@ mod tests {
use super::create_token;
/// Test a token creation
#[test]
fn test_create_token() {
let secret = b"secret";

View File

@ -2,4 +2,6 @@ mod auth;
mod jwt;
mod validate_json;
pub use {auth::*, jwt::*, validate_json::*};
pub use auth::*;
pub use jwt::*;
pub use validate_json::*;

View File

@ -6,11 +6,17 @@ pub fn validate_json<Typ>(
payload: Result<Json<Typ>, JsonRejection>,
) -> Result<Json<Typ>, ServerError> {
match payload {
// if success return payload
Ok(payload) => Ok(payload),
// mission json in Content-Type Header
Err(JsonRejection::MissingJsonContentType(_)) => Err(ServerError::MissingJsonContentType),
// failed to deserialize json
Err(JsonRejection::JsonDataError(_)) => Err(ServerError::JsonDataError),
// syntax error in json
Err(JsonRejection::JsonSyntaxError(_)) => Err(ServerError::JsonSyntaxError),
// failed to extract the request body
Err(JsonRejection::BytesRejection(_)) => Err(ServerError::BytesRejection),
// other error
Err(err) => Err(ServerError::Other(err.to_string())),
}
}

View File

@ -1,9 +1,9 @@
//! `/auth/login` Request and Response types
//! HTTP `/auth/login` Request and Response types
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
/// `/auth/login` Request
/// HTTP `/auth/login` Request
#[derive(Debug, Serialize, Deserialize, Clone, Zeroize, ZeroizeOnDrop)]
pub struct Request {
/// Username
@ -12,11 +12,11 @@ pub struct Request {
pub password: String,
}
/// `/auth/login` Response
/// HTTP `/auth/login` Response
#[derive(Debug, Serialize, Deserialize, Clone, Zeroize, ZeroizeOnDrop)]
pub enum Response {
LoggedIn {
/// Token of a user
/// User access token
access_token: String,
},
}

View File

@ -1,8 +1,8 @@
//! `/auth/whoami` Response type
//! HTTP `/auth/whoami` Response type
use serde::{Deserialize, Serialize};
/// `/auth/whoami` Response
/// HTTP `/auth/whoami` Response
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Response {
/// Logged user username

View File

@ -1,4 +1,4 @@
//! Typed for a database
//! Types for a database
mod user;

View File

@ -1,29 +1,31 @@
use serde::{Deserialize, Serialize};
/// `/auth/*` Error
/// HTTP `/auth/*` Errors
#[derive(Debug, Clone, Serialize, Deserialize, thiserror::Error)]
pub enum Error {
/// User not found!
/// Username or Password incorrect.
#[error("user not found")]
UserNotFound,
/// Cannot create a user because username already exists.
#[error("user already exists")]
UserAlreadyExists,
/// Username is too short.
#[error("username is too short")]
UsernameTooShort,
/// Username is too long.
#[error("username is too long")]
UsernameTooLong,
/// Password is too short.
#[error("password is too short")]
PasswordTooShort,
/// Failed to generate user token.
#[error("generate jwt token")]
TokenGenerate,
/// Incorrect user token.
#[error("invalid jwt token")]
InvalidToken,
#[error("unknown error - {0}")]
UnknownError(String),
/// Other error.
#[error("other error - {0}")]
Other(String),
}

View File

@ -1,6 +1,8 @@
/// Database Error
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// User not found!
/// Username or Password incorrect.
#[error("user not found")]
UserNotFound,
/// sqlx::Error

View File

@ -3,36 +3,37 @@ use serde::{Deserialize, Serialize};
/// `/fs/*` Error
#[derive(Debug, Clone, Serialize, Deserialize, thiserror::Error)]
pub enum Error {
#[error("file already exists")]
FileAlreadyExists,
/// File doesn't exists.
#[error("file doesn't exists")]
FileDoesNotExist,
/// File already exists.
#[error("file already exists")]
FileAlreadyExists,
/// Error when parsing multipart.
#[error("unexpected multipart error")]
MultipartError,
/// Failed to create a file.
#[error("create file - {0}")]
CreateFile(String),
/// Failed to create a directory.
#[error("create dir - {0}")]
CreateDirectory(String),
/// Failed to delete file.
#[error("delete file - {0}")]
DeleteFile(String),
/// Failed to delete directory.
#[error("delete dir - {0}")]
DeleteDirectory(String),
/// Failed to write content to file.
#[error("write file - {0}")]
WriteFile(String),
/// Failed decoding base64.
#[error("base64 - {0}")]
Base64(String),
/// Error when paths in directory.
#[error("read dir - {0}")]
ReadDir(String),
#[error("unknown error - {0}")]
UnknownError(String),
ReadDirectory(String),
/// Other error.
#[error("other error - {0}")]
Other(String),
}

View File

@ -6,27 +6,28 @@ use super::{AuthError, FsError};
#[derive(Debug, Clone, Serialize, Deserialize, thiserror::Error)]
#[serde(tag = "error", content = "error_message", rename_all = "kebab-case")]
pub enum Error {
/// Auth error.
#[error("auth error - {0}")]
AuthError(#[from] AuthError),
/// File System Error.
#[error("fs error - {0}")]
FsError(#[from] FsError),
/// User sends too many requests.
#[error("too may requests, please slow down")]
TooManyRequests,
/// Missing Json in Content-Type Header.
#[error("missing json content type")]
MissingJsonContentType,
/// Failed to deserialize json.
#[error("error deserialize json")]
JsonDataError,
/// Syntax error in JSON
#[error("json syntax error")]
JsonSyntaxError,
/// Failed to extract the Request body
#[error("failed to extract the request body")]
BytesRejection,
/// Other error
#[error("unknown error - {0}")]
Other(String),
}
@ -45,7 +46,7 @@ impl axum::response::IntoResponse for Error {
AuthError::PasswordTooShort => StatusCode::NOT_ACCEPTABLE,
AuthError::TokenGenerate => StatusCode::INTERNAL_SERVER_ERROR,
AuthError::InvalidToken => StatusCode::BAD_REQUEST,
AuthError::UnknownError(_) => StatusCode::INTERNAL_SERVER_ERROR,
AuthError::Other(_) => StatusCode::INTERNAL_SERVER_ERROR,
},
Self::FsError(ref err) => match err {
FsError::FileAlreadyExists => StatusCode::BAD_REQUEST,
@ -57,8 +58,8 @@ impl axum::response::IntoResponse for Error {
FsError::DeleteDirectory(_) => StatusCode::INTERNAL_SERVER_ERROR,
FsError::WriteFile(_) => StatusCode::INTERNAL_SERVER_ERROR,
FsError::Base64(_) => StatusCode::BAD_REQUEST,
FsError::ReadDir(_) => StatusCode::BAD_REQUEST,
FsError::UnknownError(_) => StatusCode::INTERNAL_SERVER_ERROR,
FsError::ReadDirectory(_) => StatusCode::BAD_REQUEST,
FsError::Other(_) => StatusCode::INTERNAL_SERVER_ERROR,
},
Self::TooManyRequests => StatusCode::TOO_MANY_REQUESTS,
Self::MissingJsonContentType => StatusCode::BAD_REQUEST,

View File

@ -2,15 +2,9 @@
use serde::{Deserialize, Serialize};
/// `/fs/createdir` Request
/// HTTP `/fs/createdir` Request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Request {
/// Path to directory wich will be created
pub path: String,
}
/// `/fs/createdir` Reponse
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Response {
/// Directory created?
pub created: bool,
}

View File

@ -2,9 +2,9 @@
use serde::{Deserialize, Serialize};
/// `/fs/delete` Request
/// HTTP `/fs/delete` Request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Request {
/// Path of file/directory to delete
/// Path of file/directory to be delete
pub path: String,
}

View File

@ -2,9 +2,9 @@
use serde::{Deserialize, Serialize};
/// `/fs/download` Request
/// HTTP `/fs/download` Request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Request {
/// Path of file to download
/// Path of file to be download
pub path: String,
}

View File

@ -2,13 +2,13 @@
use serde::{Deserialize, Serialize};
/// `/fs/list` Request
/// HTTP `/fs/list` Request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Request {
pub path: String,
}
/// `/fs/list` Response
/// HTTP `/fs/list` Response
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Response {
pub files: Vec<FileInfo>,

View File

@ -2,16 +2,9 @@
use serde::{Deserialize, Serialize};
/// `/fs/upload` Queries
/// HTTP `/fs/upload` Queries
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Pagination {
/// Path where the file will be uploaded
pub path: String,
}
/// `/fs/upload` Response
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Response {
/// The file has been uploaded?
pub uploaded: bool,
}