Configured Rustfmt

develop
Jef Roosens 2021-08-22 16:45:01 +02:00
parent b13b760e2f
commit 16ddc9aecd
Signed by untrusted user: Jef Roosens
GPG Key ID: 955C0660072F691F
12 changed files with 169 additions and 63 deletions

69
rustfmt.toml 100644
View File

@ -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

View File

@ -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 argon2::verify_encoded;
use chrono::Utc; use chrono::Utc;
use diesel::prelude::*; use diesel::{insert_into, prelude::*, PgConnection};
use diesel::{insert_into, PgConnection};
use hmac::{Hmac, NewMac}; use hmac::{Hmac, NewMac};
use jwt::SignWithKey; use jwt::SignWithKey;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::Sha256; use sha2::Sha256;
pub fn verify_user(conn: &PgConnection, username: &str, password: &str) -> crate::Result<User> { 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<User>
{
// TODO handle non-"NotFound" Diesel errors accordingely // TODO handle non-"NotFound" Diesel errors accordingely
let user = users::users let user = users::users
.filter(users::username.eq(username)) .filter(users::username.eq(username))
@ -35,20 +37,23 @@ pub fn verify_user(conn: &PgConnection, username: &str, password: &str) -> crate
#[derive(Serialize)] #[derive(Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct JWTResponse { pub struct JWTResponse
{
token: String, token: String,
refresh_token: String, refresh_token: String,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Claims { pub struct Claims
{
pub id: uuid::Uuid, pub id: uuid::Uuid,
pub username: String, pub username: String,
pub admin: bool, pub admin: bool,
pub exp: i64, pub exp: i64,
} }
pub fn generate_jwt_token(conn: &PgConnection, user: &User) -> crate::Result<JWTResponse> { pub fn generate_jwt_token(conn: &PgConnection, user: &User) -> crate::Result<JWTResponse>
{
let secret = std::env::var("JWT_KEY").map_err(|_| RBError::MissingJWTKey)?; let secret = std::env::var("JWT_KEY").map_err(|_| RBError::MissingJWTKey)?;
let key: Hmac<Sha256> = let key: Hmac<Sha256> =
Hmac::new_from_slice(secret.as_bytes()).map_err(|_| RBError::JWTCreationError)?; 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<JWT
}) })
} }
pub fn hash_password(password: &str) -> crate::Result<String> { pub fn hash_password(password: &str) -> crate::Result<String>
{
// Generate a random salt // Generate a random salt
let mut salt = [0u8; 64]; let mut salt = [0u8; 64];
thread_rng().fill(&mut salt[..]); thread_rng().fill(&mut salt[..]);
@ -102,11 +108,9 @@ pub fn hash_password(password: &str) -> crate::Result<String> {
argon2::hash_encoded(password.as_bytes(), &salt, &config).map_err(|_| RBError::PWSaltError) argon2::hash_encoded(password.as_bytes(), &salt, &config).map_err(|_| RBError::PWSaltError)
} }
pub fn create_admin_user( pub fn create_admin_user(conn: &PgConnection, username: &str, password: &str)
conn: &PgConnection, -> crate::Result<bool>
username: &str, {
password: &str,
) -> crate::Result<bool> {
let pass_hashed = hash_password(password)?; let pass_hashed = hash_password(password)?;
let new_user = NewUser { let new_user = NewUser {
username: username.to_string(), username: username.to_string(),
@ -125,7 +129,8 @@ pub fn create_admin_user(
Ok(true) Ok(true)
} }
pub fn refresh_token(conn: &PgConnection, refresh_token: &str) -> crate::Result<JWTResponse> { pub fn refresh_token(conn: &PgConnection, refresh_token: &str) -> crate::Result<JWTResponse>
{
let token_bytes = base64::decode(refresh_token).map_err(|_| RBError::InvalidRefreshToken)?; 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 // First, we request the token from the database to see if it's really a valid token

View File

@ -1,2 +1,2 @@
pub mod users;
pub mod tokens; pub mod tokens;
pub mod users;

View File

@ -1,10 +1,11 @@
use diesel::{Insertable, Queryable};
use uuid::Uuid; use uuid::Uuid;
use diesel::{Queryable, Insertable};
use crate::schema::refresh_tokens; use crate::schema::refresh_tokens;
#[derive(Queryable)] #[derive(Queryable)]
pub struct RefreshToken { pub struct RefreshToken
{
pub token: Vec<u8>, pub token: Vec<u8>,
pub user_id: Uuid, pub user_id: Uuid,
pub expires_at: chrono::NaiveDateTime, pub expires_at: chrono::NaiveDateTime,
@ -13,7 +14,8 @@ pub struct RefreshToken {
#[derive(Insertable)] #[derive(Insertable)]
#[table_name = "refresh_tokens"] #[table_name = "refresh_tokens"]
pub struct NewRefreshToken { pub struct NewRefreshToken
{
pub token: Vec<u8>, pub token: Vec<u8>,
pub user_id: Uuid, pub user_id: Uuid,
pub expires_at: chrono::NaiveDateTime, pub expires_at: chrono::NaiveDateTime,

View File

@ -1,12 +1,15 @@
use crate::schema::users; use diesel::{prelude::*, AsChangeset, Insertable, Queryable};
use diesel::{AsChangeset, Insertable, Queryable, prelude::*};
use serde::Serialize; use serde::Serialize;
use uuid::Uuid; use uuid::Uuid;
use crate::schema::users::dsl::*;
use crate::errors::RBError; use crate::{
errors::RBError,
schema::{users, users::dsl::*},
};
#[derive(Queryable, Serialize)] #[derive(Queryable, Serialize)]
pub struct User { pub struct User
{
pub id: Uuid, pub id: Uuid,
pub username: String, pub username: String,
#[serde(skip_serializing)] #[serde(skip_serializing)]
@ -18,12 +21,14 @@ pub struct User {
#[derive(Insertable, AsChangeset)] #[derive(Insertable, AsChangeset)]
#[table_name = "users"] #[table_name = "users"]
pub struct NewUser { pub struct NewUser
{
pub username: String, pub username: String,
pub password: String, pub password: String,
pub admin: bool, pub admin: bool,
} }
pub fn all(conn: &PgConnection) -> crate::Result<Vec<User>> { pub fn all(conn: &PgConnection) -> crate::Result<Vec<User>>
{
users.load::<User>(conn).map_err(|_| RBError::DBError) users.load::<User>(conn).map_err(|_| RBError::DBError)
} }

View File

@ -1,10 +1,14 @@
use rocket::http::Status;
use rocket::request::Request;
use rocket::response::{self, Responder, Response};
use std::io; use std::io;
use rocket::{
http::Status,
request::Request,
response::{self, Responder, Response},
};
#[derive(Debug)] #[derive(Debug)]
pub enum RBError { pub enum RBError
{
/// When the login requests an unknown user /// When the login requests an unknown user
UnknownUser, UnknownUser,
BlockedUser, BlockedUser,
@ -26,8 +30,10 @@ pub enum RBError {
DBError, DBError,
} }
impl<'r> Responder<'r, 'static> for RBError { impl<'r> Responder<'r, 'static> for RBError
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { {
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static>
{
let (status, message): (Status, &str) = match self { let (status, message): (Status, &str) = match self {
RBError::UnknownUser => (Status::NotFound, "Unknown user"), RBError::UnknownUser => (Status::NotFound, "Unknown user"),
RBError::BlockedUser => (Status::Unauthorized, "This user is blocked"), RBError::BlockedUser => (Status::Unauthorized, "This user is blocked"),

View File

@ -12,10 +12,12 @@ use sha2::Sha256;
pub struct Bearer(String); pub struct Bearer(String);
#[rocket::async_trait] #[rocket::async_trait]
impl<'r> FromRequest<'r> for Bearer { impl<'r> FromRequest<'r> for Bearer
{
type Error = rb::errors::RBError; type Error = rb::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(()),
@ -40,10 +42,12 @@ impl<'r> FromRequest<'r> for Bearer {
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 = rb::errors::RBError; type Error = rb::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 bearer = try_outcome!(req.guard::<Bearer>().await).0; let bearer = try_outcome!(req.guard::<Bearer>().await).0;
// Get secret & key // Get secret & key
@ -73,10 +77,12 @@ 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 = rb::errors::RBError; type Error = rb::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
@ -92,10 +98,12 @@ 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 = rb::errors::RBError; type Error = rb::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); let user = try_outcome!(req.guard::<User>().await);
if user.0.admin { if user.0.admin {
Outcome::Success(Self(user.0)) Outcome::Success(Self(user.0))

View File

@ -1,8 +1,8 @@
#[macro_use] #[macro_use]
extern crate diesel; extern crate diesel;
pub mod db;
pub mod auth; pub mod auth;
pub mod db;
pub mod errors; pub mod errors;
pub(crate) mod schema; pub(crate) mod schema;

View File

@ -18,7 +18,8 @@ embed_migrations!();
#[database("postgres_rb")] #[database("postgres_rb")]
pub struct RbDbConn(diesel::PgConnection); pub struct RbDbConn(diesel::PgConnection);
async fn run_db_migrations(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocket<Build>> { async fn run_db_migrations(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocket<Build>>
{
let conn = RbDbConn::get_one(&rocket) let conn = RbDbConn::get_one(&rocket)
.await .await
.expect("database connection"); .expect("database connection");
@ -29,7 +30,8 @@ async fn run_db_migrations(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocke
.await .await
} }
async fn create_admin_user(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocket<Build>> { async fn create_admin_user(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocket<Build>>
{
let admin_user = std::env::var("ADMIN_USER").unwrap_or(String::from("admin")); 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")); let admin_password = std::env::var("ADMIN_PASSWORD").unwrap_or(String::from("password"));
@ -46,7 +48,8 @@ async fn create_admin_user(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocke
} }
#[launch] #[launch]
fn rocket() -> _ { fn rocket() -> _
{
rocket::build() rocket::build()
.attach(RbDbConn::fairing()) .attach(RbDbConn::fairing())
.attach(AdHoc::try_on_ignite( .attach(AdHoc::try_on_ignite(

View File

@ -1,13 +1,15 @@
use crate::guards::Admin;
use crate::RbDbConn;
use rb::db::users::User; use rb::db::users::User;
use rocket::serde::json::Json; use rocket::serde::json::Json;
pub fn routes() -> Vec<rocket::Route> { use crate::{guards::Admin, RbDbConn};
pub fn routes() -> Vec<rocket::Route>
{
routes![get_users] routes![get_users]
} }
#[get("/users")] #[get("/users")]
async fn get_users(admin: Admin, conn: RbDbConn) -> rb::Result<Json<Vec<User>>> { async fn get_users(admin: Admin, conn: RbDbConn) -> rb::Result<Json<Vec<User>>>
{
Ok(Json(conn.run(|c| rb::db::users::all(c)).await?)) Ok(Json(conn.run(|c| rb::db::users::all(c)).await?))
} }

View File

@ -1,26 +1,30 @@
use crate::guards::User;
use crate::RbDbConn;
use rb::auth::{generate_jwt_token, verify_user, JWTResponse}; use rb::auth::{generate_jwt_token, verify_user, JWTResponse};
use rocket::serde::json::Json; use rocket::serde::json::Json;
use serde::Deserialize; use serde::Deserialize;
pub(crate) fn routes() -> Vec<rocket::Route> { use crate::{guards::User, RbDbConn};
pub(crate) fn routes() -> Vec<rocket::Route>
{
routes![login, already_logged_in, refresh_token] routes![login, already_logged_in, refresh_token]
} }
#[derive(Deserialize)] #[derive(Deserialize)]
struct Credentials { struct Credentials
{
username: String, username: String,
password: String, password: String,
} }
#[post("/login")] #[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!") String::from("You're already logged in!")
} }
#[post("/login", data = "<credentials>", rank = 2)] #[post("/login", data = "<credentials>", rank = 2)]
async fn login(conn: RbDbConn, credentials: Json<Credentials>) -> rb::Result<Json<JWTResponse>> { async fn login(conn: RbDbConn, credentials: Json<Credentials>) -> rb::Result<Json<JWTResponse>>
{
let credentials = credentials.into_inner(); let credentials = credentials.into_inner();
// Get the user, if credentials are valid // Get the user, if credentials are valid
@ -33,7 +37,8 @@ async fn login(conn: RbDbConn, credentials: Json<Credentials>) -> rb::Result<Jso
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct RefreshTokenRequest { struct RefreshTokenRequest
{
pub refresh_token: String, pub refresh_token: String,
} }
@ -41,7 +46,8 @@ struct RefreshTokenRequest {
async fn refresh_token( async fn refresh_token(
conn: RbDbConn, conn: RbDbConn,
refresh_token_request: Json<RefreshTokenRequest>, refresh_token_request: Json<RefreshTokenRequest>,
) -> rb::Result<Json<JWTResponse>> { ) -> rb::Result<Json<JWTResponse>>
{
let refresh_token = refresh_token_request.into_inner().refresh_token; let refresh_token = refresh_token_request.into_inner().refresh_token;
Ok(Json( Ok(Json(

View File

@ -1,2 +1,2 @@
pub mod auth;
pub mod admin; pub mod admin;
pub mod auth;