diff options
author | alpaylan | 2021-04-12 22:15:17 +0300 |
---|---|---|
committer | alpaylan | 2021-04-12 22:15:17 +0300 |
commit | 65bcc5ce455bdc298af3b9c4a30039afed047a4f (patch) | |
tree | 63fb58ba3762501ae872d518266cf9f7cc834976 /src/routes.rs | |
parent | df484d87424674b88ccfa9392cfa9a75cc4abe48 (diff) | |
download | gradecoin-65bcc5ce455bdc298af3b9c4a30039afed047a4f.tar.gz gradecoin-65bcc5ce455bdc298af3b9c4a30039afed047a4f.tar.bz2 gradecoin-65bcc5ce455bdc298af3b9c4a30039afed047a4f.zip |
implement user authentication using jwt
Diffstat (limited to 'src/routes.rs')
-rw-r--r-- | src/routes.rs | 132 |
1 files changed, 121 insertions, 11 deletions
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 |