feat: admin routing part 1

This commit is contained in:
Ahmet Kaan GÜMÜŞ 2025-01-25 23:50:45 +03:00
parent f4765630ee
commit a462d3a82d
11 changed files with 520 additions and 506 deletions

View file

@ -8,3 +8,12 @@ CREATE TABLE IF NOT EXISTS "user"(
role_id BIGINT NOT NULL REFERENCES "role" DEFAULT 10, role_id BIGINT NOT NULL REFERENCES "role" DEFAULT 10,
creation_time TIMESTAMPTZ NOT NULL DEFAULT NOW() 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;

View file

@ -3,5 +3,6 @@ CREATE TABLE IF NOT EXISTS "user_contact"(
user_id BIGSERIAL NOT NULL REFERENCES "user"(user_id), user_id BIGSERIAL NOT NULL REFERENCES "user"(user_id),
contact_id BIGSERIAL NOT NULL REFERENCES "contact"(id), contact_id BIGSERIAL NOT NULL REFERENCES "contact"(id),
contact_value VARCHAR(256) NOT NULL, contact_value VARCHAR(256) NOT NULL,
PRIMARY KEY (user_id, contact_id) PRIMARY KEY (user_id, contact_id),
UNIQUE (contact_id, contact_value)
); );

View file

@ -41,7 +41,7 @@ impl std::fmt::Display for ForumMailError {
ForumMailError::TemplateLackOfParameter => { ForumMailError::TemplateLackOfParameter => {
write!(f, "Template Parameters Are Not Enough") 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)] #[derive(Debug, Serialize, Deserialize)]
pub enum ForumAuthError { pub enum ForumAuthError {
AuthenticationFailed(String),
TokenRefreshTimeOver, TokenRefreshTimeOver,
} }
impl std::fmt::Display for ForumAuthError { impl std::fmt::Display for ForumAuthError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
ForumAuthError::AuthenticationFailed(err_val) => {
write!(f, "Authentication Failed | {}", err_val)
}
ForumAuthError::TokenRefreshTimeOver => write!(f, "Token Refresh Time is Over"), ForumAuthError::TokenRefreshTimeOver => write!(f, "Token Refresh Time is Over"),
} }
} }

View file

@ -1,3 +1,4 @@
pub mod admin;
pub mod comment; pub mod comment;
pub mod comment_interaction; pub mod comment_interaction;
pub mod contact; pub mod contact;
@ -29,6 +30,7 @@ pub async fn route(concurrency_limit: &usize) -> Router {
.nest("/comment_interactions", comment_interaction::route()) .nest("/comment_interactions", comment_interaction::route())
.nest("/contacts", contact::route()) .nest("/contacts", contact::route())
.nest("/user_contacts", user_contact::route()) .nest("/user_contacts", user_contact::route())
.nest("/admin", admin::route())
.layer(CorsLayer::permissive()) .layer(CorsLayer::permissive())
.layer(ConcurrencyLimitLayer::new(*concurrency_limit)) .layer(ConcurrencyLimitLayer::new(*concurrency_limit))
.layer(TraceLayer::new_for_http()) .layer(TraceLayer::new_for_http())

15
src/routing/admin.rs Normal file
View file

@ -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,
))
}

58
src/routing/admin/role.rs Normal file
View file

@ -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<CreateRole>) -> 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<UpdateRole>) -> 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<i64>) -> 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())),
),
}
}

299
src/routing/admin/user.rs Normal file
View file

@ -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<CreateUser>) -> 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<Arc<User>>,
Json(update_user): Json<UpdateUser>,
) -> 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<Arc<User>>) -> 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<String>) -> 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<String>) -> 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<NaiveDate>) -> 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<i64>) -> 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<bool>) -> 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<String>) -> 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<String>) -> 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<NaiveDate>) -> 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<i64>) -> 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<bool>) -> 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<String>) -> 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<String>) -> 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<NaiveDate>) -> 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<i64>) -> 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<bool>) -> 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())),
),
}
}

View file

