User passwords

This commit is contained in:
Greg Shuflin 2025-02-01 18:31:22 -08:00
parent 348d37d26e
commit 22edb9aac9
6 changed files with 90 additions and 12 deletions

View File

@ -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<chrono::Utc>\"\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<chrono::Utc>\"\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<chrono::Utc>",
"name": "display_name",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "created_at: chrono::DateTime<chrono::Utc>",
"ordinal": 5,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
false,
true,
@ -40,5 +46,5 @@
false
]
},
"hash": "3577e56d051f360866f893491584cccae7d2e985742ca78be06011e6c6640fc2"
"hash": "d04ca7e16ebbeb1213fc829a4ff1337427c6875e750c6d3570a314fe3136d36f"
}

55
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,2 @@
-- Add password_hash column
ALTER TABLE users ADD COLUMN password_hash TEXT NOT NULL DEFAULT '';

View File

@ -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<String>,
display_name: Option<String>,
created_at: chrono::DateTime<chrono::Utc>,
}
impl User {
fn new(username: String, email: Option<String>, display_name: Option<String>) -> Self {
fn new(username: String, password_hash: String, email: Option<String>, display_name: Option<String>) -> 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<String>,
display_name: Option<String>,
}
@ -68,11 +72,24 @@ async fn create_user(
new_user: Json<NewUser>,
) -> Result<Json<User>, 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<Db>) -> Result<Json<Vec<User>>, Status> {
SELECT
id as "id: String",
username,
password_hash,
email,
display_name,
created_at as "created_at: chrono::DateTime<chrono::Utc>"
@ -118,6 +136,7 @@ async fn get_users(mut db: Connection<Db>) -> Result<Json<Vec<User>>, 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,