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