feat: user_and_token middleware

refactor: ♻️ getting otp from client
This commit is contained in:
Ahmet Kaan GÜMÜŞ 2025-01-24 04:15:06 +03:00
parent 0bb5a0b753
commit f4765630ee
4 changed files with 86 additions and 47 deletions

View file

@ -5,7 +5,7 @@ use tokio::sync::RwLock;
use crate::{
error::ForumMailError,
mail::{MailFieldsOneTimePassword, MailTemplate},
//mail::{MailFieldsOneTimePassword, MailTemplate},
};
use super::user::User;
@ -31,10 +31,11 @@ impl OneTimePassword {
one_time_password,
};
let mail_template =
MailTemplate::OneTimePassword(MailFieldsOneTimePassword::new(&user.name, &new_self));
mail_template.send_mail(user_email).await?;
//todo vice versa comments :D
// let mail_template =
// MailTemplate::OneTimePassword(MailFieldsOneTimePassword::new(&user.name, &new_self));
println!("{}", user_email);
//mail_template.send_mail(user_email).await?;
let mut one_time_passwords = ONE_TIME_PASSWORDS.write().await;
one_time_passwords.push(new_self);

View file

@ -1,14 +1,18 @@
use std::sync::Arc;
use axum::{
extract::Path,
http::StatusCode,
response::IntoResponse,
routing::{delete, get, patch, post},
Json, Router,
Extension, Json, Router,
};
use serde::{Deserialize, Serialize};
use crate::feature::{auth::OneTimePassword, login::Login, user::User, user_contact::UserContact};
use super::middleware::{self, UserAndToken};
const CONTACT_EMAIL_DEFAULT_ID: i64 = 0;
#[derive(Debug, Serialize, Deserialize)]
@ -18,13 +22,8 @@ struct CreateOneTimePassword {
#[derive(Debug, Serialize, Deserialize)]
struct CreateLogin {
pub one_time_password: OneTimePassword,
}
#[derive(Debug, Serialize, Deserialize)]
struct UpdateLogin {
pub user_id: i64,
pub token: String,
user_id: i64,
one_time_password: String,
}
pub fn route() -> Router {
@ -32,7 +31,10 @@ pub fn route() -> Router {
.route("/one_time_password", post(create_one_time_password))
.route("/", post(create))
.route("/users/{user_id}/tokens/{token}", get(read))
.route("/", patch(update))
.route(
"/",
patch(update).route_layer(axum::middleware::from_fn(middleware::user_and_token)),
)
.route("/users/{user_id}/tokens/{token}", delete(delete_))
.route("/users/{user_id}", get(read_all_for_user))
.route("/users/{user_id}", delete(delete_all_for_user))
@ -58,8 +60,13 @@ async fn create_one_time_password(
}
}
async fn create(Json(create_login): Json<CreateLogin>) -> impl IntoResponse {
match OneTimePassword::verify(&create_login.one_time_password).await {
true => match Login::create(&create_login.one_time_password.user_id).await {
let one_time_password = OneTimePassword {
user_id: create_login.user_id,
one_time_password: create_login.one_time_password,
};
match OneTimePassword::verify(&one_time_password).await {
true => match Login::create(&one_time_password.user_id).await {
Ok(login) => (StatusCode::CREATED, Json(serde_json::json!(login))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
@ -85,8 +92,8 @@ async fn read(Path((user_id, token)): Path<(i64, String)>) -> impl IntoResponse
}
}
async fn update(Json(update_login): Json<UpdateLogin>) -> impl IntoResponse {
match Login::update(&update_login.user_id, &update_login.token).await {
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 {
Ok(login) => (StatusCode::ACCEPTED, Json(serde_json::json!(login))),
Err(err_val) => (
StatusCode::BAD_REQUEST,

View file

@ -3,10 +3,11 @@ use std::sync::Arc;
use axum::{
body::{to_bytes, Body},
extract::Request,
http::{self, Method, StatusCode},
http::{self, HeaderMap, Method, StatusCode},
middleware::Next,
response::IntoResponse,
};
use serde::{Deserialize, Serialize};
use crate::feature::{login::TokenMeta, user::User};
@ -29,22 +30,18 @@ struct UserAndTargetUserAndRequest {
request: Request,
}
async fn user_extraction(request: Request) -> Option<UserAndRequest> {
if let Some(authorization_header) = request.headers().get(http::header::AUTHORIZATION) {
#[derive(Debug, Serialize, Deserialize)]
pub struct UserAndToken {
pub user: User,
pub token: String,
}
async fn authorization_token_extraction(request_headers: &HeaderMap) -> Option<String> {
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 let Some((bearer, authorization_token)) = authorization_header.split_once(' ') {
if bearer.to_lowercase() == "bearer" {
match TokenMeta::verify_token(&authorization_header.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);
}
}
return Some(authorization_token.to_owned());
}
}
}
@ -52,6 +49,23 @@ async fn user_extraction(request: Request) -> Option<UserAndRequest> {
None
}
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);
}
}
}
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() {
@ -213,3 +227,17 @@ 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 });
request.extensions_mut().insert(user_and_token);
return Ok(next.run(request).await);
}
}
Err(StatusCode::FORBIDDEN)
}

View file

@ -22,7 +22,7 @@ struct CreateUser {
#[derive(Debug, Serialize, Deserialize)]
struct UpdateUser {
id: i64,
user_id: i64,
name: String,
surname: String,
gender: bool,
@ -34,7 +34,7 @@ pub fn route() -> Router {
Router::new()
.route("/", post(create))
.route(
"/{id}",
"/{user_id}",
get(read).route_layer(axum::middleware::from_fn(middleware::pass)),
)
.route(
@ -42,7 +42,7 @@ pub fn route() -> Router {
patch(update).route_layer(axum::middleware::from_fn(middleware::pass_higher_or_self)),
)
.route(
"/{id}",
"/{user_id}",
delete(delete_).route_layer(axum::middleware::from_fn(middleware::pass_higher_or_self)),
)
.route(
@ -54,15 +54,18 @@ pub fn route() -> Router {
.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("/ids", get(read_all_id))
.route("/ids/names/{name}", get(read_all_id_for_name))
.route("/ids/surnames/{surname}", get(read_all_id_for_surname))
.route("/users_ids", get(read_all_id))
.route("/users_ids/names/{name}", get(read_all_id_for_name))
.route(
"/ids/birth_dates/{birth_date}",
"/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("/ids/roles/{role}", get(read_all_id_for_role))
.route("/ids/genders/{gender}", get(read_all_id_for_gender))
.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))
@ -91,8 +94,8 @@ async fn create(Json(create_user): Json<CreateUser>) -> impl IntoResponse {
}
}
async fn read(Path(id): Path<i64>) -> impl IntoResponse {
match User::read(&id).await {
async fn read(Path(user_id): Path<i64>) -> impl IntoResponse {
match User::read(&user_id).await {
Ok(user) => (StatusCode::OK, Json(serde_json::json!(user))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
@ -103,7 +106,7 @@ async fn read(Path(id): Path<i64>) -> impl IntoResponse {
async fn update(Json(update_user): Json<UpdateUser>) -> impl IntoResponse {
match User::update(
&update_user.id,
&update_user.user_id,
&update_user.name,
&update_user.surname,
&update_user.gender,
@ -120,8 +123,8 @@ async fn update(Json(update_user): Json<UpdateUser>) -> impl IntoResponse {
}
}
async fn delete_(Path(id): Path<i64>) -> impl IntoResponse {
match User::delete(&id).await {
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,