Initial commit.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
use jsonwebtoken::{encode, EncodingKey, Header};
|
||||
use std::env;
|
||||
use surveillant_systeme_domain::user::model::Claims;
|
||||
|
||||
fn get_jwt_secret() -> String {
|
||||
env::var("JWT_SECRET").expect("JWT_SECRET must be set.")
|
||||
}
|
||||
|
||||
pub fn generate_jwt_token(user_id: &str) -> Result<String, jsonwebtoken::errors::Error> {
|
||||
let jwt_secret = get_jwt_secret();
|
||||
let claims = Claims::new(user_id.to_string());
|
||||
encode(
|
||||
&Header::default(),
|
||||
&claims,
|
||||
&EncodingKey::from_secret(jwt_secret.as_bytes())
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod service;
|
||||
mod auth;
|
||||
@@ -0,0 +1,169 @@
|
||||
use std::env;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use bcrypt::{hash_with_salt, verify};
|
||||
use chrono::Utc;
|
||||
use uuid::Uuid;
|
||||
use surveillant_systeme_domain::user::model::{LoginResponse, RefreshToken, User};
|
||||
use crate::configuration::service::ConfigurationService;
|
||||
use crate::user::auth::generate_jwt_token;
|
||||
use surveillant_systeme_domain::user::port::UserPort;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UserService {
|
||||
configuration_service: ConfigurationService,
|
||||
user_port: Box<dyn UserPort>,
|
||||
}
|
||||
|
||||
impl UserService {
|
||||
pub fn new(configuration_service: ConfigurationService, user_port: Box<dyn UserPort>) -> Self {
|
||||
Self { configuration_service, user_port }
|
||||
}
|
||||
|
||||
pub fn initialise(
|
||||
&self,
|
||||
user_initialisation_code: &String,
|
||||
username: &String,
|
||||
password: &String,
|
||||
) -> Result<(), Error> {
|
||||
match self.configuration_service.get_initialisation_code()? {
|
||||
None => Err(Error::new(ErrorKind::InvalidInput, "Application has already been initialised.")),
|
||||
Some(system_initialisation_code) => {
|
||||
let is_initialisation_code_valid = system_initialisation_code
|
||||
.eq(&user_initialisation_code.to_string());
|
||||
if is_initialisation_code_valid {
|
||||
self.create_user(username, password)
|
||||
.expect("Unable to create the first user.");
|
||||
self.configuration_service.set_database_initialised()
|
||||
} else {
|
||||
Err(Error::new(ErrorKind::InvalidInput, "Invalid initialisation code!"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn create_user(&self, username: &String, password: &String) -> Result<(), Error> {
|
||||
let new_user = User::new(
|
||||
Uuid::new_v4(),
|
||||
username.to_string(),
|
||||
Self::encrypt_password(&password),
|
||||
);
|
||||
|
||||
self.user_port
|
||||
.insert(&new_user)
|
||||
.expect("Unable to insert new user.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_user(&self, user_id: Uuid, new_username: String) -> Result<(), Error> {
|
||||
let existing_user = self.user_port.get_by_id(&user_id)
|
||||
.map_err(|_| Error::new(ErrorKind::Other, "An error occurred while retrieving the user to update it."))?
|
||||
.ok_or(Error::new(ErrorKind::InvalidInput, "User does not exist."))?;
|
||||
|
||||
if existing_user.username.eq(&new_username) {
|
||||
Err(Error::new(ErrorKind::InvalidInput, "New username is unchanged."))?;
|
||||
}
|
||||
|
||||
let username_already_exist_for_another_user = self.user_port.exists_by_name(&new_username)
|
||||
.map_err(|_| Error::new(ErrorKind::Other, "An error occurred while checking if the new username is available."))?;
|
||||
if username_already_exist_for_another_user {
|
||||
Err(Error::new(ErrorKind::InvalidInput, "New username is already used."))?;
|
||||
}
|
||||
|
||||
let updated_user = User::new(
|
||||
existing_user.id,
|
||||
new_username,
|
||||
existing_user.encrypted_password
|
||||
);
|
||||
self.user_port.update(&updated_user)
|
||||
.expect("An error occurred while saving user updates.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_user_password(&self, user_id: Uuid, new_password: String) -> Result<(), Error> {
|
||||
let existing_user = self.user_port.get_by_id(&user_id)
|
||||
.map_err(|_| Error::new(ErrorKind::Other, "An error occurred while retrieving the user to update it."))?
|
||||
.expect("User does not exist.");
|
||||
|
||||
let new_encrypted_password = Self::encrypt_password(&new_password);
|
||||
let updated_user = User::new(
|
||||
existing_user.id,
|
||||
existing_user.username,
|
||||
new_encrypted_password
|
||||
);
|
||||
self.user_port.update(&updated_user)
|
||||
.expect("An error occurred while saving user updates.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_by_id(&self, authenticated_user_id: &Uuid, user_to_delete_id: &Uuid) -> Result<(), Error> {
|
||||
if authenticated_user_id == user_to_delete_id {
|
||||
Err(Error::new(ErrorKind::InvalidInput, "Authenticated user cannot delete himself."))?
|
||||
}
|
||||
|
||||
let is_last_user = self.user_port.count()
|
||||
.map_err(|_| Error::new(ErrorKind::Other, "An error occurred while counting existing users…"))? == 1;
|
||||
if is_last_user {
|
||||
Err(Error::new(ErrorKind::InvalidInput, "The last existing user cannot be deleted."))?
|
||||
}
|
||||
|
||||
self.user_port.delete_by_id(user_to_delete_id)
|
||||
.map_err(|_| Error::new(ErrorKind::Other, "An error occurred while deleting the user."))
|
||||
}
|
||||
|
||||
pub fn login(&self, username: &String, password: &String) -> Result<LoginResponse, Error> {
|
||||
let existing_user = self
|
||||
.user_port
|
||||
.get_by_username(username)
|
||||
.map_err(|_| Error::new(ErrorKind::InvalidInput, "Username or password incorrect."))?;
|
||||
|
||||
let user = existing_user.ok_or_else(|| Error::new(ErrorKind::InvalidInput, "Username or password incorrect."))?;
|
||||
|
||||
let passwords_match = verify(password, user.encrypted_password.as_str())
|
||||
.map_err(|_| Error::new(ErrorKind::InvalidInput, "Username or password incorrect."))?;
|
||||
|
||||
if passwords_match {
|
||||
Self::generate_login_response(&user)
|
||||
} else {
|
||||
Err(Error::new(ErrorKind::InvalidInput, "Username or password incorrect."))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_users(&self) -> Result<Vec<User>, Error> {
|
||||
let users = self
|
||||
.user_port
|
||||
.get_all()
|
||||
.expect("Unable to retrieve all users.");
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
fn encrypt_password(password: &String) -> String {
|
||||
let salt = env::var("BCRYPT_SALT").expect("BCRYPT_SALT must be set!");
|
||||
hash_with_salt(password, 12, string_to_salt(salt))
|
||||
.map(|hash_parts| hash_parts.to_string())
|
||||
.expect("Unable to encrypt password.")
|
||||
}
|
||||
|
||||
fn generate_login_response(user: &User) -> Result<LoginResponse, Error> {
|
||||
let jwt = generate_jwt_token(&user.id.to_string()).expect("Unable to generate a JWT…");
|
||||
|
||||
let response = LoginResponse {
|
||||
token_type: "Bearer".to_string(),
|
||||
access_token: jwt,
|
||||
refresh_token: RefreshToken {
|
||||
user_id: user.id,
|
||||
value: "<empty>".to_string(),
|
||||
expiration_date: Utc::now(),
|
||||
}
|
||||
};
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_salt(salt_str: String) -> [u8; 16] {
|
||||
let mut salt = [0u8; 16];
|
||||
let bytes = salt_str.as_bytes();
|
||||
let len = std::cmp::min(bytes.len(), 16);
|
||||
salt[..len].copy_from_slice(&bytes[..len]);
|
||||
salt
|
||||
}
|
||||
Reference in New Issue
Block a user