feat: #8 impl a credentials store
This commit is contained in:
		
							parent
							
								
									2c7418f333
								
							
						
					
					
						commit
						8bf503df14
					
				
							
								
								
									
										12
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -123,6 +123,17 @@ version = "4.3.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" | checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "async-trait" | ||||||
|  | version = "0.1.57" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "atomic-waker" | name = "atomic-waker" | ||||||
| version = "1.0.0" | version = "1.0.0" | ||||||
| @ -504,6 +515,7 @@ name = "simple-auth" | |||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "async-std", |  "async-std", | ||||||
|  |  "async-trait", | ||||||
|  "json", |  "json", | ||||||
|  "lazy_static", |  "lazy_static", | ||||||
|  "regex", |  "regex", | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ json = "0.12.4" | |||||||
| lazy_static = "1.4.0" | lazy_static = "1.4.0" | ||||||
| regex = "1" | regex = "1" | ||||||
| tokio = { version = "1.21.1", features = ["full"] } | tokio = { version = "1.21.1", features = ["full"] } | ||||||
|  | async-trait = "0.1.57" | ||||||
| 
 | 
 | ||||||
| [dependencies.async-std] | [dependencies.async-std] | ||||||
| version = "1.6" | version = "1.6" | ||||||
|  | |||||||
| @ -68,6 +68,8 @@ impl From<HTTPRequest> for HTTPResponse { | |||||||
|             return response; |             return response; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         // TODO: impl a valid credentials in `Store`
 | ||||||
|  | 
 | ||||||
|         let body = json::parse( |         let body = json::parse( | ||||||
|             r#"{"token": "header.payload.signature", "refresh": "header.payload.signature"}"#, |             r#"{"token": "header.payload.signature", "refresh": "header.payload.signature"}"#, | ||||||
|         ) |         ) | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| mod handlers; | mod handlers; | ||||||
|  | mod stores; | ||||||
| 
 | 
 | ||||||
| use tokio::{ | use tokio::{ | ||||||
|     io::{AsyncReadExt, AsyncWriteExt}, |     io::{AsyncReadExt, AsyncWriteExt}, | ||||||
|  | |||||||
							
								
								
									
										68
									
								
								src/stores/file.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/stores/file.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | |||||||
|  | use async_trait::async_trait; | ||||||
|  | use json; | ||||||
|  | use json::object::Object; | ||||||
|  | use std::path::Path; | ||||||
|  | 
 | ||||||
|  | use tokio::fs::File; | ||||||
|  | use tokio::io::AsyncReadExt; // for read_to_end()
 | ||||||
|  | 
 | ||||||
|  | use super::store::{Credentials, Store}; | ||||||
|  | 
 | ||||||
|  | /// FileStore references a `store` file
 | ||||||
|  | pub struct FileStore { | ||||||
|  |     path: String, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl FileStore { | ||||||
|  |     fn new(path: String) -> Self { | ||||||
|  |         FileStore { path } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn parse_contents(&self, mut file: File) -> Vec<u8> { | ||||||
|  |         let mut contents = vec![]; | ||||||
|  |         let byte_read = file.read_to_end(&mut contents).await; | ||||||
|  |         if byte_read.is_err() { | ||||||
|  |             eprintln!( | ||||||
|  |                 "error occurred while reading store file: {}, err={:?}", | ||||||
|  |                 self.path, | ||||||
|  |                 byte_read.err() | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         contents | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[async_trait] | ||||||
|  | impl Store for FileStore { | ||||||
|  |     async fn is_auth(&self, data: &json::JsonValue) -> bool { | ||||||
|  |         // ensure that the store file already exists even after its instanciation
 | ||||||
|  |         if !Path::new(&self.path).is_file() { | ||||||
|  |             eprintln!("{} path referencing file store does not exist", self.path); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let credentials = Credentials::from(data); | ||||||
|  |         if credentials.is_empty() { | ||||||
|  |             eprintln!("unable to parse the credentials correctly from the incoming request"); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let store = File::open(&self.path).await; | ||||||
|  |         match store { | ||||||
|  |             Ok(f) => { | ||||||
|  |                 let contents = self.parse_contents(f).await; | ||||||
|  |                 println!("file contents : {:?}", contents.len()); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             Err(e) => eprintln!("error while opening the file {}, err={:?}", self.path, e), | ||||||
|  |         } | ||||||
|  |         false | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[tokio::test] | ||||||
|  | async fn test_store() { | ||||||
|  |     let store = FileStore::new("/tmp/thegux.pid".to_string()); | ||||||
|  |     let data = json::parse(r#"{"access_token": "toto", "refresh_token": "tutu"}"#).unwrap(); | ||||||
|  |     assert_eq!(store.is_auth(&data).await, false); | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								src/stores/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/stores/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | mod file; | ||||||
|  | mod store; | ||||||
|  | 
 | ||||||
|  | pub use file::FileStore; | ||||||
|  | pub use store::Store; | ||||||
							
								
								
									
										72
									
								
								src/stores/store.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/stores/store.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | |||||||
|  | use async_trait::async_trait; | ||||||
|  | use json; | ||||||
|  | use json::object::Object; | ||||||
|  | 
 | ||||||
|  | #[async_trait] | ||||||
|  | pub trait Store { | ||||||
|  |     async fn is_auth(&self, data: &json::JsonValue) -> bool; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// extract_json_value extracts String json value from a key
 | ||||||
|  | fn extract_json_value(data: &Object, key: &str) -> String { | ||||||
|  |     if let Some(u) = data.get(key) { | ||||||
|  |         match u.as_str() { | ||||||
|  |             Some(s) => return s.to_string(), | ||||||
|  |             None => return "".to_string(), | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     "".to_string() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Default)] | ||||||
|  | pub struct Credentials { | ||||||
|  |     pub username: String, | ||||||
|  |     pub password: String, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Credentials { | ||||||
|  |     fn new(username: String, password: String) -> Self { | ||||||
|  |         Credentials { username, password } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn is_empty(&self) -> bool { | ||||||
|  |         self.username == "" || self.password == "" | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl From<&json::JsonValue> for Credentials { | ||||||
|  |     fn from(data: &json::JsonValue) -> Self { | ||||||
|  |         let mut credentials = Credentials::default(); | ||||||
|  |         match data { | ||||||
|  |             json::JsonValue::Object(ref d) => { | ||||||
|  |                 credentials.username = extract_json_value(&d, "username"); | ||||||
|  |                 credentials.password = extract_json_value(&d, "password"); | ||||||
|  |             } | ||||||
|  |             _ => return credentials, | ||||||
|  |         } | ||||||
|  |         credentials | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn test_credentials() { | ||||||
|  |     struct Expect { | ||||||
|  |         data: json::JsonValue, | ||||||
|  |         is_empty: bool, | ||||||
|  |     } | ||||||
|  |     let test_cases: [Expect; 2] = [ | ||||||
|  |         Expect { | ||||||
|  |             data: json::parse(r#"{"access_token":"AAAAAAAAAAAA.BBBBBBBBBB.CCCCCCCCCC","refresh_token": "DDDDDDDDDDD.EEEEEEEEEEE.FFFFF"}"#).unwrap(), | ||||||
|  |             is_empty: true | ||||||
|  |         }, | ||||||
|  |         Expect { | ||||||
|  |             data: json::parse(r#"{"username":"toto","password": "tata"}"#).unwrap(), | ||||||
|  |             is_empty: false | ||||||
|  |         } | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     for t in test_cases { | ||||||
|  |         let credentials = Credentials::from(&t.data); | ||||||
|  |         assert_eq!(t.is_empty, credentials.is_empty()) | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user