From 8bf503df14a23d9c19fa70e18a134aab9c209fcb Mon Sep 17 00:00:00 2001 From: landrigun Date: Thu, 29 Sep 2022 14:25:38 +0000 Subject: [PATCH] feat: #8 impl a credentials store --- Cargo.lock | 12 +++++++ Cargo.toml | 1 + src/handlers/response.rs | 2 ++ src/main.rs | 1 + src/stores/file.rs | 68 +++++++++++++++++++++++++++++++++++++ src/stores/mod.rs | 5 +++ src/stores/store.rs | 72 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 161 insertions(+) create mode 100644 src/stores/file.rs create mode 100644 src/stores/mod.rs create mode 100644 src/stores/store.rs diff --git a/Cargo.lock b/Cargo.lock index 9eb98ec..f896643 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,6 +123,17 @@ version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "atomic-waker" version = "1.0.0" @@ -504,6 +515,7 @@ name = "simple-auth" version = "0.1.0" dependencies = [ "async-std", + "async-trait", "json", "lazy_static", "regex", diff --git a/Cargo.toml b/Cargo.toml index 841e835..30c9f67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ json = "0.12.4" lazy_static = "1.4.0" regex = "1" tokio = { version = "1.21.1", features = ["full"] } +async-trait = "0.1.57" [dependencies.async-std] version = "1.6" diff --git a/src/handlers/response.rs b/src/handlers/response.rs index c2a8d6f..23de852 100644 --- a/src/handlers/response.rs +++ b/src/handlers/response.rs @@ -68,6 +68,8 @@ impl From for HTTPResponse { return response; } + // TODO: impl a valid credentials in `Store` + let body = json::parse( r#"{"token": "header.payload.signature", "refresh": "header.payload.signature"}"#, ) diff --git a/src/main.rs b/src/main.rs index aa6e660..83c2b0d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod handlers; +mod stores; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, diff --git a/src/stores/file.rs b/src/stores/file.rs new file mode 100644 index 0000000..a9e7837 --- /dev/null +++ b/src/stores/file.rs @@ -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 { + 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); +} diff --git a/src/stores/mod.rs b/src/stores/mod.rs new file mode 100644 index 0000000..5f1331c --- /dev/null +++ b/src/stores/mod.rs @@ -0,0 +1,5 @@ +mod file; +mod store; + +pub use file::FileStore; +pub use store::Store; diff --git a/src/stores/store.rs b/src/stores/store.rs new file mode 100644 index 0000000..a444225 --- /dev/null +++ b/src/stores/store.rs @@ -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()) + } +}