summaryrefslogtreecommitdiffstats
path: root/src/routes.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/routes.rs')
-rw-r--r--src/routes.rs380
1 files changed, 3 insertions, 377 deletions
diff --git a/src/routes.rs b/src/routes.rs
index ed2acad..e4bdee4 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -1,10 +1,11 @@
1use gradecoin::schema::Db; 1/// Endpoints and their construction
2use warp::{Filter, Rejection, Reply}; 2use warp::{Filter, Rejection, Reply};
3 3
4use crate::custom_filters; 4use crate::custom_filters;
5use crate::handlers; 5use crate::handlers;
6use crate::schema::Db;
6 7
7/// Root, all routes combined 8/// Every route combined
8pub fn consensus_routes(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { 9pub fn consensus_routes(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
9 transaction_list(db.clone()) 10 transaction_list(db.clone())
10 .or(register_user(db.clone())) 11 .or(register_user(db.clone()))
@@ -39,15 +40,6 @@ pub fn block_list(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection
39} 40}
40 41
41/// POST /transaction warp route 42/// POST /transaction warp route
42pub fn transaction_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
43 warp::path!("transaction")
44 .and(warp::post())
45 .and(custom_filters::transaction_json_body())
46 .and(custom_filters::with_db(db))
47 .and_then(handlers::propose_transaction)
48}
49
50/// POST /transaction warp route
51pub fn auth_transaction_propose( 43pub fn auth_transaction_propose(
52 db: Db, 44 db: Db,
53) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { 45) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
@@ -68,369 +60,3 @@ pub fn block_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = Reject
68 .and_then(handlers::propose_block) 60 .and_then(handlers::propose_block)
69} 61}
70 62
71#[cfg(test)]
72mod tests {
73 use gradecoin::schema::{create_database, AuthRequest, Block, MetuId, Transaction, User};
74 use handlers::Claims;
75 // use chrono::prelude::*;
76 // use parking_lot::RwLock;
77 // use std::sync::Arc;
78 use warp::http::StatusCode;
79
80 use super::*;
81
82 use jsonwebtoken::{Header, encode, EncodingKey, Algorithm};
83 const private_key_pem: &str = "-----BEGIN RSA PRIVATE KEY-----
84MIIEpAIBAAKCAQEA4nU0G4WjkmcQUx0hq6LQuV5Q+ACmUFL/OjoYMDwC/O/6pCd1
85UZgCfgHN2xEffDPznzcTn8OiFRxr4oWyBinyrUpnY4mhy0SQUwoeCw7YkcHAyhCj
86NT74aR/ohX0MCj0qRRdbt5ZQXM/GC3HJuXE1ptSuhFgQxziItamn8maoJ6JUSVEX
87VO1NOrrjoM3r7Q+BK2B+sX4/bLZ+VG5g1q2nEbFdTHS6pHqtZNHQndTmEKwRfh0R
88YtzEzOXuO6e1gQY42Tujkof40dhGCIU7TeIGGHwdFxy1niLkXwtHNjV7lnIOkTbx
896+sSPamRfQAlZqUWM2Lf5o+7h3qWP3ENB138sQIDAQABAoIBAD23nYTmrganag6M
90wPFrBSGP79c3Lhx0EjUHQjJbGKFgsdltG48qM3ut+DF9ACy0Z+/7bbC7+39vaIOq
911jLR2d6aiYTaLKseO4s2FawD1sgamvU3BZPsXn0gAhnnU5Gyy8Nas1dccvhoc9wI
92neaZUPrvucQ90AzLfo6r9yacDbYHB1lOyomApUvpJxOgHISGEtc9qGPDrdH19aF0
938fCv2bbQRh+TChgN3IB0o5w0wXaI7YAyAouAv/AzHCoEMpt7OGjFTkjh/ujlPL9O
94+FLuJNsQRHDN0gJo2pcvwGwDCsioMixQ9bZ7ZrUu2BNpEQygyeSbj9ZI1iRvhosO
95JU3rwEECgYEA9MppTYA6A9WQbCCwPH1QMpUAmPNVSWVhUVag4lGOEhdCDRcz9ook
96DohQMKctiEB1luKuvDokxo0uMOfMO9/YwjsRB7qjQip7Th1zMJIjD+A+juLzHK4r
97/RiRtWYGAnF8mptDvE+93JsPb3C/lQLvIhio5GQYWBqPJu6SpeosIskCgYEA7NPi
98Gbffzr2UQhW8BNKmctEEh8yFRVojFo3wwwWxSNUVXGSmSm31CL+Q8h817R+2OkPV
991ZMUOBU4UJiqFt28kIvTDFqbAJlJQGCpY2mY7OLQiD2A+TVLcFrHmoCaPfCAK1Qd
100hQ0PmFK7Mf8qClpA3E5chop/WfKQfiu46sZv1qkCgYAhGdXPcw1lQ1W6KVlrdI6J
101qHhiNlVMDXdxZkNvFxQdAiQeXQrbxaZGiMw/J/wSNpUwCAsUzM/4QVMDrfSCDCzl
102ZtNQtj4pTlFKKNVQthIjrXEIJUw2jp7IJLBfVSJu5iWxSlmId0f3MsiNizN81N69
103P5Rm/doE3+KHoy8VXGsHcQKBgQCkNh62enqjHWypjex6450qS6f6iWN3PRLLVsw0
104TcQpniZblCaBwVCAKmRUnjOEIdL2/4ZLutnwMTaFG/YEOOfAylMiY8jKV38lNmD9
105X4D78CFr9klxgvS2CRwSE03f2NzmLkLxuKaxldvaxPTfjMkgeO1LFMlNExYBhkuH
1067uQpUQKBgQCKX6qMNh2gSdgG7qyxfTFZ4y5EGOBoKe/dE+IcVF3Vnh6DZVbCAbBL
1075EdFWZSrCnDjA4xiKW55mwp95Ud9EZsZAb13L8V9t82eK+UDBoWlb7VRNYpda/x1
1085/i4qQJ28x2UNJDStpYFpnp4Ba1lvXjKngIbDPkjU+hbBJ+BNGAIeg==
109-----END RSA PRIVATE KEY-----";
110 /// Create a mock database to be used in tests
111 fn mocked_db() -> Db {
112 let db = create_database();
113
114 db.users.write().insert(
115 "mock_transaction_source".to_owned(),
116 User {
117 user_id: MetuId::new("e254275".to_owned()).unwrap(),
118 public_key:
119"-----BEGIN PUBLIC KEY-----
120MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4nU0G4WjkmcQUx0hq6LQ
121uV5Q+ACmUFL/OjoYMDwC/O/6pCd1UZgCfgHN2xEffDPznzcTn8OiFRxr4oWyBiny
122rUpnY4mhy0SQUwoeCw7YkcHAyhCjNT74aR/ohX0MCj0qRRdbt5ZQXM/GC3HJuXE1
123ptSuhFgQxziItamn8maoJ6JUSVEXVO1NOrrjoM3r7Q+BK2B+sX4/bLZ+VG5g1q2n
124EbFdTHS6pHqtZNHQndTmEKwRfh0RYtzEzOXuO6e1gQY42Tujkof40dhGCIU7TeIG
125GHwdFxy1niLkXwtHNjV7lnIOkTbx6+sSPamRfQAlZqUWM2Lf5o+7h3qWP3ENB138
126sQIDAQAB
127-----END PUBLIC KEY-----"
128 .to_owned(),
129 balance: 0,
130 },
131 );
132 db.pending_transactions.write().insert(
133 "hash_value".to_owned(),
134 Transaction {
135 by: "source_account".to_owned(),
136 source: "source_account".to_owned(),
137 target: "target_account".to_owned(),
138 amount: 20,
139 timestamp: chrono::NaiveDate::from_ymd(2021, 04, 09).and_hms(1, 30, 30),
140 },
141 );
142
143 *db.blockchain.write() = Block {
144 transaction_list: vec![
145 "old_transaction_hash_1".to_owned(),
146 "old_transaction_hash_2".to_owned(),
147 "old_transaction_hash_3".to_owned(),
148 ],
149 nonce: 0,
150 timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30),
151 hash: "not_a_thing_yet".to_owned(),
152 };
153
154 db
155 }
156
157 fn mocked_jwt() -> String {
158
159 let claims = Claims {
160 tha: "6692e774eba7fb92dc0fe6cf7347591e".to_owned(),
161 iat: 1516239022,
162 };
163 let header = Header::new(Algorithm::RS256);
164 encode(&header, &claims, &EncodingKey::from_rsa_pem(private_key_pem.as_bytes()).unwrap()).unwrap()
165 }
166 /// Create a mock user that is allowed to be in gradecoin to be used in tests
167 fn priviliged_mocked_user() -> AuthRequest {
168 AuthRequest {
169 student_id: String::from("e254275"),
170 public_key: "NOT IMPLEMENTED".to_owned(),
171 }
172 }
173
174 /// Create a mock user that is NOT allowed to be in gradecoin to be used in tests
175 fn unpriviliged_mocked_user() -> AuthRequest {
176 AuthRequest {
177 student_id: String::from("foobarbaz"),
178 public_key: "NOT IMPLEMENTED".to_owned(),
179 }
180 }
181
182 /// Create a mock transaction to be used in tests
183 fn mocked_transaction() -> Transaction {
184 Transaction {
185 by: "mock_transaction_source".to_owned(),
186 source: "mock_transaction_source".to_owned(),
187 target: "mock_transaction_target".to_owned(),
188 amount: 25,
189 timestamp: chrono::NaiveDate::from_ymd(2021, 04, 09).and_hms(14, 30, 00),
190 }
191 }
192
193 /// Create a mock block with a correct mined hash to be used in tests
194 fn mocked_block() -> Block {
195 Block {
196 transaction_list: vec!["hash_value".to_owned()],
197 nonce: 3831993,
198 timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30),
199 hash: "2b648ffab5d9af1d5d5fc052fc9e51b882fc4fb0c998608c99232f9282000000".to_owned(),
200 }
201 }
202
203 /// Create a mock block with a wrong hash and nonce
204 fn mocked_wrong_block() -> Block {
205 Block {
206 transaction_list: vec!["foobarbaz".to_owned(), "dazsaz".to_owned()],
207 nonce: 1000, // can you imagine
208 timestamp: chrono::NaiveDate::from_ymd(2021, 04, 12).and_hms(05, 29, 30),
209 hash: "tnarstnarsuthnarsthlarjstk".to_owned(),
210 }
211 }
212
213 /// Test simple GET request to /transaction, resource that exists
214 /// https://tools.ietf.org/html/rfc7231#section-6.3.1
215 /// We should get the only pending transaction available in the database as json
216 #[tokio::test]
217 async fn get_pending_transactions() {
218 let db = mocked_db();
219
220 let reply = consensus_routes(db);
221
222 let res = warp::test::request()
223 .method("GET")
224 .path("/transaction")
225 .reply(&reply)
226 .await;
227
228 assert_eq!(res.status(), StatusCode::OK);
229
230 let expected_json_body = r#"[{"by":"source_account","source":"source_account","target":"target_account","amount":20,"timestamp":"2021-04-09T01:30:30"}]"#;
231
232 assert_eq!(res.body(), expected_json_body);
233 }
234
235 /// Test simple GET request to /block, resource that exists
236 /// https://tools.ietf.org/html/rfc7231#section-6.3.1
237 /// Should return the single block available in the database as json
238 #[tokio::test]
239 async fn get_blockchain() {
240 let db = mocked_db();
241 let filter = consensus_routes(db);
242
243 let res = warp::test::request()
244 .method("GET")
245 .path("/block")
246 .reply(&filter)
247 .await;
248
249 assert_eq!(res.status(), StatusCode::OK);
250
251 let expected_json_body = r#"{"transaction_list":["old_transaction_hash_1","old_transaction_hash_2","old_transaction_hash_3"],"nonce":0,"timestamp":"2021-04-08T12:30:30","hash":"not_a_thing_yet"}"#;
252 assert_eq!(res.body(), expected_json_body);
253 }
254
255 /// Test a simple GET request to a nonexisting path
256 /// https://tools.ietf.org/html/rfc7231#section-6.5.4
257 /// Should respond with 404 and stop
258 #[tokio::test]
259 async fn get_nonexisting_path_404() {
260 let db = mocked_db();
261 let filter = consensus_routes(db);
262
263 let res = warp::test::request()
264 .method("GET")
265 .path("/this_path_does_not_exist")
266 .reply(&filter)
267 .await;
268
269 assert_eq!(res.status(), StatusCode::NOT_FOUND);
270 }
271
272 /// Test a POST request to /transaction, a resource that exists
273 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
274 /// Should accept the json request, create
275 /// the transaction and add it to pending transactions in the db
276 #[tokio::test]
277 async fn post_json_201() {
278 let db = mocked_db();
279 let filter = consensus_routes(db.clone());
280 let res = warp::test::request()
281 .method("POST")
282 .json(&mocked_transaction())
283 .path("/transaction")
284 .reply(&filter)
285 .await;
286
287 assert_eq!(res.status(), StatusCode::CREATED);
288 assert_eq!(db.pending_transactions.read().len(), 2);
289 }
290
291 /// Test a POST request to /transaction, a resource that exists
292 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
293 /// Should accept the json request, create
294 /// the transaction and add it to pending transactions in the db
295 #[tokio::test]
296 async fn post_auth_json_201() {
297 let db = mocked_db();
298 let filter = consensus_routes(db.clone());
299
300 let res = warp::test::request()
301 .method("POST")
302 .json(&mocked_transaction())
303 .header("Authorization", format!("Bearer {}", &mocked_jwt()))
304 .path("/transaction")
305 .reply(&filter)
306 .await;
307
308 assert_eq!(res.status(), StatusCode::CREATED);
309 assert_eq!(db.pending_transactions.read().len(), 2);
310 }
311
312 /// Test a POST request to /transaction, a resource that exists
313 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
314 /// Should accept the json request, create
315 /// the transaction and add it to pending transactions in the db
316 #[tokio::test]
317 async fn post_auth_json_400() {
318 let db = mocked_db();
319 let filter = consensus_routes(db.clone());
320
321 let res = warp::test::request()
322 .method("POST")
323 .json(&mocked_transaction())
324 .header("Authorization", "Bearer aaaaaaaasdlkjaldkasljdaskjlaaaaaaaaaaaaaa")
325 .path("/transaction")
326 .reply(&filter)
327 .await;
328
329 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
330 assert_eq!(db.pending_transactions.read().len(), 1);
331 }
332
333 /// Test a POST request to /block, a resource that exists
334 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
335 /// Should accept the json request, create
336 /// the block
337 #[tokio::test]
338 async fn post_block_201() {
339 let db = mocked_db();
340 let filter = consensus_routes(db.clone());
341
342 let res = warp::test::request()
343 .method("POST")
344 .json(&mocked_block())
345 .path("/block")
346 .reply(&filter)
347 .await;
348
349 assert_eq!(res.status(), StatusCode::CREATED);
350 assert_eq!(
351 *db.blockchain.read().hash,
352 "2b648ffab5d9af1d5d5fc052fc9e51b882fc4fb0c998608c99232f9282000000".to_owned()
353 );
354 }
355
356 /// Test a POST request to /block, a resource that exists
357 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
358 /// Should reject the block because of the wrong hash
359 #[tokio::test]
360 async fn post_block_wrong_hash() {
361 let db = mocked_db();
362 let filter = consensus_routes(db.clone());
363
364 let res = warp::test::request()
365 .method("POST")
366 .json(&mocked_wrong_block())
367 .path("/block")
368 .reply(&filter)
369 .await;
370
371 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
372 }
373
374 /// Test a POST request to /register, an endpoint that exists
375 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
376 /// Should accept the json request, create a new user and
377 /// add it to the user hashmap in the db
378 #[tokio::test]
379 async fn post_register_priviliged_user() {
380 let db = mocked_db();
381 let filter = consensus_routes(db.clone());
382
383 let res = warp::test::request()
384 .method("POST")
385 .json(&priviliged_mocked_user())
386 .path("/register")
387 .reply(&filter)
388 .await;
389
390 println!("{:?}", res.body());
391 assert_eq!(res.status(), StatusCode::CREATED);
392 assert_eq!(db.users.read().len(), 2);
393 }
394
395 /// Test a POST request to /transaction, an endpoint that exists
396 /// https://tools.ietf.org/html/rfc7231#section-6.3.2
397 /// Should NOT accept the json request as the user is unpriviliged
398 #[tokio::test]
399 async fn post_register_unpriviliged_user() {
400 let db = mocked_db();
401 let filter = consensus_routes(db.clone());
402
403 let res = warp::test::request()
404 .method("POST")
405 .json(&unpriviliged_mocked_user())
406 .path("/register")
407 .reply(&filter)
408 .await;
409
410 println!("{:?}", res.body());
411 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
412 assert_eq!(db.users.read().len(), 1);
413 }
414
415 /// Test a POST request to /transaction, a resource that exists with a longer than expected
416 /// payload
417 /// https://tools.ietf.org/html/rfc7231#section-6.5.11
418 /// Should return 413 to user
419 #[tokio::test]
420 async fn post_too_long_content_413() {
421 let db = mocked_db();
422 let filter = consensus_routes(db);
423
424 let res = warp::test::request()
425 .method("POST")
426 .header("content-length", 1024 * 36)
427 .path("/transaction")
428 .reply(&filter)
429 .await;
430
431 assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
432 }
433}
434
435// TODO: POST block without correct transactions test <09-04-21, yigit> //
436// TODO: POST transaction while that source has pending transaction test <09-04-21, yigit> //