Compare commits

..

No commits in common. "ea5c65b6e590bbeed1c937ee4d8ffb0136c8f1a8" and "94bb1371faa69e13cf7ad388676593511fb76970" have entirely different histories.

25 changed files with 102 additions and 302 deletions

148
Cargo.lock generated
View File

@ -107,7 +107,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -220,7 +220,7 @@ dependencies = [
"heck 0.4.1", "heck 0.4.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -439,41 +439,6 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "darling"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.72",
]
[[package]]
name = "darling_macro"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core",
"quote",
"syn 2.0.72",
]
[[package]] [[package]]
name = "der" name = "der"
version = "0.7.9" version = "0.7.9"
@ -677,7 +642,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -956,12 +921,6 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.5.0" version = "0.5.0"
@ -991,15 +950,6 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.11" version = "1.0.11"
@ -1376,7 +1326,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -1433,30 +1383,6 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.86" version = "1.0.86"
@ -1476,7 +1402,6 @@ dependencies = [
"chrono", "chrono",
"dotenvy", "dotenvy",
"futures", "futures",
"itertools",
"jsonwebtoken", "jsonwebtoken",
"rand", "rand",
"scrypt", "scrypt",
@ -1492,7 +1417,6 @@ dependencies = [
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"uuid", "uuid",
"validator",
] ]
[[package]] [[package]]
@ -1746,7 +1670,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -1960,7 +1884,7 @@ dependencies = [
"quote", "quote",
"sqlx-core", "sqlx-core",
"sqlx-macros-core", "sqlx-macros-core",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -1983,7 +1907,7 @@ dependencies = [
"sqlx-mysql", "sqlx-mysql",
"sqlx-postgres", "sqlx-postgres",
"sqlx-sqlite", "sqlx-sqlite",
"syn 2.0.72", "syn",
"tempfile", "tempfile",
"tokio", "tokio",
"url", "url",
@ -2109,28 +2033,12 @@ dependencies = [
"unicode-properties", "unicode-properties",
] ]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.6.1" version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.72" version = "2.0.72"
@ -2184,7 +2092,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -2268,7 +2176,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -2365,7 +2273,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -2474,36 +2382,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "validator"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e"
dependencies = [
"idna",
"once_cell",
"regex",
"serde",
"serde_derive",
"serde_json",
"url",
"validator_derive",
]
[[package]]
name = "validator_derive"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55591299b7007f551ed1eb79a684af7672c19c3193fb9e0a31936987bb2438ec"
dependencies = [
"darling",
"once_cell",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.72",
]
[[package]] [[package]]
name = "valuable" name = "valuable"
version = "0.1.0" version = "0.1.0"
@ -2555,7 +2433,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2577,7 +2455,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2792,7 +2670,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]

View File

@ -22,7 +22,6 @@ axum-extra = { version = "0.9", features = ["typed-header"] }
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
dotenvy = "0.15" dotenvy = "0.15"
futures = "0.3" futures = "0.3"
itertools = "0.13.0"
jsonwebtoken = "9" jsonwebtoken = "9"
rand = "0.8" rand = "0.8"
scrypt = { version = "0.11", default-features = false, features = ["std"] } scrypt = { version = "0.11", default-features = false, features = ["std"] }
@ -53,4 +52,3 @@ tracing-subscriber = { version = "0.3", features = [
"env-filter", "env-filter",
] } ] }
uuid = { version = "1", features = ["serde", "v7"] } uuid = { version = "1", features = ["serde", "v7"] }
validator = { version = "0.18", features = ["derive"] }

View File

@ -5,7 +5,7 @@ SELECT
created_at created_at
FROM FROM
folders f folders f
LEFT JOIN permissions p ON f.folder_id = p.folder_id JOIN permissions p ON f.folder_id = p.folder_id
WHERE WHERE
parent_folder_id = $1 parent_folder_id = $1
AND (p.user_id = $2 OR f.owner_id = $2) AND p.user_id = $2

View File

@ -1,10 +1,9 @@
SELECT SELECT
user_id, username, email, user_id, username, email
GREATEST (
similarity (email, $1),
similarity (username, $1)
) as "similarity!"
FROM FROM
users users
ORDER BY ORDER BY
"similarity!" DESC GREATEST (
similarity (email, $1),
similarity (username, $1)
) DESC

View File

