From fe62b28a0363d6f378a8ea1cebd80fc448e52790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sof=C3=ADa=20Aritz?= Date: Mon, 14 Oct 2024 23:02:27 +0200 Subject: [PATCH] Improve extractors and extract some funcs --- identity-api-rs/src/auth.rs | 19 +++++++ identity-api-rs/src/database/actions.rs | 20 ++++++++ identity-api-rs/src/database/mod.rs | 1 + identity-api-rs/src/database/models.rs | 16 +++--- identity-api-rs/src/http/extractors/auth.rs | 49 +++++++++++------- identity-api-rs/src/http/routes/auth.rs | 55 +++++++++------------ identity-api-rs/src/main.rs | 1 + 7 files changed, 103 insertions(+), 58 deletions(-) create mode 100644 identity-api-rs/src/auth.rs create mode 100644 identity-api-rs/src/database/actions.rs diff --git a/identity-api-rs/src/auth.rs b/identity-api-rs/src/auth.rs new file mode 100644 index 0000000..b62807e --- /dev/null +++ b/identity-api-rs/src/auth.rs @@ -0,0 +1,19 @@ +use crate::env; +use jsonwebtoken::{TokenData, Header, Validation}; +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct JwtUser { + pub uid: String, + pub email: String, + pub name: String, + pub exp: u64, +} + +pub fn encode_jwt(claims: &JwtUser) -> jsonwebtoken::errors::Result { + jsonwebtoken::encode(&Header::new(*env::jwt_alg()), claims, &env::jwt_secret().0) +} + +pub fn decode_jwt(jwt: &str) -> jsonwebtoken::errors::Result> { + jsonwebtoken::decode::(jwt, &env::jwt_secret().1, &Validation::new(*env::jwt_alg())) +} \ No newline at end of file diff --git a/identity-api-rs/src/database/actions.rs b/identity-api-rs/src/database/actions.rs new file mode 100644 index 0000000..491abdb --- /dev/null +++ b/identity-api-rs/src/database/actions.rs @@ -0,0 +1,20 @@ +use diesel::{SqliteConnection, r2d2::{ConnectionManager, PooledConnection}, RunQueryDsl, QueryDsl, SelectableHelper, ExpressionMethods, OptionalExtension}; +use crate::database::models::User; + +pub fn user(user_id: &str, conn: &mut PooledConnection>) -> diesel::result::QueryResult { + use crate::database::schema::users::dsl::users; + users + .find(user_id) + .select(User::as_select()) + .first(conn) +} + +pub fn user_by_email(email: &str, conn: &mut PooledConnection>) -> diesel::result::QueryResult> { + use crate::database::schema::users::dsl as users; + users::users + .filter(users::email.eq(email)) + .limit(1) + .select(User::as_select()) + .first(conn) + .optional() +} \ No newline at end of file diff --git a/identity-api-rs/src/database/mod.rs b/identity-api-rs/src/database/mod.rs index 22465a7..dcc8989 100644 --- a/identity-api-rs/src/database/mod.rs +++ b/identity-api-rs/src/database/mod.rs @@ -23,6 +23,7 @@ use crate::env; pub mod models; pub mod schema; pub mod list; +pub mod actions; pub fn create_connection_pool() -> Result>, r2d2::Error> { diff --git a/identity-api-rs/src/database/models.rs b/identity-api-rs/src/database/models.rs index 5ee8bd6..aec03e2 100644 --- a/identity-api-rs/src/database/models.rs +++ b/identity-api-rs/src/database/models.rs @@ -117,12 +117,12 @@ pub struct SessionKey { #[diesel(table_name = schema::users)] #[diesel(check_for_backend(diesel::sqlite::Sqlite))] pub struct User { - id: String, - created_at: NaiveDateTime, - last_connected_at: NaiveDateTime, - email: String, - password: String, - name: String, - limits: String, - assets: String, + pub id: String, + pub created_at: NaiveDateTime, + pub last_connected_at: NaiveDateTime, + pub email: String, + pub password: String, + pub name: String, + pub limits: String, + pub assets: String, } diff --git a/identity-api-rs/src/http/extractors/auth.rs b/identity-api-rs/src/http/extractors/auth.rs index 40606f7..c1db472 100644 --- a/identity-api-rs/src/http/extractors/auth.rs +++ b/identity-api-rs/src/http/extractors/auth.rs @@ -1,21 +1,12 @@ use axum::{async_trait, extract::FromRequestParts, http::{header::AUTHORIZATION, request::Parts, StatusCode}}; -use jsonwebtoken::Validation; -use serde::{Serialize, Deserialize}; -use tracing::warn; - -use crate::env; - -#[derive(Debug, Serialize, Deserialize)] -pub struct JwtUser { - pub uid: String, - pub email: String, - pub name: String, - pub exp: u64, -} -pub struct ExtractUser(pub JwtUser); +use tracing::{warn, error}; +use crate::database::{actions, models::User}; +use crate::AppState; +use crate::auth::JwtUser; +pub struct ExtractJwtUser(pub JwtUser); #[async_trait] -impl FromRequestParts for ExtractUser +impl FromRequestParts for ExtractJwtUser where S: Send + Sync, { @@ -25,7 +16,7 @@ where if let Some(authorization) = parts.headers.get(AUTHORIZATION) { if let Ok(authorization) = authorization.to_str() { let token = authorization.replacen("Bearer ", "", 1); - match jsonwebtoken::decode::(&token, &env::jwt_secret().1, &Validation::new(*env::jwt_alg())) { + match crate::auth::decode_jwt(&token) { Ok(claims) => Ok(Self(claims.claims)), Err(err) => { warn!("token couldn't be decoded: {:?}", err); @@ -37,8 +28,32 @@ where Err((StatusCode::BAD_REQUEST, "Invalid `AUTHORIZATION` header")) } } else { - warn!("missin authorization header"); + warn!("missing authorization header"); Err((StatusCode::BAD_REQUEST, "Missing `AUTHORIZATION` header")) } } +} + +pub struct ExtractUser(pub User); + +#[async_trait] +impl FromRequestParts for ExtractUser +{ + type Rejection = (StatusCode, &'static str); + + async fn from_request_parts(parts: &mut Parts, state: &AppState) -> Result { + let jwt_user = ExtractJwtUser::from_request_parts(parts, state).await?; + + if let Ok(mut conn) = state.pool.get() { + if let Ok(user) = actions::user(&jwt_user.0.uid, &mut conn) { + Ok(Self(user)) + } else { + error!("JWT user does not exist in database"); + Err((StatusCode::INTERNAL_SERVER_ERROR, "Internal server error")) + } + } else { + error!("failed to obtain pooled connection"); + Err((StatusCode::INTERNAL_SERVER_ERROR, "Internal server error")) + } + } } \ No newline at end of file diff --git a/identity-api-rs/src/http/routes/auth.rs b/identity-api-rs/src/http/routes/auth.rs index fc62fff..cb75048 100644 --- a/identity-api-rs/src/http/routes/auth.rs +++ b/identity-api-rs/src/http/routes/auth.rs @@ -1,42 +1,36 @@ use std::time::SystemTime; - use argon2::{Argon2, PasswordHasher, password_hash::{rand_core::OsRng, SaltString}}; use axum::{extract::State, http::StatusCode, routing::{get, post}, Json, Router}; -use chrono::Utc; -use diesel::{/*query_dsl::methods::{FindDsl, SelectDsl, FilterDsl},*/ SelectableHelper, RunQueryDsl, ExpressionMethods, QueryDsl, OptionalExtension}; -use jsonwebtoken::Header; +use chrono::{Utc, NaiveDateTime}; +use diesel::{RunQueryDsl, ExpressionMethods}; use tracing::{error, info}; use serde::{Serialize, Deserialize}; use uuid::Uuid; -use crate::{database::models::User, env, http::extractors::auth::{ExtractUser, JwtUser}, AppState}; +use crate::{database::actions, http::extractors::auth::ExtractUser, auth::JwtUser, AppState}; pub fn auth_router() -> Router { - - Router::new() .route("/auth/account", get(account)) .route("/auth/register", post(register)) } -async fn account(ExtractUser(jwt_user): ExtractUser, State(state): State) -> Result, StatusCode> { - use crate::database::schema::users::dsl::users; +#[derive(Debug, Serialize)] +struct AccountResponse { + id: String, + created_at: NaiveDateTime, + last_connected_at: NaiveDateTime, + email: String, + name: String, +} - if let Ok(mut conn) = state.pool.get() { - let user = users - .find(jwt_user.uid) - .select(User::as_select()) - .first(&mut conn); - - if let Ok(user) = user { - Ok(Json(user)) - } else { - error!("JWT user does not exist in database"); - Err(StatusCode::INTERNAL_SERVER_ERROR) - } - } else { - error!("failed to obtain pooled connection"); - Err(StatusCode::INTERNAL_SERVER_ERROR) - } +async fn account(ExtractUser(user): ExtractUser) -> Result, StatusCode> { + Ok(Json(AccountResponse { + id: user.id, + created_at: user.created_at, + last_connected_at: user.last_connected_at, + email: user.email, + name: user.name, + })) } #[derive(Debug, Deserialize)] @@ -56,12 +50,7 @@ async fn register(State(state): State, Json(req): Json, Json(req): Json, Json(req): Json Ok(Json(RegisterResponse { token })), Err(err) => { error!("token couldn't be encoded: {:?}", err); diff --git a/identity-api-rs/src/main.rs b/identity-api-rs/src/main.rs index bdbefdc..4151597 100644 --- a/identity-api-rs/src/main.rs +++ b/identity-api-rs/src/main.rs @@ -28,6 +28,7 @@ use tokio::time::Duration; mod database; mod env; mod http; +mod auth; #[derive(Clone)] struct AppState {