From 3768cd26e675514ffa1a0d3795c6cc1d806c2f5d Mon Sep 17 00:00:00 2001 From: "Z. Charles Dziura" Date: Sat, 5 Oct 2024 13:20:53 -0400 Subject: [PATCH] Change status code associated with missing session --- api/src/models/error.rs | 15 +++- api/src/requests/auth/login/models/mod.rs | 2 +- api/src/requests/user/verify/handler.rs | 12 ++- api/src/services/auth_token.rs | 5 +- api/src/services/cache.rs | 7 +- api/src/services/user_session.rs | 89 +++++++++++++---------- 6 files changed, 84 insertions(+), 46 deletions(-) diff --git a/api/src/models/error.rs b/api/src/models/error.rs index 5ef7ca7..64a1dd9 100644 --- a/api/src/models/error.rs +++ b/api/src/models/error.rs @@ -5,7 +5,7 @@ use bb8_redis::bb8::RunError; use http::StatusCode; use redis::RedisError; use sqlx::{error::DatabaseError, migrate::MigrateError, Error as SqlxError}; -use tracing::{field, trace}; +use tracing::trace; use super::ApiResponse; @@ -45,10 +45,14 @@ impl AppError { Self::new(ErrorKind::MissingEnvironmentVariables(missing_vars)) } - pub fn missing_session_field(field: &'static str) -> Self { + pub fn _missing_session_field(field: &'static str) -> Self { Self::new(ErrorKind::MissingSessionField(field)) } + pub fn no_session_found() -> Self { + Self::new(ErrorKind::NoSessionFound) + } + pub fn token_key() -> Self { Self::new(ErrorKind::TokenKey) } @@ -128,6 +132,7 @@ impl Display for AppError { f, "Cannot retrieve session: missing required field: {field}" ), + ErrorKind::NoSessionFound => write!(f, "No session found"), ErrorKind::Sqlx(err) => write!(f, "{err}"), ErrorKind::TokenKey => write!( f, @@ -137,6 +142,7 @@ impl Display for AppError { } } +#[allow(dead_code)] #[derive(Debug)] enum ErrorKind { AppStartupError(io::Error), @@ -149,6 +155,7 @@ enum ErrorKind { InvalidToken, MissingEnvironmentVariables(Vec<&'static str>), MissingSessionField(&'static str), + NoSessionFound, Sqlx(SqlxError), TokenKey, } @@ -164,6 +171,10 @@ impl IntoResponse for AppError { StatusCode::BAD_REQUEST, ApiResponse::new_with_error(self).into_json_response(), ), + &ErrorKind::NoSessionFound => ( + StatusCode::UNAUTHORIZED, + ApiResponse::new_with_error(self).into_json_response(), + ), _ => ( StatusCode::INTERNAL_SERVER_ERROR, ApiResponse::new_with_error(self).into_json_response(), diff --git a/api/src/requests/auth/login/models/mod.rs b/api/src/requests/auth/login/models/mod.rs index b8be632..bfbbdc6 100644 --- a/api/src/requests/auth/login/models/mod.rs +++ b/api/src/requests/auth/login/models/mod.rs @@ -2,4 +2,4 @@ mod request; mod response; pub use request::*; -pub use response::*; +// pub use response::*; diff --git a/api/src/requests/user/verify/handler.rs b/api/src/requests/user/verify/handler.rs index 52761a9..e6b6770 100644 --- a/api/src/requests/user/verify/handler.rs +++ b/api/src/requests/user/verify/handler.rs @@ -8,7 +8,7 @@ use axum::{ }; use http::StatusCode; use pasetors::{claims::ClaimsValidationRules, keys::SymmetricKey, version4::V4}; -use tracing::{debug, error, trace}; +use tracing::{debug, error}; use uuid::Uuid; use crate::{ @@ -64,7 +64,15 @@ async fn verify_new_user_request( .map(|jti| Uuid::from_str(jti.as_str().unwrap()).unwrap()) .unwrap(); - let _ = user_session::get_user_session(cache_pool, token_id).await?; + user_session::exists(cache_pool, token_id) + .await + .and_then(|exists| { + if exists { + Ok(()) + } else { + Err(AppError::no_session_found()) + } + })?; let response = verify_token( token_key, diff --git a/api/src/services/auth_token.rs b/api/src/services/auth_token.rs index ac9e82b..4c7a197 100644 --- a/api/src/services/auth_token.rs +++ b/api/src/services/auth_token.rs @@ -46,7 +46,10 @@ pub fn verify_token( Some("TODO_ENV_NAME_HERE".as_bytes()), ) .inspect_err(|err| error!(?err)) - .map_err(|_| AppError::invalid_token())?; + .map_err(|err| { + error!(?err); + AppError::invalid_token() + })?; Ok(token) } diff --git a/api/src/services/cache.rs b/api/src/services/cache.rs index 81633e1..9a9bcb2 100644 --- a/api/src/services/cache.rs +++ b/api/src/services/cache.rs @@ -57,7 +57,7 @@ pub async fn store_object_with_expiration< Ok(()) } -pub async fn get_object( +pub async fn _get_object( cache_pool: &CachePool, key: &str, ) -> Result>, AppError> { @@ -67,6 +67,11 @@ pub async fn get_object( Ok(hash_fields) } +pub async fn exists(cache_pool: &CachePool, key: &str) -> Result { + let mut conn = get_connection_from_pool(cache_pool).await?; + conn.exists(key).await.map_err(Into::into) +} + async fn get_connection_from_pool( cache_pool: &CachePool, ) -> Result, AppError> { diff --git a/api/src/services/user_session.rs b/api/src/services/user_session.rs index 84a4738..d6a409a 100644 --- a/api/src/services/user_session.rs +++ b/api/src/services/user_session.rs @@ -38,59 +38,70 @@ pub async fn store_user_session( Ok(()) } -pub async fn get_user_session( +pub async fn _get_user_session( cache_pool: &CachePool, token_id: Uuid, ) -> Result, AppError> { let key = make_key(token_id); - let object = cache::get_object::(cache_pool, key.as_str()).await?; + let session_exists = cache::exists(cache_pool, key.as_str()).await?; - if let Some(object) = object { - let user_id: i32 = object - .get("userId") - .ok_or(AppError::missing_session_field("userId")) - .and_then(|value| { - FromRedisValue::from_redis_value(value) - .map_err(|_| AppError::missing_session_field("userId")) - })?; + if session_exists { + let object = cache::_get_object::(cache_pool, key.as_str()).await?; - let username: String = object - .get("username") - .ok_or(AppError::missing_session_field("username")) - .and_then(|value| { - FromRedisValue::from_redis_value(value) - .map_err(|_| AppError::missing_session_field("username")) - })?; + if let Some(object) = object { + let user_id: i32 = object + .get("userId") + .ok_or(AppError::_missing_session_field("userId")) + .and_then(|value| { + FromRedisValue::from_redis_value(value) + .map_err(|_| AppError::_missing_session_field("userId")) + })?; - let created_at: SystemTime = object - .get("createdAt") - .ok_or(AppError::missing_session_field("createdAt")) - .and_then(|value| { - FromRedisValue::from_redis_value(value) - .map_err(|_| AppError::missing_session_field("createdAt")) - }) - .map(|serialized: String| parse_rfc3339(serialized.as_str()).unwrap())?; + let username: String = object + .get("username") + .ok_or(AppError::_missing_session_field("username")) + .and_then(|value| { + FromRedisValue::from_redis_value(value) + .map_err(|_| AppError::_missing_session_field("username")) + })?; - let expires_at: SystemTime = object - .get("createdAt") - .ok_or(AppError::missing_session_field("expiresAt")) - .and_then(|value| { - FromRedisValue::from_redis_value(value) - .map_err(|_| AppError::missing_session_field("expiresAt")) - }) - .map(|serialized: String| parse_rfc3339(serialized.as_str()).unwrap())?; + let created_at: SystemTime = object + .get("createdAt") + .ok_or(AppError::_missing_session_field("createdAt")) + .and_then(|value| { + FromRedisValue::from_redis_value(value) + .map_err(|_| AppError::_missing_session_field("createdAt")) + }) + .map(|serialized: String| parse_rfc3339(serialized.as_str()).unwrap())?; - Ok(Some(Session { - user_id, - username, - created_at, - expires_at, - })) + let expires_at: SystemTime = object + .get("createdAt") + .ok_or(AppError::_missing_session_field("expiresAt")) + .and_then(|value| { + FromRedisValue::from_redis_value(value) + .map_err(|_| AppError::_missing_session_field("expiresAt")) + }) + .map(|serialized: String| parse_rfc3339(serialized.as_str()).unwrap())?; + + Ok(Some(Session { + user_id, + username, + created_at, + expires_at, + })) + } else { + Ok(None) + } } else { Ok(None) } } +pub async fn exists(cache_pool: &CachePool, token_id: Uuid) -> Result { + let key = make_key(token_id); + cache::exists(cache_pool, key.as_str()).await +} + fn make_key(token_id: Uuid) -> String { let hashed_token = blake3::hash(token_id.as_bytes()); format!("{USER_SESSION_CACHE_KEY_PREFIX}{hashed_token}")