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,
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),
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)
);

View file

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

View file

@ -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())

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 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<Arc<UserAndToken>>) -> 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<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))),
Err(err_val) => (
StatusCode::BAD_REQUEST,

View file

@ -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<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 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<UserAndRequest> {
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<User, ForumAuthError> {
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<TargetUserAndRequest> {
let uri_parts = request.uri().path().split('/').collect::<Vec<&str>>();
for (index, uri_part) in uri_parts.iter().enumerate() {
async fn user_extraction_from_header(request_headers: &HeaderMap) -> Result<User, ForumAuthError> {
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<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 let Some(target_user_id) = uri_parts.get(index) {
if let Ok(target_user_id) = (*target_user_id).parse::<i64>() {
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::<i64>() {
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<TargetUserAndRequest> {
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<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());
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<impl IntoResponse, StatusCode> {
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<UserAndTargetUserAndRequest> {
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<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;
pub async fn pass_by_authorization_token(
mut request: Request,
next: Next,
) -> Result<impl IntoResponse, StatusCode> {
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<impl IntoResponse, StatusCode> {
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<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 {
let user = Arc::new(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)
}
pub async fn pass_builder_or_admin(
request: Request,
pub async fn pass_builder_or_admin_by_authorization_token(
mut request: Request,
next: Next,
) -> Result<impl IntoResponse, StatusCode> {
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<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_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,
pub async fn pass_builder_by_authorization_token_with_target_user_by_request_uri(
mut 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 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<impl IntoResponse, StatusCode> {
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<impl IntoResponse, StatusCode> {
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);
}
}

View file

@ -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<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 {
match Role::read(&id).await {
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 {
match Role::read_all().await {
Ok(roles) => (StatusCode::OK, Json(serde_json::json!(roles))),

View file

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