diff options
author | alpaylan | 2021-04-12 22:15:17 +0300 |
---|---|---|
committer | alpaylan | 2021-04-12 22:15:17 +0300 |
commit | aa169ad1b3c277859f01413a945ea2d6f1375615 (patch) | |
tree | 402042bce17641759fa28e5c9a7219025caefcbb | |
parent | 87e690420cb61efc172e82a29c38b479fc734247 (diff) | |
download | gradecoin-aa169ad1b3c277859f01413a945ea2d6f1375615.tar.gz gradecoin-aa169ad1b3c277859f01413a945ea2d6f1375615.tar.bz2 gradecoin-aa169ad1b3c277859f01413a945ea2d6f1375615.zip |
implement user authentication using jwt
-rw-r--r-- | Cargo.lock | 178 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | TODO.md | 12 | ||||
-rw-r--r-- | examples/mining.rs | 4 | ||||
-rw-r--r-- | src/custom_filters.rs | 4 | ||||
-rw-r--r-- | src/handlers.rs | 60 | ||||
-rw-r--r-- | src/routes.rs | 132 | ||||
-rw-r--r-- | src/schema.rs | 14 |
8 files changed, 374 insertions, 32 deletions
@@ -75,6 +75,12 @@ dependencies = [ | |||
75 | ] | 75 | ] |
76 | 76 | ||
77 | [[package]] | 77 | [[package]] |
78 | name = "bumpalo" | ||
79 | version = "3.6.1" | ||
80 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
81 | checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" | ||
82 | |||
83 | [[package]] | ||
78 | name = "byteorder" | 84 | name = "byteorder" |
79 | version = "1.4.3" | 85 | version = "1.4.3" |
80 | source = "registry+https://github.com/rust-lang/crates.io-index" | 86 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -93,6 +99,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
93 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" | 99 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" |
94 | 100 | ||
95 | [[package]] | 101 | [[package]] |
102 | name = "cc" | ||
103 | version = "1.0.67" | ||
104 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
105 | checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" | ||
106 | |||
107 | [[package]] | ||
96 | name = "cfg-if" | 108 | name = "cfg-if" |
97 | version = "0.1.10" | 109 | version = "0.1.10" |
98 | source = "registry+https://github.com/rust-lang/crates.io-index" | 110 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -304,8 +316,10 @@ dependencies = [ | |||
304 | "blake2", | 316 | "blake2", |
305 | "chrono", | 317 | "chrono", |
306 | "hex-literal", | 318 | "hex-literal", |
319 | "jsonwebtoken", | ||
307 | "lazy_static", | 320 | "lazy_static", |
308 | "log", | 321 | "log", |
322 | "md-5", | ||
309 | "parking_lot", | 323 | "parking_lot", |
310 | "pretty_env_logger", | 324 | "pretty_env_logger", |
311 | "serde", | 325 | "serde", |
@@ -493,6 +507,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
493 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" | 507 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" |
494 | 508 | ||
495 | [[package]] | 509 | [[package]] |
510 | name = "js-sys" | ||
511 | version = "0.3.50" | ||
512 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
513 | checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" | ||
514 | dependencies = [ | ||
515 | "wasm-bindgen", | ||
516 | ] | ||
517 | |||
518 | [[package]] | ||
519 | name = "jsonwebtoken" | ||
520 | version = "7.2.0" | ||
521 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
522 | checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32" | ||
523 | dependencies = [ | ||
524 | "base64 0.12.3", | ||
525 | "pem", | ||
526 | "ring", | ||
527 | "serde", | ||
528 | "serde_json", | ||
529 | "simple_asn1", | ||
530 | ] | ||
531 | |||
532 | [[package]] | ||
496 | name = "kernel32-sys" | 533 | name = "kernel32-sys" |
497 | version = "0.2.2" | 534 | version = "0.2.2" |
498 | source = "registry+https://github.com/rust-lang/crates.io-index" | 535 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -539,6 +576,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
539 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" | 576 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" |
540 | 577 | ||
541 | [[package]] | 578 | [[package]] |
579 | name = "md-5" | ||
580 | version = "0.9.1" | ||
581 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
582 | checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" | ||
583 | dependencies = [ | ||
584 | "block-buffer", | ||
585 | "digest", | ||
586 | "opaque-debug", | ||
587 | ] | ||
588 | |||
589 | [[package]] | ||
542 | name = "memchr" | 590 | name = "memchr" |
543 | version = "2.3.4" | 591 | version = "2.3.4" |
544 | source = "registry+https://github.com/rust-lang/crates.io-index" | 592 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -621,6 +669,17 @@ dependencies = [ | |||
621 | ] | 669 | ] |
622 | 670 | ||
623 | [[package]] | 671 | [[package]] |
672 | name = "num-bigint" | ||
673 | version = "0.2.6" | ||
674 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
675 | checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" | ||
676 | dependencies = [ | ||
677 | "autocfg", | ||
678 | "num-integer", | ||
679 | "num-traits", | ||
680 | ] | ||
681 | |||
682 | [[package]] | ||
624 | name = "num-integer" | 683 | name = "num-integer" |
625 | version = "0.1.44" | 684 | version = "0.1.44" |
626 | source = "registry+https://github.com/rust-lang/crates.io-index" | 685 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -640,6 +699,12 @@ dependencies = [ | |||
640 | ] | 699 | ] |
641 | 700 | ||
642 | [[package]] | 701 | [[package]] |
702 | name = "once_cell" | ||
703 | version = "1.7.2" | ||
704 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
705 | checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" | ||
706 | |||
707 | [[package]] | ||
643 | name = "opaque-debug" | 708 | name = "opaque-debug" |
644 | version = "0.3.0" | 709 | version = "0.3.0" |
645 | source = "registry+https://github.com/rust-lang/crates.io-index" | 710 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -670,6 +735,17 @@ dependencies = [ | |||
670 | ] | 735 | ] |
671 | 736 | ||
672 | [[package]] | 737 | [[package]] |
738 | name = "pem" | ||
739 | version = "0.8.3" | ||
740 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
741 | checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" | ||
742 | dependencies = [ | ||
743 | "base64 0.13.0", | ||
744 | "once_cell", | ||
745 | "regex", | ||
746 | ] | ||
747 | |||
748 | [[package]] | ||
673 | name = "percent-encoding" | 749 | name = "percent-encoding" |
674 | version = "2.1.0" | 750 | version = "2.1.0" |
675 | source = "registry+https://github.com/rust-lang/crates.io-index" | 751 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -897,6 +973,21 @@ dependencies = [ | |||
897 | ] | 973 | ] |
898 | 974 | ||
899 | [[package]] | 975 | [[package]] |
976 | name = "ring" | ||
977 | version = "0.16.20" | ||
978 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
979 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" | ||
980 | dependencies = [ | ||
981 | "cc", | ||
982 | "libc", | ||
983 | "once_cell", | ||
984 | "spin", | ||
985 | "untrusted", | ||
986 | "web-sys", | ||
987 | "winapi 0.3.9", | ||
988 | ] | ||
989 | |||
990 | [[package]] | ||
900 | name = "ryu" | 991 | name = "ryu" |
901 | version = "1.0.5" | 992 | version = "1.0.5" |
902 | source = "registry+https://github.com/rust-lang/crates.io-index" | 993 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -986,6 +1077,17 @@ dependencies = [ | |||
986 | ] | 1077 | ] |
987 | 1078 | ||
988 | [[package]] | 1079 | [[package]] |
1080 | name = "simple_asn1" | ||
1081 | version = "0.4.1" | ||
1082 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1083 | checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" | ||
1084 | dependencies = [ | ||
1085 | "chrono", | ||
1086 | "num-bigint", | ||
1087 | "num-traits", | ||
1088 | ] | ||
1089 | |||
1090 | [[package]] | ||
989 | name = "slab" | 1091 | name = "slab" |
990 | version = "0.4.2" | 1092 | version = "0.4.2" |
991 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1009,6 +1111,12 @@ dependencies = [ | |||
1009 | ] | 1111 | ] |
1010 | 1112 | ||
1011 | [[package]] | 1113 | [[package]] |
1114 | name = "spin" | ||
1115 | version = "0.5.2" | ||
1116 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1117 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" | ||
1118 | |||
1119 | [[package]] | ||
1012 | name = "subtle" | 1120 | name = "subtle" |
1013 | version = "2.4.0" | 1121 | version = "2.4.0" |
1014 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1122 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1241,6 +1349,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
1241 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" | 1349 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" |
1242 | 1350 | ||
1243 | [[package]] | 1351 | [[package]] |
1352 | name = "untrusted" | ||
1353 | version = "0.7.1" | ||
1354 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1355 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" | ||
1356 | |||
1357 | [[package]] | ||
1244 | name = "url" | 1358 | name = "url" |
1245 | version = "2.2.1" | 1359 | version = "2.2.1" |
1246 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1321,6 +1435,70 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
1321 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" | 1435 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" |
1322 | 1436 | ||
1323 | [[package]] | 1437 | [[package]] |
1438 | name = "wasm-bindgen" | ||
1439 | version = "0.2.73" | ||
1440 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1441 | checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" | ||
1442 | dependencies = [ | ||
1443 | "cfg-if 1.0.0", | ||
1444 | "wasm-bindgen-macro", | ||
1445 | ] | ||
1446 | |||
1447 | [[package]] | ||
1448 | name = "wasm-bindgen-backend" | ||
1449 | version = "0.2.73" | ||
1450 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1451 | checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" | ||
1452 | dependencies = [ | ||
1453 | "bumpalo", | ||
1454 | "lazy_static", | ||
1455 | "log", | ||
1456 | "proc-macro2", | ||
1457 | "quote", | ||
1458 | "syn", | ||
1459 | "wasm-bindgen-shared", | ||
1460 | ] | ||
1461 | |||
1462 | [[package]] | ||
1463 | name = "wasm-bindgen-macro" | ||
1464 | version = "0.2.73" | ||
1465 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1466 | checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" | ||
1467 | dependencies = [ | ||
1468 | "quote", | ||
1469 | "wasm-bindgen-macro-support", | ||
1470 | ] | ||
1471 | |||
1472 | [[package]] | ||
1473 | name = "wasm-bindgen-macro-support" | ||
1474 | version = "0.2.73" | ||
1475 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1476 | checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" | ||
1477 | dependencies = [ | ||
1478 | "proc-macro2", | ||
1479 | "quote", | ||
1480 | "syn", | ||
1481 | "wasm-bindgen-backend", | ||
1482 | "wasm-bindgen-shared", | ||
1483 | ] | ||
1484 | |||
1485 | [[package]] | ||
1486 | name = "wasm-bindgen-shared" | ||
1487 | version = "0.2.73" | ||
1488 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1489 | checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" | ||
1490 | |||
1491 | [[package]] | ||
1492 | name = "web-sys" | ||
1493 | version = "0.3.50" | ||
1494 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1495 | checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" | ||
1496 | dependencies = [ | ||
1497 | "js-sys", | ||
1498 | "wasm-bindgen", | ||
1499 | ] | ||
1500 | |||
1501 | [[package]] | ||
1324 | name = "winapi" | 1502 | name = "winapi" |
1325 | version = "0.2.8" | 1503 | version = "0.2.8" |
1326 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1504 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -18,6 +18,8 @@ serde_json = "1.0.59" | |||
18 | lazy_static = "1.4.0" | 18 | lazy_static = "1.4.0" |
19 | blake2 = "0.9.1" | 19 | blake2 = "0.9.1" |
20 | hex-literal = "0.3.1" | 20 | hex-literal = "0.3.1" |
21 | jsonwebtoken = "7.2.0" | ||
22 | md-5 = "0.9.1" | ||
21 | 23 | ||
22 | [dev-dependencies] | 24 | [dev-dependencies] |
23 | serde_test = "1.0.117" | 25 | serde_test = "1.0.117" |
@@ -3,14 +3,7 @@ | |||
3 | ## Proof-of-work | 3 | ## Proof-of-work |
4 | So far so good | 4 | So far so good |
5 | 5 | ||
6 | ## Authentication | 6 | ## Authentication(DONE/DONE) |
7 | - [X] pick a user authentication scheme = [JWT](https://tools.ietf.org/html/rfc7519) Seems perfect | ||
8 | - [ ] implement JWT | ||
9 | - https://blog.logrocket.com/jwt-authentication-in-rust/ | ||
10 | - https://crates.io/crates/jsonwebtoken | ||
11 | - https://jwt.io/introduction/ | ||
12 | - https://jwt.io/#debugger-io | ||
13 | - [ ] users should be able to _sign_ their transactions | ||
14 | 7 | ||
15 | ## Verbosity | 8 | ## Verbosity |
16 | - [ ] Verbose error messages (use error.rs?) | 9 | - [ ] Verbose error messages (use error.rs?) |
@@ -31,3 +24,6 @@ So far so good | |||
31 | - [x] We need our own representation of students and their grades, "there is no blockchain" (done at 2021-04-12 00:05) | 24 | - [x] We need our own representation of students and their grades, "there is no blockchain" (done at 2021-04-12 00:05) |
32 | - [x] pick a block proposal scheme (= pick hash function) [list of hash functions](https://en.bitcoinwiki.org/wiki/List_of_hash_functions) (done at 2021-04-12 05:30) | 25 | - [x] pick a block proposal scheme (= pick hash function) [list of hash functions](https://en.bitcoinwiki.org/wiki/List_of_hash_functions) (done at 2021-04-12 05:30) |
33 | - [x] check the nonce for incoming blocks (done at 2021-04-12 05:30) | 26 | - [x] check the nonce for incoming blocks (done at 2021-04-12 05:30) |
27 | - [X] pick a user authentication scheme = [JWT](https://tools.ietf.org/html/rfc7519) Seems perfect | ||
28 | - [X] implement JWT | ||
29 | - [X] users should be able to _sign_ their transactions | ||
diff --git a/examples/mining.rs b/examples/mining.rs index c95c214..21fdc58 100644 --- a/examples/mining.rs +++ b/examples/mining.rs | |||
@@ -7,9 +7,7 @@ use blake2::{Blake2s, Digest}; | |||
7 | 7 | ||
8 | pub fn main() { | 8 | pub fn main() { |
9 | let mut b = NakedBlock { | 9 | let mut b = NakedBlock { |
10 | transaction_list: vec![ | 10 | transaction_list: vec!["hash_value".to_owned()], |
11 | "hash_value".to_owned(), | ||
12 | ], | ||
13 | nonce: 0, | 11 | nonce: 0, |
14 | timestamp: NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), | 12 | timestamp: NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), |
15 | }; | 13 | }; |
diff --git a/src/custom_filters.rs b/src/custom_filters.rs index 315ba4a..f93f572 100644 --- a/src/custom_filters.rs +++ b/src/custom_filters.rs | |||
@@ -20,6 +20,10 @@ pub fn transaction_json_body() -> impl Filter<Extract = (Transaction,), Error = | |||
20 | warp::body::content_length_limit(1024 * 32).and(warp::body::json()) | 20 | warp::body::content_length_limit(1024 * 32).and(warp::body::json()) |
21 | } | 21 | } |
22 | 22 | ||
23 | pub fn auth_header() -> impl Filter<Extract = (String,), Error = Rejection> + Clone { | ||
24 | warp::header::header::<String>("Authorization") | ||
25 | } | ||
26 | |||
23 | // Accept only json encoded Block body and reject big payloads | 27 | // Accept only json encoded Block body and reject big payloads |
24 | // TODO: find a good limit for this <11-04-21, yigit> // | 28 | // TODO: find a good limit for this <11-04-21, yigit> // |
25 | pub fn block_json_body() -> impl Filter<Extract = (Block,), Error = Rejection> + Clone { | 29 | pub fn block_json_body() -> impl Filter<Extract = (Block,), Error = Rejection> + Clone { |
diff --git a/src/handlers.rs b/src/handlers.rs index 38bd459..07986f5 100644 --- a/src/handlers.rs +++ b/src/handlers.rs | |||
@@ -1,16 +1,29 @@ | |||
1 | use blake2::{Blake2s, Digest}; | ||
2 | use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; | ||
1 | /// API handlers, the ends of each filter chain | 3 | /// API handlers, the ends of each filter chain |
2 | use log::debug; | 4 | use log::debug; |
5 | use md5::Md5; | ||
3 | use parking_lot::RwLockUpgradableReadGuard; | 6 | use parking_lot::RwLockUpgradableReadGuard; |
7 | use serde::{Deserialize, Serialize}; | ||
4 | use serde_json; | 8 | use serde_json; |
5 | use serde_json::json; | 9 | use serde_json::json; |
6 | use std::convert::Infallible; | 10 | use std::convert::Infallible; |
7 | use warp::{http::Response, http::StatusCode, reply}; | 11 | use std::fs; |
12 | use warp::{http::Response, http::StatusCode, reject, reply}; | ||
8 | 13 | ||
9 | use blake2::{Blake2s, Digest}; | 14 | use gradecoin::schema::{ |
15 | AuthRequest, Block, Db, MetuId, NakedBlock, PublicKeySignature, Transaction, User, | ||
16 | }; | ||
10 | 17 | ||
11 | use std::fs; | 18 | const BEARER: &str = "Bearer "; |
12 | 19 | ||
13 | use gradecoin::schema::{AuthRequest, Block, Db, MetuId, NakedBlock, Transaction, User}; | 20 | /// tha: Transaction Hash, String |
21 | /// iat: Issued At, Unix Time, epoch | ||
22 | #[derive(Debug, Serialize, Deserialize)] | ||
23 | pub struct Claims { | ||
24 | pub tha: String, | ||
25 | pub iat: usize, | ||
26 | } | ||
14 | 27 | ||
15 | /// POST /register | 28 | /// POST /register |
16 | /// Enables a student to introduce themselves to the system | 29 | /// Enables a student to introduce themselves to the system |
@@ -167,3 +180,42 @@ pub async fn propose_block(new_block: Block, db: Db) -> Result<impl warp::Reply, | |||
167 | Ok(StatusCode::BAD_REQUEST) | 180 | Ok(StatusCode::BAD_REQUEST) |
168 | } | 181 | } |
169 | } | 182 | } |
183 | |||
184 | pub async fn auth_propose_transaction( | ||
185 | new_transaction: Transaction, | ||
186 | token: String, | ||
187 | db: Db, | ||
188 | ) -> Result<impl warp::Reply, warp::Rejection> { | ||
189 | debug!("new transaction request {:?}", new_transaction); | ||
190 | let raw_jwt = token.trim_start_matches(BEARER).to_owned(); | ||
191 | |||
192 | let decoded = jsonwebtoken::decode::<Claims>( | ||
193 | &token, | ||
194 | &DecodingKey::from_rsa_pem( | ||
195 | db.users | ||
196 | .read() | ||
197 | .get(&new_transaction.by) | ||
198 | .unwrap() | ||
199 | .public_key | ||
200 | .as_bytes(), | ||
201 | ) | ||
202 | .unwrap(), | ||
203 | // todo@keles: If user is not found return user not found error | ||
204 | &Validation::new(Algorithm::PS256), | ||
205 | ) | ||
206 | .unwrap(); | ||
207 | // todo: If user is found but header is not validated, return header not valid | ||
208 | |||
209 | let hashed_transaction = Md5::digest(&serde_json::to_vec(&new_transaction).unwrap()); | ||
210 | |||
211 | // let mut transactions = db.lock().await; | ||
212 | if decoded.claims.tha == format!("{:x}", hashed_transaction) { | ||
213 | let mut transactions = db.pending_transactions.write(); | ||
214 | |||
215 | transactions.insert(new_transaction.source.to_owned(), new_transaction); | ||
216 | |||
217 | Ok(StatusCode::CREATED) | ||
218 | } else { | ||
219 | Ok(StatusCode::BAD_REQUEST) | ||
220 | } | ||
221 | } | ||
diff --git a/src/routes.rs b/src/routes.rs index 03a2569..ed2acad 100644 --- a/src/routes.rs +++ b/src/routes.rs | |||
@@ -1,14 +1,14 @@ | |||
1 | use gradecoin::schema::Db; | ||
1 | use warp::{Filter, Rejection, Reply}; | 2 | use warp::{Filter, Rejection, Reply}; |
2 | 3 | ||
3 | use crate::custom_filters; | 4 | use crate::custom_filters; |
4 | use crate::handlers; | 5 | use crate::handlers; |
5 | use gradecoin::schema::Db; | ||
6 | 6 | ||
7 | /// Root, all routes combined | 7 | /// Root, all routes combined |
8 | pub fn consensus_routes(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { | 8 | pub fn consensus_routes(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { |
9 | transaction_list(db.clone()) | 9 | transaction_list(db.clone()) |
10 | .or(register_user(db.clone())) | 10 | .or(register_user(db.clone())) |
11 | .or(transaction_propose(db.clone())) | 11 | .or(auth_transaction_propose(db.clone())) |
12 | .or(block_propose(db.clone())) | 12 | .or(block_propose(db.clone())) |
13 | .or(block_list(db.clone())) | 13 | .or(block_list(db.clone())) |
14 | } | 14 | } |
@@ -47,6 +47,18 @@ pub fn transaction_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = | |||
47 | .and_then(handlers::propose_transaction) | 47 | .and_then(handlers::propose_transaction) |
48 | } | 48 | } |
49 | 49 | ||
50 | /// POST /transaction warp route | ||
51 | pub fn auth_transaction_propose( | ||
52 | db: Db, | ||
53 | ) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { | ||
54 | warp::path!("transaction") | ||
55 | .and(warp::post()) | ||
56 | .and(custom_filters::transaction_json_body()) | ||
57 | .and(custom_filters::auth_header()) | ||
58 | .and(custom_filters::with_db(db)) | ||
59 | .and_then(handlers::auth_propose_transaction) | ||
60 | } | ||
61 | |||
50 | /// POST /block warp route | 62 | /// POST /block warp route |
51 | pub fn block_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { | 63 | pub fn block_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { |
52 | warp::path!("block") | 64 | warp::path!("block") |
@@ -58,22 +70,69 @@ pub fn block_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = Reject | |||
58 | 70 | ||
59 | #[cfg(test)] | 71 | #[cfg(test)] |
60 | mod tests { | 72 | mod tests { |
61 | use super::*; | 73 | use gradecoin::schema::{create_database, AuthRequest, Block, MetuId, Transaction, User}; |
62 | 74 | use handlers::Claims; | |
63 | // use chrono::prelude::*; | 75 | // use chrono::prelude::*; |
64 | // use parking_lot::RwLock; | 76 | // use parking_lot::RwLock; |
65 | // use std::sync::Arc; | 77 | // use std::sync::Arc; |
66 | use warp::http::StatusCode; | 78 | use warp::http::StatusCode; |
67 | 79 | ||
68 | use gradecoin::schema::{create_database, AuthRequest, Block, Transaction}; | 80 | use super::*; |
69 | 81 | ||
82 | use jsonwebtoken::{Header, encode, EncodingKey, Algorithm}; | ||
83 | const private_key_pem: &str = "-----BEGIN RSA PRIVATE KEY----- | ||
84 | MIIEpAIBAAKCAQEA4nU0G4WjkmcQUx0hq6LQuV5Q+ACmUFL/OjoYMDwC/O/6pCd1 | ||
85 | UZgCfgHN2xEffDPznzcTn8OiFRxr4oWyBinyrUpnY4mhy0SQUwoeCw7YkcHAyhCj | ||
86 | NT74aR/ohX0MCj0qRRdbt5ZQXM/GC3HJuXE1ptSuhFgQxziItamn8maoJ6JUSVEX | ||
87 | VO1NOrrjoM3r7Q+BK2B+sX4/bLZ+VG5g1q2nEbFdTHS6pHqtZNHQndTmEKwRfh0R | ||
88 | YtzEzOXuO6e1gQY42Tujkof40dhGCIU7TeIGGHwdFxy1niLkXwtHNjV7lnIOkTbx | ||
89 | 6+sSPamRfQAlZqUWM2Lf5o+7h3qWP3ENB138sQIDAQABAoIBAD23nYTmrganag6M | ||
90 | wPFrBSGP79c3Lhx0EjUHQjJbGKFgsdltG48qM3ut+DF9ACy0Z+/7bbC7+39vaIOq | ||
91 | 1jLR2d6aiYTaLKseO4s2FawD1sgamvU3BZPsXn0gAhnnU5Gyy8Nas1dccvhoc9wI | ||
92 | neaZUPrvucQ90AzLfo6r9yacDbYHB1lOyomApUvpJxOgHISGEtc9qGPDrdH19aF0 | ||
93 | 8fCv2bbQRh+TChgN3IB0o5w0wXaI7YAyAouAv/AzHCoEMpt7OGjFTkjh/ujlPL9O | ||
94 | +FLuJNsQRHDN0gJo2pcvwGwDCsioMixQ9bZ7ZrUu2BNpEQygyeSbj9ZI1iRvhosO | ||
95 | JU3rwEECgYEA9MppTYA6A9WQbCCwPH1QMpUAmPNVSWVhUVag4lGOEhdCDRcz9ook | ||
96 | DohQMKctiEB1luKuvDokxo0uMOfMO9/YwjsRB7qjQip7Th1zMJIjD+A+juLzHK4r | ||
97 | /RiRtWYGAnF8mptDvE+93JsPb3C/lQLvIhio5GQYWBqPJu6SpeosIskCgYEA7NPi | ||
98 | Gbffzr2UQhW8BNKmctEEh8yFRVojFo3wwwWxSNUVXGSmSm31CL+Q8h817R+2OkPV | ||
99 | 1ZMUOBU4UJiqFt28kIvTDFqbAJlJQGCpY2mY7OLQiD2A+TVLcFrHmoCaPfCAK1Qd | ||
100 | hQ0PmFK7Mf8qClpA3E5chop/WfKQfiu46sZv1qkCgYAhGdXPcw1lQ1W6KVlrdI6J | ||
101 | qHhiNlVMDXdxZkNvFxQdAiQeXQrbxaZGiMw/J/wSNpUwCAsUzM/4QVMDrfSCDCzl | ||
102 | ZtNQtj4pTlFKKNVQthIjrXEIJUw2jp7IJLBfVSJu5iWxSlmId0f3MsiNizN81N69 | ||
103 | P5Rm/doE3+KHoy8VXGsHcQKBgQCkNh62enqjHWypjex6450qS6f6iWN3PRLLVsw0 | ||
104 | TcQpniZblCaBwVCAKmRUnjOEIdL2/4ZLutnwMTaFG/YEOOfAylMiY8jKV38lNmD9 | ||
105 | X4D78CFr9klxgvS2CRwSE03f2NzmLkLxuKaxldvaxPTfjMkgeO1LFMlNExYBhkuH | ||
106 | 7uQpUQKBgQCKX6qMNh2gSdgG7qyxfTFZ4y5EGOBoKe/dE+IcVF3Vnh6DZVbCAbBL | ||
107 | 5EdFWZSrCnDjA4xiKW55mwp95Ud9EZsZAb13L8V9t82eK+UDBoWlb7VRNYpda/x1 | ||
108 | 5/i4qQJ28x2UNJDStpYFpnp4Ba1lvXjKngIbDPkjU+hbBJ+BNGAIeg== | ||
109 | -----END RSA PRIVATE KEY-----"; | ||
70 | /// Create a mock database to be used in tests | 110 | /// Create a mock database to be used in tests |
71 | fn mocked_db() -> Db { | 111 | fn mocked_db() -> Db { |
72 | let db = create_database(); | 112 | let db = create_database(); |
73 | 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----- | ||
120 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4nU0G4WjkmcQUx0hq6LQ | ||
121 | uV5Q+ACmUFL/OjoYMDwC/O/6pCd1UZgCfgHN2xEffDPznzcTn8OiFRxr4oWyBiny | ||
122 | rUpnY4mhy0SQUwoeCw7YkcHAyhCjNT74aR/ohX0MCj0qRRdbt5ZQXM/GC3HJuXE1 | ||
123 | ptSuhFgQxziItamn8maoJ6JUSVEXVO1NOrrjoM3r7Q+BK2B+sX4/bLZ+VG5g1q2n | ||
124 | EbFdTHS6pHqtZNHQndTmEKwRfh0RYtzEzOXuO6e1gQY42Tujkof40dhGCIU7TeIG | ||
125 | GHwdFxy1niLkXwtHNjV7lnIOkTbx6+sSPamRfQAlZqUWM2Lf5o+7h3qWP3ENB138 | ||
126 | sQIDAQAB | ||
127 | -----END PUBLIC KEY-----" | ||
128 | .to_owned(), | ||
129 | balance: 0, | ||
130 | }, | ||
131 | ); | ||
74 | db.pending_transactions.write().insert( | 132 | db.pending_transactions.write().insert( |
75 | "hash_value".to_owned(), | 133 | "hash_value".to_owned(), |
76 | Transaction { | 134 | Transaction { |
135 | by: "source_account".to_owned(), | ||
77 | source: "source_account".to_owned(), | 136 | source: "source_account".to_owned(), |
78 | target: "target_account".to_owned(), | 137 | target: "target_account".to_owned(), |
79 | amount: 20, | 138 | amount: 20, |
@@ -95,6 +154,15 @@ mod tests { | |||
95 | db | 154 | db |
96 | } | 155 | } |
97 | 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 | } | ||
98 | /// Create a mock user that is allowed to be in gradecoin to be used in tests | 166 | /// Create a mock user that is allowed to be in gradecoin to be used in tests |
99 | fn priviliged_mocked_user() -> AuthRequest { | 167 | fn priviliged_mocked_user() -> AuthRequest { |
100 | AuthRequest { | 168 | AuthRequest { |
@@ -114,6 +182,7 @@ mod tests { | |||
114 | /// Create a mock transaction to be used in tests | 182 | /// Create a mock transaction to be used in tests |
115 | fn mocked_transaction() -> Transaction { | 183 | fn mocked_transaction() -> Transaction { |
116 | Transaction { | 184 | Transaction { |
185 | by: "mock_transaction_source".to_owned(), | ||
117 | source: "mock_transaction_source".to_owned(), | 186 | source: "mock_transaction_source".to_owned(), |
118 | target: "mock_transaction_target".to_owned(), | 187 | target: "mock_transaction_target".to_owned(), |
119 | amount: 25, | 188 | amount: 25, |
@@ -125,9 +194,9 @@ mod tests { | |||
125 | fn mocked_block() -> Block { | 194 | fn mocked_block() -> Block { |
126 | Block { | 195 | Block { |
127 | transaction_list: vec!["hash_value".to_owned()], | 196 | transaction_list: vec!["hash_value".to_owned()], |
128 | nonce: 560108, | 197 | nonce: 3831993, |
129 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), | 198 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), |
130 | hash: "c7d053f3e5b056ba948db3f5c0d30408fb0c29a328a0c3c1cf435fb68d700000".to_owned(), | 199 | hash: "2b648ffab5d9af1d5d5fc052fc9e51b882fc4fb0c998608c99232f9282000000".to_owned(), |
131 | } | 200 | } |
132 | } | 201 | } |
133 | 202 | ||
@@ -158,7 +227,7 @@ mod tests { | |||
158 | 227 | ||
159 | assert_eq!(res.status(), StatusCode::OK); | 228 | assert_eq!(res.status(), StatusCode::OK); |
160 | 229 | ||
161 | let expected_json_body = r#"[{"source":"source_account","target":"target_account","amount":20,"timestamp":"2021-04-09T01:30:30"}]"#; | 230 | let expected_json_body = r#"[{"by":"source_account","source":"source_account","target":"target_account","amount":20,"timestamp":"2021-04-09T01:30:30"}]"#; |
162 | 231 | ||
163 | assert_eq!(res.body(), expected_json_body); | 232 | assert_eq!(res.body(), expected_json_body); |
164 | } | 233 | } |
@@ -208,10 +277,30 @@ mod tests { | |||
208 | async fn post_json_201() { | 277 | async fn post_json_201() { |
209 | let db = mocked_db(); | 278 | let db = mocked_db(); |
210 | let filter = consensus_routes(db.clone()); | 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()); | ||
211 | 299 | ||
212 | let res = warp::test::request() | 300 | let res = warp::test::request() |
213 | .method("POST") | 301 | .method("POST") |
214 | .json(&mocked_transaction()) | 302 | .json(&mocked_transaction()) |
303 | .header("Authorization", format!("Bearer {}", &mocked_jwt())) | ||
215 | .path("/transaction") | 304 | .path("/transaction") |
216 | .reply(&filter) | 305 | .reply(&filter) |
217 | .await; | 306 | .await; |
@@ -220,6 +309,27 @@ mod tests { | |||
220 | assert_eq!(db.pending_transactions.read().len(), 2); | 309 | assert_eq!(db.pending_transactions.read().len(), 2); |
221 | } | 310 | } |
222 | 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 | |||
223 | /// Test a POST request to /block, a resource that exists | 333 | /// Test a POST request to /block, a resource that exists |
224 | /// https://tools.ietf.org/html/rfc7231#section-6.3.2 | 334 | /// https://tools.ietf.org/html/rfc7231#section-6.3.2 |
225 | /// Should accept the json request, create | 335 | /// Should accept the json request, create |
@@ -239,7 +349,7 @@ mod tests { | |||
239 | assert_eq!(res.status(), StatusCode::CREATED); | 349 | assert_eq!(res.status(), StatusCode::CREATED); |
240 | assert_eq!( | 350 | assert_eq!( |
241 | *db.blockchain.read().hash, | 351 | *db.blockchain.read().hash, |
242 | "c7d053f3e5b056ba948db3f5c0d30408fb0c29a328a0c3c1cf435fb68d700000".to_owned() | 352 | "2b648ffab5d9af1d5d5fc052fc9e51b882fc4fb0c998608c99232f9282000000".to_owned() |
243 | ); | 353 | ); |
244 | } | 354 | } |
245 | 355 | ||
@@ -279,7 +389,7 @@ mod tests { | |||
279 | 389 | ||
280 | println!("{:?}", res.body()); | 390 | println!("{:?}", res.body()); |
281 | assert_eq!(res.status(), StatusCode::CREATED); | 391 | assert_eq!(res.status(), StatusCode::CREATED); |
282 | assert_eq!(db.users.read().len(), 1); | 392 | assert_eq!(db.users.read().len(), 2); |
283 | } | 393 | } |
284 | 394 | ||
285 | /// Test a POST request to /transaction, an endpoint that exists | 395 | /// Test a POST request to /transaction, an endpoint that exists |
@@ -299,7 +409,7 @@ mod tests { | |||
299 | 409 | ||
300 | println!("{:?}", res.body()); | 410 | println!("{:?}", res.body()); |
301 | assert_eq!(res.status(), StatusCode::BAD_REQUEST); | 411 | assert_eq!(res.status(), StatusCode::BAD_REQUEST); |
302 | assert_eq!(db.users.read().len(), 0); | 412 | assert_eq!(db.users.read().len(), 1); |
303 | } | 413 | } |
304 | 414 | ||
305 | /// Test a POST request to /transaction, a resource that exists with a longer than expected | 415 | /// Test a POST request to /transaction, a resource that exists with a longer than expected |
diff --git a/src/schema.rs b/src/schema.rs index 98291d7..39921b8 100644 --- a/src/schema.rs +++ b/src/schema.rs | |||
@@ -41,18 +41,20 @@ impl Db { | |||
41 | } | 41 | } |
42 | } | 42 | } |
43 | 43 | ||
44 | pub type PublicKeySignature = String; | ||
45 | |||
44 | /// A transaction between `source` and `target` that moves `amount` | 46 | /// A transaction between `source` and `target` that moves `amount` |
45 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] | 47 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] |
46 | pub struct Transaction { | 48 | pub struct Transaction { |
47 | // TODO: new field by <11-04-21, yigit> // | 49 | pub by: PublicKeySignature, |
48 | pub source: String, | 50 | pub source: PublicKeySignature, |
49 | pub target: String, | 51 | pub target: PublicKeySignature, |
50 | pub amount: i32, | 52 | pub amount: i32, |
51 | pub timestamp: NaiveDateTime, | 53 | pub timestamp: NaiveDateTime, |
52 | } | 54 | } |
53 | 55 | ||
54 | /// A block that was proposed with `transaction_list` and `nonce` that made `hash` valid | 56 | /// A block that was proposed with `transaction_list` and `nonce` that made `hash` valid |
55 | /// https://serde.rs/container-attrs.html might be valueable to normalize the serialize/deserialize | 57 | /// https://serde.rs/container-attrs.html might be valuable to normalize the serialize/deserialize |
56 | /// conventions as these will be hashed | 58 | /// conventions as these will be hashed |
57 | #[derive(Serialize, Deserialize, Debug)] | 59 | #[derive(Serialize, Deserialize, Debug)] |
58 | pub struct Block { | 60 | pub struct Block { |
@@ -61,7 +63,7 @@ pub struct Block { | |||
61 | // we can leave this as is and whenever we have a new block we _could_ just log it to file | 63 | // we can leave this as is and whenever we have a new block we _could_ just log it to file |
62 | // somewhere | 64 | // somewhere |
63 | // I want to keep this as a String vector because it makes things easier elsewhere | 65 | // I want to keep this as a String vector because it makes things easier elsewhere |
64 | pub transaction_list: Vec<String>, // hashes of the transactions (or just "source" for now) | 66 | pub transaction_list: Vec<PublicKeySignature>, // hashes of the transactions (or just "source" for now) |
65 | pub nonce: u32, | 67 | pub nonce: u32, |
66 | pub timestamp: NaiveDateTime, | 68 | pub timestamp: NaiveDateTime, |
67 | pub hash: String, // future proof'd baby | 69 | pub hash: String, // future proof'd baby |
@@ -70,7 +72,7 @@ pub struct Block { | |||
70 | /// For prototyping and letting serde handle everything json | 72 | /// For prototyping and letting serde handle everything json |
71 | #[derive(Serialize, Deserialize, Debug)] | 73 | #[derive(Serialize, Deserialize, Debug)] |
72 | pub struct NakedBlock { | 74 | pub struct NakedBlock { |
73 | pub transaction_list: Vec<String>, | 75 | pub transaction_list: Vec<PublicKeySignature>, |
74 | pub nonce: u32, | 76 | pub nonce: u32, |
75 | pub timestamp: NaiveDateTime, | 77 | pub timestamp: NaiveDateTime, |
76 | } | 78 | } |