summaryrefslogtreecommitdiffstats
path: root/src/handlers.rs
diff options
context:
space:
mode:
authorYigit Sever2021-04-13 04:05:44 +0300
committerYigit Sever2021-04-13 04:05:49 +0300
commit17d73bb73f4396c22ca24c3839a5449f5e28b4e5 (patch)
tree06b90d76e40101b462f2ddcfa89c768d78279ed6 /src/handlers.rs
parent0cc703bc74539f123d57d2d392eaf6b99eca26e7 (diff)
downloadgradecoin-17d73bb73f4396c22ca24c3839a5449f5e28b4e5.tar.gz
gradecoin-17d73bb73f4396c22ca24c3839a5449f5e28b4e5.tar.bz2
gradecoin-17d73bb73f4396c22ca24c3839a5449f5e28b4e5.zip
Housekeeping
Moved tests out of routes.rs into their own file Learned how to use lib.rs, now we have cargo doc support as well
Diffstat (limited to 'src/handlers.rs')
-rw-r--r--src/handlers.rs128
1 files changed, 62 insertions, 66 deletions
diff --git a/src/handlers.rs b/src/handlers.rs
index 07986f5..80ed1f7 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -1,37 +1,28 @@
1/// API handlers, the ends of each filter chain
1use blake2::{Blake2s, Digest}; 2use blake2::{Blake2s, Digest};
2use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; 3use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
3/// API handlers, the ends of each filter chain
4use log::debug; 4use log::debug;
5use md5::Md5; 5use md5::Md5;
6use parking_lot::RwLockUpgradableReadGuard; 6use parking_lot::RwLockUpgradableReadGuard;
7use serde::{Deserialize, Serialize};
8use serde_json; 7use serde_json;
9use serde_json::json;
10use std::convert::Infallible; 8use std::convert::Infallible;
11use std::fs; 9use std::fs;
12use warp::{http::Response, http::StatusCode, reject, reply}; 10use warp::{http::Response, http::StatusCode, reply};
13 11
14use gradecoin::schema::{ 12use crate::schema::{AuthRequest, Block, Claims, Db, MetuId, NakedBlock, Transaction, User};
15 AuthRequest, Block, Db, MetuId, NakedBlock, PublicKeySignature, Transaction, User,
16};
17 13
18const BEARER: &str = "Bearer "; 14const BEARER: &str = "Bearer ";
19 15
20/// tha: Transaction Hash, String 16/// POST request to /register endpoint
21/// iat: Issued At, Unix Time, epoch 17///
22#[derive(Debug, Serialize, Deserialize)] 18/// Lets a [`User`] (=student) to authenticate themselves to the system
23pub struct Claims { 19/// This `request` can be rejected if the payload is malformed (= not authenticated properly) or if
24 pub tha: String, 20/// the [`AuthRequest.user_id`] of the `request` is not in the list of users that can hold a Gradecoin account
25 pub iat: usize,
26}
27
28/// POST /register
29/// Enables a student to introduce themselves to the system
30/// Can fail
31pub async fn authenticate_user( 21pub async fn authenticate_user(
32 request: AuthRequest, 22 request: AuthRequest,
33 db: Db, 23 db: Db,
34) -> Result<impl warp::Reply, warp::Rejection> { 24) -> Result<impl warp::Reply, warp::Rejection> {
25 debug!("POST request to /register, authenticate_user");
35 let given_id = request.student_id.clone(); 26 let given_id = request.student_id.clone();
36 27
37 if let Some(priv_student_id) = MetuId::new(request.student_id) { 28 if let Some(priv_student_id) = MetuId::new(request.student_id) {
@@ -77,7 +68,7 @@ pub async fn authenticate_user(
77/// Returns JSON array of transactions 68/// Returns JSON array of transactions
78/// Cannot fail 69/// Cannot fail
79pub async fn list_transactions(db: Db) -> Result<impl warp::Reply, Infallible> { 70pub async fn list_transactions(db: Db) -> Result<impl warp::Reply, Infallible> {
80 debug!("list all transactions"); 71 debug!("GET request to /transaction, list_transactions");
81 let mut result = Vec::new(); 72 let mut result = Vec::new();
82 73
83 let transactions = db.pending_transactions.read(); 74 let transactions = db.pending_transactions.read();
@@ -95,31 +86,13 @@ pub async fn list_transactions(db: Db) -> Result<impl warp::Reply, Infallible> {
95/// Cannot fail 86/// Cannot fail
96/// Mostly around for debug purposes 87/// Mostly around for debug purposes
97pub async fn list_blocks(db: Db) -> Result<impl warp::Reply, Infallible> { 88pub async fn list_blocks(db: Db) -> Result<impl warp::Reply, Infallible> {
98 debug!("list all block"); 89 debug!("GET request to /block, list_blocks");
99 90
100 let block = db.blockchain.read(); 91 let block = db.blockchain.read();
101 92
102 Ok(reply::with_status(reply::json(&*block), StatusCode::OK)) 93 Ok(reply::with_status(reply::json(&*block), StatusCode::OK))
103} 94}
104 95
105/// POST /transaction
106/// Pushes a new transaction for pending transaction pool
107/// Can reject the transaction proposal
108/// TODO: when is a new transaction rejected <07-04-21, yigit> //
109pub async fn propose_transaction(
110 new_transaction: Transaction,
111 db: Db,
112) -> Result<impl warp::Reply, warp::Rejection> {
113 debug!("new transaction request {:?}", new_transaction);
114
115 // let mut transactions = db.lock().await;
116 let mut transactions = db.pending_transactions.write();
117
118 transactions.insert(new_transaction.source.to_owned(), new_transaction);
119
120 Ok(StatusCode::CREATED)
121}
122
123/// POST /block 96/// POST /block
124/// Proposes a new block for the next round 97/// Proposes a new block for the next round
125/// Can reject the block 98/// Can reject the block
@@ -181,41 +154,64 @@ pub async fn propose_block(new_block: Block, db: Db) -> Result<impl warp::Reply,
181 } 154 }
182} 155}
183 156
157/// POST /transaction
158///
159/// Handles the new transaction requests
160/// Can reject the block if;
161/// # Arguments
162/// * `new_transaction` - Valid JSON of a [`Transaction`]
163/// * `token` - An Authorization header value such as `Bearer aaa.bbb.ccc`
164/// * `db` - Global [`Db`] instance
165///
166/// TODO This method should check if the user has enough balance for the transaction
184pub async fn auth_propose_transaction( 167pub async fn auth_propose_transaction(
185 new_transaction: Transaction, 168 new_transaction: Transaction,
186 token: String, 169 token: String,
187 db: Db, 170 db: Db,
188) -> Result<impl warp::Reply, warp::Rejection> { 171) -> Result<impl warp::Reply, warp::Rejection> {
189 debug!("new transaction request {:?}", new_transaction); 172 debug!("POST request to /transaction, propose_transaction");
190 let raw_jwt = token.trim_start_matches(BEARER).to_owned(); 173 debug!("The transaction request: {:?}", new_transaction);
191 174
192 let decoded = jsonwebtoken::decode::<Claims>( 175 let raw_jwt = token.trim_start_matches(BEARER).to_owned();
193 &token, 176 debug!("raw_jwt: {:?}", raw_jwt);
194 &DecodingKey::from_rsa_pem( 177
195 db.users 178 if let Some(user) = db.users.read().get(&new_transaction.by) {
196 .read() 179 // This public key was already written to the database, we can panic if it's not valid at
197 .get(&new_transaction.by) 180 // *this* point
198 .unwrap() 181 let by_public_key = &user.public_key;
199 .public_key 182
200 .as_bytes(), 183 if let Ok(decoded) = decode::<Claims>(
201 ) 184 &raw_jwt,
202 .unwrap(), 185 &DecodingKey::from_rsa_pem(by_public_key.as_bytes()).unwrap(),
203 // todo@keles: If user is not found return user not found error 186 &Validation::new(Algorithm::RS256),
204 &Validation::new(Algorithm::PS256), 187 ) {
205 ) 188 // this transaction was already checked for correctness at custom_filters, we can panic
206 .unwrap(); 189 // here if it has been changed since
207 // todo: If user is found but header is not validated, return header not valid 190
208 191 let hashed_transaction = Md5::digest(&serde_json::to_vec(&new_transaction).unwrap());
209 let hashed_transaction = Md5::digest(&serde_json::to_vec(&new_transaction).unwrap()); 192
210 193 if decoded.claims.tha == format!("{:x}", hashed_transaction) {
211 // let mut transactions = db.lock().await; 194 let mut transactions = db.pending_transactions.write();
212 if decoded.claims.tha == format!("{:x}", hashed_transaction) { 195
213 let mut transactions = db.pending_transactions.write(); 196 transactions.insert(new_transaction.source.to_owned(), new_transaction);
214 197
215 transactions.insert(new_transaction.source.to_owned(), new_transaction); 198 Ok(StatusCode::CREATED)
216 199 } else {
217 Ok(StatusCode::CREATED) 200 debug!(
201 "the hash of the request {:x} did not match with the hash given in jwt {:?}",
202 hashed_transaction, decoded.claims.tha
203 );
204 Ok(StatusCode::BAD_REQUEST)
205 }
206 } else {
207 debug!("raw_jwt was malformed {:?}", raw_jwt);
208 Ok(StatusCode::BAD_REQUEST)
209 }
218 } else { 210 } else {
211 debug!(
212 "A user with public key signature {:?} is not found in the database",
213 new_transaction.by
214 );
219 Ok(StatusCode::BAD_REQUEST) 215 Ok(StatusCode::BAD_REQUEST)
220 } 216 }
221} 217}