util: refactor password API

This commit is contained in:
anna 2023-01-23 20:11:40 +01:00
parent ac6d756acb
commit 96392afc39
Signed by: fef
GPG key ID: EC22E476DC2D3D84
3 changed files with 28 additions and 26 deletions

View file

@ -5,7 +5,7 @@ use crate::core::*;
use crate::model::Account;
use crate::state::AppState;
use crate::util::password::ClearPassword;
use crate::util::{password, token};
use crate::util::token;
#[derive(Deserialize)]
struct AuthRequest {
@ -32,7 +32,7 @@ async fn auth(body: web::Json<AuthRequest>, state: AppState) -> Result<HttpRespo
Error::NotFound => Error::BadCredentials,
e => e,
})?;
password::verify(&body.password, &user.password)?;
user.password.verify(&body.password)?;
let account = state.repo.accounts.by_id(user.account_id).await?;
let token = token::issue(&state, &account)?;
info!(target: "auth", "Successful login for user {}", &account.name);

View file

@ -43,7 +43,7 @@ async fn signup(data: web::Form<SignupData>, state: AppState) -> Result<HttpResp
.create(Insane::from(NewUser {
account_id: account.id,
email: data.email,
password: password::hash(&data.password),
password: data.password.hash(),
locale: data.locale,
reason: data.reason,
private_key: privkey,

View file

@ -6,35 +6,32 @@ use serde::{Deserialize, Serialize};
use crate::core::*;
use crate::util::validate::{FieldResultBuilder, ValidateField};
#[derive(sqlx::Type, Serialize, Deserialize, Clone)]
#[sqlx(transparent)]
#[derive(Serialize, Deserialize, Clone)]
pub struct ClearPassword(String);
#[derive(sqlx::Type, Serialize, Deserialize, Clone)]
#[sqlx(transparent)]
pub struct HashedPassword(String);
pub fn hash(clear: &ClearPassword) -> HashedPassword {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
HashedPassword(
impl HashedPassword {
pub fn verify(&self, clear: &ClearPassword) -> Result<()> {
let clear = clear.0.as_str();
let hash = PasswordHash::new(self.0.as_str()).expect("Corrupt password hash in database");
let argon2 = Argon2::default();
argon2
.hash_password(clear.0.as_bytes(), &salt)
.unwrap()
.to_string(),
)
}
pub fn verify(clear: &ClearPassword, hash: &HashedPassword) -> Result<()> {
let (clear, hash) = (clear.0.as_str(), hash.0.as_str());
let parsed_hash = PasswordHash::new(hash).unwrap();
match Argon2::default().verify_password(clear.as_bytes(), &parsed_hash) {
Ok(_) => Ok(()),
Err(_) => Err(Error::BadCredentials),
.verify_password(clear.as_bytes(), &hash)
.map_err(|_| Error::BadCredentials)
}
}
impl ClearPassword {
pub fn hash(self) -> HashedPassword {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hash = argon2.hash_password(self.0.as_bytes(), &salt).unwrap();
HashedPassword(hash.to_string())
}
pub fn len(&self) -> usize {
self.0.len()
}
@ -89,13 +86,18 @@ impl<'a> ValidateField<'a> for ClearPassword {
#[cfg(test)]
mod tests {
use crate::util::password::{hash, verify};
use super::*;
#[test]
fn validate_hashes() {
let h = hash(&String::from("asdf").into());
assert!(verify(&String::from("asdf").into(), &h).is_ok());
assert!(verify(&String::from("fdsa").into(), &h).is_err());
assert!(verify(&String::from("asdf\0").into(), &h).is_err());
let clear = ClearPassword::from("asdf".to_owned());
let hash = clear.clone().hash();
assert!(hash.verify(&clear).is_ok());
assert!(hash.verify(&clear_from("fdsa")).is_err());
assert!(hash.verify(&clear_from("asdf\0")).is_err());
}
fn clear_from(s: &str) -> ClearPassword {
ClearPassword::from(s.to_owned())
}
}