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, credentials: Vec, } impl FileStore { fn new(path: String) -> Self { FileStore { path, credentials: vec![], } } /// parse_contents loads and reads the file asynchonously /// parses the file line by line to retrieve the credentials async fn parse_contents(&mut self) { let contents = tokio::fs::read_to_string(&self.path).await; let mut credentials: Vec = vec![]; match contents { Ok(c) => { let lines: Vec<&str> = c.split("\n").collect(); for line in lines { if line.starts_with("#") { continue; } let line_split: Vec<&str> = line.split(":").collect(); if line_split.len() != 2 { continue; } credentials.push(Credentials::new( line_split[0].to_string(), line_split[1].to_string(), )); } } Err(e) => { eprintln!( "error occurred while reading store file: {}, err={:?}", self.path, e ); } } self.credentials = credentials; } fn auth(&self, username: String, password: String) -> bool { let credentials: Vec<&Credentials> = self .credentials .iter() .filter(|x| x.username == username && x.password == password) .collect(); if credentials.len() == 1 { return true; } false } } #[async_trait] impl Store for FileStore { async fn is_auth(&mut 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 contents = self.parse_contents().await; self.auth(credentials.username, credentials.password) } } #[tokio::test] async fn test_store() { use std::env; let root_path = env::var("CARGO_MANIFEST_DIR").unwrap(); // TODO: path::Path should be better let store_path = format!("{}/{}/{}/{}", root_path, "tests", "data", "store.txt"); let mut store = FileStore::new(store_path); let data = json::parse(r#"{"username": "toto", "password": "tata"}"#).unwrap(); assert_eq!(store.is_auth(&data).await, true); }