Project compiles now

pull/2/head
Jef Roosens 2021-11-23 12:18:14 +01:00
parent 4c07e18787
commit 7afb44bfa9
Signed by: Jef Roosens
GPG Key ID: 955C0660072F691F
4 changed files with 78 additions and 99 deletions

View File

@ -1,19 +1,12 @@
use chrono::Utc;
use hmac::{Hmac, NewMac};
use jwt::SignWithKey;
use rand::{thread_rng, Rng};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::Sha256;
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct JwtConf { pub struct JwtConf {
key: String, pub key: String,
refresh_token_size: usize, refresh_token_size: usize,
refresh_token_expire: i64, refresh_token_expire: i64,
} }
use crate::errors::{RbError, RbResult};
#[derive(Serialize)] #[derive(Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct JWTResponse { pub struct JWTResponse {
@ -29,79 +22,79 @@ pub struct Claims {
pub exp: i64, pub exp: i64,
} }
pub fn generate_jwt_token( // pub fn generate_jwt_token(
jwt: &JwtConf, // jwt: &JwtConf,
id: uuid::Uuid, // id: uuid::Uuid,
username: String, // username: String,
is_admin: bool, // is_admin: bool,
) -> RbResult<JWTResponse> { // ) -> RbResult<JWTResponse> {
let key: Hmac<Sha256> = Hmac::new_from_slice(jwt.key.as_bytes()) // let key: Hmac<Sha256> = Hmac::new_from_slice(jwt.key.as_bytes())
.map_err(|_| RbError::Custom("Couldn't create Hmac key."))?; // .map_err(|_| RbError::Custom("Couldn't create Hmac key."))?;
let current_time = Utc::now(); // let current_time = Utc::now();
// Create the claims // // Create the claims
let claims = Claims { // let claims = Claims {
id, // id,
username: username.clone(), // username: username.clone(),
admin: is_admin, // admin: is_admin,
exp: current_time.timestamp() + jwt.refresh_token_expire, // exp: current_time.timestamp() + jwt.refresh_token_expire,
}; // };
// Sign the claims into a new token // // Sign the claims into a new token
let token = claims // let token = claims
.sign_with_key(&key) // .sign_with_key(&key)
.map_err(|_| RbError::Custom("Couldn't sign JWT."))?; // .map_err(|_| RbError::Custom("Couldn't sign JWT."))?;
// Generate a random refresh token // // Generate a random refresh token
let mut refresh_token = vec![0u8; jwt.refresh_token_size]; // let mut refresh_token = vec![0u8; jwt.refresh_token_size];
thread_rng().fill(&mut refresh_token[..]); // thread_rng().fill(&mut refresh_token[..]);
let refresh_expire = // let refresh_expire =
(current_time + chrono::Duration::seconds(jwt.refresh_token_expire)).naive_utc(); // (current_time + chrono::Duration::seconds(jwt.refresh_token_expire)).naive_utc();
Ok(JWTResponse { // Ok(JWTResponse {
token, // token,
refresh_token: base64::encode(refresh_token), // refresh_token: base64::encode(refresh_token),
}) // })
} // }
pub fn refresh_token( // pub fn refresh_token(
conn: &PgConnection, // conn: &PgConnection,
jwt: &JwtConf, // jwt: &JwtConf,
refresh_token: &str, // refresh_token: &str,
) -> RbResult<JWTResponse> { // ) -> RbResult<JWTResponse> {
let token_bytes = // let token_bytes =
base64::decode(refresh_token).map_err(|_| RbError::AuthInvalidRefreshToken)?; // base64::decode(refresh_token).map_err(|_| RbError::AuthInvalidRefreshToken)?;
// First, we request the token from the database to see if it's really a valid token // // First, we request the token from the database to see if it's really a valid token
let (token_entry, user) = // let (token_entry, user) =
db::tokens::find_with_user(conn, &token_bytes).ok_or(RbError::AuthInvalidRefreshToken)?; // db::tokens::find_with_user(conn, &token_bytes).ok_or(RbError::AuthInvalidRefreshToken)?;
// If we see that the token has already been used before, we block the user. // // If we see that the token has already been used before, we block the user.
if token_entry.last_used_at.is_some() { // if token_entry.last_used_at.is_some() {
// If we fail to block the user, the end user must know // // If we fail to block the user, the end user must know
if let Err(err) = db::users::block(conn, token_entry.user_id) { // if let Err(err) = db::users::block(conn, token_entry.user_id) {
return Err(err); // return Err(err);
} // }
return Err(RbError::AuthDuplicateRefreshToken); // return Err(RbError::AuthDuplicateRefreshToken);
} // }
// Then we check if the user is blocked // // Then we check if the user is blocked
if user.blocked { // if user.blocked {
return Err(RbError::AuthBlockedUser); // return Err(RbError::AuthBlockedUser);
} // }
// Now we check if the token has already expired // // Now we check if the token has already expired
let cur_time = Utc::now().naive_utc(); // let cur_time = Utc::now().naive_utc();
if token_entry.expires_at < cur_time { // if token_entry.expires_at < cur_time {
return Err(RbError::AuthTokenExpired); // return Err(RbError::AuthTokenExpired);
} // }
// We update the last_used_at value for the refresh token // // We update the last_used_at value for the refresh token
db::tokens::update_last_used_at(conn, &token_entry.token, cur_time)?; // db::tokens::update_last_used_at(conn, &token_entry.token, cur_time)?;
generate_jwt_token(conn, jwt, &user) // generate_jwt_token(conn, jwt, &user)
} // }

