From 091ddbf48a3f1148fb1df0ab6f12aab91127e607 Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Thu, 18 May 2023 10:05:29 -0700 Subject: [PATCH] Working user signup, looks ugly. Still no login or sessions, though. --- Cargo.lock | 7 ++++ Cargo.toml | 1 + src/lib.rs | 2 + src/main.rs | 15 +++----- src/templates.rs | 3 +- src/users.rs | 85 +++++++++++++++++++++++++++++++++++++++---- templates/base.html | 2 +- templates/signup.html | 14 +++++++ 8 files changed, 110 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0364a0a..09fecbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2447,6 +2447,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" + [[package]] name = "uuid" version = "0.8.2" @@ -2793,6 +2799,7 @@ dependencies = [ "tracing", "tracing-subscriber", "unicode-segmentation", + "urlencoding", "uuid 1.3.1", ] diff --git a/Cargo.toml b/Cargo.toml index cf9377a..49fb315 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,4 @@ justerror = "1.1.0" password-hash = { version = "0.5.0", features = ["std", "getrandom"] } axum-login = { version = "0.5.0", features = ["sqlite", "sqlx"] } unicode-segmentation = "1.10.1" +urlencoding = "2.1.2" diff --git a/src/lib.rs b/src/lib.rs index f97526d..b8bd189 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,3 +5,5 @@ pub mod db; pub mod handlers; pub(crate) mod templates; pub mod users; + +//pub type Db: axum:: diff --git a/src/main.rs b/src/main.rs index bb8f66d..47cf3be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,10 @@ use std::net::SocketAddr; use axum::{routing::get, Router}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; -use witch_watch::{db, handlers}; +use witch_watch::{ + db, + users::{get_create_user, post_create_user}, +}; #[tokio::main] async fn main() { @@ -16,17 +19,9 @@ async fn main() { let pool = db::get_pool().await; - let _ = witch_watch::users::create_user("joe", &None, &None, &[], &pool) - .await - .unwrap(); - // build our application with some routes - use handlers::*; let app = Router::new() - .route( - "/", - get(using_connection_pool_extractor).post(using_connection_extractor), - ) + .route("/signup", get(get_create_user).post(post_create_user)) .with_state(pool); tracing::debug!("binding to 0.0.0.0:3000"); diff --git a/src/templates.rs b/src/templates.rs index 9dc5aac..afbc50d 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -1,6 +1,7 @@ use askama::Template; +use serde::Deserialize; -#[derive(Template)] +#[derive(Debug, Default, Template, Deserialize)] #[template(path = "signup.html")] pub struct CreateUser { pub username: String, diff --git a/src/users.rs b/src/users.rs index a9cf004..d392081 100644 --- a/src/users.rs +++ b/src/users.rs @@ -2,11 +2,19 @@ use argon2::{ password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, Argon2, }; +use askama::Template; +use axum::{ + extract::{Form, Path, State}, + http::StatusCode, + response::IntoResponse, +}; use sqlx::{error::DatabaseError, Sqlite, SqlitePool}; use tracing::log::log; use unicode_segmentation::UnicodeSegmentation; use uuid::Uuid; +use crate::templates::CreateUser; + const CREATE_QUERY: &str = "insert into witches (id, username, displayname, email, pwhash) values ($1, $2, $3, $4, $5)"; @@ -38,20 +46,69 @@ impl From for User { } } -pub async fn create_user( - username: &str, - displayname: &Option, - email: &Option, - password: &[u8], - pool: &SqlitePool, -) -> Result { +pub async fn get_create_user() -> CreateUser { + CreateUser::default() +} + +#[axum::debug_handler] +pub async fn post_create_user( + State(pool): State>, + Form(signup): Form, +) -> Result<(), CreateUserError> { + let username = &signup.username; + let displayname = &signup.displayname; + let email = &signup.email; + let password = &signup.password; + let verify = &signup.pw_verify; let username = username.trim(); + let name_len = username.graphemes(true).size_hint().1.unwrap(); // we are not ascii exclusivists around here if !(1..=20).contains(&name_len) { return Err(CreateUserErrorKind::BadUsername.into()); } + if password != verify { + return Err(CreateUserErrorKind::PasswordMismatch.into()); + } + + let password = urlencoding::decode(password) + .map_err(|_| CreateUserErrorKind::BadPassword)? + .to_string(); + let password = password.as_bytes(); + + let displayname = if let Some(dn) = displayname { + let dn = urlencoding::decode(dn) + .map_err(|_| CreateUserErrorKind::BadDisplayname)? + .to_string(); + Some(dn) + } else { + None + }; + let displayname = &displayname; + + // TODO(2023-05-17): validate email + let email = if let Some(email) = email { + let email = urlencoding::decode(email) + .map_err(|_| CreateUserErrorKind::BadEmail)? + .to_string(); + Some(email) + } else { + None + }; + let email = &email; + + let _ = create_user(username, displayname, email, password, &pool).await?; + Ok(()) +} + +async fn create_user( + username: &str, + displayname: &Option, + email: &Option, + password: &[u8], + pool: &SqlitePool, +) -> Result { // Argon2 with default params (Argon2id v19) let argon2 = Argon2::default(); let salt = SaltString::generate(&mut OsRng); @@ -103,6 +160,17 @@ pub async fn create_user( #[non_exhaustive] pub struct CreateUserError(#[from] CreateUserErrorKind); +impl IntoResponse for CreateUserError { + fn into_response(self) -> askama_axum::Response { + match self.0 { + CreateUserErrorKind::UnknownDBError => { + (StatusCode::INTERNAL_SERVER_ERROR, format!("{self}")).into_response() + } + _ => (StatusCode::BAD_REQUEST, format!("{self}")).into_response(), + } + } +} + #[Error] #[non_exhaustive] pub enum CreateUserErrorKind { @@ -110,6 +178,9 @@ pub enum CreateUserErrorKind { #[error(desc = "Usernames must be between 1 and 20 non-whitespace characters long")] BadUsername, PasswordMismatch, + BadPassword, + BadDisplayname, + BadEmail, MissingFields, UnknownDBError, } diff --git a/templates/base.html b/templates/base.html index d3cb10f..ba52e06 100644 --- a/templates/base.html +++ b/templates/base.html @@ -12,7 +12,7 @@ {% block content %}{% endblock %} diff --git a/templates/signup.html b/templates/signup.html index 1de3f3c..7eea006 100644 --- a/templates/signup.html +++ b/templates/signup.html @@ -4,4 +4,18 @@ {% block content %} +
+ + + + + + + + + + + +
+ {% endblock %}