From d3889bd5945b2ffc63d20942b7730b5a1d0e3a42 Mon Sep 17 00:00:00 2001 From: Yigit Sever Date: Sun, 11 Apr 2021 21:39:18 +0300 Subject: Implement User handling and authentication New struct: User, corresponds to a student Blocks and users are persistent (written to a text file) PostgreSQL would've been overkill, we have 30 students AuthRequest is the representation for incoming register requests and User is the inner representation Students who are enrolled to the class are hardcoded, only they can register new accounts There are two new tests, one checks if a priviliged (=enrolled) user can create an account and the other checks if a unpriviliged one cannot There are quick verbose error messages that I'm not married to, might move on to something better honestly There's nothing stopping a malicious user to pre-register everyone with mock public keys and effectively lock everyone out, what's a good secret we can use? --- src/handlers.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 13 deletions(-) (limited to 'src/handlers.rs') diff --git a/src/handlers.rs b/src/handlers.rs index 856970d..bfd57bc 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -1,10 +1,62 @@ /// API handlers, the ends of each filter chain -use log::debug; // this is more useful than debug! learn how to use this +use log::debug; use parking_lot::RwLockUpgradableReadGuard; +use serde_json; use std::convert::Infallible; -use warp::{http::StatusCode, reply}; +use warp::{http::Response, http::StatusCode, reply}; -use crate::schema::{Block, Db, Transaction}; +use std::fs; + +use crate::schema::{AuthRequest, Block, Db, MetuId, Transaction, User}; + +/// POST /register +/// Enables a student to introduce themselves to the system +/// Can fail +pub async fn authenticate_user( + request: AuthRequest, + db: Db, +) -> Result { + let given_id = request.student_id.clone(); + + if let Some(priv_student_id) = MetuId::new(request.student_id) { + let userlist = db.users.upgradable_read(); + + if userlist.contains_key(&given_id) { + + let res = Response::builder() + .status(StatusCode::BAD_REQUEST) + .body("This user is already authenticated"); + + Ok(res) + } else { + let new_user = User { + user_id: priv_student_id, + public_key: request.public_key, + balance: 0, + }; + + let user_json = serde_json::to_string(&new_user).unwrap(); + + fs::write(format!("users/{}.guy", new_user.user_id), user_json).unwrap(); + + let mut userlist = RwLockUpgradableReadGuard::upgrade(userlist); + userlist.insert(given_id, new_user); + // TODO: signature of the public key, please <11-04-21, yigit> // + + let res = Response::builder() + .status(StatusCode::CREATED) + .body("Ready to use Gradecoin"); + + Ok(res) + } + } else { + let res = Response::builder() + .status(StatusCode::BAD_REQUEST) + .body("This user cannot have a gradecoin account"); + + Ok(res) + } +} /// GET /transaction /// Returns JSON array of transactions @@ -28,16 +80,11 @@ pub async fn list_transactions(db: Db) -> Result { /// Cannot fail /// Mostly around for debug purposes pub async fn list_blocks(db: Db) -> Result { - debug!("list all blocks"); - - let mut result = Vec::new(); - let blocks = db.blockchain.read(); + debug!("list all block"); - for block in blocks.iter() { - result.push(block); - } + let block = db.blockchain.read(); - Ok(reply::with_status(reply::json(&result), StatusCode::OK)) + Ok(reply::with_status(reply::json(&*block), StatusCode::OK)) } /// POST /transaction @@ -70,7 +117,7 @@ pub async fn propose_block(new_block: Block, db: Db) -> Result // + // check 1, new_block.transaction_list from pending_transactions pool? <07-04-21, yigit> // for transaction_hash in new_block.transaction_list.iter() { if !pending_transactions.contains_key(transaction_hash) { return Ok(StatusCode::BAD_REQUEST); @@ -81,7 +128,17 @@ pub async fn propose_block(new_block: Block, db: Db) -> Result