Refactor and enhance SurrealDB integration and resource management

- Updated `lib.rs` to selectively expose specific entities and services for better clarity.
- Improved SurrealDB client initialization with detailed logging in `surrealdb/mod.rs`.
- Enhanced resource definitions in `resource.rs` with additional utility methods for better resource management.
- Refactored user data retrieval logic in `auth_middleware/mod.rs` for improved readability and efficiency.
- Cleaned up middleware exports in `lib.rs` for clearer API surface.
- Added detailed comments and documentation throughout the SurrealDB module for better maintainability.
- Updated tests to ensure compatibility with new changes and improved structure.
- Introduced new permissions module structure in `imphnen-utils` for future enhancements.
This commit is contained in:
MythEclipse
2025-09-26 17:35:30 +07:00
parent 96debc210a
commit 14b22328de
71 changed files with 1566 additions and 632 deletions
+134 -72
View File
@@ -1,99 +1,161 @@
//! JWT token encoding and decoding utilities.
//!
//! This module provides functions for creating and validating JWT tokens
//! for authentication purposes, including access tokens, refresh tokens,
//! and password reset tokens.
use crate::enviroment::ENV;
use axum::http::StatusCode;
use chrono::{Duration, TimeDelta, Utc};
use jsonwebtoken::{
DecodingKey, EncodingKey, Header, TokenData, Validation, decode, encode,
DecodingKey, EncodingKey, Header, TokenData, Validation, decode, encode,
};
use serde::{Deserialize, Serialize};
/// JWT claims structure containing token payload information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Claims {
pub exp: usize,
pub iat: usize,
pub sub: String,
/// Expiration timestamp
pub exp: usize,
/// Issued at timestamp
pub iat: usize,
/// Subject (usually user identifier)
pub sub: String,
/// User ID
pub user_id: String,
}
// Token configuration constants
const ACCESS_TOKEN_DURATION_MINUTES: i64 = 15;
const REFRESH_TOKEN_DURATION_DAYS: i64 = 1;
const RESET_TOKEN_DURATION_MINUTES: i64 = 5;
// Lazy-initialized headers and keys for performance
static ACCESS_HEADER: once_cell::sync::Lazy<Header> = once_cell::sync::Lazy::new(Header::default);
static ACCESS_KEY: once_cell::sync::Lazy<EncodingKey> = once_cell::sync::Lazy::new(|| {
EncodingKey::from_secret(ENV.access_token_secret.as_ref())
EncodingKey::from_secret(ENV.access_token_secret.as_ref())
});
pub fn encode_access_token(sub: String, user_id: String) -> Result<String, StatusCode> {
let now = Utc::now();
let expire: TimeDelta = Duration::minutes(15);
let exp: usize = (now + expire).timestamp() as usize;
let iat: usize = now.timestamp() as usize;
let claim = Claims { iat, exp, sub, user_id };
encode(
&ACCESS_HEADER,
&claim,
&ACCESS_KEY,
)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
}
pub fn encode_reset_password_token(sub: String, user_id: String) -> Result<String, StatusCode> {
let env = &ENV;
let secret: String = env.access_token_secret.clone();
let now = Utc::now();
let expire: TimeDelta = Duration::minutes(5);
let exp: usize = (now + expire).timestamp() as usize;
let iat: usize = now.timestamp() as usize;
let claim = Claims { iat, exp, sub, user_id };
encode(
&Header::default(),
&claim,
&EncodingKey::from_secret(secret.as_ref()),
)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
}
pub fn decode_access_token(
jwt_token: &str,
) -> Result<TokenData<Claims>, StatusCode> {
let env = &ENV;
let secret: String = env.access_token_secret.clone();
let result: Result<TokenData<Claims>, StatusCode> = decode(
jwt_token,
&DecodingKey::from_secret(secret.as_ref()),
&Validation::default(),
)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR);
result
}
static REFRESH_HEADER: once_cell::sync::Lazy<Header> = once_cell::sync::Lazy::new(Header::default);
static REFRESH_KEY: once_cell::sync::Lazy<EncodingKey> = once_cell::sync::Lazy::new(|| {
EncodingKey::from_secret(ENV.refresh_token_secret.as_ref())
EncodingKey::from_secret(ENV.refresh_token_secret.as_ref())
});
/// Create JWT claims with specified expiration duration.
///
/// # Arguments
/// * `sub` - Subject identifier
/// * `user_id` - User ID
/// * `duration` - Token validity duration
///
/// # Returns
/// JWT claims structure
fn create_claims(sub: String, user_id: String, duration: TimeDelta) -> Claims {
let now = Utc::now();
let exp: usize = (now + duration).timestamp() as usize;
let iat: usize = now.timestamp() as usize;
Claims { iat, exp, sub, user_id }
}
/// Encode a JWT token with the specified header and key.
///
/// # Arguments
/// * `claims` - JWT claims to encode
/// * `header` - JWT header
/// * `key` - Encoding key
///
/// # Returns
/// Encoded JWT token or internal server error status
fn encode_token(claims: &Claims, header: &Header, key: &EncodingKey) -> Result<String, StatusCode> {
encode(header, claims, key).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
}
/// Decode a JWT token with the specified secret.
///
/// # Arguments
/// * `token` - JWT token string
/// * `secret` - Secret key for decoding
///
/// # Returns
/// Decoded token data or internal server error status
fn decode_token(token: &str, secret: &str) -> Result<TokenData<Claims>, StatusCode> {
decode(
token,
&DecodingKey::from_secret(secret.as_ref()),
&Validation::default(),
)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
}
/// Encode an access token with 15-minute expiration.
///
/// # Arguments
/// * `sub` - Subject identifier
/// * `user_id` - User ID
///
/// # Returns
/// Encoded JWT access token
pub fn encode_access_token(sub: String, user_id: String) -> Result<String, StatusCode> {
let claims = create_claims(sub, user_id, Duration::minutes(ACCESS_TOKEN_DURATION_MINUTES));
encode_token(&claims, &ACCESS_HEADER, &ACCESS_KEY)
}
/// Encode a refresh token with 1-day expiration.
///
/// # Arguments
/// * `sub` - Subject identifier
/// * `user_id` - User ID
///
/// # Returns
/// Encoded JWT refresh token
pub fn encode_refresh_token(sub: String, user_id: String) -> Result<String, StatusCode> {
let now = Utc::now();
let expire: TimeDelta = Duration::days(1);
let exp: usize = (now + expire).timestamp() as usize;
let iat: usize = now.timestamp() as usize;
let claim = Claims { iat, exp, sub, user_id };
encode(
&REFRESH_HEADER,
&claim,
&REFRESH_KEY,
)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
let claims = create_claims(sub, user_id, Duration::days(REFRESH_TOKEN_DURATION_DAYS));
encode_token(&claims, &REFRESH_HEADER, &REFRESH_KEY)
}
pub fn decode_refresh_token(
jwt_token: &str,
) -> Result<TokenData<Claims>, StatusCode> {
let env = &ENV;
let secret: String = env.refresh_token_secret.clone();
let result: Result<TokenData<Claims>, StatusCode> = decode(
jwt_token,
&DecodingKey::from_secret(secret.as_ref()),
&Validation::default(),
)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR);
result // Explicitly return result
/// Encode a password reset token with 5-minute expiration.
///
/// # Arguments
/// * `sub` - Subject identifier
/// * `user_id` - User ID
///
/// # Returns
/// Encoded JWT reset token
pub fn encode_reset_password_token(sub: String, user_id: String) -> Result<String, StatusCode> {
let claims = create_claims(sub, user_id, Duration::minutes(RESET_TOKEN_DURATION_MINUTES));
let key = EncodingKey::from_secret(ENV.access_token_secret.as_ref());
encode_token(&claims, &Header::default(), &key)
}
/// Decode an access token.
///
/// # Arguments
/// * `jwt_token` - JWT token string
///
/// # Returns
/// Decoded token data containing claims
pub fn decode_access_token(jwt_token: &str) -> Result<TokenData<Claims>, StatusCode> {
decode_token(jwt_token, &ENV.access_token_secret)
}
/// Decode a refresh token.
///
/// # Arguments
/// * `jwt_token` - JWT token string
///
/// # Returns
/// Decoded token data containing claims
pub fn decode_refresh_token(jwt_token: &str) -> Result<TokenData<Claims>, StatusCode> {
decode_token(jwt_token, &ENV.refresh_token_secret)
}
/// Generate a simple JWT access token using user_id as both sub and user_id.
///
/// # Arguments
/// * `user_id` - User identifier
///
/// # Returns
/// Encoded JWT access token
pub fn generate_jwt(user_id: &str) -> Result<String, StatusCode> {
encode_access_token(user_id.to_string(), user_id.to_string())
}