update is_auth trait + deserialize credentials with serde
This commit is contained in:
		
							parent
							
								
									141a79c409
								
							
						
					
					
						commit
						3b6e208004
					
				
							
								
								
									
										5
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -635,8 +635,8 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "http" | name = "http" | ||||||
| version = "0.1.3" | version = "0.1.4" | ||||||
| source = "git+https://gitea.thegux.fr/rmanach/http#b8c0fbba0b62906823a79e34bb2eadc1fe419d90" | source = "git+https://gitea.thegux.fr/rmanach/http#7d4aabad2c6d2cc07359b64214ba6e61f42ed80f" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "json", |  "json", | ||||||
|  "lazy_static", |  "lazy_static", | ||||||
| @ -1239,6 +1239,7 @@ dependencies = [ | |||||||
|  "log", |  "log", | ||||||
|  "regex", |  "regex", | ||||||
|  "serde", |  "serde", | ||||||
|  |  "serde_json", | ||||||
|  "simple_logger", |  "simple_logger", | ||||||
|  "tokio", |  "tokio", | ||||||
| ] | ] | ||||||
|  | |||||||
| @ -15,8 +15,9 @@ jwt-simple = "0.11.1" | |||||||
| simple_logger = "4.0.0" | simple_logger = "4.0.0" | ||||||
| log = "0.4.17" | log = "0.4.17" | ||||||
| base64 = "0.13.1" | base64 = "0.13.1" | ||||||
|  | serde_json = "1.0" | ||||||
| 
 | 
 | ||||||
| http = { git = "https://gitea.thegux.fr/rmanach/http", version = "0.1.3" } | http = { git = "https://gitea.thegux.fr/rmanach/http", version = "0.1.4" } | ||||||
| 
 | 
 | ||||||
| # useful for tests (embedded files should be delete in release ?) | # useful for tests (embedded files should be delete in release ?) | ||||||
| #rust-embed="6.4.1" | #rust-embed="6.4.1" | ||||||
|  | |||||||
| @ -5,6 +5,8 @@ use serde::{Deserialize, Serialize}; | |||||||
| use std::collections::HashSet; | use std::collections::HashSet; | ||||||
| use tokio::fs; | use tokio::fs; | ||||||
| 
 | 
 | ||||||
|  | use crate::stores::Credentials; | ||||||
|  | 
 | ||||||