@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
use crate::feature::{auth::OneTimePassword, login::Login, user::User, user_contact::UserContact}; 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; 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<Arc<UserAndToken>>) -> impl IntoResponse { async fn update(
match Login::update(&user_and_token.user.user_id, &user_and_token.token).await { Extension(user_and_authorization_token): Extension<Arc<UserAndAuthorizationToken>>,
) -> 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))), Ok(login) => (StatusCode::ACCEPTED, Json(serde_json::json!(login))),
Err(err_val) => ( Err(err_val) => (
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,

View file

@ -1,154 +1,142 @@
use std::sync::Arc; use std::sync::Arc;
use axum::{ use axum::{
body::{to_bytes, Body},
extract::Request, extract::Request,
http::{self, HeaderMap, Method, StatusCode}, http::{self, HeaderMap, StatusCode, Uri},
middleware::Next, middleware::Next,
response::IntoResponse, response::IntoResponse,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::feature::{login::TokenMeta, user::User}; use crate::{
error::ForumAuthError,
feature::{login::TokenMeta, user::User},
};
#[derive(Debug)] #[derive(Debug, Serialize, Deserialize)]
struct UserAndRequest { struct UserAndTargetUser {
user: User,
request: Request,
}
#[derive(Debug)]
struct TargetUserAndRequest {
target_user: User,
request: Request,
}
#[derive(Debug)]
struct UserAndTargetUserAndRequest {
user: User, user: User,
target_user: User, target_user: User,
request: Request,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct UserAndToken { pub struct UserAndAuthorizationToken {
pub user: User, pub user: User,
pub token: String, pub authorization_token: String,
} }
async fn authorization_token_extraction(request_headers: &HeaderMap) -> Option<String> { async fn authorization_token_extraction(
request_headers: &HeaderMap,
) -> Result<String, ForumAuthError> {
if let Some(authorization_header) = request_headers.get(http::header::AUTHORIZATION) { if let Some(authorization_header) = request_headers.get(http::header::AUTHORIZATION) {
if let Ok(authorization_header) = authorization_header.to_str() { if let Ok(authorization_header) = authorization_header.to_str() {
if let Some((bearer, authorization_token)) = authorization_header.split_once(' ') { if let Some((bearer, authorization_token)) = authorization_header.split_once(' ') {
if bearer.to_lowercase() == "bearer" { 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<UserAndRequest> { async fn user_extraction_from_authorization_token(
if let Some(authorization_token) = authorization_token_extraction(&request.headers()).await { authorization_token: &String,
match TokenMeta::verify_token(&authorization_token.to_string()).await { ) -> Result<User, ForumAuthError> {
Ok(claims) => { match TokenMeta::verify_token(&authorization_token.to_string()).await {
return Some(UserAndRequest { Ok(claims) => User::read(&claims.custom.user_id)
user: User::read(&claims.custom.user_id).await.ok()?, .await
request, .map_err(|err_val| ForumAuthError::AuthenticationFailed(err_val.to_string())),
}); Err(err_val) => Err(ForumAuthError::AuthenticationFailed(err_val.to_string())),
}
Err(err_val) => {
eprintln!("Verify Token | {}", err_val);
}
}
} }
None
} }
async fn target_user_extraction_from_uri(request: Request) -> Option<TargetUserAndRequest> { async fn user_extraction_from_header(request_headers: &HeaderMap) -> Result<User, ForumAuthError> {
let uri_parts = request.uri().path().split('/').collect::<Vec<&str>>(); match authorization_token_extraction(request_headers).await {
for (index, uri_part) in uri_parts.iter().enumerate() { 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<User, ForumAuthError> {
let request_uri_parts = request_uri.path().split('/').collect::<Vec<&str>>();
for (index, uri_part) in request_uri_parts.iter().enumerate() {
if *uri_part == "users" { if *uri_part == "users" {
if let Some(target_user_id) = uri_parts.get(index) { if let Some(user_id) = request_uri_parts.get(index) {
if let Ok(target_user_id) = (*target_user_id).parse::<i64>() { if let Ok(user_id) = (*user_id).parse::<i64>() {
if let Ok(target_user) = User::read(&target_user_id).await { User::read(&user_id).await.map_err(|err_val| {
return Some(TargetUserAndRequest { ForumAuthError::AuthenticationFailed(err_val.to_string())
target_user, })?;
request,
});
}
} }
} }
} }
} }
None Err(ForumAuthError::AuthenticationFailed("".to_owned()))
} }
async fn target_user_extraction_from_json(request: Request) -> Option<TargetUserAndRequest> { async fn user_from_header_and_target_user_from_uri_extraction(
let (parts, body) = request.into_parts(); request_headers: &HeaderMap,
let bytes = to_bytes(body, usize::MAX).await.ok()?; request_uri: &Uri,
let json: serde_json::Value = serde_json::from_slice(&bytes).ok()?; ) -> Result<UserAndTargetUser, ForumAuthError> {
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()); Ok(UserAndTargetUser { user, target_user })
let request = Request::from_parts(parts, body); }
if let Some(target_user_id) = json.get("user_id") { pub async fn user_and_token(
if target_user_id.is_i64() { mut request: Request,
if let Some(target_user_id) = target_user_id.as_i64() { next: Next,
if let Ok(target_user) = User::read(&target_user_id).await { ) -> Result<impl IntoResponse, StatusCode> {
return Some(TargetUserAndRequest { if let Ok(authorization_token) = authorization_token_extraction(&request.headers()).await {
target_user, if let Ok(user) = user_extraction_from_authorization_token(&authorization_token).await {
request, let user_and_token = Arc::new(UserAndAuthorizationToken {
}); user,
} authorization_token,
} });
request.extensions_mut().insert(user_and_token);
return Ok(next.run(request).await);
} }
} }
Err(StatusCode::FORBIDDEN)
None
} }
async fn user_and_target_user_extraction(request: Request) -> Option<UserAndTargetUserAndRequest> { pub async fn pass_by_authorization_token(
let user_and_request = user_extraction(request).await?; mut request: Request,
let user = user_and_request.user; next: Next,
let request = user_and_request.request; ) -> Result<impl IntoResponse, StatusCode> {
match user_extraction_from_header(request.headers()).await {
let target_user_and_request = if request.method() == Method::GET { Ok(user) => {
target_user_extraction_from_uri(request).await let user = Arc::new(user);
} 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<impl IntoResponse, StatusCode> {
match user_extraction(request).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); request.extensions_mut().insert(user);
Ok(next.run(request).await) Ok(next.run(request).await)
} }
None => Err(StatusCode::FORBIDDEN), Err(_) => Err(StatusCode::FORBIDDEN),
} }
} }
pub async fn pass_builder(request: Request, next: Next) -> Result<impl IntoResponse, StatusCode> { pub async fn pass_by_uri_user_extraction(
if let Some(user_and_request) = user_extraction(request).await { mut request: Request,
let user = user_and_request.user; next: Next,
let mut request = user_and_request.request; ) -> Result<impl IntoResponse, StatusCode> {
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<impl IntoResponse, StatusCode> {
if let Ok(user) = user_extraction_from_header(request.headers()).await {
if User::is_builder(&user).await { if User::is_builder(&user).await {
let user = Arc::new(user); let user = Arc::new(user);
request.extensions_mut().insert(user); request.extensions_mut().insert(user);
@ -159,14 +147,11 @@ pub async fn pass_builder(request: Request, next: Next) -> Result<impl IntoRespo
Err(StatusCode::FORBIDDEN) Err(StatusCode::FORBIDDEN)
} }
pub async fn pass_builder_or_admin( pub async fn pass_builder_or_admin_by_authorization_token(
request: Request, mut request: Request,
next: Next, next: Next,
) -> Result<impl IntoResponse, StatusCode> { ) -> Result<impl IntoResponse, StatusCode> {
if let Some(user_and_request) = user_extraction(request).await { if let Ok(user) = user_extraction_from_header(request.headers()).await {
let user = user_and_request.user;
let mut request = user_and_request.request;
if User::is_builder_or_admin(&user).await { if User::is_builder_or_admin(&user).await {
let user = Arc::new(user); let user = Arc::new(user);
request.extensions_mut().insert(user); request.extensions_mut().insert(user);
@ -177,50 +162,19 @@ pub async fn pass_builder_or_admin(
Err(StatusCode::FORBIDDEN) Err(StatusCode::FORBIDDEN)
} }
pub async fn pass_self(request: Request, next: Next) -> Result<impl IntoResponse, StatusCode> { pub async fn pass_builder_by_authorization_token_with_target_user_by_request_uri(
if let Some(user_and_target_user_and_request) = user_and_target_user_extraction(request).await { mut request: Request,
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<impl IntoResponse, StatusCode> {
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,
next: Next, next: Next,
) -> Result<impl IntoResponse, StatusCode> { ) -> Result<impl IntoResponse, StatusCode> {
if let Some(user_and_target_user_and_request) = user_and_target_user_extraction(request).await { if let Ok(user_and_target_user) =
let user = user_and_target_user_and_request.user; user_from_header_and_target_user_from_uri_extraction(request.headers(), request.uri()).await
let target_user = user_and_target_user_and_request.target_user; {
let mut request = user_and_target_user_and_request.request; 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 { if User::is_builder(&user).await {
let user = Arc::new(user); let target_user = Arc::new(target_user);
request.extensions_mut().insert(user); request.extensions_mut().insert(target_user);
return Ok(next.run(request).await); return Ok(next.run(request).await);
} }
@ -228,14 +182,20 @@ pub async fn pass_higher_or_self(
Err(StatusCode::FORBIDDEN) Err(StatusCode::FORBIDDEN)
} }
pub async fn user_and_token(request: Request, next: Next) -> Result<impl IntoResponse, StatusCode> { pub async fn pass_builder_or_admin_by_authorization_token_with_target_user_by_request_uri(
if let Some(token) = authorization_token_extraction(&request.headers()).await { mut request: Request,
if let Some(user_and_request) = user_extraction(request).await { next: Next,
let user = user_and_request.user; ) -> Result<impl IntoResponse, StatusCode> {
let mut request = user_and_request.request; if let Ok(user_and_target_user) =
let user_and_token = Arc::new(UserAndToken { user, token }); 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); return Ok(next.run(request).await);
} }
} }

View file

@ -1,44 +1,13 @@
use axum::{ use axum::{extract::Path, http::StatusCode, response::IntoResponse, routing::get, Json, Router};
extract::Path,
http::StatusCode,
response::IntoResponse,
routing::{delete, get, patch, post},
Json, Router,
};
use serde::{Deserialize, Serialize};
use crate::feature::role::Role; 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 { pub fn route() -> Router {
Router::new() Router::new()
.route("/", post(create))
.route("/{id}", get(read)) .route("/{id}", get(read))
.route("/", patch(update))
.route("/{id}", delete(delete_))
.route("/", get(read_all)) .route("/", get(read_all))
} }
async fn create(Json(create_role): Json<CreateRole>) -> 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<i64>) -> impl IntoResponse { async fn read(Path(id): Path<i64>) -> impl IntoResponse {
match Role::read(&id).await { match Role::read(&id).await {
Ok(role) => (StatusCode::OK, Json(serde_json::json!(role))), Ok(role) => (StatusCode::OK, Json(serde_json::json!(role))),
@ -49,26 +18,6 @@ async fn read(Path(id): Path<i64>) -> impl IntoResponse {
} }
} }
async fn update(Json(update_role): Json<UpdateRole>) -> 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<i64>) -> 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 { async fn read_all() -> impl IntoResponse {
match Role::read_all().await { match Role::read_all().await {
Ok(roles) => (StatusCode::OK, Json(serde_json::json!(roles))), Ok(roles) => (StatusCode::OK, Json(serde_json::json!(roles))),

View file

@ -1,97 +1,16 @@
use axum::{ use axum::{extract::Path, http::StatusCode, response::IntoResponse, routing::get, Json, Router};
extract::Path,
http::StatusCode,
response::IntoResponse,
routing::{delete, get, patch, post},
Json, Router,
};
use chrono::NaiveDate;
use serde::{Deserialize, Serialize};
use crate::feature::user::User; use crate::feature::user::User;
use super::middleware; 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 { pub fn route() -> Router {
Router::new() Router::new()
.route("/", post(create)) .route("/{user_id}", get(read))
.route( // todo just for beta I think
"/{user_id}", .route_layer(axum::middleware::from_fn(
get(read).route_layer(axum::middleware::from_fn(middleware::pass)), middleware::pass_by_authorization_token,
) ))
.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<CreateUser>) -> 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 read(Path(user_id): Path<i64>) -> impl IntoResponse { async fn read(Path(user_id): Path<i64>) -> impl IntoResponse {
@ -103,212 +22,3 @@ async fn read(Path(user_id): Path<i64>) -> impl IntoResponse {
), ),
} }
} }
async fn update(Json(update_user): Json<UpdateUser>) -> 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<i64>) -> 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<String>) -> 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<String>) -> 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<NaiveDate>) -> 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<i64>) -> 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<bool>) -> 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<String>) -> 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<String>) -> 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<NaiveDate>) -> 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<i64>) -> 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<bool>) -> 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<String>) -> 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<String>) -> 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<NaiveDate>) -> 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<i64>) -> 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<bool>) -> 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())),
),
}
}