refactor GET handler + impl JWTSigner
This commit is contained in:
parent
7073a4b88e
commit
6166310283
@ -68,13 +68,11 @@ impl Config {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: check if the file exists and rights are ok
|
||||
if self.jwt_pub_key == "" {
|
||||
eprintln!("invalid config parameter: JWT public key file path is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: check if the file exists and rights are ok
|
||||
if self.jwt_priv_key == "" {
|
||||
eprintln!("invalid config parameter: JWT private key file path is empty");
|
||||
return false;
|
||||
|
||||
@ -21,7 +21,6 @@ impl TryInto<json::JsonValue> for HTTPMessage {
|
||||
type Error = String;
|
||||
fn try_into(self) -> Result<json::JsonValue, Self::Error> {
|
||||
let message = format!(r#"{{{}}}"#, self.build_json());
|
||||
println!("message: {}", message);
|
||||
match json::parse(&message) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(e) => Err(format!(
|
||||
|
||||
@ -105,7 +105,6 @@ impl Default for HTTPStartLine {
|
||||
fn default() -> Self {
|
||||
HTTPStartLine {
|
||||
method: "".to_string(),
|
||||
|
||||
target: "".to_string(),
|
||||
version: HTTPVersion::Unknown,
|
||||
}
|
||||
@ -335,7 +334,6 @@ fn test_request() {
|
||||
|
||||
for (request, expect) in test_cases {
|
||||
let http_request = HTTPRequest::from(request.as_str());
|
||||
println!("{:?}", http_request);
|
||||
assert_eq!(expect.is_valid, http_request.is_valid());
|
||||
|
||||
let start_line: String = http_request.start_line.into();
|
||||
|
||||
@ -155,7 +155,7 @@ impl HTTPResponse {
|
||||
response
|
||||
}
|
||||
|
||||
/// build and HTTP 200 response with the generated JWT
|
||||
/// builds an HTTP 200 response with the generated JWT
|
||||
pub fn send_token(token: &str) -> Self {
|
||||
let mut http_message = HTTPMessage::default();
|
||||
http_message.put("token", token);
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
|
||||
use super::{HTTPMessage, HTTPRequest, HTTPResponse};
|
||||
use crate::config::Config;
|
||||
use crate::jwt::JWTSigner;
|
||||
use crate::stores::FileStore;
|
||||
use crate::stores::Store;
|
||||
use jwt_simple::prelude::*;
|
||||
use lazy_static::lazy_static;
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
@ -16,7 +16,7 @@ type Handler = fn(HTTPRequest, Config) -> FuturePinned<HTTPResponse>;
|
||||
|
||||
fn handle_get(request: HTTPRequest, config: Config) -> FuturePinned<HTTPResponse> {
|
||||
Box::pin(async move {
|
||||
let mut store = FileStore::new(config.filestore_path);
|
||||
let mut store = FileStore::new(config.filestore_path.clone());
|
||||
match &request.body {
|
||||
Some(ref b) => {
|
||||
let is_auth = store.is_auth(&b.get_data()).await;
|
||||
@ -24,35 +24,20 @@ fn handle_get(request: HTTPRequest, config: Config) -> FuturePinned<HTTPResponse
|
||||
return HTTPResponse::as_403();
|
||||
}
|
||||
|
||||
let priv_key_content = {
|
||||
match std::fs::read_to_string(config.jwt_priv_key) {
|
||||
Ok(c) => c,
|
||||
let jwt_signer = {
|
||||
match JWTSigner::new(config).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("error while reading JWT priv key content err={}", e);
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
};
|
||||
let jwt_key = {
|
||||
match RS384KeyPair::from_pem(priv_key_content.as_str()) {
|
||||
Ok(k) => k,
|
||||
Err(e) => {
|
||||
let message: Option<json::JsonValue> = HTTPMessage::error(
|
||||
format!("unable to load the private key, err={}", e).as_str(),
|
||||
);
|
||||
let message = HTTPMessage::error(&e);
|
||||
return HTTPResponse::as_500(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut claims = Claims::create(Duration::from_hours(config.jwt_exp_time));
|
||||
claims.issuer = Some(config.jwt_issuer);
|
||||
|
||||
match jwt_key.sign(claims) {
|
||||
Ok(token) => HTTPResponse::send_token(&token),
|
||||
match jwt_signer.sign() {
|
||||
Ok(t) => HTTPResponse::send_token(&t),
|
||||
Err(e) => {
|
||||
let message: Option<json::JsonValue> = HTTPMessage::error(
|
||||
format!("unable to sign the token, err={}", e).as_str(),
|
||||
);
|
||||
let message = HTTPMessage::error(&e);
|
||||
return HTTPResponse::as_500(message);
|
||||
}
|
||||
}
|
||||
|
||||
83
src/jwt/jwt.rs
Normal file
83
src/jwt/jwt.rs
Normal file
@ -0,0 +1,83 @@
|
||||
//! simple module to read `.pem` files and sign the token
|
||||
|
||||
use crate::config::Config;
|
||||
use jwt_simple::prelude::*;
|
||||
use tokio::fs;
|
||||
|
||||
pub struct JWTSigner {
|
||||
private_key: String,
|
||||
public_key: String,
|
||||
issuer: String,
|
||||
exp_time: u64,
|
||||
}
|
||||
|
||||
impl JWTSigner {
|
||||
// NOTE: could be included in a Trait: `TryFrom` but difficult to handle with async
|
||||
pub async fn new(config: Config) -> Result<Self, String> {
|
||||
let mut jwt_signer = JWTSigner {
|
||||
private_key: "".to_string(),
|
||||
public_key: "".to_string(),
|
||||
issuer: config.jwt_issuer,
|
||||
exp_time: config.jwt_exp_time,
|
||||
};
|
||||
|
||||
match fs::read_to_string(config.jwt_priv_key).await {
|
||||
Ok(c) => {
|
||||
jwt_signer.private_key = c;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("unable to read the private key, err={}", e));
|
||||
}
|
||||
}
|
||||
|
||||
match fs::read_to_string(config.jwt_pub_key).await {
|
||||
Ok(c) => {
|
||||
jwt_signer.public_key = c;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("unable to read the public key, err={}", e));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(jwt_signer)
|
||||
}
|
||||
|
||||
/// builds and signs the token
|
||||
pub fn sign(&self) -> Result<String, String> {
|
||||
let jwt_key = {
|
||||
match RS384KeyPair::from_pem(&self.private_key) {
|
||||
Ok(k) => k,
|
||||
Err(e) => {
|
||||
return Err(format!("unable to load the private key, err={}", e));
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut claims = Claims::create(Duration::from_hours(self.exp_time));
|
||||
claims.issuer = Some(self.issuer.clone());
|
||||
|
||||
match jwt_key.sign(claims) {
|
||||
Ok(token) => Ok(token),
|
||||
Err(e) => {
|
||||
return Err(format!("unable to sign the token, err={}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_signer() {
|
||||
use configparser::ini::Ini;
|
||||
use std::env;
|
||||
|
||||
let root_path = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
|
||||
let config_path = format!("{}/{}/{}/{}", root_path, "tests", "data", "config.ini");
|
||||
let mut config = Ini::new();
|
||||
let _r = config.load(config_path);
|
||||
|
||||
let router_config = Config::try_from(config);
|
||||
assert!(router_config.is_ok());
|
||||
|
||||
let jwt_signer = JWTSigner::new(router_config.unwrap());
|
||||
assert!(jwt_signer.await.is_ok());
|
||||
}
|
||||
3
src/jwt/mod.rs
Normal file
3
src/jwt/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod jwt;
|
||||
|
||||
pub use jwt::JWTSigner;
|
||||
@ -1,5 +1,6 @@
|
||||
mod config;
|
||||
mod http;
|
||||
mod jwt;
|
||||
mod stores;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
#
|
||||
#######################################
|
||||
|
||||
URL="https://dev.thegux.fr"
|
||||
URL="http://localhost:9001"
|
||||
|
||||
for i in {0..10}
|
||||
do
|
||||
|
||||
@ -5,7 +5,7 @@ from datetime import datetime
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
URL = "https://dev.thegux.fr"
|
||||
URL = "http://localhost:9001"
|
||||
|
||||
|
||||
class TestResponse(TestCase):
|
||||
@ -62,7 +62,7 @@ class TestResponse(TestCase):
|
||||
resp = requests.post(
|
||||
URL + "/get/", json={"username": "tutu", "password": "titi"}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 403, "bas status code returned")
|
||||
self.assertEqual(resp.status_code, 403, "bad status code returned")
|
||||
self.assertIsNotNone(resp.json(), "response data must not be empty")
|
||||
self.assertEqual(
|
||||
resp.json()["error"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user