Improve extractors and extract some funcs
This commit is contained in:
parent
546e883a9c
commit
fe62b28a03
7 changed files with 103 additions and 58 deletions
19
identity-api-rs/src/auth.rs
Normal file
19
identity-api-rs/src/auth.rs
Normal file
|
@ -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<String> {
|
||||
jsonwebtoken::encode(&Header::new(*env::jwt_alg()), claims, &env::jwt_secret().0)
|
||||
}
|
||||
|
||||
pub fn decode_jwt(jwt: &str) -> jsonwebtoken::errors::Result<TokenData<JwtUser>> {
|
||||
jsonwebtoken::decode::<JwtUser>(jwt, &env::jwt_secret().1, &Validation::new(*env::jwt_alg()))
|
||||
}
|
20
identity-api-rs/src/database/actions.rs
Normal file
20
identity-api-rs/src/database/actions.rs
Normal file
|
@ -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<ConnectionManager<SqliteConnection>>) -> diesel::result::QueryResult<User> {
|
||||
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<ConnectionManager<SqliteConnection>>) -> diesel::result::QueryResult<Option<User>> {
|
||||
use crate::database::schema::users::dsl as users;
|
||||
users::users
|
||||
.filter(users::email.eq(email))
|
||||
.limit(1)
|
||||
.select(User::as_select())
|
||||
.first(conn)
|
||||
.optional()
|
||||
}
|
|
@ -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<Pool<ConnectionManager<SqliteConnection>>, r2d2::Error> {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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<S> FromRequestParts<S> for ExtractUser
|
||||
impl<S> FromRequestParts<S> 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::<JwtUser>(&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<AppState> for ExtractUser
|
||||
{
|
||||
type Rejection = (StatusCode, &'static str);
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, state: &AppState) -> Result<Self, Self::Rejection> {
|
||||
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"))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<AppState> {
|
||||
|
||||
|
||||
Router::new()
|
||||
.route("/auth/account", get(account))
|
||||
.route("/auth/register", post(register))
|
||||
}
|
||||
|
||||
async fn account(ExtractUser(jwt_user): ExtractUser, State(state): State<AppState>) -> Result<Json<User>, 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<Json<AccountResponse>, 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<AppState>, Json(req): Json<RegisterRequest
|
|||
use crate::database::schema::limits::dsl as limits;
|
||||
|
||||
if let Ok(mut conn) = state.pool.get() {
|
||||
let user = users::users
|
||||
.filter(users::email.eq(&req.email))
|
||||
.limit(1)
|
||||
.select(User::as_select())
|
||||
.first(&mut conn)
|
||||
.optional();
|
||||
let user = actions::user_by_email(&req.email, &mut conn);
|
||||
|
||||
if user.is_err() {
|
||||
error!("failed to retrieve potential existing user from database: {}, error: {:?}", &req.email, user.err());
|
||||
|
@ -112,7 +101,7 @@ async fn register(State(state): State<AppState>, Json(req): Json<RegisterRequest
|
|||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
match jsonwebtoken::encode(&Header::new(*env::jwt_alg()), &JwtUser {
|
||||
match crate::auth::encode_jwt(&JwtUser {
|
||||
uid: user_id,
|
||||
email: req.email,
|
||||
name: req.name,
|
||||
|
@ -120,7 +109,7 @@ async fn register(State(state): State<AppState>, Json(req): Json<RegisterRequest
|
|||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.expect("time went backwards")
|
||||
.as_secs() + 180 * 24 * 3600,
|
||||
}, &env::jwt_secret().0) {
|
||||
}) {
|
||||
Ok(token) => Ok(Json(RegisterResponse { token })),
|
||||
Err(err) => {
|
||||
error!("token couldn't be encoded: {:?}", err);
|
||||
|
|
|
@ -28,6 +28,7 @@ use tokio::time::Duration;
|
|||
mod database;
|
||||
mod env;
|
||||
mod http;
|
||||
mod auth;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
|
|
Loading…
Reference in a new issue