From a462d3a82d363ef68faea624b37aa3d080a710a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20Kaan=20G=C3=9CM=C3=9C=C5=9E?= <96421894+Tahinli@users.noreply.github.com> Date: Sat, 25 Jan 2025 23:50:45 +0300 Subject: [PATCH] feat: :sparkles: admin routing part 1 --- .../20241204225135_create_user_table.up.sql | 9 + migrations/20241215002127_user_contact.up.sql | 3 +- src/error.rs | 6 +- src/routing.rs | 2 + src/routing/admin.rs | 15 + src/routing/admin/role.rs | 58 ++++ src/routing/admin/user.rs | 299 +++++++++++++++++ src/routing/login.rs | 13 +- src/routing/middleware.rs | 266 +++++++-------- src/routing/role.rs | 53 +-- src/routing/user.rs | 302 +----------------- 11 files changed, 520 insertions(+), 506 deletions(-) create mode 100644 src/routing/admin.rs create mode 100644 src/routing/admin/role.rs create mode 100644 src/routing/admin/user.rs diff --git a/migrations/20241204225135_create_user_table.up.sql b/migrations/20241204225135_create_user_table.up.sql index a57a049..bf5a2aa 100644 --- a/migrations/20241204225135_create_user_table.up.sql +++ b/migrations/20241204225135_create_user_table.up.sql @@ -8,3 +8,12 @@ CREATE TABLE IF NOT EXISTS "user"( role_id BIGINT NOT NULL REFERENCES "role" DEFAULT 10, creation_time TIMESTAMPTZ NOT NULL DEFAULT NOW() ); + +INSERT INTO "user"(user_id, name, surname, gender, birth_date, role_id) +VALUES (0, 'Builder', 'Builder', true, NOW(), 0) +ON CONFLICT(user_id) DO UPDATE SET +"name" = 'Builder', +"surname" = 'Builder', +"gender" = true, +"birth_date" = NOW(), +"role_id" = 0; diff --git a/migrations/20241215002127_user_contact.up.sql b/migrations/20241215002127_user_contact.up.sql index f0f7f52..0c56f51 100644 --- a/migrations/20241215002127_user_contact.up.sql +++ b/migrations/20241215002127_user_contact.up.sql @@ -3,5 +3,6 @@ CREATE TABLE IF NOT EXISTS "user_contact"( user_id BIGSERIAL NOT NULL REFERENCES "user"(user_id), contact_id BIGSERIAL NOT NULL REFERENCES "contact"(id), contact_value VARCHAR(256) NOT NULL, - PRIMARY KEY (user_id, contact_id) + PRIMARY KEY (user_id, contact_id), + UNIQUE (contact_id, contact_value) ); diff --git a/src/error.rs b/src/error.rs index dd63f43..5268800 100644 --- a/src/error.rs +++ b/src/error.rs @@ -41,7 +41,7 @@ impl std::fmt::Display for ForumMailError { ForumMailError::TemplateLackOfParameter => { write!(f, "Template Parameters Are Not Enough") } - ForumMailError::Send(error) => write!(f, "Sending | {}", error), + ForumMailError::Send(err_val) => write!(f, "Sending | {}", err_val), } } } @@ -54,12 +54,16 @@ impl std::error::Error for ForumMailError { #[derive(Debug, Serialize, Deserialize)] pub enum ForumAuthError { + AuthenticationFailed(String), TokenRefreshTimeOver, } impl std::fmt::Display for ForumAuthError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + ForumAuthError::AuthenticationFailed(err_val) => { + write!(f, "Authentication Failed | {}", err_val) + } ForumAuthError::TokenRefreshTimeOver => write!(f, "Token Refresh Time is Over"), } } diff --git a/src/routing.rs b/src/routing.rs index 8065596..18190e5 100644 --- a/src/routing.rs +++ b/src/routing.rs @@ -1,3 +1,4 @@ +pub mod admin; pub mod comment; pub mod comment_interaction; pub mod contact; @@ -29,6 +30,7 @@ pub async fn route(concurrency_limit: &usize) -> Router { .nest("/comment_interactions", comment_interaction::route()) .nest("/contacts", contact::route()) .nest("/user_contacts", user_contact::route()) + .nest("/admin", admin::route()) .layer(CorsLayer::permissive()) .layer(ConcurrencyLimitLayer::new(*concurrency_limit)) .layer(TraceLayer::new_for_http()) diff --git a/src/routing/admin.rs b/src/routing/admin.rs new file mode 100644 index 0000000..060971b --- /dev/null +++ b/src/routing/admin.rs @@ -0,0 +1,15 @@ +pub mod role; +pub mod user; + +use axum::Router; + +use super::middleware::pass_builder_or_admin_by_authorization_token; + +pub fn route() -> Router { + Router::new() + .nest("/users", user::route()) + .nest("/roles", role::route()) + .route_layer(axum::middleware::from_fn( + pass_builder_or_admin_by_authorization_token, + )) +} diff --git a/src/routing/admin/role.rs b/src/routing/admin/role.rs new file mode 100644 index 0000000..3576f5b --- /dev/null +++ b/src/routing/admin/role.rs @@ -0,0 +1,58 @@ +use axum::{ + extract::Path, + http::StatusCode, + response::IntoResponse, + routing::{delete, patch, post}, + Json, Router, +}; +use serde::{Deserialize, Serialize}; + +use crate::feature::role::Role; + +#[derive(Debug, Serialize, Deserialize)] +struct CreateRole { + name: String, +} + +#[derive(Debug, Serialize, Deserialize)] +struct UpdateRole { + id: i64, + name: String, +} + +pub fn route() -> Router { + Router::new() + .route("/", post(create)) + .route("/", patch(update)) + .route("/{id}", delete(delete_)) +} + +async fn create(Json(create_role): Json) -> impl IntoResponse { + match Role::create(&create_role.name).await { + Ok(role) => (StatusCode::CREATED, Json(serde_json::json!(role))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn update(Json(update_role): Json) -> impl IntoResponse { + match Role::update(&update_role.id, &update_role.name).await { + Ok(role) => (StatusCode::ACCEPTED, Json(serde_json::json!(role))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn delete_(Path(id): Path) -> impl IntoResponse { + match Role::delete(&id).await { + Ok(role) => (StatusCode::NO_CONTENT, Json(serde_json::json!(role))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} diff --git a/src/routing/admin/user.rs b/src/routing/admin/user.rs new file mode 100644 index 0000000..3864c7d --- /dev/null +++ b/src/routing/admin/user.rs @@ -0,0 +1,299 @@ +use std::sync::Arc; + +use axum::{ + extract::Path, + http::StatusCode, + response::IntoResponse, + routing::{delete, get, patch, post}, + Extension, Json, Router, +}; +use chrono::NaiveDate; +use serde::{Deserialize, Serialize}; + +use crate::{feature::user::User, routing::middleware::pass_by_uri_user_extraction}; + +#[derive(Debug, Serialize, Deserialize)] +struct CreateUser { + name: String, + surname: String, + gender: bool, + birth_date: NaiveDate, +} + +#[derive(Debug, Serialize, Deserialize)] +struct UpdateUser { + name: String, + surname: String, + gender: bool, + birth_date: NaiveDate, + role_id: i64, +} + +pub fn route() -> Router { + Router::new() + .route("/", post(create)) + .route( + "/{user_id}", + patch(update).route_layer(axum::middleware::from_fn(pass_by_uri_user_extraction)), + ) + .route( + "/{user_id}", + delete(delete_).route_layer(axum::middleware::from_fn(pass_by_uri_user_extraction)), + ) + .route("/", get(read_all)) + .route("/names/{name}", get(read_all_for_name)) + .route("/surnames/{surname}", get(read_all_for_surname)) + .route("/birth_dates/{birth_date}", get(read_all_for_birth_date)) + .route("/roles/{role}", get(read_all_for_role)) + .route("/genders/{gender}", get(read_all_for_gender)) + .route("/users_ids", get(read_all_id)) + .route("/users_ids/names/{name}", get(read_all_id_for_name)) + .route( + "/users_ids/surnames/{surname}", + get(read_all_id_for_surname), + ) + .route( + "/users_ids/birth_dates/{birth_date}", + get(read_all_id_for_birth_date), + ) + .route("/users_ids/roles/{role}", get(read_all_id_for_role)) + .route("/users_ids/genders/{gender}", get(read_all_id_for_gender)) + .route("/count", get(count_all)) + .route("/count/names/{name}", get(count_all_for_name)) + .route("/count/surnames/{surname}", get(count_all_for_surname)) + .route( + "/count/birth_dates/{birth_date}", + get(count_all_for_birth_date), + ) + .route("/count/roles/{role}", get(count_all_for_role)) + .route("/count/genders/{gender}", get(count_all_for_gender)) +} + +async fn create(Json(create_user): Json) -> impl IntoResponse { + match User::create( + &create_user.name, + &create_user.surname, + &create_user.gender, + &create_user.birth_date, + ) + .await + { + Ok(user) => (StatusCode::CREATED, Json(serde_json::json!(user))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn update( + Extension(target_user): Extension>, + Json(update_user): Json, +) -> impl IntoResponse { + match User::update( + &target_user.user_id, + &update_user.name, + &update_user.surname, + &update_user.gender, + &update_user.birth_date, + &update_user.role_id, + ) + .await + { + Ok(user) => (StatusCode::ACCEPTED, Json(serde_json::json!(user))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn delete_(Extension(target_user): Extension>) -> impl IntoResponse { + match User::delete(&target_user.user_id).await { + Ok(user) => (StatusCode::NO_CONTENT, Json(serde_json::json!(user))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all() -> impl IntoResponse { + match User::read_all().await { + Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all_for_name(Path(name): Path) -> impl IntoResponse { + match User::read_all_for_name(&name).await { + Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all_for_surname(Path(surname): Path) -> impl IntoResponse { + match User::read_all_for_surname(&surname).await { + Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all_for_birth_date(Path(birth_date): Path) -> impl IntoResponse { + match User::read_all_for_birth_date(&birth_date).await { + Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all_for_role(Path(role_id): Path) -> impl IntoResponse { + match User::read_all_for_role(&role_id).await { + Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all_for_gender(Path(gender): Path) -> impl IntoResponse { + match User::read_all_for_gender(&gender).await { + Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all_id() -> impl IntoResponse { + match User::read_all_id().await { + Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all_id_for_name(Path(name): Path) -> impl IntoResponse { + match User::read_all_id_for_name(&name).await { + Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all_id_for_surname(Path(surname): Path) -> impl IntoResponse { + match User::read_all_id_for_surname(&surname).await { + Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all_id_for_birth_date(Path(birth_date): Path) -> impl IntoResponse { + match User::read_all_id_for_birth_date(&birth_date).await { + Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all_id_for_role(Path(role_id): Path) -> impl IntoResponse { + match User::read_all_id_for_role(&role_id).await { + Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn read_all_id_for_gender(Path(gender): Path) -> impl IntoResponse { + match User::read_all_id_for_gender(&gender).await { + Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn count_all() -> impl IntoResponse { + match User::count_all().await { + Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn count_all_for_name(Path(name): Path) -> impl IntoResponse { + match User::count_all_for_name(&name).await { + Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn count_all_for_surname(Path(surname): Path) -> impl IntoResponse { + match User::count_all_for_surname(&surname).await { + Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn count_all_for_birth_date(Path(birth_date): Path) -> impl IntoResponse { + match User::count_all_for_birth_date(&birth_date).await { + Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn count_all_for_role(Path(role_id): Path) -> impl IntoResponse { + match User::count_all_for_role(&role_id).await { + Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} + +async fn count_all_for_gender(Path(gender): Path) -> impl IntoResponse { + match User::count_all_for_gender(&gender).await { + Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), + Err(err_val) => ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!(err_val.to_string())), + ), + } +} diff --git a/src/routing/login.rs b/src/routing/login.rs index 526d851..e9a4370 100644 --- a/src/routing/login.rs +++ b/src/routing/login.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use crate::feature::{auth::OneTimePassword, login::Login, user::User, user_contact::UserContact}; -use super::middleware::{self, UserAndToken}; +use super::middleware::{self, UserAndAuthorizationToken}; const CONTACT_EMAIL_DEFAULT_ID: i64 = 0; @@ -92,8 +92,15 @@ async fn read(Path((user_id, token)): Path<(i64, String)>) -> impl IntoResponse } } -async fn update(Extension(user_and_token): Extension>) -> impl IntoResponse { - match Login::update(&user_and_token.user.user_id, &user_and_token.token).await { +async fn update( + Extension(user_and_authorization_token): Extension>, +) -> impl IntoResponse { + match Login::update( + &user_and_authorization_token.user.user_id, + &user_and_authorization_token.authorization_token, + ) + .await + { Ok(login) => (StatusCode::ACCEPTED, Json(serde_json::json!(login))), Err(err_val) => ( StatusCode::BAD_REQUEST, diff --git a/src/routing/middleware.rs b/src/routing/middleware.rs index 7c326f6..8b31355 100644 --- a/src/routing/middleware.rs +++ b/src/routing/middleware.rs @@ -1,154 +1,142 @@ use std::sync::Arc; use axum::{ - body::{to_bytes, Body}, extract::Request, - http::{self, HeaderMap, Method, StatusCode}, + http::{self, HeaderMap, StatusCode, Uri}, middleware::Next, response::IntoResponse, }; use serde::{Deserialize, Serialize}; -use crate::feature::{login::TokenMeta, user::User}; +use crate::{ + error::ForumAuthError, + feature::{login::TokenMeta, user::User}, +}; -#[derive(Debug)] -struct UserAndRequest { - user: User, - request: Request, -} - -#[derive(Debug)] -struct TargetUserAndRequest { - target_user: User, - request: Request, -} - -#[derive(Debug)] -struct UserAndTargetUserAndRequest { +#[derive(Debug, Serialize, Deserialize)] +struct UserAndTargetUser { user: User, target_user: User, - request: Request, } #[derive(Debug, Serialize, Deserialize)] -pub struct UserAndToken { +pub struct UserAndAuthorizationToken { pub user: User, - pub token: String, + pub authorization_token: String, } -async fn authorization_token_extraction(request_headers: &HeaderMap) -> Option { +async fn authorization_token_extraction( + request_headers: &HeaderMap, +) -> Result { if let Some(authorization_header) = request_headers.get(http::header::AUTHORIZATION) { if let Ok(authorization_header) = authorization_header.to_str() { if let Some((bearer, authorization_token)) = authorization_header.split_once(' ') { if bearer.to_lowercase() == "bearer" { - return Some(authorization_token.to_owned()); + return Ok(authorization_token.to_owned()); } } } } - None + Err(ForumAuthError::AuthenticationFailed("".to_owned())) } -async fn user_extraction(request: Request) -> Option { - if let Some(authorization_token) = authorization_token_extraction(&request.headers()).await { - match TokenMeta::verify_token(&authorization_token.to_string()).await { - Ok(claims) => { - return Some(UserAndRequest { - user: User::read(&claims.custom.user_id).await.ok()?, - request, - }); - } - Err(err_val) => { - eprintln!("Verify Token | {}", err_val); - } - } +async fn user_extraction_from_authorization_token( + authorization_token: &String, +) -> Result { + match TokenMeta::verify_token(&authorization_token.to_string()).await { + Ok(claims) => User::read(&claims.custom.user_id) + .await + .map_err(|err_val| ForumAuthError::AuthenticationFailed(err_val.to_string())), + Err(err_val) => Err(ForumAuthError::AuthenticationFailed(err_val.to_string())), } - None } -async fn target_user_extraction_from_uri(request: Request) -> Option { - let uri_parts = request.uri().path().split('/').collect::>(); - for (index, uri_part) in uri_parts.iter().enumerate() { +async fn user_extraction_from_header(request_headers: &HeaderMap) -> Result { + match authorization_token_extraction(request_headers).await { + Ok(authorization_token) => { + user_extraction_from_authorization_token(&authorization_token).await + } + Err(err_val) => Err(err_val), + } +} + +async fn user_extraction_from_uri(request_uri: &Uri) -> Result { + let request_uri_parts = request_uri.path().split('/').collect::>(); + for (index, uri_part) in request_uri_parts.iter().enumerate() { if *uri_part == "users" { - if let Some(target_user_id) = uri_parts.get(index) { - if let Ok(target_user_id) = (*target_user_id).parse::() { - if let Ok(target_user) = User::read(&target_user_id).await { - return Some(TargetUserAndRequest { - target_user, - request, - }); - } + if let Some(user_id) = request_uri_parts.get(index) { + if let Ok(user_id) = (*user_id).parse::() { + User::read(&user_id).await.map_err(|err_val| { + ForumAuthError::AuthenticationFailed(err_val.to_string()) + })?; } } } } - None + Err(ForumAuthError::AuthenticationFailed("".to_owned())) } -async fn target_user_extraction_from_json(request: Request) -> Option { - let (parts, body) = request.into_parts(); - let bytes = to_bytes(body, usize::MAX).await.ok()?; - let json: serde_json::Value = serde_json::from_slice(&bytes).ok()?; +async fn user_from_header_and_target_user_from_uri_extraction( + request_headers: &HeaderMap, + request_uri: &Uri, +) -> Result { + let user = user_extraction_from_header(request_headers).await?; + let target_user = user_extraction_from_uri(request_uri).await?; - let body = Body::from(json.to_string()); - let request = Request::from_parts(parts, body); + Ok(UserAndTargetUser { user, target_user }) +} - if let Some(target_user_id) = json.get("user_id") { - if target_user_id.is_i64() { - if let Some(target_user_id) = target_user_id.as_i64() { - if let Ok(target_user) = User::read(&target_user_id).await { - return Some(TargetUserAndRequest { - target_user, - request, - }); - } - } +pub async fn user_and_token( + mut request: Request, + next: Next, +) -> Result { + if let Ok(authorization_token) = authorization_token_extraction(&request.headers()).await { + if let Ok(user) = user_extraction_from_authorization_token(&authorization_token).await { + let user_and_token = Arc::new(UserAndAuthorizationToken { + user, + authorization_token, + }); + + request.extensions_mut().insert(user_and_token); + return Ok(next.run(request).await); } } - - None + Err(StatusCode::FORBIDDEN) } -async fn user_and_target_user_extraction(request: Request) -> Option { - let user_and_request = user_extraction(request).await?; - let user = user_and_request.user; - let request = user_and_request.request; - - let target_user_and_request = if request.method() == Method::GET { - target_user_extraction_from_uri(request).await - } else { - target_user_extraction_from_json(request).await - }?; - - let target_user = target_user_and_request.target_user; - let request = target_user_and_request.request; - - Some(UserAndTargetUserAndRequest { - user, - target_user, - request, - }) -} - -pub async fn pass(request: Request, next: Next) -> Result { - match user_extraction(request).await { - Some(user_and_request) => { - let user = Arc::new(user_and_request.user); - let mut request = user_and_request.request; - +pub async fn pass_by_authorization_token( + mut request: Request, + next: Next, +) -> Result { + match user_extraction_from_header(request.headers()).await { + Ok(user) => { + let user = Arc::new(user); request.extensions_mut().insert(user); Ok(next.run(request).await) } - None => Err(StatusCode::FORBIDDEN), + Err(_) => Err(StatusCode::FORBIDDEN), } } -pub async fn pass_builder(request: Request, next: Next) -> Result { - if let Some(user_and_request) = user_extraction(request).await { - let user = user_and_request.user; - let mut request = user_and_request.request; +pub async fn pass_by_uri_user_extraction( + mut request: Request, + next: Next, +) -> Result { + if let Ok(target_user) = user_extraction_from_uri(request.uri()).await { + let target_user = Arc::new(target_user); + request.extensions_mut().insert(target_user); + return Ok(next.run(request).await); + } + Err(StatusCode::BAD_REQUEST) +} + +pub async fn pass_builder_by_authorization_token( + mut request: Request, + next: Next, +) -> Result { + if let Ok(user) = user_extraction_from_header(request.headers()).await { if User::is_builder(&user).await { let user = Arc::new(user); request.extensions_mut().insert(user); @@ -159,14 +147,11 @@ pub async fn pass_builder(request: Request, next: Next) -> Result Result { - if let Some(user_and_request) = user_extraction(request).await { - let user = user_and_request.user; - let mut request = user_and_request.request; - + if let Ok(user) = user_extraction_from_header(request.headers()).await { if User::is_builder_or_admin(&user).await { let user = Arc::new(user); request.extensions_mut().insert(user); @@ -177,50 +162,19 @@ pub async fn pass_builder_or_admin( Err(StatusCode::FORBIDDEN) } -pub async fn pass_self(request: Request, next: Next) -> Result { - if let Some(user_and_target_user_and_request) = user_and_target_user_extraction(request).await { - let user = user_and_target_user_and_request.user; - let target_user = user_and_target_user_and_request.target_user; - let mut request = user_and_target_user_and_request.request; - - if User::is_self(&user, &target_user).await { - let user = Arc::new(user); - request.extensions_mut().insert(user); - - return Ok(next.run(request).await); - } - } - Err(StatusCode::FORBIDDEN) -} - -pub async fn pass_higher(request: Request, next: Next) -> Result { - if let Some(user_and_target_user_and_request) = user_and_target_user_extraction(request).await { - let user = user_and_target_user_and_request.user; - let target_user = user_and_target_user_and_request.target_user; - let mut request = user_and_target_user_and_request.request; - - if User::is_higher(&user, &target_user).await { - let user = Arc::new(user); - request.extensions_mut().insert(user); - - return Ok(next.run(request).await); - } - } - Err(StatusCode::FORBIDDEN) -} - -pub async fn pass_higher_or_self( - request: Request, +pub async fn pass_builder_by_authorization_token_with_target_user_by_request_uri( + mut request: Request, next: Next, ) -> Result { - if let Some(user_and_target_user_and_request) = user_and_target_user_extraction(request).await { - let user = user_and_target_user_and_request.user; - let target_user = user_and_target_user_and_request.target_user; - let mut request = user_and_target_user_and_request.request; + if let Ok(user_and_target_user) = + user_from_header_and_target_user_from_uri_extraction(request.headers(), request.uri()).await + { + let user = user_and_target_user.user; + let target_user = user_and_target_user.target_user; - if User::is_higher_or_self(&user, &target_user).await { - let user = Arc::new(user); - request.extensions_mut().insert(user); + if User::is_builder(&user).await { + let target_user = Arc::new(target_user); + request.extensions_mut().insert(target_user); return Ok(next.run(request).await); } @@ -228,14 +182,20 @@ pub async fn pass_higher_or_self( Err(StatusCode::FORBIDDEN) } -pub async fn user_and_token(request: Request, next: Next) -> Result { - if let Some(token) = authorization_token_extraction(&request.headers()).await { - if let Some(user_and_request) = user_extraction(request).await { - let user = user_and_request.user; - let mut request = user_and_request.request; - let user_and_token = Arc::new(UserAndToken { user, token }); +pub async fn pass_builder_or_admin_by_authorization_token_with_target_user_by_request_uri( + mut request: Request, + next: Next, +) -> Result { + if let Ok(user_and_target_user) = + user_from_header_and_target_user_from_uri_extraction(request.headers(), request.uri()).await + { + let user = user_and_target_user.user; + let target_user = user_and_target_user.target_user; + + if User::is_builder_or_admin(&user).await { + let target_user = Arc::new(target_user); + request.extensions_mut().insert(target_user); - request.extensions_mut().insert(user_and_token); return Ok(next.run(request).await); } } diff --git a/src/routing/role.rs b/src/routing/role.rs index 6f8bf1f..e9478a9 100644 --- a/src/routing/role.rs +++ b/src/routing/role.rs @@ -1,44 +1,13 @@ -use axum::{ - extract::Path, - http::StatusCode, - response::IntoResponse, - routing::{delete, get, patch, post}, - Json, Router, -}; -use serde::{Deserialize, Serialize}; +use axum::{extract::Path, http::StatusCode, response::IntoResponse, routing::get, Json, Router}; use crate::feature::role::Role; -#[derive(Debug, Serialize, Deserialize)] -struct CreateRole { - name: String, -} - -#[derive(Debug, Serialize, Deserialize)] -struct UpdateRole { - id: i64, - name: String, -} - pub fn route() -> Router { Router::new() - .route("/", post(create)) .route("/{id}", get(read)) - .route("/", patch(update)) - .route("/{id}", delete(delete_)) .route("/", get(read_all)) } -async fn create(Json(create_role): Json) -> impl IntoResponse { - match Role::create(&create_role.name).await { - Ok(role) => (StatusCode::CREATED, Json(serde_json::json!(role))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - async fn read(Path(id): Path) -> impl IntoResponse { match Role::read(&id).await { Ok(role) => (StatusCode::OK, Json(serde_json::json!(role))), @@ -49,26 +18,6 @@ async fn read(Path(id): Path) -> impl IntoResponse { } } -async fn update(Json(update_role): Json) -> impl IntoResponse { - match Role::update(&update_role.id, &update_role.name).await { - Ok(role) => (StatusCode::ACCEPTED, Json(serde_json::json!(role))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn delete_(Path(id): Path) -> impl IntoResponse { - match Role::delete(&id).await { - Ok(role) => (StatusCode::NO_CONTENT, Json(serde_json::json!(role))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - async fn read_all() -> impl IntoResponse { match Role::read_all().await { Ok(roles) => (StatusCode::OK, Json(serde_json::json!(roles))), diff --git a/src/routing/user.rs b/src/routing/user.rs index 2856852..cd0d1fe 100644 --- a/src/routing/user.rs +++ b/src/routing/user.rs @@ -1,97 +1,16 @@ -use axum::{ - extract::Path, - http::StatusCode, - response::IntoResponse, - routing::{delete, get, patch, post}, - Json, Router, -}; -use chrono::NaiveDate; -use serde::{Deserialize, Serialize}; +use axum::{extract::Path, http::StatusCode, response::IntoResponse, routing::get, Json, Router}; use crate::feature::user::User; use super::middleware; -#[derive(Debug, Serialize, Deserialize)] -struct CreateUser { - name: String, - surname: String, - gender: bool, - birth_date: NaiveDate, -} - -#[derive(Debug, Serialize, Deserialize)] -struct UpdateUser { - user_id: i64, - name: String, - surname: String, - gender: bool, - birth_date: NaiveDate, - role_id: i64, -} - pub fn route() -> Router { Router::new() - .route("/", post(create)) - .route( - "/{user_id}", - get(read).route_layer(axum::middleware::from_fn(middleware::pass)), - ) - .route( - "/", - patch(update).route_layer(axum::middleware::from_fn(middleware::pass_higher_or_self)), - ) - .route( - "/{user_id}", - delete(delete_).route_layer(axum::middleware::from_fn(middleware::pass_higher_or_self)), - ) - .route( - "/", - get(read_all).route_layer(axum::middleware::from_fn(middleware::pass_builder_or_admin)), - ) - .route("/names/{name}", get(read_all_for_name)) - .route("/surnames/{surname}", get(read_all_for_surname)) - .route("/birth_dates/{birth_date}", get(read_all_for_birth_date)) - .route("/roles/{role}", get(read_all_for_role)) - .route("/genders/{gender}", get(read_all_for_gender)) - .route("/users_ids", get(read_all_id)) - .route("/users_ids/names/{name}", get(read_all_id_for_name)) - .route( - "/users_ids/surnames/{surname}", - get(read_all_id_for_surname), - ) - .route( - "/users_ids/birth_dates/{birth_date}", - get(read_all_id_for_birth_date), - ) - .route("/users_ids/roles/{role}", get(read_all_id_for_role)) - .route("/users_ids/genders/{gender}", get(read_all_id_for_gender)) - .route("/count", get(count_all)) - .route("/count/names/{name}", get(count_all_for_name)) - .route("/count/surnames/{surname}", get(count_all_for_surname)) - .route( - "/count/birth_dates/{birth_date}", - get(count_all_for_birth_date), - ) - .route("/count/roles/{role}", get(count_all_for_role)) - .route("/count/genders/{gender}", get(count_all_for_gender)) -} - -async fn create(Json(create_user): Json) -> impl IntoResponse { - match User::create( - &create_user.name, - &create_user.surname, - &create_user.gender, - &create_user.birth_date, - ) - .await - { - Ok(user) => (StatusCode::CREATED, Json(serde_json::json!(user))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } + .route("/{user_id}", get(read)) + // todo just for beta I think + .route_layer(axum::middleware::from_fn( + middleware::pass_by_authorization_token, + )) } async fn read(Path(user_id): Path) -> impl IntoResponse { @@ -103,212 +22,3 @@ async fn read(Path(user_id): Path) -> impl IntoResponse { ), } } - -async fn update(Json(update_user): Json) -> impl IntoResponse { - match User::update( - &update_user.user_id, - &update_user.name, - &update_user.surname, - &update_user.gender, - &update_user.birth_date, - &update_user.role_id, - ) - .await - { - Ok(user) => (StatusCode::ACCEPTED, Json(serde_json::json!(user))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn delete_(Path(user_id): Path) -> impl IntoResponse { - match User::delete(&user_id).await { - Ok(user) => (StatusCode::NO_CONTENT, Json(serde_json::json!(user))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all() -> impl IntoResponse { - match User::read_all().await { - Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all_for_name(Path(name): Path) -> impl IntoResponse { - match User::read_all_for_name(&name).await { - Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all_for_surname(Path(surname): Path) -> impl IntoResponse { - match User::read_all_for_surname(&surname).await { - Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all_for_birth_date(Path(birth_date): Path) -> impl IntoResponse { - match User::read_all_for_birth_date(&birth_date).await { - Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all_for_role(Path(role_id): Path) -> impl IntoResponse { - match User::read_all_for_role(&role_id).await { - Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all_for_gender(Path(gender): Path) -> impl IntoResponse { - match User::read_all_for_gender(&gender).await { - Ok(users) => (StatusCode::OK, Json(serde_json::json!(users))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all_id() -> impl IntoResponse { - match User::read_all_id().await { - Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all_id_for_name(Path(name): Path) -> impl IntoResponse { - match User::read_all_id_for_name(&name).await { - Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all_id_for_surname(Path(surname): Path) -> impl IntoResponse { - match User::read_all_id_for_surname(&surname).await { - Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all_id_for_birth_date(Path(birth_date): Path) -> impl IntoResponse { - match User::read_all_id_for_birth_date(&birth_date).await { - Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all_id_for_role(Path(role_id): Path) -> impl IntoResponse { - match User::read_all_id_for_role(&role_id).await { - Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn read_all_id_for_gender(Path(gender): Path) -> impl IntoResponse { - match User::read_all_id_for_gender(&gender).await { - Ok(user_ids) => (StatusCode::OK, Json(serde_json::json!(user_ids))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn count_all() -> impl IntoResponse { - match User::count_all().await { - Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn count_all_for_name(Path(name): Path) -> impl IntoResponse { - match User::count_all_for_name(&name).await { - Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn count_all_for_surname(Path(surname): Path) -> impl IntoResponse { - match User::count_all_for_surname(&surname).await { - Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn count_all_for_birth_date(Path(birth_date): Path) -> impl IntoResponse { - match User::count_all_for_birth_date(&birth_date).await { - Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn count_all_for_role(Path(role_id): Path) -> impl IntoResponse { - match User::count_all_for_role(&role_id).await { - Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -} - -async fn count_all_for_gender(Path(gender): Path) -> impl IntoResponse { - match User::count_all_for_gender(&gender).await { - Ok(count) => (StatusCode::OK, Json(serde_json::json!(count))), - Err(err_val) => ( - StatusCode::BAD_REQUEST, - Json(serde_json::json!(err_val.to_string())), - ), - } -}