Admin user can now be properly created

main
Jef Roosens 2021-11-25 10:19:01 +01:00
parent 8163217700
commit 0449af66d2
Signed by: Jef Roosens
GPG Key ID: 955C0660072F691F
8 changed files with 114 additions and 52 deletions

View File

@ -39,3 +39,22 @@ base64 = "0.13.0"
# Reading in configuration files # Reading in configuration files
figment = { version = "*", features = [ "yaml" ] } figment = { version = "*", features = [ "yaml" ] }
mimalloc = { version = "0.1.26", default_features = false } mimalloc = { version = "0.1.26", default_features = false }
[profile.dev]
lto = "off"
incremental = true
[profile.test]
lto = "off"
incremental = true
[profile.release]
lto = "fat"
incremental = true
codegen-units = 1
# For releases also try to max optimizations for dependencies:
[profile.release.build-override]
opt-level = 3
[profile.release.package."*"]
opt-level = 3

View File

@ -10,8 +10,9 @@ debug:
limits: limits:
forms: 32768 forms: 32768
admin_user: "bever" admin:
admin_pass: "bever" username: "bever"
password: "bever"
jwt: jwt:
key: "secret" key: "secret"

View File

@ -7,10 +7,11 @@ use rb::{
auth::JwtConf, auth::JwtConf,
errors::{RbError, RbResult}, errors::{RbError, RbResult},
}; };
use rb_gw::db;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::Sha256; use sha2::Sha256;
use crate::db;
#[derive(Serialize)] #[derive(Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct JWTResponse pub struct JWTResponse

View File

@ -2,7 +2,8 @@ use argon2::verify_encoded;
use diesel::PgConnection; use diesel::PgConnection;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use rb::errors::{RbError, RbResult}; use rb::errors::{RbError, RbResult};
use rb_gw::db;
use crate::db;
pub fn verify_user(conn: &PgConnection, username: &str, password: &str) -> RbResult<db::User> pub fn verify_user(conn: &PgConnection, username: &str, password: &str) -> RbResult<db::User>
{ {
@ -14,7 +15,7 @@ pub fn verify_user(conn: &PgConnection, username: &str, password: &str) -> RbRes
return Err(RbError::AuthBlockedUser); return Err(RbError::AuthBlockedUser);
} }
match verify_encoded(user.password.as_str(), password.as_bytes()) { match verify_encoded(user.password_hash.as_str(), password.as_bytes()) {
Ok(true) => Ok(user), Ok(true) => Ok(user),
_ => Err(RbError::AuthInvalidPassword), _ => Err(RbError::AuthInvalidPassword),
} }

View File