| #[derive(Serialize, Deserialize)] | #[derive(Serialize, Deserialize)] | ||||||
| struct JWTCustomClaims { | struct JWTCustomClaims { | ||||||
|     email: String, |     email: String, | ||||||
| @ -59,7 +61,7 @@ impl JWTSigner { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// builds and signs the token
 |     /// builds and signs the token
 | ||||||
|     pub fn sign(&self, email: String) -> Result<String, String> { |     pub fn sign(&self, credentials: Credentials) -> Result<String, String> { | ||||||
|         let jwt_key = { |         let jwt_key = { | ||||||
|             match RS384KeyPair::from_pem(&self.private_key) { |             match RS384KeyPair::from_pem(&self.private_key) { | ||||||
|                 Ok(k) => k, |                 Ok(k) => k, | ||||||
| @ -69,7 +71,7 @@ impl JWTSigner { | |||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|         let mut claims = Claims::with_custom_claims( |         let mut claims = Claims::with_custom_claims( | ||||||
|             JWTCustomClaims { email }, |             JWTCustomClaims { email: credentials.get_email() }, | ||||||
|             Duration::from_hours(self.exp_time), |             Duration::from_hours(self.exp_time), | ||||||
|         ); |         ); | ||||||
|         claims.issuer = Some(self.issuer.clone()); |         claims.issuer = Some(self.issuer.clone()); | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ use json::JsonValue; | |||||||
| 
 | 
 | ||||||
| use crate::config::Config; | use crate::config::Config; | ||||||
| use crate::jwt::JWTSigner; | use crate::jwt::JWTSigner; | ||||||
| use crate::stores::{FileStore, Store}; | use crate::stores::{Credentials, FileStore, Store}; | ||||||
| 
 | 
 | ||||||
| // TODO: must be mapped with corresponding handler
 | // TODO: must be mapped with corresponding handler
 | ||||||
| const GET_ROUTE: &'static str = "/get/"; | const GET_ROUTE: &'static str = "/get/"; | ||||||
| @ -23,8 +23,14 @@ async fn handle_get(request: HTTPRequest<'_>, config: Config, method: &str) -> H | |||||||
| 
 | 
 | ||||||
|     match request.get_body() { |     match request.get_body() { | ||||||
|         Some(d) => { |         Some(d) => { | ||||||
|             let credentials = store.is_auth(d).await; | 
 | ||||||
|             if credentials.is_none() { |             let credentials = Credentials::from(d); | ||||||
|  |             if credentials.is_empty() { | ||||||
|  |                 log::error!("unable to parse the credentials correctly from the incoming request"); | ||||||
|  |                 return HTTPResponse::as_400(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if !store.is_auth(&credentials).await { | ||||||
|                 return HTTPResponse::as_403(); |                 return HTTPResponse::as_403(); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| @ -38,7 +44,7 @@ async fn handle_get(request: HTTPRequest<'_>, config: Config, method: &str) -> H | |||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|             match jwt_signer.sign(credentials.unwrap().email) { |             match jwt_signer.sign(credentials) { | ||||||
|                 Ok(t) => send_token(&t), |                 Ok(t) => send_token(&t), | ||||||
|                 Err(e) => { |                 Err(e) => { | ||||||
|                     let message = JSONMessage::error(&e); |                     let message = JSONMessage::error(&e); | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use json::JsonValue; |  | ||||||
| use std::path::Path; | use std::path::Path; | ||||||
| 
 | 
 | ||||||
| use super::store::{Credentials, Store}; | use super::store::{Credentials, Store}; | ||||||
| @ -47,41 +46,31 @@ impl FileStore { | |||||||
|         self.credentials = credentials; |         self.credentials = credentials; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// checks if the credentials exist in the `FileStore`
 |     /// auth checks if the credentials exist in the `FileStore`
 | ||||||
|     fn auth(&self, email: String, password: String) -> Option<Credentials> { |     fn auth(&self, email: String, password: String) -> bool { | ||||||
|         let credentials: Vec<&Credentials> = self |         let credentials: Vec<&Credentials> = self | ||||||
|             .credentials |             .credentials | ||||||
|             .iter() |             .iter() | ||||||
|             .filter(|x| x.email == email && x.password == password) |             .filter(|x| *x.get_email() == email && *x.get_password() == password) | ||||||
|             .collect(); |             .collect(); | ||||||
|         if credentials.len() == 1 { |         if credentials.len() == 1 { | ||||||
|             // no need to store the password again
 |             return true; | ||||||
|             return Some(Credentials::new( |  | ||||||
|                 credentials[0].email.clone(), |  | ||||||
|                 "".to_string(), |  | ||||||
|             )); |  | ||||||
|         } |         } | ||||||
|         None |         false | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl Store for FileStore { | impl Store for FileStore { | ||||||
|     async fn is_auth(&mut self, data: &JsonValue) -> Option<Credentials> { |     async fn is_auth(&mut self, credentials: &Credentials) -> bool { | ||||||
|         // ensure that the store file already exists even after its instanciation
 |         // ensure that the store file already exists even after its instanciation
 | ||||||
|         if !Path::new(&self.path).is_file() { |         if !Path::new(&self.path).is_file() { | ||||||
|             log::error!("{} path referencing file store does not exist", self.path); |             log::error!("{} path referencing file store does not exist", self.path); | ||||||
|             return None; |             return false; | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         let credentials = Credentials::from(data); |  | ||||||
|         if credentials.is_empty() { |  | ||||||
|             log::error!("unable to parse the credentials correctly from the incoming request"); |  | ||||||
|             return None; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         self.parse_contents().await; |         self.parse_contents().await; | ||||||
|         self.auth(credentials.email, credentials.password) |         self.auth(credentials.get_email(), credentials.get_password()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,17 +1,16 @@ | |||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use json::JsonValue; | use json::JsonValue; | ||||||
| 
 | use serde::Deserialize; | ||||||
| use http::extract_json_value; |  | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| pub trait Store { | pub trait Store { | ||||||
|     async fn is_auth(&mut self, data: &JsonValue) -> Option<Credentials>; |     async fn is_auth(&mut self, data: &Credentials) -> bool; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Default, Debug)] | #[derive(Default, Debug, Deserialize)] | ||||||
| pub struct Credentials { | pub struct Credentials { | ||||||
|     pub email: String, |     email: String, | ||||||
|     pub password: String, |     password: String, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Credentials { | impl Credentials { | ||||||
| @ -19,22 +18,30 @@ impl Credentials { | |||||||
|         Credentials { email, password } |         Credentials { email, password } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn get_email(&self) -> String { | ||||||
|  |         self.email.clone() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_password(&self) -> String { | ||||||
|  |         self.password.clone() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     pub fn is_empty(&self) -> bool { |     pub fn is_empty(&self) -> bool { | ||||||
|         self.email == "" || self.password == "" |         self.email == "" || self.password == "" | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // TODO: could be less restrictive with `From<&str>`
 | ||||||
| impl From<&JsonValue> for Credentials { | impl From<&JsonValue> for Credentials { | ||||||
|     fn from(data: &JsonValue) -> Self { |     fn from(data: &JsonValue) -> Self { | ||||||
|         let mut credentials = Credentials::default(); |         let res = serde_json::from_str(&data.dump()); | ||||||
|         match data { |         match res { | ||||||
|             JsonValue::Object(ref d) => { |             Ok(c) => c, | ||||||
|                 credentials.email = extract_json_value(&d, "email").unwrap_or("".to_string()); |             Err(e) => { | ||||||
|                 credentials.password = extract_json_value(&d, "password").unwrap_or("".to_string()); |                 log::warn!("unable to deserialize credentials: {}", e); | ||||||
|  |                 return Credentials::default(); | ||||||
|             } |             } | ||||||
|             _ => return credentials, |  | ||||||
|         } |         } | ||||||
|         credentials |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user