vanikam/src/identity/application/services/register_user/service.rs

191 lines
6.4 KiB
Rust

// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// - TODO: If UID == 0; set admin
use derive_builder::Builder;
use super::*;
use crate::identity::application::port::output::{
db::{create_verification_secret::*, email_exists::*, username_exists::*},
mailer::account_validation_link::*,
};
use crate::utils::random_string::*;
pub const SECRET_LEN: usize = 20;
pub const REGISTRATION_SECRET_PURPOSE: &str = "account_validation";
#[derive(Builder)]
pub struct RegisterUserService {
db_email_exists_adapter: EmailExistsOutDBPortObj,
db_username_exists_adapter: UsernameExistsOutDBPortObj,
db_create_verification_secret_adapter: CreateVerificationSecretOutDBPortObj,
mailer_account_validation_link_adapter: AccountValidationLinkOutMailerPortObj,
random_string_adapter: GenerateRandomStringInterfaceObj,
}
#[async_trait::async_trait]
impl RegisterUserUseCase for RegisterUserService {
async fn register_user(
&self,
cmd: command::RegisterUserCommand,
) -> IdentityResult<events::UserRegisteredEvent> {
if self
.db_username_exists_adapter
.username_exists(cmd.username())
.await
.unwrap()
{
return Err(IdentityError::DuplicateUsername);
}
if self
.db_email_exists_adapter
.email_exists(cmd.email())
.await
.unwrap()
{
return Err(IdentityError::DuplicateEmail);
}
let secret = self.random_string_adapter.get_random(SECRET_LEN);
self.db_create_verification_secret_adapter
.create_verification_secret(
CreateSecretMsgBuilder::default()
.secret(secret.clone())
.username(cmd.username().into())
.build()
.unwrap(),
)
.await
.unwrap();
self.mailer_account_validation_link_adapter
.account_validation_link(cmd.email(), cmd.username(), &secret)
.await
.unwrap();
Ok(events::UserRegisteredEventBuilder::default()
.username(cmd.username().into())
.email(cmd.email().into())
.hashed_password(cmd.hashed_password().into())
.is_verified(false)
.email_verified(false)
.is_admin(false) // TODO: if UID == 0; set true
.build()
.unwrap())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::bdd::*;
use crate::utils::random_string::tests::*;
#[actix_rt::test]
async fn test_service() {
let username = "realaravinth";
let email = format!("{username}@example.com");
let password = "password";
let config = argon2_creds::Config::default();
let cmd = command::RegisterUserCommand::new(
username.into(),
email.clone(),
password.into(),
password.into(),
&config,
)
.unwrap();
// happy case
{
let s = RegisterUserServiceBuilder::default()
.db_username_exists_adapter(mock_username_exists_db_port(
IS_CALLED_ONLY_ONCE,
RETURNS_FALSE,
))
.db_create_verification_secret_adapter(mock_create_verification_secret_db_port(
IS_CALLED_ONLY_ONCE,
))
.db_email_exists_adapter(mock_email_exists_db_port(
IS_CALLED_ONLY_ONCE,
RETURNS_FALSE,
))
.random_string_adapter(mock_generate_random_string(
IS_CALLED_ONLY_ONCE,
RETURNS_RANDOM_STRING.into(),
))
.mailer_account_validation_link_adapter(mock_account_validation_link_db_port(
IS_CALLED_ONLY_ONCE,
))
.build()
.unwrap();
let res = s.register_user(cmd.clone()).await.unwrap();
assert_eq!(res.username(), cmd.username());
assert_eq!(res.email(), cmd.email());
assert!(!res.is_admin());
assert!(argon2_creds::Config::verify(res.hashed_password(), password).unwrap())
}
// username exists
{
let s = RegisterUserServiceBuilder::default()
.db_username_exists_adapter(mock_username_exists_db_port(
IS_CALLED_ONLY_ONCE,
RETURNS_TRUE,
))
.db_email_exists_adapter(mock_email_exists_db_port(IS_NEVER_CALLED, RETURNS_FALSE))
.db_create_verification_secret_adapter(mock_create_verification_secret_db_port(
IS_NEVER_CALLED,
))
.random_string_adapter(mock_generate_random_string(
IS_NEVER_CALLED,
RETURNS_RANDOM_STRING.into(),
))
.mailer_account_validation_link_adapter(mock_account_validation_link_db_port(
IS_NEVER_CALLED,
))
.build()
.unwrap();
assert_eq!(
s.register_user(cmd.clone()).await.err(),
Some(IdentityError::DuplicateUsername)
);
}
// email exists
{
let s = RegisterUserServiceBuilder::default()
.db_username_exists_adapter(mock_username_exists_db_port(
IS_CALLED_ONLY_ONCE,
RETURNS_FALSE,
))
.db_create_verification_secret_adapter(mock_create_verification_secret_db_port(
IS_NEVER_CALLED,
))
.db_email_exists_adapter(mock_email_exists_db_port(
IS_CALLED_ONLY_ONCE,
RETURNS_TRUE,
))
.random_string_adapter(mock_generate_random_string(
IS_NEVER_CALLED,
RETURNS_RANDOM_STRING.into(),
))
.mailer_account_validation_link_adapter(mock_account_validation_link_db_port(
IS_NEVER_CALLED,
))
.build()
.unwrap();
assert_eq!(
s.register_user(cmd.clone()).await.err(),
Some(IdentityError::DuplicateEmail)
);
}
}
}