@ -3,7 +3,10 @@ use rb::errors::{RbError, RbResult};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use crate::schema::{users, users::dsl::*}; use crate::{
auth::pass::hash_password,
schema::{users, users::dsl::*},
};
#[derive(Queryable, Serialize)] #[derive(Queryable, Serialize)]
pub struct User pub struct User
@ -11,12 +14,12 @@ pub struct User
pub id: Uuid, pub id: Uuid,
pub username: String, pub username: String,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub password: String, pub password_hash: String,
pub blocked: bool, pub blocked: bool,
pub admin: bool, pub admin: bool,
} }
#[derive(Insertable, Deserialize)] #[derive(Insertable, Deserialize, AsChangeset)]
#[table_name = "users"] #[table_name = "users"]
pub struct NewUser pub struct NewUser
{ {
@ -31,7 +34,9 @@ pub struct NewUser
pub struct PatchSection pub struct PatchSection
{ {
username: Option<String>, username: Option<String>,
password: Option<String>,
admin: Option<bool>, admin: Option<bool>,
blocked: Option<bool>,
} }
pub fn get(conn: &PgConnection, offset_: u32, limit_: u32) -> RbResult<Vec<User>> pub fn get(conn: &PgConnection, offset_: u32, limit_: u32) -> RbResult<Vec<User>>
@ -62,10 +67,15 @@ pub fn find_by_username(conn: &PgConnection, username_: &str) -> RbResult<User>
/// ///
/// * `conn` - database connection to use /// * `conn` - database connection to use
/// * `new_user` - user to insert /// * `new_user` - user to insert
pub fn create(conn: &PgConnection, new_user: &NewUser) -> RbResult<()> pub fn create(conn: &PgConnection, new_user: NewUser) -> RbResult<()>
{ {
let hashed_new_user = NewUser {
password: hash_password(&new_user.password)?,
..new_user
};
let count = diesel::insert_into(users) let count = diesel::insert_into(users)
.values(new_user) .values(hashed_new_user)
.execute(conn) .execute(conn)
.map_err(|_| RbError::DbError("Couldn't create user."))?; .map_err(|_| RbError::DbError("Couldn't create user."))?;
@ -82,18 +92,23 @@ pub fn create(conn: &PgConnection, new_user: &NewUser) -> RbResult<()>
/// ///
/// * `conn` - database connection to use /// * `conn` - database connection to use
/// * `new_user` - user to insert/update /// * `new_user` - user to insert/update
// pub fn create_or_update(conn: &PgConnection, new_user: &NewUser) -> RbResult<()> pub fn create_or_update(conn: &PgConnection, new_user: NewUser) -> RbResult<()>
// { {
// diesel::insert_into(users) let hashed_new_user = NewUser {
// .values(new_user) password: hash_password(&new_user.password)?,
// .on_conflict(username) ..new_user
// .do_update() };
// .set(new_user)
// .execute(conn)
// .map_err(|_| RbError::DbError("Couldn't create or update user."))?;
// Ok(()) diesel::insert_into(users)
// } .values(&hashed_new_user)
.on_conflict(username)
.do_update()
.set(&hashed_new_user)
.execute(conn)
.map_err(|_| RbError::DbError("Couldn't create or update user."))?;
Ok(())
}
/// Delete the user with the given ID. /// Delete the user with the given ID.
/// ///

View File

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

View File

@ -2,24 +2,22 @@
extern crate rocket; extern crate rocket;
#[macro_use] #[macro_use]
extern crate diesel_migrations; extern crate diesel_migrations;
#[macro_use]
extern crate diesel;
use figment::{ use figment::{
providers::{Env, Format, Yaml}, providers::{Env, Format, Yaml},
Figment, Figment,
}; };
use rb::auth::JwtConf; use rb::{auth::JwtConf, errors::RbError};
use rb_gw::db;
use rocket::{ use rocket::{
fairing::AdHoc, fairing::AdHoc,
http::Status, http::Status,
serde::json::{json, Value}, serde::json::{json, Value},
Build, Orbit, Request, Rocket, Build, Ignite, Request, Rocket,
}; };
use rocket_sync_db_pools::database; use rocket_sync_db_pools::database;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub mod auth;
pub mod v1; pub mod v1;
#[database("postgres_rb")] #[database("postgres_rb")]
@ -45,26 +43,41 @@ async fn run_db_migrations(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocke
.await .await
} }
// async fn create_admin_user<'a>(rocket: &'a Rocket<Orbit>) async fn create_admin_user(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocket<Build>>
// { {
// let config = rocket.state::<RbConfig>().expect("RbConfig instance"); let admin = rocket.state::<AdminConfig>().expect("admin config");
// let admin_user = config.admin_user.clone();
// let admin_pass = config.admin_pass.clone();
// let conn = RbDbConn::get_one(&rocket) let conn = RbDbConn::get_one(&rocket)
// .await .await
// .expect("database connection"); .expect("database connection");
// conn.run(move |c| {
// admin::create_admin_user(c, &admin_user, &admin_pass).expect("failed to create admin user") let new_user = db::NewUser {
// }) username: admin.username.clone(),
// .await; password: admin.password.clone(),
// } admin: true,
};
match conn
.run(move |c| db::users::create_or_update(c, new_user))
.await
{
Ok(_) => Ok(rocket),
Err(RbError::UMDuplicateUser) => Ok(rocket),
Err(_) => Err(rocket),
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct AdminConfig
{
username: String,
password: String,
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct RbConfig pub struct RbConfig
{ {
admin_user: String, admin: AdminConfig,
admin_pass: String,
jwt: JwtConf, jwt: JwtConf,
} }
@ -75,7 +88,7 @@ fn rocket() -> _
.merge(Yaml::file("Rb.yaml").nested()) .merge(Yaml::file("Rb.yaml").nested())
.merge(Env::prefixed("RB_").global()); .merge(Env::prefixed("RB_").global());
rocket::custom(figment) let rocket = rocket::custom(figment)
.attach(RbDbConn::fairing()) .attach(RbDbConn::fairing())
.attach(AdHoc::try_on_ignite( .attach(AdHoc::try_on_ignite(
"Run database migrations", "Run database migrations",
@ -91,5 +104,18 @@ fn rocket() -> _
v1::auth::login, v1::auth::login,
v1::auth::refresh_token, v1::auth::refresh_token,
], ],
) );
// This let's the guards properly access the JWT credentials when needed
let new_figment = rocket.figment();
let jwt_conf: JwtConf = new_figment.extract_inner("jwt").expect("jwt config");
// We do the same thing here so we can access the admin credentials for initially creating the
// admin user
let admin_conf: AdminConfig = new_figment.extract_inner("admin").expect("admin config");
rocket
.manage(jwt_conf)
.manage(admin_conf)
.attach(AdHoc::try_on_ignite("Create admin user", create_admin_user))
} }

View File

@ -1,15 +1,13 @@
use rb::{errors::RbResult, guards::User}; use rb::{errors::RbResult, guards::User};
use rocket::{serde::json::Json, State}; use rb_gw::auth::{
use serde::Deserialize;
use crate::{
auth::{
jwt::{generate_jwt_token, JWTResponse}, jwt::{generate_jwt_token, JWTResponse},
pass::verify_user, pass::verify_user,
Credentials, Credentials,
},
RbConfig, RbDbConn,
}; };
use rocket::{serde::json::Json, State};
use serde::Deserialize;
use crate::{RbConfig, RbDbConn};
#[post("/login")] #[post("/login")]
pub async fn already_logged_in(_user: User) -> String pub async fn already_logged_in(_user: User) -> String
@ -56,7 +54,7 @@ pub async fn refresh_token(
let jwt = conf.jwt.clone(); let jwt = conf.jwt.clone();
Ok(Json( Ok(Json(
conn.run(move |c| crate::auth::jwt::refresh_token(c, &jwt, &refresh_token)) conn.run(move |c| rb_gw::auth::jwt::refresh_token(c, &jwt, &refresh_token))
.await?, .await?,
)) ))
} }