From 6858e9da62bdb6c22be61b0823651c8f8b80d4b5 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 29 Aug 2021 19:04:06 +0200 Subject: [PATCH] Restructured auth --- src/{auth.rs => auth/jwt.rs} | 53 --------------------------- src/auth/mod.rs | 71 ++++++++++++++++++++++++++++++++++++ src/errors.rs | 1 + src/guards.rs | 2 +- src/routes/auth.rs | 7 +++- 5 files changed, 78 insertions(+), 56 deletions(-) rename src/{auth.rs => auth/jwt.rs} (70%) create mode 100644 src/auth/mod.rs diff --git a/src/auth.rs b/src/auth/jwt.rs similarity index 70% rename from src/auth.rs rename to src/auth/jwt.rs index cb4e4b5..5c15d57 100644 --- a/src/auth.rs +++ b/src/auth/jwt.rs @@ -1,4 +1,3 @@ -use argon2::verify_encoded; use chrono::Utc; use diesel::{insert_into, prelude::*, PgConnection}; use hmac::{Hmac, NewMac}; @@ -16,25 +15,6 @@ use crate::{ schema::{refresh_tokens::dsl as refresh_tokens, users::dsl as users}, }; -pub fn verify_user(conn: &PgConnection, username: &str, password: &str) -> crate::Result -{ - // TODO handle non-"NotFound" Diesel errors accordingely - let user = users::users - .filter(users::username.eq(username)) - .first::(conn) - .map_err(|_| RbError::AuthUnknownUser)?; - - // Check if a user is blocked - if user.blocked { - return Err(RbError::AuthBlockedUser); - } - - match verify_encoded(user.password.as_str(), password.as_bytes()) { - Ok(true) => Ok(user), - _ => Err(RbError::AuthInvalidPassword), - } -} - #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct JWTResponse @@ -96,39 +76,6 @@ pub fn generate_jwt_token(conn: &PgConnection, user: &User) -> crate::Result crate::Result -{ - // Generate a random salt - let mut salt = [0u8; 64]; - thread_rng().fill(&mut salt[..]); - - // Encode the actual password - let config = argon2::Config::default(); - argon2::hash_encoded(password.as_bytes(), &salt, &config) - .map_err(|_| RbError::Custom("Couldn't hash password.")) -} - -pub fn create_admin_user(conn: &PgConnection, username: &str, password: &str) - -> crate::Result -{ - let pass_hashed = hash_password(password)?; - let new_user = NewUser { - username: username.to_string(), - password: pass_hashed, - admin: true, - }; - - insert_into(users::users) - .values(&new_user) - .on_conflict(users::username) - .do_update() - .set(&new_user) - .execute(conn) - .map_err(|_| RbError::Custom("Couldn't create admin."))?; - - Ok(true) -} - pub fn refresh_token(conn: &PgConnection, refresh_token: &str) -> crate::Result { let token_bytes = diff --git a/src/auth/mod.rs b/src/auth/mod.rs new file mode 100644 index 0000000..838a080 --- /dev/null +++ b/src/auth/mod.rs @@ -0,0 +1,71 @@ +use ::jwt::SignWithKey; +use argon2::verify_encoded; +use chrono::Utc; +use diesel::{insert_into, prelude::*, PgConnection}; +use hmac::{Hmac, NewMac}; +use rand::{thread_rng, Rng}; +use serde::{Deserialize, Serialize}; +use sha2::Sha256; + +use crate::{ + db::{ + tokens::{NewRefreshToken, RefreshToken}, + users::{NewUser, User}, + }, + errors::RbError, + schema::{refresh_tokens::dsl as refresh_tokens, users::dsl as users}, +}; + +pub mod jwt; + +pub fn verify_user(conn: &PgConnection, username: &str, password: &str) -> crate::Result +{ + // TODO handle non-"NotFound" Diesel errors accordingely + let user = users::users + .filter(users::username.eq(username)) + .first::(conn) + .map_err(|_| RbError::AuthUnknownUser)?; + + // Check if a user is blocked + if user.blocked { + return Err(RbError::AuthBlockedUser); + } + + match verify_encoded(user.password.as_str(), password.as_bytes()) { + Ok(true) => Ok(user), + _ => Err(RbError::AuthInvalidPassword), + } +} + +pub fn hash_password(password: &str) -> crate::Result +{ + // Generate a random salt + let mut salt = [0u8; 64]; + thread_rng().fill(&mut salt[..]); + + // Encode the actual password + let config = argon2::Config::default(); + argon2::hash_encoded(password.as_bytes(), &salt, &config) + .map_err(|_| RbError::Custom("Couldn't hash password.")) +} + +pub fn create_admin_user(conn: &PgConnection, username: &str, password: &str) + -> crate::Result +{ + let pass_hashed = hash_password(password)?; + let new_user = NewUser { + username: username.to_string(), + password: pass_hashed, + admin: true, + }; + + insert_into(users::users) + .values(&new_user) + .on_conflict(users::username) + .do_update() + .set(&new_user) + .execute(conn) + .map_err(|_| RbError::Custom("Couldn't create admin."))?; + + Ok(true) +} diff --git a/src/errors.rs b/src/errors.rs index 886b6fe..daca6bb 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -79,6 +79,7 @@ impl<'r> Responder<'r, 'static> for RbError "message": self.message(), }); + // TODO add status to response content.respond_to(req) } } diff --git a/src/guards.rs b/src/guards.rs index ae6782b..26cda21 100644 --- a/src/guards.rs +++ b/src/guards.rs @@ -1,6 +1,6 @@ use hmac::{Hmac, NewMac}; use jwt::VerifyWithKey; -use rb::auth::Claims; +use rb::auth::jwt::Claims; use rocket::{ http::Status, outcome::try_outcome, diff --git a/src/routes/auth.rs b/src/routes/auth.rs index 9551ace..955cfaa 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -1,4 +1,7 @@ -use rb::auth::{generate_jwt_token, verify_user, JWTResponse}; +use rb::auth::{ + jwt::{generate_jwt_token, JWTResponse}, + verify_user, +}; use rocket::serde::json::Json; use serde::Deserialize; @@ -51,7 +54,7 @@ async fn refresh_token( let refresh_token = refresh_token_request.into_inner().refresh_token; Ok(Json( - conn.run(move |c| rb::auth::refresh_token(c, &refresh_token)) + conn.run(move |c| rb::auth::jwt::refresh_token(c, &refresh_token)) .await?, )) }