diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a2fdc7d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.vim/ +out/ +vendor/ +target/ diff --git a/.gitignore b/.gitignore index e79787c..ba2dc7a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ out/ # Added by cargo /target + +.vim/ +vendor/ diff --git a/src/admin.rs b/src/admin.rs index 2b82622..084bd9f 100644 --- a/src/admin.rs +++ b/src/admin.rs @@ -1,4 +1,4 @@ -use diesel::{insert_into, prelude::*, PgConnection}; +use diesel::PgConnection; use rocket::serde::json::Json; use uuid::Uuid; @@ -7,7 +7,6 @@ use crate::{ db, errors::{RbError, RbResult}, guards::Admin, - schema::users::dsl as users, RbDbConn, }; @@ -49,12 +48,7 @@ pub fn create_admin_user(conn: &PgConnection, username: &str, password: &str) -> admin: true, }; - insert_into(users::users) - .values(&new_user) - .on_conflict(users::username) - .do_update() - .set(&new_user) - .execute(conn) + db::users::create_or_update(conn, &new_user) .map_err(|_| RbError::Custom("Couldn't create admin."))?; Ok(true) diff --git a/src/auth/jwt.rs b/src/auth/jwt.rs index 89ce734..0f7d37e 100644 --- a/src/auth/jwt.rs +++ b/src/auth/jwt.rs @@ -1,5 +1,5 @@ use chrono::Utc; -use diesel::{prelude::*, PgConnection}; +use diesel::PgConnection; use hmac::{Hmac, NewMac}; use jwt::SignWithKey; use rand::{thread_rng, Rng}; @@ -8,9 +8,7 @@ use sha2::Sha256; use crate::{ db, - db::{tokens::NewRefreshToken, users::User}, errors::{RbError, RbResult}, - schema::{refresh_tokens::dsl as refresh_tokens, users::dsl as users}, RbJwtConf, }; @@ -34,7 +32,7 @@ pub struct Claims pub fn generate_jwt_token( conn: &PgConnection, jwt: &RbJwtConf, - user: &User, + user: &db::User, ) -> RbResult { let key: Hmac = Hmac::new_from_slice(jwt.key.as_bytes()) @@ -65,7 +63,7 @@ pub fn generate_jwt_token( // Store refresh token in database db::tokens::create( conn, - &NewRefreshToken { + &db::NewRefreshToken { token: refresh_token.to_vec(), user_id: user.id, expires_at: refresh_expire, @@ -93,11 +91,10 @@ pub fn refresh_token( // If we see that the token has already been used before, we block the user. if token_entry.last_used_at.is_some() { - let target = users::users.filter(users::id.eq(token_entry.user_id)); - diesel::update(target) - .set(users::blocked.eq(true)) - .execute(conn) - .map_err(|_| RbError::Custom("Couldn't block user."))?; + // If we fail to block the user, the end user must know + if let Err(err) = db::users::block(conn, token_entry.user_id) { + return Err(err); + } return Err(RbError::AuthDuplicateRefreshToken); } @@ -110,11 +107,7 @@ pub fn refresh_token( } // We update the last_used_at value for the refresh token - let target = refresh_tokens::refresh_tokens.filter(refresh_tokens::token.eq(token_entry.token)); - diesel::update(target) - .set(refresh_tokens::last_used_at.eq(cur_time)) - .execute(conn) - .map_err(|_| RbError::Custom("Couldn't update last used time."))?; + db::tokens::update_last_used_at(conn, &token_entry.token, cur_time)?; generate_jwt_token(conn, jwt, &user) } diff --git a/src/auth/mod.rs b/src/auth/mod.rs index 78a4fa0..4b3207f 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -5,7 +5,7 @@ use self::{ jwt::{generate_jwt_token, JWTResponse}, pass::verify_user, }; -use crate::{errors::RbResult, guards::User, RbConfig, RbDbConn, RbJwtConf}; +use crate::{errors::RbResult, guards::User, RbConfig, RbDbConn}; pub mod jwt; pub mod pass; diff --git a/src/auth/pass.rs b/src/auth/pass.rs index 1c0f04e..583908d 100644 --- a/src/auth/pass.rs +++ b/src/auth/pass.rs @@ -1,20 +1,16 @@ use argon2::verify_encoded; -use diesel::{prelude::*, PgConnection}; +use diesel::PgConnection; use rand::{thread_rng, Rng}; use crate::{ - db::users::User, + db, errors::{RbError, RbResult}, - schema::users::dsl as users, }; -pub fn verify_user(conn: &PgConnection, username: &str, password: &str) -> RbResult +pub fn verify_user(conn: &PgConnection, username: &str, password: &str) -> RbResult { // TODO handle non-"NotFound" Diesel errors accordingely - let user = users::users - .filter(users::username.eq(username)) - .first::(conn) - .map_err(|_| RbError::AuthUnknownUser)?; + let user = db::users::find_by_username(conn, username).map_err(|_| RbError::AuthUnknownUser)?; // Check if a user is blocked if user.blocked { diff --git a/src/db/tokens.rs b/src/db/tokens.rs index 7ca6b1d..8940721 100644 --- a/src/db/tokens.rs +++ b/src/db/tokens.rs @@ -56,3 +56,17 @@ pub fn find_with_user( .map_err(|_| RbError::Custom("Couldn't get refresh token & user.")) .ok() } + +pub fn update_last_used_at( + conn: &PgConnection, + token_: &[u8], + last_used_at_: chrono::NaiveDateTime, +) -> RbResult<()> +{ + diesel::update(refresh_tokens.filter(token.eq(token_))) + .set(last_used_at.eq(last_used_at_)) + .execute(conn) + .map_err(|_| RbError::DbError("Couldn't update last_used_at."))?; + + Ok(()) +} diff --git a/src/db/users.rs b/src/db/users.rs index d6ae131..efc74db 100644 --- a/src/db/users.rs +++ b/src/db/users.rs @@ -14,7 +14,6 @@ pub struct User pub username: String, #[serde(skip_serializing)] pub password: String, - #[serde(skip_serializing)] pub blocked: bool, pub admin: bool, } @@ -40,6 +39,14 @@ pub fn find(conn: &PgConnection, user_id: Uuid) -> Option users.find(user_id).first::(conn).ok() } +pub fn find_by_username(conn: &PgConnection, username_: &str) -> RbResult +{ + Ok(users + .filter(username.eq(username_)) + .first::(conn) + .map_err(|_| RbError::DbError("Couldn't find users by username."))?) +} + pub fn create(conn: &PgConnection, new_user: &NewUser) -> RbResult<()> { let count = diesel::insert_into(users) @@ -54,6 +61,19 @@ pub fn create(conn: &PgConnection, new_user: &NewUser) -> RbResult<()> Ok(()) } +pub fn create_or_update(conn: &PgConnection, new_user: &NewUser) -> RbResult<()> +{ + diesel::insert_into(users) + .values(new_user) + .on_conflict(username) + .do_update() + .set(new_user) + .execute(conn) + .map_err(|_| RbError::DbError("Couldn't create or update user."))?; + + Ok(()) +} + pub fn delete(conn: &PgConnection, user_id: Uuid) -> RbResult<()> { diesel::delete(users.filter(id.eq(user_id))) @@ -62,3 +82,13 @@ pub fn delete(conn: &PgConnection, user_id: Uuid) -> RbResult<()> Ok(()) } + +pub fn block(conn: &PgConnection, user_id: Uuid) -> RbResult<()> +{ + diesel::update(users.filter(id.eq(user_id))) + .set(blocked.eq(true)) + .execute(conn) + .map_err(|_| RbError::DbError("Couldn't block user."))?; + + Ok(()) +}