@ -10,7 +10,6 @@ use axum_extra::{
headers::{authorization::Bearer, Authorization}, headers::{authorization::Bearer, Authorization},
TypedHeader, TypedHeader,
}; };
use chrono::{TimeDelta, Utc};
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation}; use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use rand::{rngs::OsRng, RngCore}; use rand::{rngs::OsRng, RngCore};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -123,13 +122,6 @@ pub struct Claims {
} }
impl Claims { impl Claims {
pub fn new(user_id: i32) -> Self {
Self {
user_id,
exp: (Utc::now() + TimeDelta::days(30)).timestamp(),
}
}
pub fn encode(self) -> Result<Token, Error> { pub fn encode(self) -> Result<Token, Error> {
let access_token = encode( let access_token = encode(
&Header::new(jsonwebtoken::Algorithm::HS256), &Header::new(jsonwebtoken::Algorithm::HS256),

View File

@ -1,6 +1,9 @@
use db::permissions::PermissionType; use futures::Stream;
use uuid::Uuid;
use crate::prelude::*; use crate::Pool;
use super::permissions::PermissionType;
pub async fn insert( pub async fn insert(
file_id: Uuid, file_id: Uuid,

View File

@ -1,4 +1,7 @@
use crate::{db::permissions::PermissionRaw, prelude::*}; use futures::{Stream, TryStreamExt};
use uuid::Uuid;
use crate::{db::permissions::PermissionRaw, Pool};
use super::permissions::PermissionType; use super::permissions::PermissionType;

View File

@ -1,6 +1,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::prelude::*; use axum::http::StatusCode;
use futures::TryStreamExt as _;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::Pool;
#[derive(sqlx::Type, Debug, Serialize, Deserialize)] #[derive(sqlx::Type, Debug, Serialize, Deserialize)]
#[sqlx(type_name = "permission")] #[sqlx(type_name = "permission")]

View File

@ -1,4 +1,8 @@
use crate::prelude::*; use futures::{stream::BoxStream, Stream, TryStreamExt};
use serde::Serialize;
use uuid::Uuid;
use crate::Pool;
/// Creates user and returns its id /// Creates user and returns its id
pub async fn create_user( pub async fn create_user(
@ -6,19 +10,16 @@ pub async fn create_user(
user_email: &str, user_email: &str,
hashed_password: &[u8], hashed_password: &[u8],
pool: &Pool, pool: &Pool,
) -> sqlx::Result<Option<i32>> { ) -> sqlx::Result<i32> {
let Some(record) = sqlx::query!( let id = sqlx::query!(
"INSERT INTO users(username, email, hashed_password) VALUES ($1, $2, $3) RETURNING user_id", "INSERT INTO users(username, email, hashed_password) VALUES ($1, $2, $3) RETURNING user_id",
user_name, user_name,
user_email, user_email,
hashed_password hashed_password
) )
.fetch_optional(pool) .fetch_one(pool)
.await? .await?
else { .user_id;
return Ok(None);
};
let id = record.user_id;
sqlx::query!( sqlx::query!(
"INSERT INTO folders(owner_id, folder_name) VALUES ($1, $2)", "INSERT INTO folders(owner_id, folder_name) VALUES ($1, $2)",
id, id,
@ -26,7 +27,7 @@ pub async fn create_user(
) )
.execute(pool) .execute(pool)
.await?; .await?;
Ok(Some(id)) Ok(id)
} }
/// Deletes the user and returns the files that must be deleted /// Deletes the user and returns the files that must be deleted
@ -91,17 +92,9 @@ pub async fn get_hash(search_string: &str, pool: &Pool) -> sqlx::Result<Option<(
Ok(record.map(|record| (record.user_id, record.hashed_password))) Ok(record.map(|record| (record.user_id, record.hashed_password)))
} }
#[derive(Serialize, Debug)]
pub struct UserSearch {
pub user_id: i32,
pub username: String,
pub email: String,
pub similarity: f32,
}
pub fn search_for_user<'a>( pub fn search_for_user<'a>(
search_string: &str, search_string: &str,
pool: &'a Pool, pool: &'a Pool,
) -> BoxStream<'a, sqlx::Result<UserSearch>> { ) -> BoxStream<'a, sqlx::Result<UserInfo>> {
sqlx::query_file_as!(UserSearch, "sql/search_for_user.sql", search_string).fetch(pool) sqlx::query_file_as!(UserInfo, "sql/search_for_user.sql", search_string).fetch(pool)
} }

View File

@ -1,4 +1,5 @@
use axum::Form; use axum::Form;
use chrono::TimeDelta;
use crate::{ use crate::{
auth::{authenticate_user, Error, Token}, auth::{authenticate_user, Error, Token},
@ -11,13 +12,24 @@ pub struct Params {
password: String, password: String,
} }
pub async fn login( fn get_exp() -> i64 {
State(pool): State<Pool>, let mut time = chrono::Utc::now();
time += TimeDelta::days(30);
time.timestamp()
}
pub async fn post(
State(state): State<AppState>,
Form(payload): Form<Params>, Form(payload): Form<Params>,
) -> Result<Json<Token>, Error> { ) -> Result<Json<Token>, Error> {
let user_id = authenticate_user(&payload.username, &payload.password, &pool) let user_id = authenticate_user(&payload.username, &payload.password, &state.pool)
.await .await
.map_err(|_| Error::WrongCredentials)? .map_err(|_| Error::WrongCredentials)?
.ok_or(Error::WrongCredentials)?; .ok_or(Error::WrongCredentials)?;
Claims::new(user_id).encode().map(Json) Claims {
user_id,
exp: get_exp(),
}
.encode()
.map(Json)
} }

View File

@ -0,0 +1 @@
pub mod auth_post;

View File

@ -1,6 +1,7 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use axum::extract::multipart::{self, Multipart}; use axum::extract::multipart::{self, Multipart};
use futures::TryStreamExt;
use tokio::io::AsyncWrite; use tokio::io::AsyncWrite;
use crate::prelude::*; use crate::prelude::*;
@ -46,7 +47,6 @@ pub async fn upload(
.try_collect() .try_collect()
.await .await
.handle_internal()?; .handle_internal()?;
let mut result = HashMap::new(); let mut result = HashMap::new();
while let Ok(Some(mut field)) = multi.next_field().await { while let Ok(Some(mut field)) = multi.next_field().await {
let Some(file_name) = field.file_name().map(ToOwned::to_owned) else { let Some(file_name) = field.file_name().map(ToOwned::to_owned) else {
@ -55,15 +55,10 @@ pub async fn upload(
if existing_names.contains(&file_name) { if existing_names.contains(&file_name) {
continue; continue;
} }
if file_name.len() > 50 {
continue;
}
let Ok((file_id, mut file)) = state.storage.create().await else { let Ok((file_id, mut file)) = state.storage.create().await else {
tracing::warn!("Couldn't create uuid for new file"); tracing::warn!("Couldn't create uuid for new file");
continue; continue;
}; };
let is_success = create_file( let is_success = create_file(
file_id, file_id,
&mut file, &mut file,
@ -77,7 +72,6 @@ pub async fn upload(
let _ = state.storage.delete(file_id).await; let _ = state.storage.delete(file_id).await;
continue; continue;
} }
result.insert(file_name, file_id); result.insert(file_name, file_id);
} }

View File

@ -1,3 +1,5 @@
use futures::TryStreamExt;
use crate::prelude::*; use crate::prelude::*;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]

View File

@ -1,3 +1,4 @@
use futures::TryStreamExt;
use tokio::try_join; use tokio::try_join;
use super::list::Params; use super::list::Params;
@ -21,27 +22,34 @@ impl From<db::folder::FolderWithoutParentId> for FolderStructure {
} }
} }
#[derive(Debug, Serialize)]
pub struct Response {
folder_id: Uuid,
structure: FolderStructure,
}
pub async fn structure( pub async fn structure(
Query(params): Query<Params>, Query(params): Query<Params>,
State(pool): State<Pool>, State(pool): State<Pool>,
claims: Claims, claims: Claims,
) -> Result<Json<FolderStructure>, StatusCode> { ) -> Result<Json<Response>, StatusCode> {
let folder_id = db::folder::process_id(params.folder_id, claims.user_id, &pool) let folder_id = db::folder::process_id(params.folder_id, claims.user_id, &pool)
.await .await
.handle_internal()? .handle_internal()?
.ok_or(StatusCode::NOT_FOUND)?; .ok_or(StatusCode::NOT_FOUND)?;
let folder = db::folder::get_by_id(folder_id, &pool) let folder = db::folder::get_by_id(folder_id, &pool)
.await .await
.handle_internal()? .handle_internal()?
.ok_or(StatusCode::NOT_FOUND)?; .ok_or(StatusCode::NOT_FOUND)?;
let mut response = Response {
let mut response: FolderStructure = folder.into(); folder_id,
let mut stack = vec![&mut response]; structure: folder.into(),
};
let mut stack = vec![&mut response.structure];
while let Some(folder) = stack.pop() { while let Some(folder) = stack.pop() {
let (files, folders) = try_join!( let (files, folders) = try_join!(
db::file::get_files(folder.folder_base.folder_id, &pool).try_collect(), db::file::get_files(folder_id, &pool).try_collect(),
db::folder::get_folders(folder.folder_base.folder_id, claims.user_id, &pool) db::folder::get_folders(folder_id, claims.user_id, &pool)
.map_ok(Into::into) .map_ok(Into::into)
.try_collect() .try_collect()
) )
@ -50,6 +58,5 @@ pub async fn structure(
folder.files = files; folder.files = files;
stack.extend(folder.folders.iter_mut()); stack.extend(folder.folders.iter_mut());
} }
Ok(Json(response)) Ok(Json(response))
} }

View File

@ -1,3 +1,4 @@
use futures::TryStreamExt;
use tokio::try_join; use tokio::try_join;
use crate::prelude::*; use crate::prelude::*;

View File

@ -1,3 +1,4 @@
pub mod authorization;
pub mod file; pub mod file;
pub mod folder; pub mod folder;
pub mod permissions; pub mod permissions;

View File

@ -26,14 +26,6 @@ pub async fn set(
.handle_internal()? .handle_internal()?
.can_manage_guard()?; .can_manage_guard()?;
let folder_info = db::folder::get_by_id(params.folder_id, &pool)
.await
.handle_internal()?
.ok_or(StatusCode::NOT_FOUND)?;
if folder_info.owner_id == params.user_id {
return Err(StatusCode::BAD_REQUEST);
}
db::permissions::insert( db::permissions::insert(
params.user_id, params.user_id,
params.folder_id, params.folder_id,

View File

@ -1,3 +1,5 @@
use futures::TryStreamExt;
use crate::prelude::*; use crate::prelude::*;
pub async fn delete( pub async fn delete(

View File

@ -1,6 +1,4 @@
pub mod delete; pub mod delete;
pub mod get; pub mod get;
pub mod login;
pub mod put; pub mod put;
pub mod register;
pub mod search; pub mod search;

View File

@ -1,12 +1,8 @@
use validator::Validate;
use crate::prelude::*; use crate::prelude::*;
#[derive(Deserialize, Debug, Validate)] #[derive(Deserialize, Debug)]
pub struct Params { pub struct Params {
#[validate(email)]
username: String, username: String,
#[validate(email)]
email: String, email: String,
} }
@ -14,13 +10,9 @@ pub async fn put(
State(pool): State<Pool>, State(pool): State<Pool>,
claims: Claims, claims: Claims,
Json(params): Json<Params>, Json(params): Json<Params>,
) -> Result<Json<db::users::UserInfo>, (StatusCode, String)> { ) -> Result<Json<db::users::UserInfo>, StatusCode> {
params let info = db::users::update(claims.user_id, &params.username, &params.email, &pool)
.validate()
.map_err(|err| (StatusCode::BAD_REQUEST, err.to_string()))?;
db::users::update(claims.user_id, &params.username, &params.email, &pool)
.await .await
.handle_internal() .handle_internal()?;
.map_err(|status| (status, String::new())) Ok(Json(info))
.map(Json)
} }

View File

@ -1,70 +0,0 @@
use axum::Form;
use axum_extra::either::Either;
use itertools::Itertools;
use validator::{Validate, ValidationError};
use crate::{
auth::{Error, HashedBytes, Token},
prelude::*,
};
#[derive(Deserialize, Debug, Validate)]
pub struct Params {
#[validate(length(min = 3, max = 10))]
username: String,
#[validate(email)]
email: String,
#[validate(length(min = 6), custom(function = "validate_password"))]
password: String,
}
fn validate_password(password: &str) -> Result<(), ValidationError> {
let mut has_lower = false;
let mut has_upper = false;
let mut has_number = false;
let mut has_special = false;
for char in password.chars() {
if char.is_lowercase() {
has_lower = true;
} else if char.is_uppercase() {
has_upper = true;
} else if char.is_ascii_digit() {
has_number = true;
} else {
has_special = true;
}
}
let error_msgs = [has_lower, has_upper, has_number, has_special]
.into_iter()
.zip(["No lower", "No upper", "No numbers", "No special"])
.filter_map(|(param, msg)| (!param).then_some(msg));
let msg = error_msgs.format(" ").to_string();
if !msg.is_empty() {
return Err(ValidationError::new("invalid_password").with_message(msg.into()));
}
Ok(())
}
pub async fn register(
State(pool): State<Pool>,
Form(params): Form<Params>,
) -> Result<Json<Token>, Either<(StatusCode, String), Error>> {
params
.validate()
.map_err(|err| Either::E1((StatusCode::BAD_REQUEST, err.to_string())))?;
let password = HashedBytes::hash_bytes(params.password.as_bytes()).as_bytes();
let Some(id) = db::users::create_user(&params.username, &params.email, &password, &pool)
.await
.handle_internal()
.map_err(|status| Either::E1((status, String::new())))?
else {
return Err(Either::E1((
StatusCode::BAD_REQUEST,
"Either the user name or the email are taken".to_owned(),
)));
};
let token = Claims::new(id).encode().map_err(Either::E2)?;
Ok(Json(token))
}

View File

@ -1,3 +1,5 @@
use futures::TryStreamExt;
use crate::prelude::*; use crate::prelude::*;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -8,12 +10,11 @@ pub struct Params {
pub async fn search( pub async fn search(
State(pool): State<Pool>, State(pool): State<Pool>,
Query(params): Query<Params>, Query(params): Query<Params>,
) -> sqlx::Result<Json<Vec<db::users::UserSearch>>, StatusCode> { ) -> sqlx::Result<Json<Vec<db::users::UserInfo>>, StatusCode> {
db::users::search_for_user(&params.search_string, &pool) let users = db::users::search_for_user(&params.search_string, &pool)
.take(20) .take(20)
.try_filter(|user| future::ready(user.similarity > 0.1))
.try_collect() .try_collect()
.await .await
.handle_internal() .handle_internal()?;
.map(Json) Ok(Json(users))
} }

View File

@ -5,14 +5,14 @@ use std::{
}; };
use axum::body::Bytes; use axum::body::Bytes;
use futures::{Stream, StreamExt};
use sha2::Digest as _; use sha2::Digest as _;
use tokio::{ use tokio::{
fs, fs,
io::{AsyncWrite, AsyncWriteExt, BufWriter}, io::{AsyncWrite, AsyncWriteExt, BufWriter},
}; };
use tokio_util::io::StreamReader; use tokio_util::io::StreamReader;
use uuid::Uuid;
use crate::prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct FileStorage(Arc<Path>); pub struct FileStorage(Arc<Path>);
@ -95,7 +95,6 @@ impl FileStorage {
const BUF_CAP: usize = 64 * 1024 * 1024; // 64 MiB const BUF_CAP: usize = 64 * 1024 * 1024; // 64 MiB
let mut hash = sha2::Sha512::new(); let mut hash = sha2::Sha512::new();
let mut size: i64 = 0; let mut size: i64 = 0;
let stream = stream.map(|value| { let stream = stream.map(|value| {
let bytes = value.map_err(io::Error::other)?; let bytes = value.map_err(io::Error::other)?;
hash.update(&bytes); hash.update(&bytes);
@ -105,12 +104,10 @@ impl FileStorage {
.ok_or_else(|| io::Error::other(anyhow::anyhow!("Size calculation overflow")))?; .ok_or_else(|| io::Error::other(anyhow::anyhow!("Size calculation overflow")))?;
io::Result::Ok(bytes) io::Result::Ok(bytes)
}); });
let mut reader = StreamReader::new(stream); let mut reader = StreamReader::new(stream);
let mut writer = BufWriter::with_capacity(BUF_CAP, file); let mut writer = BufWriter::with_capacity(BUF_CAP, file);
tokio::io::copy_buf(&mut reader, &mut writer).await?; tokio::io::copy_buf(&mut reader, &mut writer).await?;
writer.flush().await?; writer.flush().await?;
let hash = hash.finalize().to_vec(); let hash = hash.finalize().to_vec();
Ok((hash, size)) Ok((hash, size))
} }

View File

@ -74,7 +74,7 @@ async fn main() -> anyhow::Result<()> {
fn app(state: AppState) -> Router { fn app(state: AppState) -> Router {
use axum::{http::header, routing::get}; use axum::{http::header, routing::get};
use endpoints::{ use endpoints::{
file, folder, authorization, file, folder,
permissions::{self, get_top_level::get_top_level}, permissions::{self, get_top_level::get_top_level},
users, users,
}; };
@ -121,8 +121,7 @@ fn app(state: AppState) -> Router {
) )
.route("/users/current", get(users::get::current)) .route("/users/current", get(users::get::current))
.route("/users/search", get(users::search::search)) .route("/users/search", get(users::search::search))
.route("/users/register", post(users::register::register)) .route("/authorize", post(authorization::auth_post::post))
.route("/users/authorize", post(users::login::login))
.layer(middleware) .layer(middleware)
.with_state(state) .with_state(state)
} }

View File

@ -3,6 +3,6 @@ pub use axum::{
extract::{Json, Query, State}, extract::{Json, Query, State},
http::StatusCode, http::StatusCode,
}; };
pub use futures::{future, stream::BoxStream, Stream, StreamExt as _, TryStreamExt as _}; pub use futures::StreamExt as _;
pub use serde::{Deserialize, Serialize}; pub use serde::{Deserialize, Serialize};
pub use uuid::Uuid; pub use uuid::Uuid;