diff options
Diffstat (limited to 'crates/rocie-server/src/storage/sql/user.rs')
| -rw-r--r-- | crates/rocie-server/src/storage/sql/user.rs | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/crates/rocie-server/src/storage/sql/user.rs b/crates/rocie-server/src/storage/sql/user.rs new file mode 100644 index 0000000..2bac555 --- /dev/null +++ b/crates/rocie-server/src/storage/sql/user.rs @@ -0,0 +1,86 @@ +use std::fmt::Display; + +use argon2::{ + Argon2, PasswordHasher, PasswordVerifier, + password_hash::{SaltString, rand_core::OsRng}, +}; +use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; + +use crate::storage::sql::mk_id; + +/// The definition of an rocie user. +#[derive(ToSchema, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)] +pub(crate) struct User { + /// The unique ID for this user. + pub(crate) id: UserId, + + /// The user-displayed name of this user. + pub(crate) name: String, + + /// The hash of the user's password. + pub(crate) password_hash: PasswordHash, + + /// An description of this user. + #[schema(nullable = false)] + pub(crate) description: Option<String>, +} + +/// This is stored as an PHC password string. +/// +/// This type corresponds to the string representation of a PHC string as +/// described in the [PHC string format specification][1]. +/// +/// PHC strings have the following format: +/// +/// ```text +/// $<id>[$v=<version>][$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]] +/// ``` +#[derive(ToSchema, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)] +pub(crate) struct PasswordHash { + value: String, +} +impl PasswordHash { + pub(crate) fn from_db(password_hash: String) -> PasswordHash { + Self { + value: password_hash, + } + } + + pub(crate) fn from_password(password: &str) -> Self { + let salt = SaltString::generate(&mut OsRng); + + let argon2 = Argon2::default(); + + let password_hash = argon2 + .hash_password(password.as_bytes(), &salt) + .expect("to not fail") + .to_string(); + + Self { + value: password_hash, + } + } + + /// Check that self, and the other password have the same hash. + pub(crate) fn verify(&self, other: &str) -> bool { + let argon2 = Argon2::default(); + + argon2 + .verify_password(other.as_bytes(), &self.as_argon_hash()) + .is_ok() + } + + fn as_argon_hash(&self) -> argon2::PasswordHash<'_> { + argon2::PasswordHash::new(&self.value) + .expect("to be valid, as we are just deserializing a previously serialize value") + } +} + +impl Display for PasswordHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.value.fmt(f) + } +} + +mk_id!(UserId and UserIdStub); |
