From a1af17aad7c1308fc714a60595bae07cc8bb8a9a Mon Sep 17 00:00:00 2001 From: Yigit Sever Date: Fri, 16 Apr 2021 00:53:03 +0300 Subject: Load users from disk at start --- src/handlers.rs | 36 ++++++++++++++++++++------------ src/schema.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/src/handlers.rs b/src/handlers.rs index 515caa5..fe60ded 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -8,7 +8,6 @@ use jsonwebtoken::errors::ErrorKind; use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation}; use log::{debug, warn}; use md5::Md5; -use parking_lot::RwLockUpgradableReadGuard; use rsa::{PaddingScheme, RSAPrivateKey}; use serde::Serialize; use sha2::Sha256; @@ -38,6 +37,7 @@ enum ResponseType { use crate::schema::{ AuthRequest, Block, Claims, Db, InitialAuthRequest, MetuId, NakedBlock, Transaction, User, + UserAtRest, }; const BEARER: &str = "Bearer "; @@ -238,17 +238,19 @@ pub async fn authenticate_user( } }; - let userlist = db.users.upgradable_read(); + { + let userlist = db.users.read(); - if userlist.contains_key(&provided_id) { - let res_json = warp::reply::json(&GradeCoinResponse { - res: ResponseType::Error, - message: - "This user is already authenticated, do you think this is a mistake? Contact me" - .to_owned(), - }); + if userlist.contains_key(&provided_id) { + let res_json = warp::reply::json(&GradeCoinResponse { + res: ResponseType::Error, + message: + "This user is already authenticated, do you think this is a mistake? Contact me" + .to_owned(), + }); - return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); + return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); + } } // We're using this as the validator @@ -270,11 +272,19 @@ pub async fn authenticate_user( balance: 0, }; - let user_json = serde_json::to_string(&new_user).unwrap(); + let user_at_rest_json = serde_json::to_string(&UserAtRest { + user: User { + user_id: new_user.user_id.clone(), + public_key: new_user.public_key.clone(), + balance: 0, + }, + fingerprint: fingerprint.clone(), + }) + .unwrap(); - fs::write(format!("users/{}.guy", new_user.user_id), user_json).unwrap(); + fs::write(format!("users/{}.guy", new_user.user_id), user_at_rest_json).unwrap(); - let mut userlist = RwLockUpgradableReadGuard::upgrade(userlist); + let mut userlist = db.users.write(); userlist.insert(fingerprint.clone(), new_user); diff --git a/src/schema.rs b/src/schema.rs index 79fee2d..80cf73f 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -9,6 +9,7 @@ //! Users are held in memory and they're also backed up to text files use chrono::{NaiveDate, NaiveDateTime}; use lazy_static::lazy_static; +use log::debug; use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; @@ -23,17 +24,15 @@ use std::vec::Vec; pub type Fingerprint = String; -// TODO: start by reading users from file too <14-04-21, yigit> // - -fn last_block_exists() -> (bool, String) { +fn last_block_exists() -> Option { let blocks = read_block_name().unwrap(); for block in blocks { let block = block.to_str().unwrap(); if block.contains("last.block") { - return (true, block.to_string()); + return Some(block.to_string()); } } - (false, "".to_string()) + None } fn read_block_name() -> io::Result> { @@ -44,26 +43,64 @@ fn read_block_name() -> io::Result> { Ok(entries) } -fn create_db_with_last_block(path: String) -> Db { +fn read_users() -> io::Result> { + let entries = fs::read_dir("./users")? + .map(|res| res.map(|e| e.path())) + .collect::, io::Error>>()?; + + Ok(entries) +} + +fn populate_db_with_last_block(db: &mut Db, path: String) -> &mut Db { + debug!("Populating db with last block {}", path); let file = fs::read(path).unwrap(); let json = std::str::from_utf8(&file).unwrap(); let block: Block = serde_json::from_str(json).unwrap(); - let db = Db::new(); *db.blockchain.write() = block; db } -/// Creates a new database, uses the previous last block if one exists +#[derive(Debug, Serialize, Deserialize, PartialEq)] +pub struct UserAtRest { + pub fingerprint: Fingerprint, + pub user: User, +} + +fn populate_db_with_users(db: &mut Db, files: Vec) -> &mut Db { + for fs in files { + if let Ok(file_content) = fs::read(fs) { + let json = + String::from_utf8(file_content).expect("we have written a malformed user file"); + let user_at_rest: UserAtRest = serde_json::from_str(&json).unwrap(); + + debug!("Populating db with user: {:?}", user_at_rest); + db.users + .write() + .insert(user_at_rest.fingerprint, user_at_rest.user); + } + } + + db +} + +/// Creates a new database, uses the previous last block if one exists and attempts the populate +/// the users pub fn create_database() -> Db { fs::create_dir_all("blocks").unwrap(); fs::create_dir_all("users").unwrap(); - let (res, path) = last_block_exists(); - if res { - create_db_with_last_block(path) - } else { - Db::new() + + let mut db = Db::new(); + + if let Some(blocks_path) = last_block_exists() { + populate_db_with_last_block(&mut db, blocks_path); + } + + if let Ok(users_path) = read_users() { + populate_db_with_users(&mut db, users_path); } + + db } /// A JWT Payload/Claims representation @@ -188,7 +225,7 @@ pub struct User { } /// The values are hard coded in [`OUR_STUDENTS`] so MetuId::new() can accept/reject values based on that -#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct MetuId { id: String, passwd: String, -- cgit v1.2.3-70-g09d2