use super::{Account, AccountRepository};
use crate::accounts::{AccountOperation, AccountsError};
#[cfg(feature = "audit-logging")]
use crate::audit;
use crate::authz::AccessHierarchy;
use crate::errors::{Error, Result};
use crate::hashing::argon2::Argon2Hasher;
use crate::permissions::Permissions;
use crate::secrets::{Secret, SecretRepository};
use std::sync::Arc;
use tracing::debug;
pub struct AccountInsertService<R, G>
where
R: AccessHierarchy + Eq,
G: Eq,
{
user_id: String,
secret: String,
roles: Vec<R>,
groups: Vec<G>,
permissions: Permissions,
}
impl<R, G> AccountInsertService<R, G>
where
R: AccessHierarchy + Eq,
G: Eq + Clone,
{
pub fn insert(user_id: &str, secret: &str) -> Self {
Self {
user_id: user_id.to_string(),
secret: secret.to_string(),
roles: vec![],
groups: vec![],
permissions: Permissions::new(),
}
}
pub fn with_roles(self, roles: Vec<R>) -> Self {
Self { roles, ..self }
}
pub fn with_groups(self, groups: Vec<G>) -> Self {
Self { groups, ..self }
}
pub fn with_permissions(self, permissions: Permissions) -> Self {
Self {
permissions,
..self
}
}
pub async fn into_repositories<AccRepo, SecRepo>(
self,
account_repository: Arc<AccRepo>,
secret_repository: Arc<SecRepo>,
) -> Result<Option<Account<R, G>>>
where
AccRepo: AccountRepository<R, G>,
SecRepo: SecretRepository,
{
let account = Account::new(&self.user_id, &self.roles, &self.groups)
.with_permissions(self.permissions);
debug!("Created account.");
let Some(account) = account_repository.store_account(account).await? else {
#[cfg(feature = "audit-logging")]
{
audit::account_insert_failure(&self.user_id, "account_repo_none");
}
return Err(Error::Accounts(AccountsError::operation(
AccountOperation::Create,
"Account repository returned None on insertion",
Some(self.user_id.clone()),
)));
};
#[cfg(feature = "audit-logging")]
{
audit::account_created(&self.user_id, &account.account_id);
}
debug!("Stored account in account repository.");
let id = &account.account_id;
let secret = Secret::new(id, &self.secret, Argon2Hasher::new_recommended()?)?;
if !secret_repository.store_secret(secret).await? {
#[cfg(feature = "audit-logging")]
{
audit::account_insert_failure(&self.user_id, "secret_store_false");
}
Err(Error::Accounts(AccountsError::operation(
AccountOperation::Create,
"Storing secret in repository returned false",
Some(account.account_id.to_string()),
)))
} else {
debug!("Stored secret in secret repository.");
Ok(Some(account))
}
}
}