Merge branch 'release/v0.3.0'
This commit is contained in:
commit
bf9393354e
142
Cargo.lock
generated
142
Cargo.lock
generated
@ -169,6 +169,12 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.5.2"
|
||||
@ -297,6 +303,17 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"lazy_static",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "1.2.4"
|
||||
@ -781,7 +798,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -842,6 +859,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.15.0"
|
||||
@ -902,7 +928,7 @@ dependencies = [
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1209,15 +1235,31 @@ version = "0.2.0"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
"base64",
|
||||
"clap",
|
||||
"configparser",
|
||||
"json",
|
||||
"jwt-simple",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"regex",
|
||||
"simple_logger",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simple_logger"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e190a521c2044948158666916d9e872cbb9984f755e9bb3b5b75a836205affcd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"colored",
|
||||
"log",
|
||||
"time",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
@ -1327,6 +1369,35 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.21.2"
|
||||
@ -1520,43 +1591,100 @@ version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_msvc 0.36.1",
|
||||
"windows_i686_gnu 0.36.1",
|
||||
"windows_i686_msvc 0.36.1",
|
||||
"windows_x86_64_gnu 0.36.1",
|
||||
"windows_x86_64_msvc 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc 0.42.0",
|
||||
"windows_i686_gnu 0.42.0",
|
||||
"windows_i686_msvc 0.42.0",
|
||||
"windows_x86_64_gnu 0.42.0",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.5.7"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "simple-auth"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@ -12,6 +12,9 @@ regex = "1"
|
||||
tokio = { version = "1.21.1", features = ["full"] }
|
||||
async-trait = "0.1.57"
|
||||
jwt-simple = "0.11.1"
|
||||
simple_logger = "4.0.0"
|
||||
log = "0.4.17"
|
||||
base64 = "0.13.1"
|
||||
|
||||
# useful for tests (embedded files should be delete in release ?)
|
||||
#rust-embed="6.4.1"
|
||||
|
||||
@ -45,13 +45,19 @@ expiration_time = 2 # in hours
|
||||
```bash
|
||||
./simple-auth <ini_path>
|
||||
|
||||
# get a JWT
|
||||
curl http://<ip>:<port>/get/ -d '{"username":"<user>", "password":"<password>"}'
|
||||
# should returned
|
||||
{"token":"<header>.<payload>.<signature>"}
|
||||
|
||||
# validate a JWT
|
||||
curl http://<ip>:<port>/validate/ -d '{"token":"<header>.<payload>.<signature>"}'
|
||||
# should returned (if valid)
|
||||
{"valid":"true"}
|
||||
|
||||
# get the public key for local validation
|
||||
curl http://<ip>:<port>/pubkey/
|
||||
{"pubkey":"<b64_encoded_public_key>"}
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
//! config module implements all the utilities to properly create and validate a router config
|
||||
|
||||
use configparser::ini::Ini;
|
||||
use std::str::FromStr;
|
||||
|
||||
@ -34,7 +32,10 @@ impl TryFrom<Ini> for Config {
|
||||
match u64::from_str(&exp_time) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
eprintln!("unable to convert JWT expiration time into u64 err={}", e);
|
||||
log::error!(
|
||||
"unable to convert JWT expiration time into u64 details={}",
|
||||
e
|
||||
);
|
||||
0
|
||||
}
|
||||
}
|
||||
@ -59,27 +60,27 @@ impl Config {
|
||||
/// validates config ini file
|
||||
fn validate(&self) -> bool {
|
||||
if self.jwt_exp_time <= 0 {
|
||||
eprintln!("invalid config parameter: JWT expiration time is negative or equals to 0");
|
||||
log::error!("invalid config parameter: JWT expiration time is negative or equals to 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.jwt_issuer == "" {
|
||||
eprintln!("invalid config parameter: JWT issuer is empty");
|
||||
log::error!("invalid config parameter: JWT issuer is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.jwt_pub_key == "" {
|
||||
eprintln!("invalid config parameter: JWT public key file path is empty");
|
||||
log::error!("invalid config parameter: JWT public key file path is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.jwt_priv_key == "" {
|
||||
eprintln!("invalid config parameter: JWT private key file path is empty");
|
||||
log::error!("invalid config parameter: JWT private key file path is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.filestore_path == "" {
|
||||
eprintln!("invalid config parameter: filestore path is empty");
|
||||
log::error!("invalid config parameter: filestore path is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//! provides `Config` struct to load and validate `.ini` file
|
||||
mod config;
|
||||
|
||||
pub use config::Config;
|
||||
|
||||
@ -143,10 +143,7 @@ impl TryFrom<String> for HTTPBody {
|
||||
let body = body.replace(NULL_CHAR, "");
|
||||
match json::parse(&body) {
|
||||
Ok(v) => Ok(HTTPBody::new(v)),
|
||||
Err(e) => Err(format!(
|
||||
"error occurred during request body parsing err={}",
|
||||
e
|
||||
)),
|
||||
Err(e) => Err(format!("during request body parsing details={}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,6 +153,8 @@ impl TryFrom<String> for HTTPBody {
|
||||
pub struct HTTPRequest {
|
||||
pub start_line: HTTPStartLine,
|
||||
pub body: Option<HTTPBody>,
|
||||
// includes the client IP + port (should be in the headers)
|
||||
pub addr: String,
|
||||
}
|
||||
|
||||
impl HTTPRequest {
|
||||
@ -190,19 +189,19 @@ impl HTTPRequest {
|
||||
let start_line = HTTPStartLine::parse(&rp.0);
|
||||
match start_line {
|
||||
Ok(v) => request.start_line = v,
|
||||
Err(e) => eprintln!("error occurred while parsing start_line err={}", e),
|
||||
Err(e) => log::error!("while parsing start_line details={}", e),
|
||||
}
|
||||
|
||||
let body = HTTPBody::try_from(rp.2);
|
||||
match body {
|
||||
Ok(v) => request.body = Some(v),
|
||||
Err(e) => eprintln!("error occurred during body parsing err={}", e),
|
||||
Err(e) => log::warn!("{}", e),
|
||||
}
|
||||
|
||||
return Ok(request);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("error occurred getting request parts err={}", e));
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,10 +217,18 @@ impl HTTPRequest {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_method(&self) -> String {
|
||||
self.start_line.method.clone()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_valid(&self) -> bool {
|
||||
return self.start_line.is_valid();
|
||||
}
|
||||
|
||||
pub fn set_addr(&mut self, addr: String) {
|
||||
self.addr = addr;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HTTPRequest {
|
||||
@ -229,6 +236,7 @@ impl Default for HTTPRequest {
|
||||
HTTPRequest {
|
||||
start_line: HTTPStartLine::default(),
|
||||
body: None,
|
||||
addr: "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -237,8 +245,8 @@ impl From<&str> for HTTPRequest {
|
||||
fn from(request: &str) -> Self {
|
||||
match Self::parse(request) {
|
||||
Ok(v) => v,
|
||||
Err(v) => {
|
||||
eprintln!("{}", format!("[ERR]: {v}"));
|
||||
Err(e) => {
|
||||
log::error!("{}", e);
|
||||
return HTTPRequest::default();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
//! router aims to handle correctly the request corresponding to the target
|
||||
//! it implements all the logic to build an `HTTPResponse`
|
||||
|
||||
use base64;
|
||||
use json;
|
||||
|
||||
use super::{HTTPMessage, HTTPRequest, HTTPResponse};
|
||||
@ -11,8 +12,13 @@ use crate::stores::{FileStore, Store};
|
||||
// TODO: must be mapped with corresponding handler
|
||||
const GET_ROUTE: &'static str = "/get/";
|
||||
const VALIDATE_ROUTE: &'static str = "/validate/";
|
||||
const PUBKEY_ROUTE: &'static str = "/pubkey/";
|
||||
|
||||
async fn handle_get(request: HTTPRequest, config: Config, method: &str) -> HTTPResponse {
|
||||
if method.trim().to_lowercase() != "post" {
|
||||
return HTTPResponse::as_400();
|
||||
}
|
||||
|
||||
async fn handle_get(request: HTTPRequest, config: Config) -> HTTPResponse {
|
||||
let mut store = FileStore::new(config.filestore_path.clone());
|
||||
match &request.body {
|
||||
Some(ref b) => {
|
||||
@ -46,7 +52,11 @@ async fn handle_get(request: HTTPRequest, config: Config) -> HTTPResponse {
|
||||
/// validates the token by checking:
|
||||
/// * expiration time
|
||||
/// * signature
|
||||
async fn handle_validate(request: HTTPRequest, config: Config) -> HTTPResponse {
|
||||
async fn handle_validate(request: HTTPRequest, config: Config, method: &str) -> HTTPResponse {
|
||||
if request.get_method().trim().to_lowercase() != method {
|
||||
return HTTPResponse::as_400();
|
||||
}
|
||||
|
||||
let token = {
|
||||
match request.get_body_value("token") {
|
||||
Some(t) => t,
|
||||
@ -87,16 +97,46 @@ async fn handle_validate(request: HTTPRequest, config: Config) -> HTTPResponse {
|
||||
HTTPResponse::as_200(Some(json))
|
||||
}
|
||||
|
||||
/// returns the JWT public key in base64 encoded
|
||||
async fn handle_public_key(request: HTTPRequest, config: Config, method: &str) -> HTTPResponse {
|
||||
if request.get_method().trim().to_lowercase() != method {
|
||||
return HTTPResponse::as_400();
|
||||
}
|
||||
|
||||
let jwt_signer = {
|
||||
match JWTSigner::new(config).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
let message = HTTPMessage::error(&e);
|
||||
let json = message.try_into().unwrap();
|
||||
return HTTPResponse::as_500(Some(json));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let public_key = jwt_signer.get_public_key();
|
||||
|
||||
let mut message = HTTPMessage::default();
|
||||
message.put("pubkey", &base64::encode(public_key));
|
||||
|
||||
let json = message.try_into().unwrap();
|
||||
HTTPResponse::as_200(Some(json))
|
||||
}
|
||||
|
||||
pub struct Router;
|
||||
|
||||
impl Router {
|
||||
pub async fn route(&self, request_str: &str, config: Config) -> HTTPResponse {
|
||||
let request = HTTPRequest::from(request_str);
|
||||
/// routes the request to the corresponding handling method
|
||||
pub async fn route(&self, request_str: &str, addr: String, config: Config) -> HTTPResponse {
|
||||
let mut request = HTTPRequest::from(request_str);
|
||||
request.set_addr(addr);
|
||||
|
||||
let target = request.start_line.get_target();
|
||||
|
||||
match target.as_str() {
|
||||
GET_ROUTE => handle_get(request, config).await,
|
||||
VALIDATE_ROUTE => handle_validate(request, config).await,
|
||||
GET_ROUTE => handle_get(request, config, "post").await,
|
||||
VALIDATE_ROUTE => handle_validate(request, config, "post").await,
|
||||
PUBKEY_ROUTE => handle_public_key(request, config, "get").await,
|
||||
_ => HTTPResponse::as_404(),
|
||||
}
|
||||
}
|
||||
@ -113,7 +153,7 @@ async fn test_route() {
|
||||
let config: Config = Config::default();
|
||||
let request_str = "POST /get/ HTTP/1.1\r\n\r\n";
|
||||
|
||||
let response: HTTPResponse = router.route(request_str, config).await;
|
||||
let response: HTTPResponse = router.route(request_str, "".to_string(), config).await;
|
||||
assert_eq!(
|
||||
HTTPStatusCode::Http400,
|
||||
response.status_line.get_status_code()
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
//! simple module to read `.pem` files and sign the token
|
||||
|
||||
use crate::config::Config;
|
||||
use jwt_simple::common::VerificationOptions;
|
||||
use jwt_simple::prelude::*;
|
||||
@ -28,7 +26,7 @@ impl JWTSigner {
|
||||
jwt_signer.private_key = c;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("unable to read the private key err={}", e));
|
||||
return Err(format!("unable to read the private key details={}", e));
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,7 +35,7 @@ impl JWTSigner {
|
||||
jwt_signer.public_key = c;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("unable to read the public key err={}", e));
|
||||
return Err(format!("unable to read the public key details={}", e));
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +58,7 @@ impl JWTSigner {
|
||||
match RS384KeyPair::from_pem(&self.private_key) {
|
||||
Ok(k) => k,
|
||||
Err(e) => {
|
||||
return Err(format!("unable to load the private key err={}", e));
|
||||
return Err(format!("unable to load the private key details={}", e));
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -70,7 +68,7 @@ impl JWTSigner {
|
||||
match jwt_key.sign(claims) {
|
||||
Ok(token) => Ok(token),
|
||||
Err(e) => {
|
||||
return Err(format!("unable to sign the token err={}", e));
|
||||
return Err(format!("unable to sign the token details={}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -82,16 +80,20 @@ impl JWTSigner {
|
||||
if let Err(e) =
|
||||
key.verify_token::<NoCustomClaims>(token, Some(verification_options))
|
||||
{
|
||||
return Err(format!("token validation failed err={}", e));
|
||||
return Err(format!("token validation failed details={}", e));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(format!(
|
||||
"token validation failed can't read the public key err={}",
|
||||
"token validation failed, can't read the public key details={}",
|
||||
e
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_public_key(&self) -> String {
|
||||
self.public_key.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//! simple module to read `.pem` files and sign the token
|
||||
mod jwt;
|
||||
|
||||
pub use jwt::JWTSigner;
|
||||
|
||||
26
src/main.rs
26
src/main.rs
@ -24,13 +24,14 @@ struct Cli {
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
simple_logger::init_with_level(log::Level::Info).unwrap();
|
||||
let args = Cli::parse();
|
||||
|
||||
let mut config = Ini::new();
|
||||
match config.load(args.config) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
eprintln!("error while loading the config file, err={}", e);
|
||||
log::error!("error while loading the config file details={}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
@ -39,11 +40,11 @@ async fn main() {
|
||||
let listener = {
|
||||
match TcpListener::bind(&server_url).await {
|
||||
Ok(t) => {
|
||||
println!("server is listening on '{}'", server_url);
|
||||
log::info!("server is listening on '{}'", server_url);
|
||||
t
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("error occurred while initializing tcp listener err={}", e);
|
||||
log::error!("while initializing tcp listener details={}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
@ -52,28 +53,31 @@ async fn main() {
|
||||
let router_config: Config = {
|
||||
match Config::try_from(config) {
|
||||
Ok(c) => c,
|
||||
Err(_e) => {
|
||||
Err(e) => {
|
||||
log::error!("unable to load the configuration details={}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let (stream, _) = listener.accept().await.unwrap();
|
||||
let (stream, addr) = listener.accept().await.unwrap();
|
||||
let conf = router_config.clone();
|
||||
tokio::spawn(handle_connection(stream, conf.clone()));
|
||||
tokio::spawn(handle_connection(stream, addr.to_string(), conf.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
/// parses the incoming request (partial spec implementation) and build an HTTP response
|
||||
async fn handle_connection(mut stream: TcpStream, config: Config) {
|
||||
async fn handle_connection(mut stream: TcpStream, addr: String, config: Config) {
|
||||
log::info!("client connected: {}", addr);
|
||||
|
||||
let mut message = vec![];
|
||||
let mut buffer: [u8; 1024] = [0; 1024];
|
||||
|
||||
let duration = Duration::from_micros(500);
|
||||
let duration = Duration::from_millis(5);
|
||||
|
||||
// loop until the message is read
|
||||
// the stream can be fragmented so, using a timeout (500um should be enough) for the future for completion
|
||||
// the stream can be fragmented so, using a timeout (5ms should be far enough) for the future for completion
|
||||
// after the timeout, the message is "considered" as entirely read
|
||||
loop {
|
||||
match timeout(duration, stream.read(&mut buffer)).await {
|
||||
@ -86,9 +90,11 @@ async fn handle_connection(mut stream: TcpStream, config: Config) {
|
||||
}
|
||||
|
||||
let request_string = std::str::from_utf8(&message).unwrap();
|
||||
let response = ROUTER.route(request_string, config).await;
|
||||
let response = ROUTER.route(request_string, addr.clone(), config).await;
|
||||
let response_str: String = response.into();
|
||||
|
||||
stream.write(response_str.as_bytes()).await.unwrap();
|
||||
stream.flush().await.unwrap();
|
||||
|
||||
log::info!("connection closed: {}", addr);
|
||||
}
|
||||
|
||||
@ -41,10 +41,7 @@ impl FileStore {
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"error occurred while reading store file: {}, err={:?}",
|
||||
self.path, e
|
||||
);
|
||||
log::error!("while reading store file: {}, details={:?}", self.path, e);
|
||||
}
|
||||
}
|
||||
self.credentials = credentials;
|
||||
@ -69,13 +66,13 @@ 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);
|
||||
log::error!("{} 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");
|
||||
log::error!("unable to parse the credentials correctly from the incoming request");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//! includes utility function, that's all !
|
||||
mod utils;
|
||||
|
||||
pub use utils::extract_json_value;
|
||||
|
||||
@ -6,10 +6,11 @@
|
||||
#
|
||||
#######################################
|
||||
|
||||
if [ -z ${SIMPLE_AUTH_URL} ]
|
||||
URL=${SIMPLE_AUTH_URL}
|
||||
if [ -z ${URL} ]
|
||||
then
|
||||
echo "[WARN]: SIMPLE_AUTH_URL is empty, set to http://localhost:9001"
|
||||
URL="http://localhost:9001"
|
||||
echo "[WARN]: SIMPLE_AUTH_URL is empty, set to http://localhost:5555"
|
||||
URL="http://localhost:5555"
|
||||
fi
|
||||
|
||||
for i in {0..10}
|
||||
@ -38,3 +39,14 @@ do
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
for i in {0..10}
|
||||
do
|
||||
http_response=$(curl -s -o response.txt -w "%{http_code}" ${URL}/pubkey/)
|
||||
if [ $http_response != "200" ]
|
||||
then
|
||||
echo "bad http status code : ${http_response}, expect 400"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import base64
|
||||
import jwt
|
||||
import os
|
||||
import requests
|
||||
@ -5,7 +6,7 @@ import requests
|
||||
from datetime import datetime
|
||||
from unittest import TestCase
|
||||
|
||||
URL = os.getenv("SIMPLE_AUTH_URL", "http://127.0.0.1:9001")
|
||||
URL = os.getenv("SIMPLE_AUTH_URL", "http://127.0.0.1:5555")
|
||||
PUB_KEY_PATH = os.getenv("SIMPLE_AUTH_PUB_KEY", "")
|
||||
|
||||
|
||||
@ -56,7 +57,7 @@ class TestResponse(TestCase):
|
||||
self.assertEqual(resp.json()["valid"], "false", "bad status returned")
|
||||
self.assertEqual(
|
||||
resp.json()["reason"],
|
||||
"token validation failed err=JWT compact encoding error",
|
||||
"token validation failed details=JWT compact encoding error",
|
||||
)
|
||||
|
||||
def test_validate_target(self):
|
||||
@ -113,3 +114,23 @@ class TestResponse(TestCase):
|
||||
"the url requested does not exist",
|
||||
"invalid error message returned",
|
||||
)
|
||||
|
||||
def test_get_pubkey(self):
|
||||
resp = requests.get(URL + "/pubkey/")
|
||||
self.assertEqual(resp.status_code, 200, "bad status code returned")
|
||||
self.assertIsNotNone(resp.json(), "response data must not be empty")
|
||||
self.assertIsNotNone(resp.json()["pubkey"], "invalid error message returned")
|
||||
|
||||
b64_pubkey = base64.b64decode(resp.json()["pubkey"])
|
||||
self.assertIsNotNone(b64_pubkey, "public key b64 decoded can't be empty")
|
||||
self.assertIn("-BEGIN PUBLIC KEY-", b64_pubkey.decode())
|
||||
|
||||
def test_get_pubkey_bad_method(self):
|
||||
resp = requests.post(URL + "/pubkey/", json={"tutu": "toto"})
|
||||
self.assertEqual(resp.status_code, 400, "bad status code returned")
|
||||
self.assertIsNotNone(resp.json(), "response data must not be empty")
|
||||
self.assertEqual(
|
||||
resp.json()["error"],
|
||||
"the incoming request is not valid",
|
||||
"invalid error message returned",
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user