From bcfcd2c6f04c136accdf54719d2641e4a4e57981 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: Sun, 19 Jan 2025 23:47:09 +0300 Subject: [PATCH] refactor: :recycle: new permission strategy part 2 --- .gitignore | 1 + .../20241204225135_create_user_table.up.sql | 4 +- .../20241204225143_create_post_table.up.sql | 4 +- ...20241204225151_create_comment_table.up.sql | 4 +- ...30240_create_post_interaction_table.up.sql | 4 +- ...48_create_comment_interaction_table.up.sql | 4 +- migrations/20241215002127_user_contact.up.sql | 4 +- migrations/20250110224901_login.up.sql | 2 +- src/database/user.rs | 46 ++-- src/error.rs | 19 ++ src/feature/auth.rs | 12 +- src/feature/login.rs | 51 ++-- src/feature/user.rs | 75 +++++- src/routing.rs | 7 +- src/routing/middleware.rs | 229 ++++++++++++++++++ 15 files changed, 393 insertions(+), 73 deletions(-) create mode 100644 src/routing/middleware.rs diff --git a/.gitignore b/.gitignore index 1041a51..d11f571 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ debug/ target/ Cargo.lock +cspell.json diff --git a/migrations/20241204225135_create_user_table.up.sql b/migrations/20241204225135_create_user_table.up.sql index 94fdcf3..e1bee62 100644 --- a/migrations/20241204225135_create_user_table.up.sql +++ b/migrations/20241204225135_create_user_table.up.sql @@ -1,10 +1,10 @@ -- Add up migration script here CREATE TABLE IF NOT EXISTS "user"( - id BIGSERIAL PRIMARY KEY NOT NULL UNIQUE, + user_id BIGSERIAL PRIMARY KEY NOT NULL UNIQUE, name VARCHAR(256) NOT NULL, surname VARCHAR(256) NOT NULL, gender boolean NOT NULL, birth_date DATE NOT NULL, role_id BIGSERIAL NOT NULL REFERENCES "role"(id), creation_time TIMESTAMPTZ NOT NULL DEFAULT NOW() -); \ No newline at end of file +); diff --git a/migrations/20241204225143_create_post_table.up.sql b/migrations/20241204225143_create_post_table.up.sql index 93ef941..56bc47e 100644 --- a/migrations/20241204225143_create_post_table.up.sql +++ b/migrations/20241204225143_create_post_table.up.sql @@ -1,6 +1,6 @@ -- Add up migration script here CREATE TABLE IF NOT EXISTS "post"( creation_time TIMESTAMPTZ PRIMARY KEY UNIQUE NOT NULL DEFAULT NOW(), - user_id BIGSERIAL NOT NULL REFERENCES "user"(id), + user_id BIGSERIAL NOT NULL REFERENCES "user"(user_id), post VARCHAR(8192) NOT NULL UNIQUE -); \ No newline at end of file +); diff --git a/migrations/20241204225151_create_comment_table.up.sql b/migrations/20241204225151_create_comment_table.up.sql index d89afc6..b5263f0 100644 --- a/migrations/20241204225151_create_comment_table.up.sql +++ b/migrations/20241204225151_create_comment_table.up.sql @@ -2,6 +2,6 @@ CREATE TABLE IF NOT EXISTS "comment"( creation_time TIMESTAMPTZ PRIMARY KEY NOT NULL UNIQUE DEFAULT NOW(), post_creation_time TIMESTAMPTZ NOT NULL REFERENCES "post"(creation_time), - user_id BIGSERIAL NOT NULL REFERENCES "user"(id), + user_id BIGSERIAL NOT NULL REFERENCES "user"(user_id), comment VARCHAR(8192) NOT NULL -); \ No newline at end of file +); diff --git a/migrations/20241204230240_create_post_interaction_table.up.sql b/migrations/20241204230240_create_post_interaction_table.up.sql index a028e2a..0ddb8d1 100644 --- a/migrations/20241204230240_create_post_interaction_table.up.sql +++ b/migrations/20241204230240_create_post_interaction_table.up.sql @@ -2,6 +2,6 @@ CREATE TABLE IF NOT EXISTS "post_interaction"( interaction_time TIMESTAMPTZ PRIMARY KEY NOT NULL UNIQUE DEFAULT NOW(), post_creation_time TIMESTAMPTZ NOT NULL REFERENCES "post"(creation_time), - user_id BIGSERIAL NOT NULL REFERENCES "user"(id), + user_id BIGSERIAL NOT NULL REFERENCES "user"(user_id), interaction_id BIGSERIAL NOT NULL REFERENCES "interaction"(id) -); \ No newline at end of file +); diff --git a/migrations/20241204230248_create_comment_interaction_table.up.sql b/migrations/20241204230248_create_comment_interaction_table.up.sql index 7839f08..9d7aec5 100644 --- a/migrations/20241204230248_create_comment_interaction_table.up.sql +++ b/migrations/20241204230248_create_comment_interaction_table.up.sql @@ -2,6 +2,6 @@ CREATE TABLE IF NOT EXISTS "comment_interaction"( interaction_time TIMESTAMPTZ PRIMARY KEY NOT NULL UNIQUE DEFAULT NOW(), comment_creation_time TIMESTAMPTZ NOT NULL REFERENCES "comment"(creation_time), - user_id BIGSERIAL NOT NULL REFERENCES "user"(id), + user_id BIGSERIAL NOT NULL REFERENCES "user"(user_id), interaction_id BIGSERIAL NOT NULL REFERENCES "interaction"(id) -); \ No newline at end of file +); diff --git a/migrations/20241215002127_user_contact.up.sql b/migrations/20241215002127_user_contact.up.sql index 58b1404..67440c1 100644 --- a/migrations/20241215002127_user_contact.up.sql +++ b/migrations/20241215002127_user_contact.up.sql @@ -1,6 +1,6 @@ -- Add up migration script here CREATE TABLE IF NOT EXISTS "user_contact"( - user_id BIGSERIAL NOT NULL REFERENCES "user"(id), + user_id BIGSERIAL NOT NULL REFERENCES "user"(user_id), contact_id BIGSERIAL NOT NULL REFERENCES "contact"(id), PRIMARY KEY (user_id, contact_id) -); \ No newline at end of file +); diff --git a/migrations/20250110224901_login.up.sql b/migrations/20250110224901_login.up.sql index e889a5b..b28c0b4 100644 --- a/migrations/20250110224901_login.up.sql +++ b/migrations/20250110224901_login.up.sql @@ -1,6 +1,6 @@ -- Add up migration script here CREATE TABLE IF NOT EXISTS "login" ( - user_id BIGSERIAL NOT NULL REFERENCES "user" (id), + user_id BIGSERIAL NOT NULL REFERENCES "user" (user_id), token VARCHAR(1024) NOT NULL, token_creation_time TIMESTAMPTZ NOT NULL DEFAULT NOW (), PRIMARY KEY (user_id, token) diff --git a/src/database/user.rs b/src/database/user.rs index b3c970b..9099cc8 100644 --- a/src/database/user.rs +++ b/src/database/user.rs @@ -13,8 +13,8 @@ pub async fn create( sqlx::query_as!( User, r#" - INSERT INTO "user"(name, surname, gender, birth_date, role_id) - VALUES ($1, $2, $3, $4, $5) + INSERT INTO "user"(name, surname, gender, birth_date, role_id) + VALUES ($1, $2, $3, $4, $5) RETURNING * "#, name, @@ -31,7 +31,7 @@ pub async fn read(id: &i64, database_connection: &Pool) -> Result Result { sqlx::query_as!(User, r#" - UPDATE "user" SET "name" = $2, "surname" = $3, "gender" = $4, "birth_date" = $5, "role_id" = $6 WHERE "id" = $1 + UPDATE "user" SET "name" = $2, "surname" = $3, "gender" = $4, "birth_date" = $5, "role_id" = $6 WHERE "user_id" = $1 RETURNING * "#, id, name, surname, gender, birth_date, role_id).fetch_one(database_connection).await } @@ -59,7 +59,7 @@ pub async fn delete(id: &i64, database_connection: &Pool) -> Result) -> Result, sqlx::Error> { Ok(sqlx::query!( r#" - SELECT "id" FROM "user" + SELECT "user_id" FROM "user" "#, ) .fetch_all(database_connection) .await? .iter() - .map(|record| record.id) + .map(|record| record.user_id) .collect::>()) } @@ -173,14 +173,14 @@ pub async fn read_all_id_for_name( ) -> Result, sqlx::Error> { Ok(sqlx::query!( r#" - SELECT "id" FROM "user" WHERE "name" = $1 + SELECT "user_id" FROM "user" WHERE "name" = $1 "#, name ) .fetch_all(database_connection) .await? .iter() - .map(|record| record.id) + .map(|record| record.user_id) .collect::>()) } @@ -190,14 +190,14 @@ pub async fn read_all_id_for_surname( ) -> Result, sqlx::Error> { Ok(sqlx::query!( r#" - SELECT "id" FROM "user" WHERE "surname" = $1 + SELECT "user_id" FROM "user" WHERE "surname" = $1 "#, surname ) .fetch_all(database_connection) .await? .iter() - .map(|record| record.id) + .map(|record| record.user_id) .collect::>()) } @@ -207,14 +207,14 @@ pub async fn read_all_id_for_birth_date( ) -> Result, sqlx::Error> { Ok(sqlx::query!( r#" - SELECT "id" FROM "user" WHERE "birth_date" = $1 + SELECT "user_id" FROM "user" WHERE "birth_date" = $1 "#, birth_date ) .fetch_all(database_connection) .await? .iter() - .map(|record| record.id) + .map(|record| record.user_id) .collect::>()) } @@ -224,14 +224,14 @@ pub async fn read_all_id_for_role( ) -> Result, sqlx::Error> { Ok(sqlx::query!( r#" - SELECT "id" FROM "user" WHERE "role_id" = $1 + SELECT "user_id" FROM "user" WHERE "role_id" = $1 "#, role_id ) .fetch_all(database_connection) .await? .iter() - .map(|record| record.id) + .map(|record| record.user_id) .collect::>()) } @@ -241,21 +241,21 @@ pub async fn read_all_id_for_gender( ) -> Result, sqlx::Error> { Ok(sqlx::query!( r#" - SELECT "id" FROM "user" WHERE "gender" = $1 + SELECT "user_id" FROM "user" WHERE "gender" = $1 "#, gender ) .fetch_all(database_connection) .await? .iter() - .map(|record| record.id) + .map(|record| record.user_id) .collect::>()) } pub async fn count_all(database_connection: &Pool) -> Result { sqlx::query!( r#" - SELECT COUNT(id) FROM "user" + SELECT COUNT(user_id) FROM "user" "#, ) .fetch_one(database_connection) @@ -272,7 +272,7 @@ pub async fn count_all_for_name( ) -> Result { sqlx::query!( r#" - SELECT COUNT(id) FROM "user" WHERE "name" = $1 + SELECT COUNT(user_id) FROM "user" WHERE "name" = $1 "#, name ) @@ -290,7 +290,7 @@ pub async fn count_all_for_surname( ) -> Result { sqlx::query!( r#" - SELECT COUNT(id) FROM "user" WHERE "surname" = $1 + SELECT COUNT(user_id) FROM "user" WHERE "surname" = $1 "#, surname ) @@ -308,7 +308,7 @@ pub async fn count_all_for_birth_date( ) -> Result { sqlx::query!( r#" - SELECT COUNT(id) FROM "user" WHERE "birth_date" = $1 + SELECT COUNT(user_id) FROM "user" WHERE "birth_date" = $1 "#, birth_date ) @@ -326,7 +326,7 @@ pub async fn count_all_for_role( ) -> Result { sqlx::query!( r#" - SELECT COUNT(id) FROM "user" WHERE "role_id" = $1 + SELECT COUNT(user_id) FROM "user" WHERE "role_id" = $1 "#, role_id ) @@ -344,7 +344,7 @@ pub async fn count_all_for_gender( ) -> Result { sqlx::query!( r#" - SELECT COUNT(id) FROM "user" WHERE "gender" = $1 + SELECT COUNT(user_id) FROM "user" WHERE "gender" = $1 "#, gender ) diff --git a/src/error.rs b/src/error.rs index 2be81ce..dd63f43 100644 --- a/src/error.rs +++ b/src/error.rs @@ -51,3 +51,22 @@ impl std::error::Error for ForumMailError { self.source() } } + +#[derive(Debug, Serialize, Deserialize)] +pub enum ForumAuthError { + TokenRefreshTimeOver, +} + +impl std::fmt::Display for ForumAuthError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ForumAuthError::TokenRefreshTimeOver => write!(f, "Token Refresh Time is Over"), + } + } +} + +impl std::error::Error for ForumAuthError { + fn cause(&self) -> Option<&dyn std::error::Error> { + self.source() + } +} diff --git a/src/feature/auth.rs b/src/feature/auth.rs index 55bd888..009e078 100644 --- a/src/feature/auth.rs +++ b/src/feature/auth.rs @@ -7,6 +7,8 @@ use crate::{ ONE_TIME_PASSWORDS, }; +use super::user::User; + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct OneTimePassword { pub user_id: i64, @@ -18,19 +20,15 @@ impl OneTimePassword { RwLock::new(vec![]) } - pub async fn new( - user_id: &i64, - user_name: &String, - user_email: &String, - ) -> Result<(), ForumMailError> { + pub async fn new(user: &User, user_email: &String) -> Result<(), ForumMailError> { let one_time_password = "123".to_owned(); let new_self = Self { - user_id: *user_id, + user_id: user.user_id, one_time_password, }; let mail_template = - MailTemplate::OneTimePassword(MailFieldsOneTimePassword::new(user_name, &new_self)); + MailTemplate::OneTimePassword(MailFieldsOneTimePassword::new(&user.name, &new_self)); mail_template.send_mail(user_email).await?; diff --git a/src/feature/login.rs b/src/feature/login.rs index faa2ddf..12329a4 100644 --- a/src/feature/login.rs +++ b/src/feature/login.rs @@ -2,18 +2,20 @@ use std::sync::LazyLock; use chrono::{DateTime, Utc}; use jwt_simple::{ - claims::Claims, + claims::{Claims, JWTClaims}, common::VerificationOptions, prelude::{HS256Key, MACLike}, }; use serde::{Deserialize, Serialize}; use sqlx::{Pool, Postgres}; -use crate::{database::login, SERVER_CONFIG}; +use crate::{database::login, error::ForumAuthError, SERVER_CONFIG}; + +use super::user::User; static TOKEN_META: LazyLock = LazyLock::new(TokenMeta::init); -struct TokenMeta { +pub struct TokenMeta { token_key: HS256Key, token_verification_options: Option, } @@ -30,27 +32,28 @@ impl TokenMeta { } } - async fn create_token() -> Option { + async fn create_token(user_id: &i64) -> Option { let key = &TOKEN_META.token_key; - let claims = Claims::create(jwt_simple::prelude::Duration::from_mins( - SERVER_CONFIG.login_token_expiration_time_limit as u64, - )); + + let claims = Claims::with_custom_claims( + *user_id, + jwt_simple::prelude::Duration::from_mins( + SERVER_CONFIG.login_token_expiration_time_limit as u64, + ), + ); + let token = key.authenticate(claims).unwrap(); match TokenMeta::verify_token(&token).await { - true => Some(token), - false => None, + Ok(_) => Some(token), + Err(_) => None, } } - async fn verify_token(token: &String) -> bool { + pub async fn verify_token(token: &String) -> Result, jwt_simple::Error> { let token_meta = &TOKEN_META; token_meta .token_key - .verify_token::( - token, - token_meta.token_verification_options.clone(), - ) - .is_ok() + .verify_token::(token, token_meta.token_verification_options.clone()) } } @@ -66,7 +69,9 @@ impl Login { user_id: &i64, database_connection: &Pool, ) -> Result { - let token = TokenMeta::create_token() + User::read(user_id, database_connection).await?; + + let token = TokenMeta::create_token(user_id) .await .expect("Should not panic if it isn't configured wrong"); login::create(user_id, &token, database_connection).await @@ -77,6 +82,8 @@ impl Login { token: &String, database_connection: &Pool, ) -> Result { + User::read(user_id, database_connection).await?; + login::read(user_id, token, database_connection).await } @@ -84,21 +91,21 @@ impl Login { user_id: &i64, token: &String, database_connection: &Pool, - ) -> Result { + ) -> Result> { let login = Login::read(user_id, token, database_connection).await?; - match TokenMeta::verify_token(token).await { - true => Ok(login), - false => { + Ok(_) => Ok(login), + Err(_) => { if DateTime::::default() .signed_duration_since(&login.token_creation_time) .num_minutes() <= SERVER_CONFIG.login_token_refresh_time_limit as i64 { Login::delete(user_id, token, database_connection).await?; - Login::create(user_id, database_connection).await - } else { + let login = Login::create(user_id, database_connection).await?; Ok(login) + } else { + Err(Box::new(ForumAuthError::TokenRefreshTimeOver)) } } } diff --git a/src/feature/user.rs b/src/feature/user.rs index a66b657..b68e145 100644 --- a/src/feature/user.rs +++ b/src/feature/user.rs @@ -13,7 +13,7 @@ pub struct Contact { #[derive(Debug, Serialize, Deserialize)] pub struct User { - pub id: i64, + pub user_id: i64, pub name: String, pub surname: String, pub gender: bool, @@ -33,12 +33,15 @@ impl User { user::create(name, surname, gender, birth_date, database_connection).await } - pub async fn read(id: &i64, database_connection: &Pool) -> Result { - user::read(id, database_connection).await + pub async fn read( + user_id: &i64, + database_connection: &Pool, + ) -> Result { + user::read(user_id, database_connection).await } pub async fn update( - id: &i64, + user_id: &i64, name: &String, surname: &String, gender: &bool, @@ -47,7 +50,7 @@ impl User { database_connection: &Pool, ) -> Result { user::update( - id, + user_id, name, surname, gender, @@ -59,10 +62,10 @@ impl User { } pub async fn delete( - id: &i64, + user_id: &i64, database_connection: &Pool, ) -> Result { - user::delete(id, database_connection).await + user::delete(user_id, database_connection).await } pub async fn read_all(database_connection: &Pool) -> Result, sqlx::Error> { @@ -183,4 +186,62 @@ impl User { ) -> Result { user::count_all_for_gender(gender, database_connection).await } + + pub async fn is_builder(user: &User) -> bool { + if user.role_id == 0 { + true + } else { + false + } + } + + pub async fn is_admin(user: &User) -> bool { + if user.role_id == 1 { + true + } else { + false + } + } + + pub async fn is_banned(user: &User) -> bool { + if user.role_id == -1 { + true + } else { + false + } + } + + pub async fn is_builder_or_admin(user: &User) -> bool { + if user.role_id == 0 || user.role_id == 1 { + true + } else { + false + } + } + + pub async fn is_self(user: &User, target_user: &User) -> bool { + if user.user_id == target_user.user_id { + true + } else { + false + } + } + + pub async fn is_higher(user: &User, target_user: &User) -> bool { + if user.user_id >= 0 { + if user.user_id < target_user.user_id { + return true; + } + } + + false + } + + pub async fn is_higher_or_self(user: &User, target_user: &User) -> bool { + if User::is_self(user, target_user).await { + true + } else { + User::is_higher(user, target_user).await + } + } } diff --git a/src/routing.rs b/src/routing.rs index 4ae2c6d..910306b 100644 --- a/src/routing.rs +++ b/src/routing.rs @@ -3,6 +3,7 @@ pub mod comment_interaction; pub mod contact; pub mod interaction; pub mod login; +pub mod middleware; pub mod post; pub mod post_interaction; pub mod role; @@ -18,6 +19,10 @@ use crate::{database, AppState}; pub async fn route(concurrency_limit: &usize, State(app_state): State) -> Router { Router::new() .route("/", get(alive)) + .route_layer(axum::middleware::from_fn_with_state( + app_state.clone(), + middleware::pass, + )) .nest( "/roles", role::route(axum::extract::State(app_state.clone())), @@ -59,7 +64,7 @@ pub async fn route(concurrency_limit: &usize, State(app_state): State) .with_state(app_state) } -async fn alive(State(app_state): State) -> impl IntoResponse { +pub async fn alive(State(app_state): State) -> impl IntoResponse { match database::is_alive(&app_state.database_connection).await { true => StatusCode::OK, false => StatusCode::SERVICE_UNAVAILABLE, diff --git a/src/routing/middleware.rs b/src/routing/middleware.rs new file mode 100644 index 0000000..6e6cf5c --- /dev/null +++ b/src/routing/middleware.rs @@ -0,0 +1,229 @@ +use std::sync::Arc; + +use axum::{ + body::{to_bytes, Body}, + extract::{Request, State}, + http::{self, StatusCode}, + middleware::Next, + response::IntoResponse, +}; +use sqlx::{Pool, Postgres}; + +use crate::{ + feature::{login::TokenMeta, user::User}, + AppState, +}; + +#[derive(Debug)] +struct UserAndRequest { + user: User, + request: Request, +} + +#[derive(Debug)] +struct UserAndTargetUserAndRequest { + user: User, + target_user: User, + request: Request, +} + +async fn user_extraction( + request: Request, + database_connection: &Pool, +) -> Option { + 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_header)) = authorization_header.split_once(' ') { + if bearer == "bearer" { + if let Ok(claims) = + TokenMeta::verify_token(&authorization_header.to_string()).await + { + return Some(UserAndRequest { + user: User::read(&claims.custom, database_connection).await.ok()?, + request, + }); + } + } + } + } + } + None +} + +async fn target_user_extraction_from_json( + json: &serde_json::Value, + database_connection: &Pool, +) -> Option { + 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() { + return User::read(&target_user_id, database_connection).await.ok(); + } + } + } + + None +} + +async fn user_and_target_user_extraction( + request: Request, + database_connection: &Pool, +) -> Option { + let user_and_request = user_extraction(request, database_connection).await?; + let user = user_and_request.user; + let request = user_and_request.request; + + 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()?; + + let body = Body::from(json.to_string()); + let request = Request::from_parts(parts, body); + + Some(UserAndTargetUserAndRequest { + user, + target_user: target_user_extraction_from_json(&json, database_connection).await?, + request, + }) +} + +pub async fn pass( + State(app_state): State, + request: Request, + next: Next, +) -> Result { + match user_extraction(request, &app_state.database_connection).await { + Some(user_and_request) => { + let user = Arc::new(user_and_request.user); + let mut request = user_and_request.request; + + request.extensions_mut().insert(user); + + Ok(next.run(request).await) + } + None => Err(StatusCode::FORBIDDEN), + } +} + +pub async fn pass_builder( + State(app_state): State, + request: Request, + next: Next, +) -> Result { + if let Some(user_and_request) = user_extraction(request, &app_state.database_connection).await { + let user = user_and_request.user; + let mut request = user_and_request.request; + + if User::is_builder(&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_admin( + State(app_state): State, + request: Request, + next: Next, +) -> Result { + if let Some(user_and_request) = user_extraction(request, &app_state.database_connection).await { + let user = user_and_request.user; + let mut request = user_and_request.request; + + if User::is_admin(&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_builder_or_admin( + State(app_state): State, + request: Request, + next: Next, +) -> Result { + if let Some(user_and_request) = user_extraction(request, &app_state.database_connection).await { + let user = user_and_request.user; + let mut request = user_and_request.request; + + if User::is_builder_or_admin(&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_self( + State(app_state): State, + request: Request, + next: Next, +) -> Result { + if let Some(user_and_target_user_and_request) = + user_and_target_user_extraction(request, &app_state.database_connection).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( + State(app_state): State, + request: Request, + next: Next, +) -> Result { + if let Some(user_and_target_user_and_request) = + user_and_target_user_extraction(request, &app_state.database_connection).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( + State(app_state): State, + request: Request, + next: Next, +) -> Result { + if let Some(user_and_target_user_and_request) = + user_and_target_user_extraction(request, &app_state.database_connection).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_or_self(&user, &target_user).await { + let user = Arc::new(user); + request.extensions_mut().insert(user); + + return Ok(next.run(request).await); + } + } + Err(StatusCode::FORBIDDEN) +}