#[doc(hidden)]
pub use base64;
#[doc(hidden)]
pub use chrono::{DateTime, Utc};
#[doc(hidden)]
pub use constant_time_eq;
#[doc(hidden)]
pub use rocket::{
async_trait,
http::Status,
data::{FromData, Outcome},
request::{self},
};
#[doc(hidden)]
pub use rocket::figment::Figment;
pub use serde::Deserialize;
#[derive(Debug)]
pub enum TokenError {
InvalidToken,
SignatureInvalid,
InvalidRequest,
ServerError,
}
#[async_trait::async_trait]
pub trait AuthorizationTrait: Sync + Send + 'static {
async fn get_token_from_access_token(
access_token: String,
figment: Figment,
) -> Result<crate::Token, TokenError>;
async fn get_user_from_token(
token: crate::Token,
figment: Figment,
) -> Result<Box<Self>, TokenError>;
}
#[derive(Debug, Clone)]
pub struct RequestData<T: AuthorizationTrait> {
pub identity: T,
pub data: String
}
impl<T: AuthorizationTrait + Clone> RequestData<T> {
pub fn get_identity(&self) -> T {
return self.identity.clone();
}
pub fn get_data<D: rocket::serde::DeserializeOwned>(&self) -> Result<crate::rocket::Json<D>, anyhow::Error> {
match crate::rocket::Json::<D>::from_str(self.data.clone().as_str()) {
Ok(s) => Ok(s),
Err(e) => Err(anyhow::anyhow!(e.to_string()))
}
}
}
use crate::Authorization;
#[crate::rocket::async_trait]
impl<'r, T: AuthorizationTrait> FromData<'r> for RequestData<T> {
type Error = TokenError;
async fn from_data(
req: &'r rocket::request::Request<'_>,
data: rocket::Data<'r>
) -> rocket::data::Outcome<'r, Self> {
let _ = crate::rocket::parse_body::<serde_json::Value>(req, data).await;
let dbs = req.rocket().figment().focus("databases");
let body = req.local_cache(|| return "".to_string());
let header: String = match req.headers().get_one("Authorization") {
Some(h) => h.to_string(),
None => return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
};
let params = match crate::Authorization::extract_params_from_header_string(header) {
Ok(params) => params,
Err(_) => return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
};
match <T>::get_token_from_access_token(params.access_token, dbs.clone()).await {
Ok(token) => {
let date: crate::rocket::DateTime<Utc> = match params.date {
Some(date) => date,
None => {
let date: crate::rocket::DateTime<Utc> = match req.headers().get_one("X-Date") {
Some(h) => {
let date = crate::rocket::DateTime::parse_from_rfc2822(&h.to_string());
date.unwrap().with_timezone(&Utc)
},
None => {
return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken));
}
};
date
}
};
let method = req.method().to_string();
let uri = req.uri().to_string();
let data = body.clone();
match Authorization::from(
method,
uri,
token.clone(),
date,
data.clone().to_owned(),
Some(params.salt),
params.version
) {
Ok(auth) => {
if auth.verify(params.hmac, crate::shared::NCRYPTF_DRIFT_ALLOWANCE) {
match <T>::get_user_from_token(token, dbs).await {
Ok(user) => return Outcome::Success(RequestData { identity: *user, data: data.clone() }),
Err(_) => return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
};
}
},
Err(_) => return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
};
},
Err(_) => return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
};
return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
}
}
#[macro_export]
macro_rules! auth {
($T: ty) => {
use $crate::rocket::TokenError;
use $crate::rocket::AuthorizationTrait;
use $crate::Authorization;
use rocket::request::{self, FromRequest, Outcome};
#[$crate::rocket::async_trait]
impl<'r> $crate::rocket::request::FromRequest<'r> for $T {
type Error = TokenError;
async fn from_request(req: &'r $crate::rocket::request::Request<'_>) -> rocket::request::Outcome<Self, TokenError> {
let dbs = req.rocket().figment().focus("databases");
let body = req.local_cache(|| return "".to_string());
let header: String = match req.headers().get_one("Authorization") {
Some(h) => h.to_string(),
None => return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
};
let params = match $crate::Authorization::extract_params_from_header_string(header) {
Ok(params) => params,
Err(_) => return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
};
match <$T>::get_token_from_access_token(params.access_token, dbs.clone()).await {
Ok(token) => {
let date: $crate::rocket::DateTime<$crate::rocket::Utc> = match params.date {
Some(date) => date,
None => {
let date: $crate::rocket::DateTime<$crate::rocket::Utc> = match req.headers().get_one("X-Date") {
Some(h) => {
let date = $crate::rocket::DateTime::parse_from_rfc2822(&h.to_string());
date.unwrap().with_timezone(&$crate::rocket::Utc)
},
None => {
return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken));
}
};
date
}
};
let method = req.method().to_string();
let uri = req.uri().to_string();
let data = body.to_owned();
match $crate::Authorization::from(
method,
uri,
token.clone(),
date,
data,
Some(params.salt),
params.version
) {
Ok(auth) => {
if auth.verify(params.hmac, $crate::shared::NCRYPTF_DRIFT_ALLOWANCE) {
match <$T>::get_user_from_token(token, dbs).await {
Ok(user) => return rocket::request::Outcome::Success(*user),
Err(_) => return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
};
}
},
Err(_) => return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
};
},
Err(_) => return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
};
return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
}
}
}
}