diff --git a/src/main.rs b/src/main.rs index fa1f2b9..2153db7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,7 @@ use hmac::Hmac; use jwt::SignWithKey; use sha2::Sha256; use std::io::Result; +use std::time::{SystemTime, UNIX_EPOCH}; #[actix_web::main] async fn main() -> Result<()> { @@ -222,8 +223,14 @@ async fn login(payload: Json) -> impl Responder { .unwrap(); if is_valid { + let expiry_date = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() + + 24 * 60 * 60 * 1000; let claims = TokenClaims { id: database_user.id, + expiry_date, }; let token_str = claims.sign_with_key(&jwt_secret).unwrap(); @@ -816,27 +823,89 @@ mod tests { use crate::database::DataBase; use crate::get_tickets; use crate::middleware::validator; - use crate::models::NewSession; + use crate::models::{NewSession, TokenClaims}; use crate::schema::sessions::dsl::sessions; use actix_web::http::StatusCode; use actix_web::{test, web}; use actix_web_httpauth::middleware::HttpAuthentication; use diesel::RunQueryDsl; + use hmac::{Hmac, Mac}; + use jwt::SignWithKey; use serial_test::serial; + use sha2::Sha256; + use std::time::{SystemTime, UNIX_EPOCH}; + + #[actix_web::test] + #[serial] + async fn test_expiry_date() { + setup_database(); + + let jwt_secret: Hmac = Hmac::new_from_slice( + std::env::var("JWT_SECRET") + .expect("JWT_SECRET must be set!") + .as_bytes(), + ) + .unwrap(); + + let expiry_date = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() + // 24 hours in present + - 24 * 60 * 60 * 1000; + let claims = TokenClaims { id: 1, expiry_date }; + let token_str = claims.sign_with_key(&jwt_secret).unwrap(); + + let mut db = DataBase::new(); + diesel::insert_into(sessions) + .values(NewSession { + token: token_str.clone(), + }) + .execute(&mut db.connection) + .unwrap(); + + let app = test::init_service( + App::new().service( + web::scope("") + .wrap(HttpAuthentication::bearer(validator)) + .service(get_tickets), + ), + ) + .await; + let req = TestRequest::get() + .uri("/tickets") + .insert_header(("Authorization", format!("Bearer {token_str}"))) + .to_request(); + + let response = test::call_service(&app, req).await; + + assert_eq!(response.status().as_u16(), StatusCode::UNAUTHORIZED); + } #[actix_web::test] #[serial] async fn test_middleware() { setup_database(); - // bearer for "123" - let bearer_token = - "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MX0.oi92tHHWj5HdQO8Hd9vIYD6suTWosoiBnpdRBIcNGpM"; + let jwt_secret: Hmac = Hmac::new_from_slice( + std::env::var("JWT_SECRET") + .expect("JWT_SECRET must be set!") + .as_bytes(), + ) + .unwrap(); + + let expiry_date = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() + + 24 * 60 * 60 * 60 * 1000; + let claims = TokenClaims { id: 1, expiry_date }; + let token_str = claims.sign_with_key(&jwt_secret).unwrap(); let mut db = DataBase::new(); diesel::insert_into(sessions) .values(NewSession { - token: bearer_token.to_string(), + token: token_str.clone(), }) .execute(&mut db.connection) .unwrap(); @@ -851,7 +920,7 @@ mod tests { .await; let req = TestRequest::get() .uri("/tickets") - .insert_header(("Authorization", format!("Bearer {bearer_token}"))) + .insert_header(("Authorization", format!("Bearer {token_str}"))) .to_request(); let response = test::call_service(&app, req).await; diff --git a/src/middleware.rs b/src/middleware.rs index f05a5db..ac780de 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -2,13 +2,14 @@ use crate::database::{session_in_db, DataBase}; use crate::models::TokenClaims; use actix_web::dev::ServiceRequest; use actix_web::{Error, HttpMessage}; -use actix_web_httpauth::extractors::bearer::BearerAuth; +use actix_web_httpauth::extractors::bearer::{BearerAuth, Config}; use actix_web_httpauth::extractors::{bearer, AuthenticationError}; use dotenvy::dotenv; use hmac::digest::KeyInit; use hmac::Hmac; use jwt::VerifyWithKey; use sha2::Sha256; +use std::time::{SystemTime, UNIX_EPOCH}; pub async fn validator( req: ServiceRequest, @@ -38,15 +39,22 @@ pub async fn validator( match claims { Ok(value) => { + if value.expiry_date + < SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() + { + let config = Config::default().scope(""); + + return Err((AuthenticationError::from(config).into(), req)); + } + req.extensions_mut().insert(value); Ok(req) } Err(_) => { - let config = req - .app_data::() - .cloned() - .unwrap_or_default() - .scope(""); + let config = Config::default().scope(""); Err((AuthenticationError::from(config).into(), req)) } diff --git a/src/models.rs b/src/models.rs index 63a2ade..a930029 100644 --- a/src/models.rs +++ b/src/models.rs @@ -113,6 +113,7 @@ pub struct NewUser { #[derive(Serialize, Deserialize)] pub struct TokenClaims { pub id: i32, + pub expiry_date: u128, } #[derive(Serialize, Deserialize, Queryable, Debug)]