Splitting database access logic from routing code

This commit is contained in:
Imbus 2023-10-21 07:54:26 +02:00
parent fa9b2b6fc1
commit 508cf528af
5 changed files with 131 additions and 75 deletions

View file

@ -1,4 +1,5 @@
use crate::db::{db_get_latest_posts, db_new_post};
use crate::jwt::validate_token;
use crate::ServerState;
use actix_web::web::{Data, Query};
@ -63,7 +64,29 @@ pub async fn get_posts(
/// Creates a new post, requires a token in release mode
#[post("/posts")]
pub async fn new_post(new_post: Json<NewPost>, state: Data<ServerState>) -> Result<impl Responder> {
return match db_new_post(new_post.into_inner(), &state.pool).await {
let user_claims = validate_token(&new_post.token);
if let Err(e) = user_claims {
info!("Error validating token: {}", e);
return Ok(HttpResponse::BadRequest().json("Error"));
}
// Bail if the token is invalid
let claims = user_claims.unwrap();
info!("User {:?} created a new post", &claims.sub);
let content = new_post.content.clone();
let username = claims.sub.clone();
// This one is avoidable if we just store the user id in the token
let userid = sqlx::query!("SELECT id FROM users WHERE username = ?", username)
.fetch_one(&state.pool)
.await
.unwrap()
.id;
// By now we know that the token is valid, so we can create the post
return match db_new_post(userid, &content, &state.pool).await {
Some(post) => {
info!("Created post {:?}", post.id);
Ok(HttpResponse::Ok().json(post))

View file

@ -1,15 +1,12 @@
use crate::db::{db_new_user, db_user_login};
use crate::jwt::token_factory;
use crate::state::CaptchaState;
use crate::ServerState;
use actix_web::web::Data;
use actix_web::{post, web::Json, HttpResponse, Responder, Result};
use argon2::password_hash::rand_core::{OsRng, RngCore};
use argon2::password_hash::SaltString;
use argon2::password_hash::rand_core::RngCore;
use argon2::password_hash::*;
use argon2::Argon2;
use argon2::PasswordHasher;
use argon2::PasswordVerifier;
use biosvg::BiosvgBuilder;
use log::*;
use serde::{Deserialize, Serialize};
@ -21,7 +18,7 @@ pub struct LoginData {
}
#[derive(Debug, Serialize, Deserialize)]
struct LoginResponse {
pub struct LoginResponse {
username: String,
token: String,
}
@ -38,75 +35,23 @@ pub async fn register(
data: Json<RegisterData>,
state: Data<ServerState>,
) -> Result<impl Responder> {
// First check if the user already exists
let exists = sqlx::query!(
"SELECT username FROM users WHERE username = ?",
data.username
)
.fetch_one(&state.pool)
.await
.ok()
.map(|row| row.username);
// Bail out if the user already exists
if exists.is_some() {
info!("User \"{}\" already exists", data.username);
return Ok(HttpResponse::BadRequest().json("Error"));
}
// Unwrapping here because if this fails, we have a serious problem
let phc_hash = Argon2::default()
.hash_password(data.password.as_bytes(), &SaltString::generate(&mut OsRng))
.unwrap()
.to_string();
// Insert our new user into the database
sqlx::query!(
"INSERT INTO users (username, password) VALUES (?, ?)",
data.username,
phc_hash
)
.execute(&state.pool)
.await
.unwrap();
db_new_user(data.username.clone(), data.password.clone(), &state.pool).await;
info!("User: {} registered", data.username);
Ok(HttpResponse::Ok().json("User registered"))
}
#[post("/login")]
pub async fn login(data: Json<LoginData>, state: Data<ServerState>) -> Result<impl Responder> {
let uname = data.username.clone();
let q = sqlx::query!("SELECT password FROM users WHERE username = ?", uname)
.fetch_one(&state.pool)
.await
.ok();
let result = db_user_login(data.username.clone(), data.password.clone(), &state.pool).await;
if q.is_none() {
info!("User \"{}\" failed to log in", data.username);
return Ok(HttpResponse::BadRequest().json("Error"));
}
let phc_password = q.unwrap().password;
let phc_password = PasswordHash::new(&phc_password).unwrap_or_else(|_| {
warn!(
"Invalid hash for user {} fetched from database (not a valid PHC string)",
data.username
);
panic!();
});
match Argon2::default().verify_password(data.password.as_bytes(), &phc_password) {
Ok(_) => {
info!("User {} logged in", data.username);
let token = token_factory(&data.username).unwrap();
println!("{:?}", token);
match result {
Some(_) => {
return Ok(HttpResponse::Ok().json(LoginResponse {
username: data.username.clone(),
token,
token: token_factory(&data.username).unwrap(),
}));
}
Err(_) => {
None => {
info!("User \"{}\" failed to log in", data.username);
return Ok(HttpResponse::BadRequest().json("Error"));
}
@ -159,6 +104,7 @@ fn get_captcha() -> (String, String) {
.length(4)
.difficulty(6)
.colors(vec![
// Feel free to change these
"#0078D6".to_string(),
"#aa3333".to_string(),
"#f08012".to_string(),