diff --git a/src/auth/jwt.rs b/src/auth.rs similarity index 70% rename from src/auth/jwt.rs rename to src/auth.rs index 8767bb3..cb4e4b5 100644 --- a/src/auth/jwt.rs +++ b/src/auth.rs @@ -1,3 +1,4 @@ +use argon2::verify_encoded; use chrono::Utc; use diesel::{insert_into, prelude::*, PgConnection}; use hmac::{Hmac, NewMac}; @@ -9,12 +10,31 @@ use sha2::Sha256; use crate::{ db::{ tokens::{NewRefreshToken, RefreshToken}, - users::User, + users::{NewUser, User}, }, errors::RbError, 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 @@ -76,6 +96,39 @@ 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 deleted file mode 100644 index b315e82..0000000 --- a/src/auth/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -use argon2::verify_encoded; -use diesel::{insert_into, prelude::*, PgConnection}; -use rand::{thread_rng, Rng}; - -use crate::{ - db::users::{NewUser, User}, - errors::RbError, - schema::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 daca6bb..886b6fe 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -79,7 +79,6 @@ 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 55df193..ae6782b 100644 --- a/src/guards.rs +++ b/src/guards.rs @@ -1,6 +1,6 @@ use hmac::{Hmac, NewMac}; use jwt::VerifyWithKey; -use rb::auth::jwt::Claims; +use rb::auth::Claims; use rocket::{ http::Status, outcome::try_outcome, @@ -39,10 +39,10 @@ impl<'r> FromRequest<'r> for Bearer<'r> } /// Verifies the provided JWT is valid. -pub struct Jwt(Claims); +pub struct JWT(Claims); #[rocket::async_trait] -impl<'r> FromRequest<'r> for Jwt +impl<'r> FromRequest<'r> for JWT { type Error = rb::errors::RbError; @@ -91,7 +91,7 @@ impl<'r> FromRequest<'r> for User async fn from_request(req: &'r Request<'_>) -> Outcome { - let claims = try_outcome!(req.guard::().await).0; + let claims = try_outcome!(req.guard::().await).0; // Verify key hasn't yet expired if chrono::Utc::now().timestamp() > claims.exp { diff --git a/src/routes/admin.rs b/src/routes/admin.rs index 9da75f5..bd6f53f 100644 --- a/src/routes/admin.rs +++ b/src/routes/admin.rs @@ -10,13 +10,13 @@ pub fn routes() -> Vec } #[get("/users")] -async fn get_users(_admin: Admin, conn: RbDbConn) -> rb::Result>> +async fn get_users(admin: Admin, conn: RbDbConn) -> rb::Result>> { Ok(Json(conn.run(|c| db::users::all(c)).await?)) } #[post("/users", data = "")] -async fn create_user(_admin: Admin, conn: RbDbConn, user: Json) -> rb::Result<()> +async fn create_user(admin: Admin, conn: RbDbConn, user: Json) -> rb::Result<()> { Ok(conn .run(move |c| db::users::create(c, &user.into_inner())) diff --git a/src/routes/auth.rs b/src/routes/auth.rs index 955cfaa..9551ace 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -1,7 +1,4 @@ -use rb::auth::{ - jwt::{generate_jwt_token, JWTResponse}, - verify_user, -}; +use rb::auth::{generate_jwt_token, verify_user, JWTResponse}; use rocket::serde::json::Json; use serde::Deserialize; @@ -54,7 +51,7 @@ async fn refresh_token( let refresh_token = refresh_token_request.into_inner().refresh_token; Ok(Json( - conn.run(move |c| rb::auth::jwt::refresh_token(c, &refresh_token)) + conn.run(move |c| rb::auth::refresh_token(c, &refresh_token)) .await?, )) }