2022-06-11 19:36:29 +00:00
|
|
|
use crypto_utils::sha::{Algorithm, CryptographicHash};
|
2022-04-17 22:07:37 +00:00
|
|
|
use uuid::Uuid;
|
|
|
|
|
2022-06-08 19:16:12 +00:00
|
|
|
/// SQL user table
|
2022-04-21 18:53:30 +00:00
|
|
|
#[derive(Debug, sqlx::FromRow)]
|
2022-04-16 22:09:23 +00:00
|
|
|
pub struct User {
|
2022-06-08 22:02:20 +00:00
|
|
|
/// UUID of the user
|
2022-04-16 22:09:23 +00:00
|
|
|
pub id: String,
|
2022-06-08 22:02:20 +00:00
|
|
|
/// Username
|
2022-04-16 22:09:23 +00:00
|
|
|
pub username: String,
|
2022-06-08 22:02:20 +00:00
|
|
|
/// Encryped user password
|
2022-04-16 22:09:23 +00:00
|
|
|
pub password: String,
|
|
|
|
}
|
2022-04-17 22:07:37 +00:00
|
|
|
|
|
|
|
impl User {
|
2022-04-23 10:52:56 +00:00
|
|
|
/// **Note this doesn't create a new user in the database!**
|
2022-04-17 22:07:37 +00:00
|
|
|
///
|
2022-04-23 10:52:56 +00:00
|
|
|
/// This function creates a unique UUID for a user and creates a password hash using SHA-512
|
|
|
|
/// and returns in the User type
|
2022-04-17 22:07:37 +00:00
|
|
|
/// ```
|
2022-04-24 12:50:25 +00:00
|
|
|
/// use homedisk_types::database::User;
|
2022-04-17 22:07:37 +00:00
|
|
|
///
|
2022-04-19 11:08:30 +00:00
|
|
|
/// let user = User::new("medzik", "SuperSecretPassword123!");
|
2022-04-17 22:07:37 +00:00
|
|
|
/// ```
|
2022-04-19 11:05:47 +00:00
|
|
|
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-08 17:08:06 +00:00
|
|
|
// generate a user UUID
|
2022-04-23 10:52:56 +00:00
|
|
|
let sha1_name = CryptographicHash::hash(Algorithm::SHA1, username.as_bytes());
|
2022-04-17 22:07:37 +00:00
|
|
|
let id = Uuid::new_v5(&Uuid::NAMESPACE_X500, &sha1_name).to_string();
|
|
|
|
|
2022-06-16 12:14:40 +00:00
|
|
|
// salting the password
|
|
|
|
let password = format!("{username}${password}");
|
|
|
|
|
2022-04-23 18:44:02 +00:00
|
|
|
// hash password using SHA-512
|
2022-06-11 19:36:29 +00:00
|
|
|
let password = hex::encode(CryptographicHash::hash(
|
2022-04-23 18:44:02 +00:00
|
|
|
Algorithm::SHA512,
|
|
|
|
password.as_bytes(),
|
|
|
|
));
|
2022-04-17 22:07:37 +00:00
|
|
|
|
2022-06-08 22:02:20 +00:00
|
|
|
// return `User`
|
2022-04-19 11:05:47 +00:00
|
|
|
Self {
|
2022-04-17 22:07:37 +00:00
|
|
|
id,
|
2022-04-21 18:53:30 +00:00
|
|
|
username,
|
2022-04-17 22:07:37 +00:00
|
|
|
password,
|
2022-04-19 11:05:47 +00:00
|
|
|
}
|
2022-04-17 22:07:37 +00:00
|
|
|
}
|
2022-05-01 18:34:28 +00:00
|
|
|
|
|
|
|
/// User directory
|
|
|
|
/// function returns the directory where the user file is located
|
|
|
|
/// e.g.
|
|
|
|
/// ```
|
|
|
|
/// use homedisk_types::database::User;
|
|
|
|
///
|
|
|
|
/// let user = User::new("medzik", "whatever");
|
|
|
|
///
|
2022-06-11 19:36:29 +00:00
|
|
|
/// let dir = user.user_dir("/storage"); // will return `/storage/medzik`
|
2022-05-01 20:44:28 +00:00
|
|
|
///
|
2022-06-11 19:36:29 +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
|
|
|
|
}
|
2022-04-17 22:07:37 +00:00
|
|
|
}
|
2022-04-21 18:53:30 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2022-06-16 12:14:40 +00:00
|
|
|
use crypto_utils::sha::{Algorithm, CryptographicHash};
|
|
|
|
|
2022-04-21 18:53:30 +00:00
|
|
|
use super::User;
|
|
|
|
|
2022-06-16 12:14:40 +00:00
|
|
|
/// Check if the username is in lowercase
|
2022-04-21 18:53:30 +00:00
|
|
|
#[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-04-21 18:53:30 +00:00
|
|
|
|
2022-06-16 12:14:40 +00:00
|
|
|
// username validation with expected username
|
|
|
|
assert_eq!(user.username, username_expected)
|
2022-04-21 18:53:30 +00:00
|
|
|
}
|
|
|
|
|
2022-06-16 12:14:40 +00:00
|
|
|
/// Check that the password is a checksum with a salt
|
2022-04-21 18:53:30 +00:00
|
|
|
#[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-04-21 18:53:30 +00:00
|
|
|
|
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)
|
2022-04-21 18:53:30 +00:00
|
|
|
}
|
|
|
|
}
|