HomeDisk/types/src/database/user.rs

139 lines
3.8 KiB
Rust
Raw Normal View History

use crypto_utils::sha::{Algorithm, CryptographicHash};
use uuid::Uuid;
2022-06-08 19:16:12 +00:00
/// SQL user table
#[derive(Debug, sqlx::FromRow)]
pub struct User {
2022-06-08 22:02:20 +00:00
/// UUID of the user
pub id: String,
2022-06-08 22:02:20 +00:00
/// Username
pub username: String,
2022-06-08 22:02:20 +00:00
/// Encryped user password
pub password: String,
}
impl User {
/// The function create a unique user UUID and create SHA-512 hash from salted user password
/// and returns the [User] type.
///
/// **Note: This doesn't create a user in the database!**
///
/// ```
/// use homedisk_types::database::User;
///
2022-04-19 11:08:30 +00:00
/// let user = User::new("medzik", "SuperSecretPassword123!");
2022-06-23 09:52:48 +00:00
///
/// # assert_eq!(user.username, "medzik")
/// ```
pub fn new(username: &str, password: &str) -> Self {
2022-04-23 10:52:56 +00:00
// change username to lowercase
let username = username.to_lowercase();
2022-06-16 12:14:40 +00:00
// salting the password
let password = format!("{username}${password}");
2022-06-16 14:58:37 +00:00
// hash password using SHA-512 and encode it to String from Vec<u8>
let password = hex::encode(CryptographicHash::hash(
Algorithm::SHA512,
password.as_bytes(),
));
// generate a user UUID
let id_sha1 = CryptographicHash::hash(
Algorithm::SHA1,
(format!("{username}${password}")).as_bytes(),
);
let id = Uuid::new_v5(&Uuid::NAMESPACE_X500, &id_sha1).to_string();
2022-06-08 22:02:20 +00:00
// return `User`
Self {
id,
username,
password,
}
}
2022-05-01 18:34:28 +00:00
/// The function returns the directory where the user file is located.
///
2022-05-01 18:34:28 +00:00
/// ```
/// use homedisk_types::database::User;
///
/// let user = User::new("medzik", "whatever");
///
/// let dir = user.user_dir("/storage"); // will return `/storage/medzik`
2022-05-01 20:44:28 +00:00
///
/// assert_eq!(dir, "/storage/medzik")
2022-05-01 18:34:28 +00:00
/// ```
pub fn user_dir(&self, storage: &str) -> String {
2022-06-08 19:16:12 +00:00
// get a user storage path
2022-05-01 18:34:28 +00:00
let path = format!(
"{path}/{username}",
path = storage,
username = self.username,
);
2022-06-08 19:16:12 +00:00
// return user storage path
2022-05-01 18:34:28 +00:00
path
}
}
#[cfg(test)]
mod tests {
2022-06-16 12:14:40 +00:00
use crypto_utils::sha::{Algorithm, CryptographicHash};
use super::User;
2022-06-23 09:52:48 +00:00
/// Check if the id is reproducable
#[test]
fn check_id_reproducable() {
// example user data
let username = "test";
let password = "password";
let user_a = User::new(username, password);
let user_b = User::new(username, password);
assert_eq!(user_a.id, user_b.id)
}
2022-06-16 12:14:40 +00:00
/// Check if the username is in lowercase
#[test]
fn check_username_is_in_lowercase() {
2022-06-16 12:14:40 +00:00
// example user data
let username = "mEDZIk";
let password = "password";
// username in lowercase (expected username)
let username_expected = "medzik";
// create a new `User` type
let user = User::new(username, password);
2022-06-16 12:14:40 +00:00
// username validation with expected username
assert_eq!(user.username, username_expected)
}
2022-06-16 12:14:40 +00:00
/// Check that the password is a checksum with a salt
#[test]
2022-06-16 12:14:40 +00:00
fn check_if_password_is_hashed_and_salted() {
// example user data
let username = "username";
2022-04-23 10:52:56 +00:00
let password = "password";
2022-06-16 12:14:40 +00:00
// create a new `User` type
let user = User::new(username, password);
// expected password salt (string)
let password_expected_salt = format!("{username}${password}");
// expected password (hashed)
let password_expected = hex::encode(CryptographicHash::hash(
Algorithm::SHA512,
password_expected_salt.as_bytes(),
));
// password validation with expected password salt
assert_eq!(user.password, password_expected)
}
}