From 22edb9aac9eaa4b25562546c816f25aa65188864 Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Sat, 1 Feb 2025 18:31:22 -0800 Subject: [PATCH] User passwords --- ...337427c6875e750c6d3570a314fe3136d36f.json} | 18 ++++-- Cargo.lock | 55 ++++++++++++++++++- Cargo.toml | 1 + .../20240320000000_add_password_hash.sql | 1 + .../20240320000001_add_password_hash.sql | 2 + src/main.rs | 25 ++++++++- 6 files changed, 90 insertions(+), 12 deletions(-) rename .sqlx/{query-3577e56d051f360866f893491584cccae7d2e985742ca78be06011e6c6640fc2.json => query-d04ca7e16ebbeb1213fc829a4ff1337427c6875e750c6d3570a314fe3136d36f.json} (58%) create mode 100644 migrations/20240320000000_add_password_hash.sql create mode 100644 migrations/20240320000001_add_password_hash.sql diff --git a/.sqlx/query-3577e56d051f360866f893491584cccae7d2e985742ca78be06011e6c6640fc2.json b/.sqlx/query-d04ca7e16ebbeb1213fc829a4ff1337427c6875e750c6d3570a314fe3136d36f.json similarity index 58% rename from .sqlx/query-3577e56d051f360866f893491584cccae7d2e985742ca78be06011e6c6640fc2.json rename to .sqlx/query-d04ca7e16ebbeb1213fc829a4ff1337427c6875e750c6d3570a314fe3136d36f.json index 62766da..e9bbe3d 100644 --- a/.sqlx/query-3577e56d051f360866f893491584cccae7d2e985742ca78be06011e6c6640fc2.json +++ b/.sqlx/query-d04ca7e16ebbeb1213fc829a4ff1337427c6875e750c6d3570a314fe3136d36f.json @@ -1,10 +1,10 @@ { "db_name": "SQLite", - "query": "\n SELECT \n id as \"id: Uuid\",\n username,\n email,\n display_name,\n created_at as \"created_at: chrono::DateTime\"\n FROM users\n ", + "query": "\n SELECT \n id as \"id: String\",\n username,\n password_hash,\n email,\n display_name,\n created_at as \"created_at: chrono::DateTime\"\n FROM users\n ", "describe": { "columns": [ { - "name": "id: Uuid", + "name": "id: String", "ordinal": 0, "type_info": "Text" }, @@ -14,25 +14,31 @@ "type_info": "Text" }, { - "name": "email", + "name": "password_hash", "ordinal": 2, "type_info": "Text" }, { - "name": "display_name", + "name": "email", "ordinal": 3, "type_info": "Text" }, { - "name": "created_at: chrono::DateTime", + "name": "display_name", "ordinal": 4, "type_info": "Text" + }, + { + "name": "created_at: chrono::DateTime", + "ordinal": 5, + "type_info": "Text" } ], "parameters": { "Right": 0 }, "nullable": [ + false, false, false, true, @@ -40,5 +46,5 @@ false ] }, - "hash": "3577e56d051f360866f893491584cccae7d2e985742ca78be06011e6c6640fc2" + "hash": "d04ca7e16ebbeb1213fc829a4ff1337427c6875e750c6d3570a314fe3136d36f" } diff --git a/Cargo.lock b/Cargo.lock index e3629f7..5055d3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,12 +169,31 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bcrypt" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e65938ed058ef47d92cf8b346cc76ef48984572ade631927e9937b5ffc7662c7" +dependencies = [ + "base64 0.22.1", + "blowfish", + "getrandom 0.2.15", + "subtle", + "zeroize", +] + [[package]] name = "binascii" version = "0.1.4" @@ -214,6 +233,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + [[package]] name = "bstr" version = "1.11.3" @@ -300,6 +329,16 @@ dependencies = [ "phf_codegen", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -1247,6 +1286,15 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "is-terminal" version = "0.4.15" @@ -2144,6 +2192,7 @@ version = "0.1.0" dependencies = [ "argon2", "atom_syndication", + "bcrypt", "chrono", "rocket", "rocket_db_pools", @@ -2189,7 +2238,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64", + "base64 0.21.7", ] [[package]] @@ -2515,7 +2564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" dependencies = [ "atoi", - "base64", + "base64 0.21.7", "bitflags 2.8.0", "byteorder", "bytes", @@ -2559,7 +2608,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" dependencies = [ "atoi", - "base64", + "base64 0.21.7", "bitflags 2.8.0", "byteorder", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 8311454..76fccc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ rocket_dyn_templates = { version = "0.2.0", features = ["tera"] } rss = "2.0.11" sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite", "chrono", "uuid"] } uuid = { version = "1.7.0", features = ["v4", "serde"] } +bcrypt = "0.15" diff --git a/migrations/20240320000000_add_password_hash.sql b/migrations/20240320000000_add_password_hash.sql new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/migrations/20240320000000_add_password_hash.sql @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/migrations/20240320000001_add_password_hash.sql b/migrations/20240320000001_add_password_hash.sql new file mode 100644 index 0000000..d783c0e --- /dev/null +++ b/migrations/20240320000001_add_password_hash.sql @@ -0,0 +1,2 @@ +-- Add password_hash column +ALTER TABLE users ADD COLUMN password_hash TEXT NOT NULL DEFAULT ''; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 20fe02c..43e45ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use rocket::serde::{json::Json, Deserialize, Serialize}; use rocket_db_pools::{sqlx, Connection, Database}; use rocket_dyn_templates::{context, Template}; use uuid::Uuid; +use bcrypt; #[derive(Database)] #[database("rss_data")] @@ -24,16 +25,18 @@ struct Message { struct User { id: Uuid, username: String, + password_hash: String, email: Option, display_name: Option, created_at: chrono::DateTime, } impl User { - fn new(username: String, email: Option, display_name: Option) -> Self { + fn new(username: String, password_hash: String, email: Option, display_name: Option) -> Self { User { id: Uuid::new_v4(), username, + password_hash, email, display_name, created_at: chrono::Utc::now(), @@ -45,6 +48,7 @@ impl User { #[serde(crate = "rocket::serde")] struct NewUser { username: String, + password: String, email: Option, display_name: Option, } @@ -68,11 +72,24 @@ async fn create_user( new_user: Json, ) -> Result, Status> { let new_user = new_user.into_inner(); - let user = User::new(new_user.username, new_user.email, new_user.display_name); + + // Hash the password - we'll use bcrypt + let password_hash = bcrypt::hash(new_user.password.as_bytes(), bcrypt::DEFAULT_COST) + .map_err(|_| Status::InternalServerError)?; + + let user = User::new( + new_user.username, + password_hash, + new_user.email, + new_user.display_name + ); - let query = sqlx::query("INSERT INTO users (id, username, email, display_name, created_at) VALUES (?1, ?2, ?3, ?4, ?5)") + let query = sqlx::query( + "INSERT INTO users (id, username, password_hash, email, display_name, created_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6)" + ) .bind(user.id.to_string()) .bind(user.username.as_str()) + .bind(user.password_hash.as_str()) .bind(user.email.as_ref()) .bind(user.display_name.as_ref()) .bind(user.created_at.to_rfc3339()) @@ -99,6 +116,7 @@ async fn get_users(mut db: Connection) -> Result>, Status> { SELECT id as "id: String", username, + password_hash, email, display_name, created_at as "created_at: chrono::DateTime" @@ -118,6 +136,7 @@ async fn get_users(mut db: Connection) -> Result>, Status> { .map(|row| User { id: Uuid::parse_str(&row.id).unwrap(), username: row.username, + password_hash: row.password_hash, email: row.email, display_name: row.display_name, created_at: row.created_at,