View File

@ -1,8 +0,0 @@
use serde::{Serialize, Deserialize}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct RbJwtConf {
key: String,
refresh_token_size: usize,
refresh_token_expire: i64,
}

View File

@ -8,18 +8,19 @@ use rocket::{
}; };
use sha2::Sha256; use sha2::Sha256;
use crate::{auth::jwt::Claims, errors::RbError, RbConfig}; use crate::{
auth::jwt::{Claims, JwtConf},
errors::RbError,
};
/// Extracts an "Authorization: Bearer" string from the headers. /// Extracts an "Authorization: Bearer" string from the headers.
pub struct Bearer<'a>(&'a str); pub struct Bearer<'a>(&'a str);
#[rocket::async_trait] #[rocket::async_trait]
impl<'r> FromRequest<'r> for Bearer<'r> impl<'r> FromRequest<'r> for Bearer<'r> {
{
type Error = crate::errors::RbError; type Error = crate::errors::RbError;
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
{
// If the header isn't present, just forward to the next route // If the header isn't present, just forward to the next route
let header = match req.headers().get_one("Authorization") { let header = match req.headers().get_one("Authorization") {
None => return Outcome::Forward(()), None => return Outcome::Forward(()),
@ -41,26 +42,24 @@ impl<'r> FromRequest<'r> for Bearer<'r>
pub struct Jwt(Claims); pub struct Jwt(Claims);
#[rocket::async_trait] #[rocket::async_trait]
impl<'r> FromRequest<'r> for Jwt impl<'r> FromRequest<'r> for Jwt {
{
type Error = RbError; type Error = RbError;
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
{
let bearer = try_outcome!(req.guard::<Bearer>().await).0; let bearer = try_outcome!(req.guard::<Bearer>().await).0;
let config = try_outcome!(req.guard::<&State<RbConfig>>().await.map_failure(|_| ( let config = try_outcome!(req.guard::<&State<JwtConf>>().await.map_failure(|_| (
Status::InternalServerError, Status::InternalServerError,
RbError::Custom("Couldn't get config guard.") RbError::Custom("Couldn't get config guard.")
))); )));
let key: Hmac<Sha256> = match Hmac::new_from_slice(&config.jwt.key.as_bytes()) { let key: Hmac<Sha256> = match Hmac::new_from_slice(&config.key.as_bytes()) {
Ok(key) => key, Ok(key) => key,
Err(_) => { Err(_) => {
return Outcome::Failure(( return Outcome::Failure((
Status::InternalServerError, Status::InternalServerError,
Self::Error::Custom("Failed to do Hmac thing."), Self::Error::Custom("Failed to do Hmac thing."),
)) ))
}, }
}; };
// Verify token using key // Verify token using key
@ -68,7 +67,7 @@ impl<'r> FromRequest<'r> for Jwt
Ok(claims) => Outcome::Success(Self(claims)), Ok(claims) => Outcome::Success(Self(claims)),
Err(_) => { Err(_) => {
return Outcome::Failure((Status::Unauthorized, Self::Error::AuthUnauthorized)) return Outcome::Failure((Status::Unauthorized, Self::Error::AuthUnauthorized))
}, }
} }
} }
} }
@ -77,12 +76,10 @@ impl<'r> FromRequest<'r> for Jwt
pub struct User(Claims); pub struct User(Claims);
#[rocket::async_trait] #[rocket::async_trait]
impl<'r> FromRequest<'r> for User impl<'r> FromRequest<'r> for User {
{
type Error = crate::errors::RbError; type Error = crate::errors::RbError;
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
{
let claims = try_outcome!(req.guard::<Jwt>().await).0; let claims = try_outcome!(req.guard::<Jwt>().await).0;
// Verify key hasn't yet expired // Verify key hasn't yet expired
@ -98,12 +95,10 @@ impl<'r> FromRequest<'r> for User
pub struct Admin(Claims); pub struct Admin(Claims);
#[rocket::async_trait] #[rocket::async_trait]
impl<'r> FromRequest<'r> for Admin impl<'r> FromRequest<'r> for Admin {
{
type Error = crate::errors::RbError; type Error = crate::errors::RbError;
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
{
let user = try_outcome!(req.guard::<User>().await).0; let user = try_outcome!(req.guard::<User>().await).0;
if user.admin { if user.admin {

View File

@ -1,4 +1,3 @@
pub mod auth; pub mod auth;
pub mod config;
pub mod errors; pub mod errors;
pub mod guards; pub mod guards;