feat: admin routing part 3

This commit is contained in:
Ahmet Kaan GÜMÜŞ 2025-01-27 17:17:13 +03:00
parent bf2b0a439c
commit 8fb39f27ca
14 changed files with 305 additions and 139 deletions

View file

@ -1,7 +1,7 @@
-- Add up migration script here -- Add up migration script here
CREATE TABLE IF NOT EXISTS "login" ( CREATE TABLE IF NOT EXISTS "login" (
user_id BIGSERIAL NOT NULL REFERENCES "user" (user_id), user_id BIGSERIAL NOT NULL REFERENCES "user" (user_id),
token VARCHAR(1024) NOT NULL, authorization_token VARCHAR(1024) NOT NULL,
token_creation_time TIMESTAMPTZ NOT NULL DEFAULT NOW (), token_creation_time TIMESTAMPTZ NOT NULL DEFAULT NOW (),
PRIMARY KEY (user_id, token) PRIMARY KEY (user_id, authorization_token)
); );

View file

@ -2,43 +2,43 @@ use crate::feature::login::Login;
use super::DATABASE_CONNECTIONS; use super::DATABASE_CONNECTIONS;
pub async fn create(user_id: &i64, token: &String) -> Result<Login, sqlx::Error> { pub async fn create(user_id: &i64, authorization_token: &String) -> Result<Login, sqlx::Error> {
sqlx::query_as!( sqlx::query_as!(
Login, Login,
r#" r#"
INSERT INTO "login"(user_id, token) INSERT INTO "login"(user_id, authorization_token)
VALUES ($1, $2) VALUES ($1, $2)
RETURNING * RETURNING *
"#, "#,
user_id, user_id,
token, authorization_token,
) )
.fetch_one(&*DATABASE_CONNECTIONS) .fetch_one(&*DATABASE_CONNECTIONS)
.await .await
} }
pub async fn read(user_id: &i64, token: &String) -> Result<Login, sqlx::Error> { pub async fn read(user_id: &i64, authorization_token: &String) -> Result<Login, sqlx::Error> {
sqlx::query_as!( sqlx::query_as!(
Login, Login,
r#" r#"
SELECT * FROM "login" WHERE "user_id" = $1 AND "token" = $2 SELECT * FROM "login" WHERE "user_id" = $1 AND "authorization_token" = $2
"#, "#,
user_id, user_id,
token authorization_token
) )
.fetch_one(&*DATABASE_CONNECTIONS) .fetch_one(&*DATABASE_CONNECTIONS)
.await .await
} }
pub async fn delete(user_id: &i64, token: &String) -> Result<Login, sqlx::Error> { pub async fn delete(user_id: &i64, authorization_token: &String) -> Result<Login, sqlx::Error> {
sqlx::query_as!( sqlx::query_as!(
Login, Login,
r#" r#"
DELETE FROM "login" WHERE "user_id" = $1 AND "token" = $2 DELETE FROM "login" WHERE "user_id" = $1 AND "authorization_token" = $2
RETURNING * RETURNING *
"#, "#,
user_id, user_id,
token, authorization_token,
) )
.fetch_one(&*DATABASE_CONNECTIONS) .fetch_one(&*DATABASE_CONNECTIONS)
.await .await

View file

@ -67,6 +67,22 @@ pub async fn delete(user_id: &i64, contact_id: &i64) -> Result<UserContact, sqlx
.await .await
} }
pub async fn read_for_value(
contact_id: &i64,
contact_value: &String,
) -> Result<UserContact, sqlx::Error> {
sqlx::query_as!(
UserContact,
r#"
SELECT * FROM "user_contact" WHERE "contact_id" = $1 AND "contact_value" = $2
"#,
contact_id,
contact_value,
)
.fetch_one(&*DATABASE_CONNECTIONS)
.await
}
pub async fn read_all_for_user(user_id: &i64) -> Result<Vec<UserContact>, sqlx::Error> { pub async fn read_all_for_user(user_id: &i64) -> Result<Vec<UserContact>, sqlx::Error> {
sqlx::query_as!( sqlx::query_as!(
UserContact, UserContact,

View file

@ -15,7 +15,7 @@ static ONE_TIME_PASSWORDS: LazyLock<RwLock<Vec<OneTimePassword>>> =
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct OneTimePassword { pub struct OneTimePassword {
pub user_id: i64, user_id: i64,
pub one_time_password: String, pub one_time_password: String,
} }
@ -24,6 +24,13 @@ impl OneTimePassword {
RwLock::new(vec![]) RwLock::new(vec![])
} }
pub async fn from_string(user: &User, one_time_password: &String) -> Self {
OneTimePassword {
user_id: user.user_id,
one_time_password: one_time_password.to_owned(),
}
}
pub async fn new(user: &User, user_email: &String) -> Result<(), ForumMailError> { pub async fn new(user: &User, user_email: &String) -> Result<(), ForumMailError> {
let one_time_password = "123".to_owned(); let one_time_password = "123".to_owned();
let new_self = Self { let new_self = Self {

View file

@ -12,23 +12,23 @@ use crate::{database::login, error::ForumAuthError, SERVER_CONFIG};
use super::user::User; use super::user::User;
static TOKEN_META: LazyLock<TokenMeta> = LazyLock::new(TokenMeta::init); static TOKEN_META: LazyLock<AuthorizationTokenMeta> = LazyLock::new(AuthorizationTokenMeta::init);
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct CustomClaim { pub struct CustomClaim {
pub user_id: i64, pub user_id: i64,
} }
pub struct TokenMeta { pub struct AuthorizationTokenMeta {
token_key: HS256Key, authorization_token: HS256Key,
token_verification_options: Option<VerificationOptions>, authorization_token_verification_options: Option<VerificationOptions>,
} }
impl TokenMeta { impl AuthorizationTokenMeta {
fn init() -> Self { fn init() -> Self {
Self { Self {
token_key: HS256Key::generate(), authorization_token: HS256Key::generate(),
token_verification_options: { authorization_token_verification_options: {
let mut verification_options = VerificationOptions::default(); let mut verification_options = VerificationOptions::default();
verification_options.time_tolerance = Some(jwt_simple::prelude::Duration::from(0)); verification_options.time_tolerance = Some(jwt_simple::prelude::Duration::from(0));
Some(verification_options) Some(verification_options)
@ -37,7 +37,7 @@ impl TokenMeta {
} }
async fn create_token(user_id: &i64) -> Option<String> { async fn create_token(user_id: &i64) -> Option<String> {
let key = &TOKEN_META.token_key; let key = &TOKEN_META.authorization_token;
let custom_claim = CustomClaim { user_id: *user_id }; let custom_claim = CustomClaim { user_id: *user_id };
let claims = Claims::with_custom_claims( let claims = Claims::with_custom_claims(
custom_claim, custom_claim,
@ -47,23 +47,24 @@ impl TokenMeta {
); );
let token = key.authenticate(claims).unwrap(); let token = key.authenticate(claims).unwrap();
match TokenMeta::verify_token(&token).await { match AuthorizationTokenMeta::verify_token(&token).await {
Ok(_) => Some(token), Ok(_) => Some(token),
Err(_) => None, Err(_) => None,
} }
} }
pub async fn verify_token(token: &String) -> Result<JWTClaims<CustomClaim>, jwt_simple::Error> { pub async fn verify_token(token: &String) -> Result<JWTClaims<CustomClaim>, jwt_simple::Error> {
TOKEN_META TOKEN_META.authorization_token.verify_token::<CustomClaim>(
.token_key token,
.verify_token::<CustomClaim>(token, TOKEN_META.token_verification_options.clone()) TOKEN_META.authorization_token_verification_options.clone(),
)
} }
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct Login { pub struct Login {
pub user_id: i64, pub user_id: i64,
pub token: String, pub authorization_token: String,
pub token_creation_time: DateTime<Utc>, pub token_creation_time: DateTime<Utc>,
} }
@ -71,24 +72,24 @@ impl Login {
pub async fn create(user_id: &i64) -> Result<Login, sqlx::Error> { pub async fn create(user_id: &i64) -> Result<Login, sqlx::Error> {
User::read(user_id).await?; User::read(user_id).await?;
let token = TokenMeta::create_token(user_id) let token = AuthorizationTokenMeta::create_token(user_id)
.await .await
.expect("Should not panic if it isn't configured wrong"); .expect("Should not panic if it isn't configured wrong");
login::create(user_id, &token).await login::create(user_id, &token).await
} }
pub async fn read(user_id: &i64, token: &String) -> Result<Login, sqlx::Error> { pub async fn read(user_id: &i64, authorization_token: &String) -> Result<Login, sqlx::Error> {
User::read(user_id).await?; User::read(user_id).await?;
login::read(user_id, token).await login::read(user_id, authorization_token).await
} }
pub async fn update( pub async fn update(
user_id: &i64, user_id: &i64,
token: &String, authorization_token: &String,
) -> Result<Login, Box<dyn std::error::Error>> { ) -> Result<Login, Box<dyn std::error::Error>> {
let login = Login::read(user_id, token).await?; let login = Login::read(user_id, authorization_token).await?;
match TokenMeta::verify_token(token).await { match AuthorizationTokenMeta::verify_token(authorization_token).await {
Ok(_) => Ok(login), Ok(_) => Ok(login),
Err(_) => { Err(_) => {
if DateTime::<Utc>::default() if DateTime::<Utc>::default()
@ -96,7 +97,7 @@ impl Login {
.num_minutes() .num_minutes()
<= SERVER_CONFIG.login_token_refresh_time_limit as i64 <= SERVER_CONFIG.login_token_refresh_time_limit as i64
{ {
Login::delete(user_id, token).await?; Login::delete(user_id, authorization_token).await?;
let login = Login::create(user_id).await?; let login = Login::create(user_id).await?;
Ok(login) Ok(login)
} else { } else {
@ -105,8 +106,8 @@ impl Login {
} }
} }
} }
pub async fn delete(user_id: &i64, token: &String) -> Result<Login, sqlx::Error> { pub async fn delete(user_id: &i64, authorization_token: &String) -> Result<Login, sqlx::Error> {
login::delete(user_id, token).await login::delete(user_id, authorization_token).await
} }
pub async fn read_all_for_user(user_id: &i64) -> Result<Vec<Login>, sqlx::Error> { pub async fn read_all_for_user(user_id: &i64) -> Result<Vec<Login>, sqlx::Error> {

View file

@ -24,6 +24,13 @@ impl UserContact {
user_contact::read(&user.user_id, contact_id).await user_contact::read(&user.user_id, contact_id).await
} }
pub async fn read_for_value(
contact_id: &i64,
contact_value: &String,
) -> Result<UserContact, sqlx::Error> {
user_contact::read_for_value(contact_id, contact_value).await
}
pub async fn update( pub async fn update(
user: &User, user: &User,
contact_id: &i64, contact_id: &i64,

View file

@ -1,4 +1,6 @@
pub mod contact; pub mod contact;
pub mod interaction;
pub mod login;
pub mod role; pub mod role;
pub mod user; pub mod user;
pub mod user_contact; pub mod user_contact;
@ -9,10 +11,12 @@ use super::middleware::builder_or_admin_by_authorization_token;
pub fn route() -> Router { pub fn route() -> Router {
Router::new() Router::new()
.nest("/logins", login::route())
.nest("/users", user::route()) .nest("/users", user::route())
.nest("/roles", role::route()) .nest("/roles", role::route())
.nest("/contacts", contact::route()) .nest("/contacts", contact::route())
.nest("/user_contacts", user_contact::route()) .nest("/user_contacts", user_contact::route())
.nest("/interactions", interaction::route())
.route_layer(axum::middleware::from_fn( .route_layer(axum::middleware::from_fn(
builder_or_admin_by_authorization_token, builder_or_admin_by_authorization_token,
)) ))

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::interaction::Interaction;
#[derive(Debug, Serialize, Deserialize)]
struct CreateInteraction {
name: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct UpdateInteraction {
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_interaction): Json<CreateInteraction>) -> impl IntoResponse {
match Interaction::create(&create_interaction.name).await {
Ok(interaction) => (StatusCode::CREATED, Json(serde_json::json!(interaction))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(err_val.to_string())),
),
}
}
async fn update(Json(update_interaction): Json<UpdateInteraction>) -> impl IntoResponse {
match Interaction::update(&update_interaction.id, &update_interaction.name).await {
Ok(interaction) => (StatusCode::ACCEPTED, Json(serde_json::json!(interaction))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(err_val.to_string())),
),
}
}
async fn delete_(Path(id): Path<i64>) -> impl IntoResponse {
match Interaction::delete(&id).await {
Ok(interaction) => (StatusCode::NO_CONTENT, Json(serde_json::json!(interaction))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(err_val.to_string())),
),
}
}

View file

@ -0,0 +1,45 @@
use std::sync::Arc;
use axum::{
http::StatusCode,
response::IntoResponse,
routing::{delete, get},
Extension, Json, Router,
};
use crate::{
feature::{login::Login, user::User},
routing::middleware::by_uri_then_insert,
};
pub fn route() -> Router {
Router::new()
.route(
"/users/{user_id}",
delete(delete_all_for_user).route_layer(axum::middleware::from_fn(by_uri_then_insert)),
)
.route(
"/count/users/{user_id}",
get(count_all_for_user).route_layer(axum::middleware::from_fn(by_uri_then_insert)),
)
}
async fn delete_all_for_user(Extension(user): Extension<Arc<User>>) -> impl IntoResponse {
match Login::delete_all_for_user(&user.user_id).await {
Ok(logins) => (StatusCode::OK, Json(serde_json::json!(logins))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(err_val.to_string())),
),
}
}
async fn count_all_for_user(Extension(user): Extension<Arc<User>>) -> impl IntoResponse {
match Login::count_all_for_user(&user.user_id).await {
Ok(login_count) => (StatusCode::OK, Json(serde_json::json!(login_count))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(err_val.to_string())),
),
}
}

View file

@ -90,21 +90,25 @@ async fn update(
Extension(target_user): Extension<Arc<User>>, Extension(target_user): Extension<Arc<User>>,
Json(update_user): Json<UpdateUser>, Json(update_user): Json<UpdateUser>,
) -> impl IntoResponse { ) -> impl IntoResponse {
match User::update( if update_user.role_id == 0 {
&target_user.user_id, (StatusCode::FORBIDDEN, Json(serde_json::json!({})))
&update_user.name, } else {
&update_user.surname, match User::update(
&update_user.gender, &target_user.user_id,
&update_user.birth_date, &update_user.name,
&update_user.role_id, &update_user.surname,
) &update_user.gender,
.await &update_user.birth_date,
{ &update_user.role_id,
Ok(user) => (StatusCode::ACCEPTED, Json(serde_json::json!(user))), )
Err(err_val) => ( .await
StatusCode::BAD_REQUEST, {
Json(serde_json::json!(err_val.to_string())), Ok(user) => (StatusCode::ACCEPTED, Json(serde_json::json!(user))),
), Err(err_val) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(err_val.to_string())),
),
}
} }
} }

View file

@ -1,10 +1,4 @@
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 serde::{Deserialize, Serialize};
use crate::feature::interaction::Interaction; use crate::feature::interaction::Interaction;
@ -22,23 +16,10 @@ struct UpdateInteraction {
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_interaction): Json<CreateInteraction>) -> impl IntoResponse {
match Interaction::create(&create_interaction.name).await {
Ok(interaction) => (StatusCode::CREATED, Json(serde_json::json!(interaction))),
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 Interaction::read(&id).await { match Interaction::read(&id).await {
Ok(interaction) => (StatusCode::OK, Json(serde_json::json!(interaction))), Ok(interaction) => (StatusCode::OK, Json(serde_json::json!(interaction))),
@ -49,26 +30,6 @@ async fn read(Path(id): Path<i64>) -> impl IntoResponse {
} }
} }
async fn update(Json(update_interaction): Json<UpdateInteraction>) -> impl IntoResponse {
match Interaction::update(&update_interaction.id, &update_interaction.name).await {
Ok(interaction) => (StatusCode::ACCEPTED, Json(serde_json::json!(interaction))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(err_val.to_string())),
),
}
}
async fn delete_(Path(id): Path<i64>) -> impl IntoResponse {
match Interaction::delete(&id).await {
Ok(interaction) => (StatusCode::NO_CONTENT, Json(serde_json::json!(interaction))),
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 Interaction::read_all().await { match Interaction::read_all().await {
Ok(interactions) => (StatusCode::OK, Json(serde_json::json!(interactions))), Ok(interactions) => (StatusCode::OK, Json(serde_json::json!(interactions))),

View file

@ -1,7 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use axum::{ use axum::{
extract::Path,
http::StatusCode, http::StatusCode,
response::IntoResponse, response::IntoResponse,
routing::{delete, get, patch, post}, routing::{delete, get, patch, post},
@ -11,18 +10,20 @@ 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::{user_and_token_then_insert, UserAndAuthorizationToken}; use super::middleware::{
by_authorization_token_then_insert, user_and_token_then_insert, UserAndAuthorizationToken,
};
const CONTACT_EMAIL_DEFAULT_ID: i64 = 0; const CONTACT_EMAIL_DEFAULT_ID: i64 = 0;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
struct CreateOneTimePassword { struct CreateOneTimePassword {
pub user_id: i64, pub user_email: String,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
struct CreateLogin { struct CreateLogin {
user_id: i64, user_email: String,
one_time_password: String, one_time_password: String,
} }
@ -30,26 +31,48 @@ pub fn route() -> Router {
Router::new() Router::new()
.route("/one_time_password", post(create_one_time_password)) .route("/one_time_password", post(create_one_time_password))
.route("/", post(create)) .route("/", post(create))
.route("/users/{user_id}/tokens/{token}", get(read)) .route(
"/",
get(read).route_layer(axum::middleware::from_fn(user_and_token_then_insert)),
)
.route( .route(
"/", "/",
patch(update).route_layer(axum::middleware::from_fn(user_and_token_then_insert)), patch(update).route_layer(axum::middleware::from_fn(user_and_token_then_insert)),
) )
.route("/users/{user_id}/tokens/{token}", delete(delete_)) .route(
.route("/users/{user_id}", get(read_all_for_user)) "/",
.route("/users/{user_id}", delete(delete_all_for_user)) delete(delete_).route_layer(axum::middleware::from_fn(user_and_token_then_insert)),
.route("/count/users/{user_id}", get(count_all_for_user)) )
.route(
"/users",
delete(delete_all_for_user).route_layer(axum::middleware::from_fn(
by_authorization_token_then_insert,
)),
)
.route("/count/users", get(count_all_for_user))
} }
async fn create_one_time_password( async fn create_one_time_password(
Json(create_one_time_password): Json<CreateOneTimePassword>, Json(create_one_time_password): Json<CreateOneTimePassword>,
) -> impl IntoResponse { ) -> impl IntoResponse {
//todo get user from middleware or something match UserContact::read_for_value(
let user = User::read(&create_one_time_password.user_id).await.unwrap(); &CONTACT_EMAIL_DEFAULT_ID,
match UserContact::read(&user, &CONTACT_EMAIL_DEFAULT_ID).await { &create_one_time_password.user_email,
Ok(user_email) => match OneTimePassword::new(&user, &user_email.contact_value).await { )
Ok(_) => (StatusCode::CREATED, Json(serde_json::json!(""))), .await
{
Ok(user_contact) => match User::read(&user_contact.user_id).await {
Ok(user) => {
match OneTimePassword::new(&user, &create_one_time_password.user_email).await {
Ok(_) => (StatusCode::CREATED, Json(serde_json::json!({}))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(err_val.to_string())),
),
}
}
Err(err_val) => ( Err(err_val) => (
StatusCode::BAD_REQUEST, // this must be impossible that's why I send 500
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!(err_val.to_string())), Json(serde_json::json!(err_val.to_string())),
), ),
}, },
@ -60,30 +83,50 @@ async fn create_one_time_password(
} }
} }
async fn create(Json(create_login): Json<CreateLogin>) -> impl IntoResponse { async fn create(Json(create_login): Json<CreateLogin>) -> impl IntoResponse {
let one_time_password = OneTimePassword { match UserContact::read_for_value(&CONTACT_EMAIL_DEFAULT_ID, &create_login.user_email).await {
user_id: create_login.user_id, Ok(user_contact) => match User::read(&user_contact.user_id).await {
one_time_password: create_login.one_time_password, Ok(user) => {
}; let one_time_password =
OneTimePassword::from_string(&user, &create_login.one_time_password).await;
match OneTimePassword::verify(&one_time_password).await { match OneTimePassword::verify(&one_time_password).await {
true => match Login::create(&one_time_password.user_id).await { true => match Login::create(&user.user_id).await {
Ok(login) => (StatusCode::CREATED, Json(serde_json::json!(login))), Ok(login) => (StatusCode::CREATED, Json(serde_json::json!(login))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(err_val.to_string())),
),
},
false => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(
"One Time Password Authentication Failed".to_string()
)),
),
}
}
Err(err_val) => ( Err(err_val) => (
StatusCode::BAD_REQUEST, // this must be impossible that's why I send 500
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!(err_val.to_string())), Json(serde_json::json!(err_val.to_string())),
), ),
}, },
false => ( Err(err_val) => (
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,
Json(serde_json::json!( Json(serde_json::json!(err_val.to_string())),
"One Time Password Authentication Failed".to_string()
)),
), ),
} }
} }
async fn read(Path((user_id, token)): Path<(i64, String)>) -> impl IntoResponse { async fn read(
match Login::read(&user_id, &token).await { Extension(user_and_authorization_token): Extension<Arc<UserAndAuthorizationToken>>,
) -> impl IntoResponse {
match Login::read(
&user_and_authorization_token.user.user_id,
&user_and_authorization_token.authorization_token,
)
.await
{
Ok(login) => (StatusCode::OK, Json(serde_json::json!(login))), Ok(login) => (StatusCode::OK, Json(serde_json::json!(login))),
Err(err_val) => ( Err(err_val) => (
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,
@ -109,8 +152,15 @@ async fn update(
} }
} }
async fn delete_(Path((user_id, token)): Path<(i64, String)>) -> impl IntoResponse { async fn delete_(
match Login::delete(&user_id, &token).await { Extension(user_and_authorization_token): Extension<Arc<UserAndAuthorizationToken>>,
) -> impl IntoResponse {
match Login::delete(
&user_and_authorization_token.user.user_id,
&user_and_authorization_token.authorization_token,
)
.await
{
Ok(login) => (StatusCode::NO_CONTENT, Json(serde_json::json!(login))), Ok(login) => (StatusCode::NO_CONTENT, Json(serde_json::json!(login))),
Err(err_val) => ( Err(err_val) => (
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,
@ -119,8 +169,8 @@ async fn delete_(Path((user_id, token)): Path<(i64, String)>) -> impl IntoRespon
} }
} }
async fn read_all_for_user(Path(user_id): Path<i64>) -> impl IntoResponse { async fn delete_all_for_user(Extension(user): Extension<Arc<User>>) -> impl IntoResponse {
match Login::read_all_for_user(&user_id).await { match Login::delete_all_for_user(&user.user_id).await {
Ok(logins) => (StatusCode::OK, Json(serde_json::json!(logins))), Ok(logins) => (StatusCode::OK, Json(serde_json::json!(logins))),
Err(err_val) => ( Err(err_val) => (
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,
@ -129,18 +179,8 @@ async fn read_all_for_user(Path(user_id): Path<i64>) -> impl IntoResponse {
} }
} }
async fn delete_all_for_user(Path(user_id): Path<i64>) -> impl IntoResponse { async fn count_all_for_user(Extension(user): Extension<Arc<User>>) -> impl IntoResponse {
match Login::delete_all_for_user(&user_id).await { match Login::count_all_for_user(&user.user_id).await {
Ok(logins) => (StatusCode::OK, Json(serde_json::json!(logins))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(err_val.to_string())),
),
}
}
async fn count_all_for_user(Path(user_id): Path<i64>) -> impl IntoResponse {
match Login::count_all_for_user(&user_id).await {
Ok(login_count) => (StatusCode::OK, Json(serde_json::json!(login_count))), Ok(login_count) => (StatusCode::OK, Json(serde_json::json!(login_count))),
Err(err_val) => ( Err(err_val) => (
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,

View file

@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
error::ForumAuthError, error::ForumAuthError,
feature::{login::TokenMeta, user::User}, feature::{login::AuthorizationTokenMeta, user::User},
}; };
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -43,7 +43,7 @@ async fn authorization_token_extraction(
async fn user_extraction_from_authorization_token( async fn user_extraction_from_authorization_token(
authorization_token: &String, authorization_token: &String,
) -> Result<User, ForumAuthError> { ) -> Result<User, ForumAuthError> {
match TokenMeta::verify_token(&authorization_token.to_string()).await { match AuthorizationTokenMeta::verify_token(&authorization_token.to_string()).await {
Ok(claims) => User::read(&claims.custom.user_id) Ok(claims) => User::read(&claims.custom.user_id)
.await .await
.map_err(|err_val| ForumAuthError::AuthenticationFailed(err_val.to_string())), .map_err(|err_val| ForumAuthError::AuthenticationFailed(err_val.to_string())),

View file

@ -1,12 +1,35 @@
use axum::{extract::Path, http::StatusCode, response::IntoResponse, routing::get, Json, Router}; use std::sync::Arc;
use axum::{
extract::Path, http::StatusCode, response::IntoResponse, routing::get, Extension, Json, Router,
};
use crate::feature::user::User; use crate::feature::user::User;
use super::middleware::by_authorization_token_then_insert;
pub fn route() -> Router { pub fn route() -> Router {
Router::new().route("/{user_id}", get(read)) Router::new()
.route(
"/",
get(read).route_layer(axum::middleware::from_fn(
by_authorization_token_then_insert,
)),
)
.route("/{user_id}", get(read_anybody))
} }
async fn read(Path(user_id): Path<i64>) -> impl IntoResponse { async fn read(Extension(user): Extension<Arc<User>>) -> impl IntoResponse {
match User::read(&user.user_id).await {
Ok(user) => (StatusCode::OK, Json(serde_json::json!(user))),
Err(err_val) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!(err_val.to_string())),
),
}
}
async fn read_anybody(Path(user_id): Path<i64>) -> impl IntoResponse {
match User::read(&user_id).await { match User::read(&user_id).await {
Ok(user) => (StatusCode::OK, Json(serde_json::json!(user))), Ok(user) => (StatusCode::OK, Json(serde_json::json!(user))),
Err(err_val) => ( Err(err_val) => (