diff --git a/Cargo.lock b/Cargo.lock index 7ec9378..c147da5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -372,6 +372,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "ct-codecs" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b916ba8ce9e4182696896f015e8a5ae6081b305f74690baa8465e35f5a142ea4" + [[package]] name = "der" version = "0.7.9" @@ -460,6 +466,7 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", + "orion", "rand", "regex", "rocket", @@ -467,6 +474,7 @@ dependencies = [ "rocket_dyn_templates", "serde_json", "sqlx", + "uuid", ] [[package]] @@ -532,6 +540,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "figment" version = "0.10.19" @@ -1488,6 +1502,19 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "orion" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ab5415cf60cd271259e576f2ddee7a5f9fed42659035224c01af766943fad3" +dependencies = [ + "ct-codecs", + "fiat-crypto", + "getrandom", + "subtle", + "zeroize", +] + [[package]] name = "overload" version = "0.1.1" @@ -2304,6 +2331,7 @@ dependencies = [ "tokio-stream", "tracing", "url", + "uuid", "webpki-roots", ] @@ -2386,6 +2414,7 @@ dependencies = [ "stringprep", "thiserror 1.0.69", "tracing", + "uuid", "whoami", ] @@ -2425,6 +2454,7 @@ dependencies = [ "stringprep", "thiserror 1.0.69", "tracing", + "uuid", "whoami", ] @@ -2450,6 +2480,7 @@ dependencies = [ "tracing", "url", "urlencoding", + "uuid", ] [[package]] @@ -2989,6 +3020,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "serde", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index e253010..b9cfe26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,12 @@ sqlx = { version = "0.7", default-features = false, features = [ "macros", "chrono", "migrate", + "uuid", ] } chrono = { version = "0.4", features = ["serde"] } regex = "1.11" rand = "0.8" serde_json = "1.0" anyhow = "1.0" +orion = "0.17" +uuid = { version = "1.11", features = ["serde"] } diff --git a/migrations/2_users.sql b/migrations/2_users.sql new file mode 100644 index 0000000..4142e3e --- /dev/null +++ b/migrations/2_users.sql @@ -0,0 +1,24 @@ +create extension "uuid-ossp"; + +create table users ( + id uuid not null primary key default gen_random_uuid(), + created_at timestamptz not null default current_timestamp, + updated_at timestamptz, + + username varchar(256) not null, + password varchar(512) not null, + + enabled boolean not null default true +); + +insert into users (id, username, password) +values ('00000000-0000-0000-0000-000000000000', 'legacy', ''); + +alter table doll_profiles + add column bound_to_id uuid not null + default '00000000-0000-0000-0000-000000000000' + references users (id) + on delete cascade; + +alter table doll_profiles + alter column bound_to_id drop default; diff --git a/src/auth/mod.rs b/src/auth/mod.rs new file mode 100644 index 0000000..3fbed9e --- /dev/null +++ b/src/auth/mod.rs @@ -0,0 +1 @@ +pub mod pw; diff --git a/src/auth/pw.rs b/src/auth/pw.rs new file mode 100644 index 0000000..8e4e298 --- /dev/null +++ b/src/auth/pw.rs @@ -0,0 +1,27 @@ +use orion::{errors::UnknownCryptoError, pwhash}; + +// see guidelines on determining those values: https://doc.libsodium.org/password_hashing/default_phf#guidelines-for-choosing-the-parameters +// 3 password hash iterations +const ITERATIONS: u32 = 3; +// 64MB RAM allocated to a password hash +const MEMORY: u32 = 1 << 16; +// TODO: make those configurable through the .toml file + +pub fn hash(input: &str) -> Result { + let password = pwhash::Password::from_slice(input.as_bytes())?; + + Ok(pwhash::hash_password(&password, ITERATIONS, MEMORY)? + .unprotected_as_encoded() + .to_string()) +} + +pub fn verify(input: &str, against: &str) -> Result { + if input.len() == 0 { + // disabled accounts + return Ok(false); + } + let password = pwhash::Password::from_slice(input.as_bytes())?; + let hash = pwhash::PasswordHash::from_encoded(against)?; + + Ok(pwhash::hash_password_verify(&hash, &password).is_ok()) +} diff --git a/src/db/doll.rs b/src/db/doll.rs index 29c5918..65a7054 100644 --- a/src/db/doll.rs +++ b/src/db/doll.rs @@ -48,8 +48,8 @@ pub async fn create(mut db: DollTagsDb, doll: CreateDollProfile<'_>) -> sqlx::Re sqlx::query!( r#" insert into doll_profiles - (id, microchip_id, name, pronoun_subject, pronoun_object, pronoun_possessive, handler_name, handler_link, kind, breed, behaviour, description, chassis_type, chassis_id, chassis_color) - values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) + (id, microchip_id, name, pronoun_subject, pronoun_object, pronoun_possessive, handler_name, handler_link, kind, breed, behaviour, description, chassis_type, chassis_id, chassis_color, bound_to_id) + values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) "#, doll.id, doll.microchip_id, @@ -66,6 +66,7 @@ pub async fn create(mut db: DollTagsDb, doll: CreateDollProfile<'_>) -> sqlx::Re doll.chassis_type, doll.chassis_id, doll.chassis_color, + doll.bound_to_id, ).execute(&mut **db).await?; Ok(()) diff --git a/src/db/schema.rs b/src/db/schema.rs index 3482582..dfd027d 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -1,7 +1,7 @@ use ::chrono::Utc; use rocket::serde::Serialize; use rocket_db_pools::{Connection, Database}; -use sqlx::types::chrono; +use sqlx::types::{chrono, Uuid}; #[derive(Database)] #[database("dolltags")] @@ -17,6 +17,8 @@ pub struct DollProfile { pub created_at: chrono::DateTime, pub updated_at: Option>, + pub bound_to_id: Uuid, + pub name: String, pub pronoun_subject: String, pub pronoun_object: String, @@ -48,6 +50,7 @@ impl DollProfile { pub struct CreateDollProfile<'a> { pub id: i32, pub microchip_id: Option<&'a str>, + pub bound_to_id: &'a Uuid, pub name: &'a str, pub pronoun_subject: &'a str, diff --git a/src/main.rs b/src/main.rs index 26b8f7a..9b29da2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ use rocket_dyn_templates::Template; use routes::{error_handlers, form, public}; use serde_json::{to_value, Value}; +pub mod auth; pub mod db; pub mod ids; pub mod routes; diff --git a/src/routes/form/register_tag.rs b/src/routes/form/register_tag.rs index 669e110..f776430 100644 --- a/src/routes/form/register_tag.rs +++ b/src/routes/form/register_tag.rs @@ -3,6 +3,7 @@ use rocket::{ response::Redirect, }; use rocket_dyn_templates::{context, Template}; +use uuid::uuid; use crate::{ db::{ @@ -148,6 +149,7 @@ pub async fn handle_register( chassis_type: normalize_opt(tag.chassis_type), chassis_id: normalize_opt(tag.chassis_id), chassis_color: normalize_opt(tag.chassis_color), + bound_to_id: &uuid!("00000000-0000-0000-0000-000000000000"), // TODO: remove once accounts exist }, ) .await?;