lmrc_http_common/auth/
password.rs1use bcrypt::{hash, verify, BcryptError, DEFAULT_COST};
4use thiserror::Error;
5
6#[derive(Debug, Error)]
8pub enum PasswordError {
9 #[error("Failed to hash password: {0}")]
10 HashError(String),
11
12 #[error("Failed to verify password: {0}")]
13 VerifyError(String),
14
15 #[error("Invalid password")]
16 InvalidPassword,
17}
18
19impl From<BcryptError> for PasswordError {
20 fn from(err: BcryptError) -> Self {
21 PasswordError::HashError(err.to_string())
22 }
23}
24
25pub fn hash_password(password: &str) -> Result<String, PasswordError> {
39 hash(password, DEFAULT_COST).map_err(Into::into)
40}
41
42pub fn hash_password_with_cost(password: &str, cost: u32) -> Result<String, PasswordError> {
47 hash(password, cost).map_err(Into::into)
48}
49
50pub fn verify_password(password: &str, hash: &str) -> Result<bool, PasswordError> {
65 verify(password, hash).map_err(|e| PasswordError::VerifyError(e.to_string()))
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn test_hash_and_verify() {
74 let password = "test_password_123";
75 let hashed = hash_password(password).unwrap();
76
77 assert_ne!(password, hashed);
78 assert!(verify_password(password, &hashed).unwrap());
79 assert!(!verify_password("wrong_password", &hashed).unwrap());
80 }
81
82 #[test]
83 fn test_custom_cost() {
84 let password = "test_password";
85 let hashed = hash_password_with_cost(password, 10).unwrap();
86
87 assert!(verify_password(password, &hashed).unwrap());
88 }
89}