diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..03acab0 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,69 @@ +binop_separator = "Front" +blank_lines_lower_bound = 0 +blank_lines_upper_bound = 1 +# Trying something new +brace_style = "AlwaysNextLine" +color = "Auto" +combine_control_expr = false +comment_width = 80 +condense_wildcard_suffixes = false +control_brace_style = "AlwaysSameLine" +disable_all_formatting = false +edition = "2018" +emit_mode = "Files" +empty_item_single_line = true +enum_discrim_align_threshold = 0 +error_on_line_overflow = false +error_on_unformatted = false +fn_args_layout = "Tall" +fn_single_line = false +force_explicit_abi = true +force_multiline_blocks = false +format_code_in_doc_comments = false +format_macro_bodies = true +format_macro_matchers = false +format_strings = false +group_imports = "StdExternalCrate" +hard_tabs = false +hide_parse_errors = false +ignore = [] +imports_granularity = "Crate" +imports_indent = "Block" +imports_layout = "Mixed" +indent_style = "Block" +inline_attribute_width = 0 +license_template_path = "" +make_backup = false +match_arm_blocks = true +match_arm_leading_pipes = "Never" +match_block_trailing_comma = false +max_width = 100 +merge_derives = true +newline_style = "Auto" +normalize_comments = false +normalize_doc_attributes = false +overflow_delimited_expr = false +remove_nested_parens = true +reorder_impl_items = false +reorder_imports = true +reorder_modules = true +report_fixme = "Always" +report_todo = "Always" +required_version = "1.4.36" +skip_children = false +space_after_colon = true +space_before_colon = false +spaces_around_ranges = false +struct_field_align_threshold = 0 +struct_lit_single_line = true +tab_spaces = 4 +trailing_comma = "Vertical" +trailing_semicolon = true +type_punctuation_density = "Wide" +unstable_features = false +use_field_init_shorthand = false +use_small_heuristics = "Default" +use_try_shorthand = false +version = "One" +where_single_line = false +wrap_comments = false diff --git a/src/auth.rs b/src/auth.rs index c010674..613c210 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,21 +1,23 @@ -use crate::errors::RBError; -use crate::db::{ - users::{User, NewUser}, - tokens::{RefreshToken, NewRefreshToken} -}; -use crate::schema::refresh_tokens::dsl as refresh_tokens; -use crate::schema::users::dsl as users; use argon2::verify_encoded; use chrono::Utc; -use diesel::prelude::*; -use diesel::{insert_into, PgConnection}; +use diesel::{insert_into, prelude::*, PgConnection}; use hmac::{Hmac, NewMac}; use jwt::SignWithKey; use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; use sha2::Sha256; -pub fn verify_user(conn: &PgConnection, username: &str, password: &str) -> crate::Result { +use crate::{ + db::{ + tokens::{NewRefreshToken, RefreshToken}, + 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)) @@ -35,20 +37,23 @@ pub fn verify_user(conn: &PgConnection, username: &str, password: &str) -> crate #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct JWTResponse { +pub struct JWTResponse +{ token: String, refresh_token: String, } #[derive(Serialize, Deserialize)] -pub struct Claims { +pub struct Claims +{ pub id: uuid::Uuid, pub username: String, pub admin: bool, pub exp: i64, } -pub fn generate_jwt_token(conn: &PgConnection, user: &User) -> crate::Result { +pub fn generate_jwt_token(conn: &PgConnection, user: &User) -> crate::Result +{ let secret = std::env::var("JWT_KEY").map_err(|_| RBError::MissingJWTKey)?; let key: Hmac = Hmac::new_from_slice(secret.as_bytes()).map_err(|_| RBError::JWTCreationError)?; @@ -92,7 +97,8 @@ pub fn generate_jwt_token(conn: &PgConnection, user: &User) -> crate::Result crate::Result { +pub fn hash_password(password: &str) -> crate::Result +{ // Generate a random salt let mut salt = [0u8; 64]; thread_rng().fill(&mut salt[..]); @@ -102,11 +108,9 @@ pub fn hash_password(password: &str) -> crate::Result { argon2::hash_encoded(password.as_bytes(), &salt, &config).map_err(|_| RBError::PWSaltError) } -pub fn create_admin_user( - conn: &PgConnection, - username: &str, - password: &str, -) -> crate::Result { +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(), @@ -125,7 +129,8 @@ pub fn create_admin_user( Ok(true) } -pub fn refresh_token(conn: &PgConnection, refresh_token: &str) -> crate::Result { +pub fn refresh_token(conn: &PgConnection, refresh_token: &str) -> crate::Result +{ let token_bytes = base64::decode(refresh_token).map_err(|_| RBError::InvalidRefreshToken)?; // First, we request the token from the database to see if it's really a valid token diff --git a/src/db/mod.rs b/src/db/mod.rs index f65c924..bf3b714 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,2 +1,2 @@ -pub mod users; pub mod tokens; +pub mod users; diff --git a/src/db/tokens.rs b/src/db/tokens.rs index 25011d8..cfe5904 100644 --- a/src/db/tokens.rs +++ b/src/db/tokens.rs @@ -1,10 +1,11 @@ +use diesel::{Insertable, Queryable}; use uuid::Uuid; -use diesel::{Queryable, Insertable}; + use crate::schema::refresh_tokens; - #[derive(Queryable)] -pub struct RefreshToken { +pub struct RefreshToken +{ pub token: Vec, pub user_id: Uuid, pub expires_at: chrono::NaiveDateTime, @@ -13,7 +14,8 @@ pub struct RefreshToken { #[derive(Insertable)] #[table_name = "refresh_tokens"] -pub struct NewRefreshToken { +pub struct NewRefreshToken +{ pub token: Vec, pub user_id: Uuid, pub expires_at: chrono::NaiveDateTime, diff --git a/src/db/users.rs b/src/db/users.rs index 72fc125..7b5a642 100644 --- a/src/db/users.rs +++ b/src/db/users.rs @@ -1,12 +1,15 @@ -use crate::schema::users; -use diesel::{AsChangeset, Insertable, Queryable, prelude::*}; +use diesel::{prelude::*, AsChangeset, Insertable, Queryable}; use serde::Serialize; use uuid::Uuid; -use crate::schema::users::dsl::*; -use crate::errors::RBError; + +use crate::{ + errors::RBError, + schema::{users, users::dsl::*}, +}; #[derive(Queryable, Serialize)] -pub struct User { +pub struct User +{ pub id: Uuid, pub username: String, #[serde(skip_serializing)] @@ -18,12 +21,14 @@ pub struct User { #[derive(Insertable, AsChangeset)] #[table_name = "users"] -pub struct NewUser { +pub struct NewUser +{ pub username: String, pub password: String, pub admin: bool, } -pub fn all(conn: &PgConnection) -> crate::Result> { +pub fn all(conn: &PgConnection) -> crate::Result> +{ users.load::(conn).map_err(|_| RBError::DBError) } diff --git a/src/errors.rs b/src/errors.rs index 2e1afca..bc3243f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,10 +1,14 @@ -use rocket::http::Status; -use rocket::request::Request; -use rocket::response::{self, Responder, Response}; use std::io; +use rocket::{ + http::Status, + request::Request, + response::{self, Responder, Response}, +}; + #[derive(Debug)] -pub enum RBError { +pub enum RBError +{ /// When the login requests an unknown user UnknownUser, BlockedUser, @@ -26,8 +30,10 @@ pub enum RBError { DBError, } -impl<'r> Responder<'r, 'static> for RBError { - fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { +impl<'r> Responder<'r, 'static> for RBError +{ + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> + { let (status, message): (Status, &str) = match self { RBError::UnknownUser => (Status::NotFound, "Unknown user"), RBError::BlockedUser => (Status::Unauthorized, "This user is blocked"), diff --git a/src/guards.rs b/src/guards.rs index 25d74a5..d475eb4 100644 --- a/src/guards.rs +++ b/src/guards.rs @@ -12,10 +12,12 @@ use sha2::Sha256; pub struct Bearer(String); #[rocket::async_trait] -impl<'r> FromRequest<'r> for Bearer { +impl<'r> FromRequest<'r> for Bearer +{ type Error = rb::errors::RBError; - async fn from_request(req: &'r Request<'_>) -> Outcome { + async fn from_request(req: &'r Request<'_>) -> Outcome + { // If the header isn't present, just forward to the next route let header = match req.headers().get_one("Authorization") { None => return Outcome::Forward(()), @@ -40,10 +42,12 @@ impl<'r> FromRequest<'r> for Bearer { pub struct JWT(Claims); #[rocket::async_trait] -impl<'r> FromRequest<'r> for JWT { +impl<'r> FromRequest<'r> for JWT +{ type Error = rb::errors::RBError; - async fn from_request(req: &'r Request<'_>) -> Outcome { + async fn from_request(req: &'r Request<'_>) -> Outcome + { let bearer = try_outcome!(req.guard::().await).0; // Get secret & key @@ -73,10 +77,12 @@ impl<'r> FromRequest<'r> for JWT { pub struct User(Claims); #[rocket::async_trait] -impl<'r> FromRequest<'r> for User { +impl<'r> FromRequest<'r> for User +{ type Error = rb::errors::RBError; - async fn from_request(req: &'r Request<'_>) -> Outcome { + async fn from_request(req: &'r Request<'_>) -> Outcome + { let claims = try_outcome!(req.guard::().await).0; // Verify key hasn't yet expired @@ -92,10 +98,12 @@ impl<'r> FromRequest<'r> for User { pub struct Admin(Claims); #[rocket::async_trait] -impl<'r> FromRequest<'r> for Admin { +impl<'r> FromRequest<'r> for Admin +{ type Error = rb::errors::RBError; - async fn from_request(req: &'r Request<'_>) -> Outcome { + async fn from_request(req: &'r Request<'_>) -> Outcome + { let user = try_outcome!(req.guard::().await); if user.0.admin { Outcome::Success(Self(user.0)) diff --git a/src/lib.rs b/src/lib.rs index 6d408d8..2c7d7f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ #[macro_use] extern crate diesel; -pub mod db; pub mod auth; +pub mod db; pub mod errors; pub(crate) mod schema; diff --git a/src/main.rs b/src/main.rs index 7bb9081..2ca414d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,8 @@ embed_migrations!(); #[database("postgres_rb")] pub struct RbDbConn(diesel::PgConnection); -async fn run_db_migrations(rocket: Rocket) -> Result, Rocket> { +async fn run_db_migrations(rocket: Rocket) -> Result, Rocket> +{ let conn = RbDbConn::get_one(&rocket) .await .expect("database connection"); @@ -29,7 +30,8 @@ async fn run_db_migrations(rocket: Rocket) -> Result, Rocke .await } -async fn create_admin_user(rocket: Rocket) -> Result, Rocket> { +async fn create_admin_user(rocket: Rocket) -> Result, Rocket> +{ let admin_user = std::env::var("ADMIN_USER").unwrap_or(String::from("admin")); let admin_password = std::env::var("ADMIN_PASSWORD").unwrap_or(String::from("password")); @@ -46,7 +48,8 @@ async fn create_admin_user(rocket: Rocket) -> Result, Rocke } #[launch] -fn rocket() -> _ { +fn rocket() -> _ +{ rocket::build() .attach(RbDbConn::fairing()) .attach(AdHoc::try_on_ignite( diff --git a/src/routes/admin.rs b/src/routes/admin.rs index 84ad6a4..db3ecc2 100644 --- a/src/routes/admin.rs +++ b/src/routes/admin.rs @@ -1,13 +1,15 @@ -use crate::guards::Admin; -use crate::RbDbConn; use rb::db::users::User; use rocket::serde::json::Json; -pub fn routes() -> Vec { +use crate::{guards::Admin, RbDbConn}; + +pub fn routes() -> Vec +{ routes![get_users] } #[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| rb::db::users::all(c)).await?)) } diff --git a/src/routes/auth.rs b/src/routes/auth.rs index 903b52f..9551ace 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -1,26 +1,30 @@ -use crate::guards::User; -use crate::RbDbConn; use rb::auth::{generate_jwt_token, verify_user, JWTResponse}; use rocket::serde::json::Json; use serde::Deserialize; -pub(crate) fn routes() -> Vec { +use crate::{guards::User, RbDbConn}; + +pub(crate) fn routes() -> Vec +{ routes![login, already_logged_in, refresh_token] } #[derive(Deserialize)] -struct Credentials { +struct Credentials +{ username: String, password: String, } #[post("/login")] -async fn already_logged_in(_user: User) -> String { +async fn already_logged_in(_user: User) -> String +{ String::from("You're already logged in!") } #[post("/login", data = "", rank = 2)] -async fn login(conn: RbDbConn, credentials: Json) -> rb::Result> { +async fn login(conn: RbDbConn, credentials: Json) -> rb::Result> +{ let credentials = credentials.into_inner(); // Get the user, if credentials are valid @@ -33,7 +37,8 @@ async fn login(conn: RbDbConn, credentials: Json) -> rb::Result, -) -> rb::Result> { +) -> rb::Result> +{ let refresh_token = refresh_token_request.into_inner().refresh_token; Ok(Json( diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 78b1e23..52815c1 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,2 +1,2 @@ -pub mod auth; pub mod admin; +pub mod auth;