use crate::{Error, error::{JWTError,ConstructionError}, sign_algorithms::HS256, jwt_session::JWTSession, JWT};
use chrono::{NaiveDateTime, DateTime, Utc};
use std::collections::HashMap;
use cataclysm::{session::{SessionCreator, Session}, http::Request};
#[derive(Clone)]
pub struct JWTHS256Session {
pub aud: String,
pub iss: String,
pub verification_key: HS256
}
impl JWTHS256Session {
pub fn builder() -> JWTHS256Builder {
JWTHS256Builder::default()
}
}
impl SessionCreator for JWTHS256Session {
fn apply(&self, _values: &HashMap<String, String>, res: cataclysm::http::Response) -> cataclysm::http::Response {
res
}
fn create(&self, req: &cataclysm::http::Request) -> Result<cataclysm::session::Session, cataclysm::Error> {
match self.build_from_req(req) {
Ok(payload) => {
return Ok(Session::new_with_values(self.clone(),payload))
},
Err(_) => {
return Err(cataclysm::Error::Custom(format!("Unable to create session!!")));
}
}
}
}
impl JWTSession for JWTHS256Session {
fn build_from_req(&self, req: &Request) -> Result<HashMap<String,String>, Error> {
let jwt = Self::obtain_token_from_req(req)?;
self.initial_validation(&jwt)?;
return Ok(jwt.payload)
}
fn initial_validation(&self, jwt: &JWT) -> Result<(),Error> {
match jwt.header.get("alg") {
Some(a) => {
if a.to_lowercase().as_str() != self.verification_key.to_string() {
return Err(Error::JWT(JWTError::WrongAlgorithm));
}
},
None => {
return Err(Error::JWT(JWTError::NoAlgorithm));
}
};
#[cfg(not(feature = "lax-security"))]
{
match jwt.payload.get("aud") {
Some(a) => {
if a.as_str() != &self.aud {
return Err(Error::JWT(JWTError::WrongAudience));
}
},
None => {
return Err(Error::JWT(JWTError::NoAudience))
}
}
match jwt.payload.get("iss") {
Some(i) => {
if i.as_str() != &self.iss {
return Err(Error::JWT(JWTError::WrongIss));
}
},
None => {
return Err(Error::JWT(JWTError::NoIss))
}
}
match jwt.payload.get("exp") {
Some(e) => {
let num_e = str::parse::<i64>(e)?;
let date = NaiveDateTime::from_timestamp_opt(num_e,0).ok_or(Error::ParseTimestamp)?;
let date_utc: DateTime<Utc> = DateTime::from_utc(date, Utc);
let now = Utc::now();
if date_utc < now {
return Err(Error::JWT(JWTError::Expired));
}
},
None => {
return Err(Error::JWT(JWTError::NoExp))
}
}
match jwt.payload.get("iat") {
Some(ia) => {
let num_ia = str::parse::<i64>(ia)?;
let date = NaiveDateTime::from_timestamp_opt(num_ia,0).ok_or(Error::ParseTimestamp)?;
let date_utc: DateTime<Utc> = DateTime::from_utc(date, Utc);
let now = Utc::now();
if date_utc > now {
return Err(Error::JWT(JWTError::ToBeValid));
}
},
None => {
return Err(Error::JWT(JWTError::NoIat))
}
}
match jwt.payload.get("nbf") {
Some(nb) => {
let num_nb = str::parse::<i64>(nb)?;
let date = NaiveDateTime::from_timestamp_opt(num_nb,0).ok_or(Error::ParseTimestamp)?;
let date_utc: DateTime<Utc> = DateTime::from_utc(date, Utc);
let now = Utc::now();
if date_utc > now {
return Err(Error::JWT(JWTError::ToBeValid));
}
},
None => {
return Err(Error::JWT(JWTError::NoNbf))
}
}
}
self.verification_key.verify_jwt(&jwt.raw_jwt)
}
}
#[derive(Default)]
pub struct JWTHS256Builder {
aud: Option<String>,
iss: Option<String>,
verification_key: Option<HS256>
}
impl JWTHS256Builder {
pub fn aud<A: AsRef<str>>(self, aud: A) -> Self {
Self {
aud: Some(aud.as_ref().to_string()),
..self
}
}
pub fn iss<A: AsRef<str>>(self, iss: A) -> Self {
Self {
iss: Some(iss.as_ref().to_string()),
..self
}
}
pub fn add_from_secret<A: AsRef<str>>(self, secret: A) -> Self {
let verification_key = HS256::new(secret);
Self {
verification_key: Some(verification_key),
..self
}
}
pub fn build(self) -> Result<JWTHS256Session, Error> {
let aud = match self.aud {
Some(a) => a,
None => {
return Err(Error::Construction(ConstructionError::Aud));
}
};
let iss = match self.iss {
Some(i) => i,
None => {
return Err(Error::Construction(ConstructionError::Iss));
}
};
let verification_key = match self.verification_key {
Some(k) => k,
None => {
return Err(Error::Construction(ConstructionError::Keys))
}
};
Ok(JWTHS256Session {
aud,
iss,
verification_key,
})
}
}