cleaning up

This commit is contained in:
Hamcha 2023-06-30 17:39:31 +02:00
parent 1d6270c551
commit 50e85f7de9
Signed by: hamcha
GPG key ID: 1669C533B8CF6D89
4 changed files with 64 additions and 18 deletions

View file

@ -23,7 +23,7 @@ pub struct User {
pub email: Option<String>, pub email: Option<String>,
/// Hashed password /// Hashed password
pub password: Option<Vec<u8>>, pub password: Option<String>,
/// User's chosen displayed name /// User's chosen displayed name
pub display_name: Option<String>, pub display_name: Option<String>,
@ -43,6 +43,55 @@ pub struct User {
pub deleted_at: Option<DateTime<Utc>>, pub deleted_at: Option<DateTime<Utc>>,
} }
impl Default for User {
fn default() -> Self {
Self {
id: Uuid::nil(),
name: Default::default(),
email: Default::default(),
password: Default::default(),
display_name: Default::default(),
bio: Default::default(),
roles: Default::default(),
created_at: Default::default(),
modified_at: Default::default(),
deleted_at: Default::default(),
}
}
}
impl User {
pub async fn create(
pool: &Pool<Postgres>,
username: &str,
password: &str,
roles: &Vec<Uuid>,
) -> Result<Self> {
let result = sqlx::query!(
r#"INSERT INTO users ( name, password, roles ) VALUES ( $1,$2,$3 ) RETURNING id, created_at"#,
username,
hash(&password)?,
roles,
)
.fetch_one(pool)
.await?;
Ok(Self {
id: result.id,
name: username.to_owned(),
roles: roles.to_owned(),
created_at: result.created_at.and_utc(),
..Default::default()
})
}
pub async fn find(pool: &Pool<Postgres>, name: &str) -> Result<Option<Self>> {
Ok(sqlx::query_as("SELECT * FROM users WHERE name = $1")
.bind(name)
.fetch_optional(pool)
.await?)
}
}
#[derive(Deserialize, Serialize, FromRow)] #[derive(Deserialize, Serialize, FromRow)]
pub struct Role { pub struct Role {
/// Role ID /// Role ID
@ -137,7 +186,7 @@ pub fn random() -> String {
SaltString::generate(&mut OsRng).to_string() SaltString::generate(&mut OsRng).to_string()
} }
pub fn hash(plaintext: &String) -> Result<String> { pub fn hash(plaintext: &str) -> Result<String> {
let salt = SaltString::generate(&mut OsRng); let salt = SaltString::generate(&mut OsRng);
let hashed = Argon2::default() let hashed = Argon2::default()
.hash_password(plaintext.as_bytes(), &salt) .hash_password(plaintext.as_bytes(), &salt)
@ -146,7 +195,7 @@ pub fn hash(plaintext: &String) -> Result<String> {
Ok(hashed) Ok(hashed)
} }
pub fn verify(plaintext: &String, hash: &String) -> Result<bool> { pub fn verify(plaintext: &str, hash: &str) -> Result<bool> {
let parsed_hash = PasswordHash::new(hash).map_err(|err| anyhow!(err))?; let parsed_hash = PasswordHash::new(hash).map_err(|err| anyhow!(err))?;
Ok(Argon2::default() Ok(Argon2::default()
.verify_password(plaintext.as_bytes(), &parsed_hash) .verify_password(plaintext.as_bytes(), &parsed_hash)

View file

@ -4,7 +4,7 @@ use axum::Json;
use serde_json::{json, Value}; use serde_json::{json, Value};
use std::sync::Arc; use std::sync::Arc;
use crate::auth::{hash, random}; use crate::auth::{random, User};
use crate::error::AppError; use crate::error::AppError;
use crate::roles::ROLE_SUPERADMIN; use crate::roles::ROLE_SUPERADMIN;
use crate::state::AppState; use crate::state::AppState;
@ -29,14 +29,12 @@ pub async fn bootstrap(State(state): State<Arc<AppState>>) -> Result<Json<Value>
let username = "admin"; let username = "admin";
let password = random(); let password = random();
sqlx::query!( User::create(
r#"INSERT INTO users ( name, display_name, password, roles ) VALUES ( $1, $2, $3, $4 ) RETURNING id"#, &state.database,
username, &username,
"Administrator", &password,
hash(&password)?, &[ROLE_SUPERADMIN].to_vec(),
&[ROLE_SUPERADMIN],
) )
.fetch_one(&state.database)
.await?; .await?;
Ok(Json(json!({"username": username, "password": password}))) Ok(Json(json!({"username": username, "password": password})))

View file

@ -5,7 +5,7 @@ use serde_json::{json, Value};
use std::sync::Arc; use std::sync::Arc;
use crate::{ use crate::{
auth::{verify, Session}, auth::{verify, Session, User},
error::AppError, error::AppError,
state::AppState, state::AppState,
}; };
@ -20,9 +20,7 @@ pub async fn login(
State(state): State<Arc<AppState>>, State(state): State<Arc<AppState>>,
Json(payload): Json<LoginRequest>, Json(payload): Json<LoginRequest>,
) -> Result<Json<Value>, AppError> { ) -> Result<Json<Value>, AppError> {
let user = sqlx::query!("SELECT * FROM users WHERE name = $1", payload.username) let user = User::find(&state.database, payload.username.as_str()).await?;
.fetch_optional(&state.database)
.await?;
let invalid = || -> AppError { let invalid = || -> AppError {
AppError::ClientError { AppError::ClientError {

View file

@ -1,7 +1,8 @@
use crate::{content::Page, error::AppError, state::AppState};
use axum::extract::{Path, State}; use axum::extract::{Path, State};
use std::sync::Arc; use std::sync::Arc;
use crate::{content::Page, error::AppError, state::AppState};
pub async fn page( pub async fn page(
State(state): State<Arc<AppState>>, State(state): State<Arc<AppState>>,
Path((site, slug)): Path<(String, String)>, Path((site, slug)): Path<(String, String)>,