Skip to content

Commit

Permalink
backend/get_login_salt: respond with random salt when called with non…
Browse files Browse the repository at this point in the history
… existing user. fix #137
  • Loading branch information
ffreddow committed Dec 18, 2024
1 parent 94688b9 commit 68af5a8
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 7 deletions.
6 changes: 5 additions & 1 deletion backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ pub fn clean_chargers(conn: &mut PooledConnection<diesel::r2d2::ConnectionManage

#[cfg(test)]
pub(crate) mod tests {
use std::{net::Ipv4Addr, str::FromStr};
use std::{net::Ipv4Addr, num::NonZeroUsize, str::FromStr};

use super::*;
use actix_web::{
Expand All @@ -192,6 +192,7 @@ pub(crate) mod tests {
use lettre::transport::smtp::authentication::Credentials;
use chrono::Utc;
use db_connector::{models::{recovery_tokens::RecoveryToken, refresh_tokens::RefreshToken, users::User}, test_connection_pool};
use lru::LruCache;
use rand::RngCore;
use rand_core::OsRng;
use rate_limit::LoginRateLimiter;
Expand Down Expand Up @@ -261,12 +262,15 @@ pub(crate) mod tests {
socket: UdpSocket::bind(("0", 0)).unwrap(),
};

let cache: web::Data<Mutex<LruCache<String, Vec<u8>>>> = 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]
Expand Down
52 changes: 46 additions & 6 deletions backend/src/routes/auth/get_login_salt.rs
Original file line number Diff line number Diff line change
@@ -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,
};

Expand All @@ -30,24 +33,26 @@ pub struct GetSaltQuery {
pub async fn get_login_salt(
state: web::Data<AppState>,
query: web::Query<GetSaltQuery>,
cache: web::Data<Mutex<LruCache<String, Vec<u8>>>>,
) -> actix_web::Result<impl Responder> {
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<u8> = 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)]
Expand Down Expand Up @@ -104,4 +109,39 @@ pub mod tests {
let resp: Vec<u8> = 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<u8> = 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<u8> = 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<u8> = test::read_body_json(resp).await;
assert_ne!(second_salt, first_salt);
}

}

0 comments on commit 68af5a8

Please sign in to comment.