diff --git a/Cargo.lock b/Cargo.lock index d925400..18db34c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -635,8 +635,8 @@ dependencies = [ [[package]] name = "http" -version = "0.1.3" -source = "git+https://gitea.thegux.fr/rmanach/http#b8c0fbba0b62906823a79e34bb2eadc1fe419d90" +version = "0.1.4" +source = "git+https://gitea.thegux.fr/rmanach/http#7d4aabad2c6d2cc07359b64214ba6e61f42ed80f" dependencies = [ "json", "lazy_static", @@ -1239,6 +1239,7 @@ dependencies = [ "log", "regex", "serde", + "serde_json", "simple_logger", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index a0dc9e1..4b2a916 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,9 @@ jwt-simple = "0.11.1" simple_logger = "4.0.0" log = "0.4.17" 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 ?) #rust-embed="6.4.1" diff --git a/src/jwt/jwt.rs b/src/jwt/jwt.rs index 56f3aa8..4b9d7e0 100644 --- a/src/jwt/jwt.rs +++ b/src/jwt/jwt.rs @@ -5,6 +5,8 @@ use serde::{Deserialize, Serialize}; use std::collections::HashSet; use tokio::fs; +use crate::stores::Credentials; + #[derive(Serialize, Deserialize)] struct JWTCustomClaims { email: String, @@ -59,7 +61,7 @@ impl JWTSigner { } /// builds and signs the token - pub fn sign(&self, email: String) -> Result { + pub fn sign(&self, credentials: Credentials) -> Result { let jwt_key = { match RS384KeyPair::from_pem(&self.private_key) { Ok(k) => k, @@ -69,7 +71,7 @@ impl JWTSigner { } }; let mut claims = Claims::with_custom_claims( - JWTCustomClaims { email }, + JWTCustomClaims { email: credentials.get_email() }, Duration::from_hours(self.exp_time), ); claims.issuer = Some(self.issuer.clone()); diff --git a/src/router/router.rs b/src/router/router.rs index 3000e13..810b66b 100644 --- a/src/router/router.rs +++ b/src/router/router.rs @@ -7,7 +7,7 @@ use json::JsonValue; use crate::config::Config; use crate::jwt::JWTSigner; -use crate::stores::{FileStore, Store}; +use crate::stores::{Credentials, FileStore, Store}; // TODO: must be mapped with corresponding handler 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() { 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(); } @@ -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), Err(e) => { let message = JSONMessage::error(&e); diff --git a/src/stores/file.rs b/src/stores/file.rs index f3839d0..90a5b12 100644 --- a/src/stores/file.rs +++ b/src/stores/file.rs @@ -1,5 +1,4 @@ use async_trait::async_trait; -use json::JsonValue; use std::path::Path; use super::store::{Credentials, Store}; @@ -47,41 +46,31 @@ impl FileStore { self.credentials = credentials; } - /// checks if the credentials exist in the `FileStore` - fn auth(&self, email: String, password: String) -> Option { + /// auth checks if the credentials exist in the `FileStore` + fn auth(&self, email: String, password: String) -> bool { let credentials: Vec<&Credentials> = self .credentials .iter() - .filter(|x| x.email == email && x.password == password) + .filter(|x| *x.get_email() == email && *x.get_password() == password) .collect(); if credentials.len() == 1 { - // no need to store the password again - return Some(Credentials::new( - credentials[0].email.clone(), - "".to_string(), - )); + return true; } - None + false } } #[async_trait] impl Store for FileStore { - async fn is_auth(&mut self, data: &JsonValue) -> Option { + async fn is_auth(&mut self, credentials: &Credentials) -> bool { // ensure that the store file already exists even after its instanciation if !Path::new(&self.path).is_file() { log::error!("{} path referencing file store does not exist", self.path); - return None; - } - - let credentials = Credentials::from(data); - if credentials.is_empty() { - log::error!("unable to parse the credentials correctly from the incoming request"); - return None; + return false; } self.parse_contents().await; - self.auth(credentials.email, credentials.password) + self.auth(credentials.get_email(), credentials.get_password()) } } diff --git a/src/stores/store.rs b/src/stores/store.rs index ec84e04..9cf23ef 100644 --- a/src/stores/store.rs +++ b/src/stores/store.rs @@ -1,17 +1,16 @@ use async_trait::async_trait; use json::JsonValue; - -use http::extract_json_value; +use serde::Deserialize; #[async_trait] pub trait Store { - async fn is_auth(&mut self, data: &JsonValue) -> Option; + async fn is_auth(&mut self, data: &Credentials) -> bool; } -#[derive(Default, Debug)] +#[derive(Default, Debug, Deserialize)] pub struct Credentials { - pub email: String, - pub password: String, + email: String, + password: String, } impl Credentials { @@ -19,22 +18,30 @@ impl Credentials { 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 { self.email == "" || self.password == "" } } +// TODO: could be less restrictive with `From<&str>` impl From<&JsonValue> for Credentials { fn from(data: &JsonValue) -> Self { - let mut credentials = Credentials::default(); - match data { - JsonValue::Object(ref d) => { - credentials.email = extract_json_value(&d, "email").unwrap_or("".to_string()); - credentials.password = extract_json_value(&d, "password").unwrap_or("".to_string()); + let res = serde_json::from_str(&data.dump()); + match res { + Ok(c) => c, + Err(e) => { + log::warn!("unable to deserialize credentials: {}", e); + return Credentials::default(); } - _ => return credentials, } - credentials } }