feat: ✨ admin routing part 3
This commit is contained in:
parent
bf2b0a439c
commit
8fb39f27ca
14 changed files with 305 additions and 139 deletions
|
@ -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)
|
||||||
);
|
);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
))
|
))
|
||||||
|
|
58
src/routing/admin/interaction.rs
Normal file
58
src/routing/admin/interaction.rs
Normal 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())),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
45
src/routing/admin/login.rs
Normal file
45
src/routing/admin/login.rs
Normal 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())),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
|
@ -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())),
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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))),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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())),
|
||||||
|
|
|
@ -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) => (
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue