Compare commits
4 Commits
cd3ab9b6bc
...
0614c4cad0
Author | SHA1 | Date | |
---|---|---|---|
0614c4cad0 | |||
c4ff602ec7 | |||
9f36d8e663 | |||
40f0526500 |
121
Cargo.lock
generated
121
Cargo.lock
generated
@ -107,7 +107,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -220,7 +220,7 @@ dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -642,7 +642,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -939,7 +939,6 @@ checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1327,7 +1326,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1384,30 +1383,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
@ -1441,8 +1416,6 @@ dependencies = [
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"utoipa",
|
||||
"utoipauto",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@ -1697,7 +1670,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1911,7 +1884,7 @@ dependencies = [
|
||||
"quote",
|
||||
"sqlx-core",
|
||||
"sqlx-macros-core",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1934,7 +1907,7 @@ dependencies = [
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
"sqlx-sqlite",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"url",
|
||||
@ -2066,16 +2039,6 @@ version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "syn"
|
||||
version = "2.0.72"
|
||||
@ -2129,7 +2092,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2213,7 +2176,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2310,7 +2273,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2409,64 +2372,6 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa"
|
||||
version = "4.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa-gen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-gen"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bf0e16c02bc4bf5322ab65f10ab1149bdbcaa782cba66dc7057370a3f8190be"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.72",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipauto"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4713aabc5ed18aabcd594345b48983b112c0b5dab3d24754352e7f5cf924da03"
|
||||
dependencies = [
|
||||
"utoipauto-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipauto-core"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17e82ab96c5a55263b5bed151b8426410d93aa909a453acdbd4b6792b5af7d64"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipauto-macro"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8338dc3c9526011ffaa2aa6bd60ddfda9d49d2123108690755c6e34844212"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"utoipauto-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.10.0"
|
||||
@ -2528,7 +2433,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -2550,7 +2455,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -2765,7 +2670,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -51,6 +51,4 @@ tracing-subscriber = { version = "0.3", features = [
|
||||
"parking_lot",
|
||||
"env-filter",
|
||||
] }
|
||||
utoipa = { version = "4", features = ["axum_extras", "uuid", "chrono"] }
|
||||
utoipauto = "0.1"
|
||||
uuid = { version = "1", features = ["serde", "v7"] }
|
||||
|
59
src/auth.rs
59
src/auth.rs
@ -1,7 +1,7 @@
|
||||
use std::{array::TryFromSliceError, sync::LazyLock};
|
||||
|
||||
use axum::{
|
||||
extract::FromRequestParts,
|
||||
extract::{FromRef, FromRequestParts},
|
||||
http::{request::Parts, StatusCode},
|
||||
response::IntoResponse,
|
||||
RequestPartsExt,
|
||||
@ -15,7 +15,7 @@ use rand::{rngs::OsRng, RngCore};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
use crate::{db, Pool};
|
||||
use crate::{db, errors::handle_error, Pool};
|
||||
|
||||
pub const HASH_LENGTH: usize = 64;
|
||||
pub const SALT_LENGTH: usize = 64;
|
||||
@ -46,7 +46,7 @@ pub fn force_init_keys() {
|
||||
LazyLock::force(&KEYS);
|
||||
}
|
||||
|
||||
/// Hashes the bytes with Scrypt with the given salt
|
||||
/// Hashes the bytes using Scrypt with the given salt
|
||||
#[must_use]
|
||||
fn hash_scrypt(bytes: &[u8], salt: &[u8]) -> [u8; HASH_LENGTH] {
|
||||
let mut hash = [0; HASH_LENGTH];
|
||||
@ -55,6 +55,7 @@ fn hash_scrypt(bytes: &[u8], salt: &[u8]) -> [u8; HASH_LENGTH] {
|
||||
}
|
||||
|
||||
/// Verifieble scrypt hashed bytes
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct HashedBytes {
|
||||
pub hash: [u8; HASH_LENGTH],
|
||||
pub salt: [u8; SALT_LENGTH],
|
||||
@ -140,6 +141,7 @@ impl Claims {
|
||||
pub enum Error {
|
||||
WrongCredentials,
|
||||
TokenCreation,
|
||||
Validation,
|
||||
InvalidToken,
|
||||
}
|
||||
|
||||
@ -148,6 +150,7 @@ impl IntoResponse for Error {
|
||||
let (status, error_message) = match self {
|
||||
Error::WrongCredentials => (StatusCode::UNAUTHORIZED, "Wrong credentials"),
|
||||
Error::TokenCreation => (StatusCode::INTERNAL_SERVER_ERROR, "Token creation error"),
|
||||
Error::Validation => (StatusCode::INTERNAL_SERVER_ERROR, "Token validation error"),
|
||||
Error::InvalidToken => (StatusCode::BAD_REQUEST, "Invalid token"),
|
||||
};
|
||||
(status, error_message).into_response()
|
||||
@ -155,19 +158,53 @@ impl IntoResponse for Error {
|
||||
}
|
||||
|
||||
#[axum::async_trait]
|
||||
impl<T> FromRequestParts<T> for Claims {
|
||||
impl<T> FromRequestParts<T> for Claims
|
||||
where
|
||||
Pool: FromRef<T>,
|
||||
T: Sync,
|
||||
{
|
||||
type Rejection = Error;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, _state: &T) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request_parts(parts: &mut Parts, state: &T) -> Result<Self, Self::Rejection> {
|
||||
let pool = Pool::from_ref(state);
|
||||
let TypedHeader(Authorization(bearer)) = parts
|
||||
.extract::<TypedHeader<Authorization<Bearer>>>()
|
||||
.await
|
||||
.map_err(|_| Error::InvalidToken)?;
|
||||
// Decode the user data
|
||||
let token_data =
|
||||
decode::<Claims>(bearer.token(), &KEYS.decoding_key, &Validation::default())
|
||||
.map_err(|_| Error::InvalidToken)?;
|
||||
|
||||
Ok(token_data.claims)
|
||||
let claims: Claims = decode(bearer.token(), &KEYS.decoding_key, &Validation::default())
|
||||
.map_err(|_| Error::InvalidToken)?
|
||||
.claims;
|
||||
match db::users::exists(claims.user_id, &pool).await {
|
||||
Ok(true) => Ok(claims),
|
||||
Ok(false) => Err(Error::WrongCredentials),
|
||||
Err(err) => {
|
||||
handle_error(err);
|
||||
Err(Error::Validation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::HashedBytes;
|
||||
|
||||
const PASSWORD: &str = "Password12313#!#4)$*!#";
|
||||
|
||||
#[test]
|
||||
fn test_hash_conversion() {
|
||||
let bytes = HashedBytes::hash_bytes(PASSWORD.as_bytes());
|
||||
let bytes2 = HashedBytes::from_bytes(&bytes.as_bytes()).unwrap();
|
||||
assert!(bytes == bytes2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash() {
|
||||
assert!(HashedBytes::hash_bytes(PASSWORD.as_bytes()).verify(PASSWORD.as_bytes()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_different_hash() {
|
||||
assert!(!HashedBytes::hash_bytes(PASSWORD.as_bytes()).verify(b"Different Password"));
|
||||
}
|
||||
}
|
||||
|
@ -61,13 +61,23 @@ pub async fn update(
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get(user_id: i32, pool: &Pool) -> sqlx::Result<UserInfo> {
|
||||
pub async fn exists(user_id: i32, pool: &Pool) -> sqlx::Result<bool> {
|
||||
sqlx::query!(
|
||||
"SELECT EXISTS(SELECT user_id FROM users WHERE user_id = $1)",
|
||||
user_id
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.map(|record| record.exists.unwrap_or(false))
|
||||
}
|
||||
|
||||
pub async fn get(user_id: i32, pool: &Pool) -> sqlx::Result<Option<UserInfo>> {
|
||||
sqlx::query_as!(
|
||||
UserInfo,
|
||||
"SELECT user_id, username, email FROM users WHERE user_id = $1",
|
||||
user_id
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ pub struct Params {
|
||||
|
||||
fn get_exp() -> i64 {
|
||||
let mut time = chrono::Utc::now();
|
||||
time += TimeDelta::minutes(30);
|
||||
time += TimeDelta::days(30);
|
||||
time.timestamp()
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,22 @@ pub struct Params {
|
||||
user_id: i32,
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
State(pool): State<Pool>,
|
||||
Query(params): Query<Params>,
|
||||
) -> Result<Json<db::users::UserInfo>, StatusCode> {
|
||||
type Response = Result<Json<db::users::UserInfo>, StatusCode>;
|
||||
|
||||
pub async fn get(State(pool): State<Pool>, Query(params): Query<Params>) -> Response {
|
||||
let info = db::users::get(params.user_id, &pool)
|
||||
.await
|
||||
.handle_internal()?;
|
||||
.handle_internal()?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
Ok(Json(info))
|
||||
}
|
||||
|
||||
pub async fn current(state: State<Pool>, claims: Claims) -> Response {
|
||||
get(
|
||||
state,
|
||||
Query(Params {
|
||||
user_id: claims.user_id,
|
||||
}),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ async fn create_test_users(pool: &Pool) -> anyhow::Result<()> {
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
// TODO: add utoipa and utoipauto for swagger
|
||||
let _ = dotenvy::dotenv();
|
||||
|
||||
tracing_subscriber::fmt::init();
|
||||
@ -120,6 +119,7 @@ fn app(state: AppState) -> Router {
|
||||
.delete(users::delete::delete)
|
||||
.put(users::put::put),
|
||||
)
|
||||
.route("/users/current", get(users::get::current))
|
||||
.route("/users/search", get(users::search::search))
|
||||
.route("/authorize", post(authorization::auth_post::post))
|
||||
.layer(middleware)
|
||||
|
Reference in New Issue
Block a user