From 68af5a88c957c5f308c0ea4e70624573ac6a1fe7 Mon Sep 17 00:00:00 2001 From: Frederic Henrichs Date: Wed, 18 Dec 2024 08:40:10 +0100 Subject: [PATCH] backend/get_login_salt: respond with random salt when called with non existing user. fix #137 --- backend/src/lib.rs | 6 ++- backend/src/routes/auth/get_login_salt.rs | 52 ++++++++++++++++++++--- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 89f382c..0302ce6 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -179,7 +179,7 @@ pub fn clean_chargers(conn: &mut PooledConnection>>> = web::Data::new(Mutex::new(LruCache::new(NonZeroUsize::new(10000).unwrap()))); + let state = web::Data::new(state); let bridge_state = web::Data::new(bridge_state); let login_rate_limiter = web::Data::new(LoginRateLimiter::new()); cfg.app_data(login_rate_limiter); cfg.app_data(state); cfg.app_data(bridge_state); + cfg.app_data(cache); } #[actix_web::test] diff --git a/backend/src/routes/auth/get_login_salt.rs b/backend/src/routes/auth/get_login_salt.rs index 87460ab..b89f236 100644 --- a/backend/src/routes/auth/get_login_salt.rs +++ b/backend/src/routes/auth/get_login_salt.rs @@ -1,12 +1,15 @@ +use std::sync::Mutex; + use actix_web::{get, web, HttpResponse, Responder}; use db_connector::models::users::User; use diesel::{prelude::*, result::Error::NotFound}; +use lru::LruCache; use serde::Deserialize; use utoipa::IntoParams; use crate::{ error::Error, - utils::{get_connection, web_block_unpacked}, + utils::{generate_random_bytes, get_connection, web_block_unpacked}, AppState, }; @@ -30,24 +33,26 @@ pub struct GetSaltQuery { pub async fn get_login_salt( state: web::Data, query: web::Query, + cache: web::Data>>>, ) -> actix_web::Result { use db_connector::schema::users::dsl::*; + let mail = query.email.to_lowercase(); let mut conn = get_connection(&state)?; - let user: User = web_block_unpacked(move || { + let salt: Vec = web_block_unpacked(move || { match users - .filter(email.eq(&query.email.to_lowercase())) + .filter(email.eq(&mail)) .select(User::as_select()) .get_result(&mut conn) { - Ok(user) => Ok(user), - Err(NotFound) => Err(Error::UserDoesNotExist), + Ok(user) => Ok(user.login_salt), + Err(NotFound) => Ok(cache.lock().unwrap().get_or_insert(mail, || generate_random_bytes()).to_vec()), Err(_err) => Err(Error::InternalError), } }) .await?; - Ok(HttpResponse::Ok().json(user.login_salt)) + Ok(HttpResponse::Ok().json(salt)) } #[cfg(test)] @@ -104,4 +109,39 @@ pub mod tests { let resp: Vec = test::read_body_json(resp).await; assert_eq!(user.login_salt, resp); } + + #[actix_web::test] + async fn test_nonexisting_user() { + let app = App::new().configure(configure).service(get_login_salt); + let app = test::init_service(app).await; + + let mail = format!("{}@example.invalid", uuid::Uuid::new_v4().to_string()); + + let req = test::TestRequest::get() + .uri(&format!("/get_login_salt?email={}", mail)) + .to_request(); + let resp = test::call_service(&app, req).await; + assert!(resp.status().is_success()); + + let first_salt: Vec = test::read_body_json(resp).await; + assert_eq!(first_salt.len(), 24); + + let req = test::TestRequest::get() + .uri(&format!("/get_login_salt?email={}", mail)) + .to_request(); + let resp = test::call_service(&app, req).await; + assert!(resp.status().is_success()); + let second_salt: Vec = test::read_body_json(resp).await; + assert_eq!(second_salt, first_salt); + + let mail = format!("{}@example.invalid", uuid::Uuid::new_v4().to_string()); + let req = test::TestRequest::get() + .uri(&format!("/get_login_salt?email={}", mail)) + .to_request(); + let resp = test::call_service(&app, req).await; + assert!(resp.status().is_success()); + let second_salt: Vec = test::read_body_json(resp).await; + assert_ne!(second_salt, first_salt); + } + }