291 lines
10 KiB
PHP
291 lines
10 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace MailAccountAdmin\Frontend\Accounts;
|
|
|
|
use MailAccountAdmin\Common\PasswordHelper;
|
|
use MailAccountAdmin\Exceptions\AccountNotFoundException;
|
|
use MailAccountAdmin\Exceptions\InputValidationError;
|
|
use MailAccountAdmin\Repositories\AccountRepository;
|
|
use MailAccountAdmin\Repositories\AliasRepository;
|
|
use MailAccountAdmin\Repositories\DomainRepository;
|
|
|
|
class AccountHandler
|
|
{
|
|
private AccountRepository $accountRepository;
|
|
private AliasRepository $aliasRepository;
|
|
private DomainRepository $domainRepository;
|
|
private PasswordHelper $passwordHelper;
|
|
|
|
public function __construct(AccountRepository $accountRepository, AliasRepository $aliasRepository, DomainRepository $domainRepository,
|
|
PasswordHelper $passwordHelper)
|
|
{
|
|
$this->accountRepository = $accountRepository;
|
|
$this->aliasRepository = $aliasRepository;
|
|
$this->domainRepository = $domainRepository;
|
|
$this->passwordHelper = $passwordHelper;
|
|
}
|
|
|
|
|
|
// -- /accounts - List all accounts
|
|
|
|
public function listAccounts(string $filterByDomain): array
|
|
{
|
|
$accountList = $this->accountRepository->fetchAccountList($filterByDomain);
|
|
|
|
return [
|
|
'filterDomain' => $filterByDomain,
|
|
'accountList' => $accountList,
|
|
];
|
|
}
|
|
|
|
|
|
// -- /accounts/{id} - Show account details
|
|
|
|
public function getAccountDetails(int $accountId): array
|
|
{
|
|
// Get account data from database
|
|
$account = $this->accountRepository->fetchAccountById($accountId);
|
|
|
|
// Don't display the password hash, but at least the type of hash (used hash algorithm)
|
|
$passwordHashType = $this->passwordHelper->getPasswordHashType($account->getPasswordHash());
|
|
|
|
// Get list of aliases for this account
|
|
$aliases = $this->aliasRepository->fetchAliasesForUserId($accountId);
|
|
|
|
return [
|
|
'id' => $accountId,
|
|
'accountUsername' => $account->getUsername(),
|
|
'account' => $account,
|
|
'passwordHashType' => $passwordHashType,
|
|
'aliases' => $aliases,
|
|
];
|
|
}
|
|
|
|
|
|
// -- /accounts/new - Create new account
|
|
|
|
public function getPageDataForCreate(): array
|
|
{
|
|
return [
|
|
'domainList' => array_keys($this->domainRepository->fetchDomainList()),
|
|
];
|
|
}
|
|
|
|
public function createNewAccount(AccountCreateData $createData): array
|
|
{
|
|
// Check if new username is still available
|
|
$username = $createData->getUsername();
|
|
if (!$this->accountRepository->checkUsernameAvailable($username) || !$this->aliasRepository->checkAliasAvailable($username)) {
|
|
throw new InputValidationError("Username \"$username\" is not available.");
|
|
}
|
|
|
|
// Array returned by the function on success
|
|
$returnData = [
|
|
'username' => $username,
|
|
];
|
|
|
|
// Hash new password
|
|
$password = $createData->getPassword();
|
|
if ($password === null) {
|
|
$password = $this->passwordHelper->generateRandomPassword();
|
|
$returnData['generatedPassword'] = $password;
|
|
}
|
|
$passwordHash = $this->passwordHelper->hashPassword($password);
|
|
|
|
// Construct home directory from username if necessary
|
|
if ($createData->getHomeDir() !== null) {
|
|
$homeDir = $createData->getHomeDir();
|
|
} else {
|
|
[$localPart, $domainPart] = explode('@', $username, 2);
|
|
$homeDir = $domainPart . '/' . $localPart;
|
|
}
|
|
|
|
// Create account in database
|
|
$returnData['id'] = $this->accountRepository->insertAccount(
|
|
$username,
|
|
$passwordHash,
|
|
$createData->getActive(),
|
|
$homeDir,
|
|
$createData->getMemo()
|
|
);
|
|
|
|
return $returnData;
|
|
}
|
|
|
|
|
|
// -- /accounts/{id}/edit - Edit account data
|
|
|
|
public function getAccountDataForEdit(int $accountId): array
|
|
{
|
|
// Get account data from database
|
|
$account = $this->accountRepository->fetchAccountById($accountId);
|
|
|
|
return [
|
|
'id' => $accountId,
|
|
'accountUsername' => $account->getUsername(),
|
|
'account' => $account,
|
|
];
|
|
}
|
|
|
|
public function editAccountData(int $accountId, AccountEditData $editData): array
|
|
{
|
|
// Array returned by the function on success
|
|
$returnData = [];
|
|
|
|
// Check if account exists
|
|
try {
|
|
$account = $this->accountRepository->fetchAccountById($accountId);
|
|
} catch (AccountNotFoundException $e) {
|
|
throw new InputValidationError('Account with ID ' . $accountId . ' does not exist!');
|
|
}
|
|
|
|
$newUsername = $editData->getUsername();
|
|
if ($newUsername === $account->getUsername()) {
|
|
// Username is unchanged
|
|
$newUsername = null;
|
|
}
|
|
|
|
// This variable will be set to true if the user wants to change their username, has an existing alias with this username,
|
|
// and checked the "replace existing alias" option.
|
|
$aliasNeedsToBeReplaced = false;
|
|
|
|
// Check if new username is still available
|
|
if ($newUsername !== null) {
|
|
$newUsernameAvailable = true;
|
|
|
|
// Check if account with this username already exists
|
|
if (!$this->accountRepository->checkUsernameAvailable($newUsername)) {
|
|
$newUsernameAvailable = false;
|
|
}
|
|
|
|
// Check if alias with this username/address already exists
|
|
if (!$this->aliasRepository->checkAliasAvailable($newUsername)) {
|
|
$newUsernameAvailable = false;
|
|
|
|
// Alias already exists. If user wants to replace an existing alias, check if the alias belongs to this user.
|
|
if ($editData->getUsernameReplaceAlias()) {
|
|
$existingAlias = $this->aliasRepository->fetchAliasByAddress($newUsername);
|
|
if ($existingAlias->getUserId() === $accountId) {
|
|
$aliasNeedsToBeReplaced = true;
|
|
$newUsernameAvailable = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$newUsernameAvailable) {
|
|
throw new InputValidationError("Username \"$newUsername\" is not available.");
|
|
}
|
|
}
|
|
|
|
// Start database transaction
|
|
$this->accountRepository->beginTransaction();
|
|
|
|
// Create alias for old username (if wanted)
|
|
if ($editData->getUsernameCreateAlias()) {
|
|
$oldUsername = $account->getUsername();
|
|
if (!$this->aliasRepository->checkAliasAvailable($oldUsername)) {
|
|
throw new InputValidationError("Alias \"$oldUsername\" cannot be created: Alias already exists.");
|
|
}
|
|
$this->aliasRepository->createNewAlias($accountId, $oldUsername);
|
|
}
|
|
|
|
// Generate new password (if wanted)
|
|
$newPassword = $editData->getPassword();
|
|
if ($editData->getPasswordGenerateRandom()) {
|
|
$newPassword = $this->passwordHelper->generateRandomPassword();
|
|
$returnData['generatedPassword'] = $newPassword;
|
|
}
|
|
|
|
// Hash new password
|
|
$newPasswordHash = $newPassword !== null ? $this->passwordHelper->hashPassword($newPassword) : null;
|
|
|
|
// Update account in database
|
|
$this->accountRepository->updateAccountWithId(
|
|
$accountId,
|
|
$newUsername,
|
|
$newPasswordHash,
|
|
$editData->getActive(),
|
|
$editData->getHomeDir(),
|
|
$editData->getMemo()
|
|
);
|
|
|
|
// Remove existing alias for new username (if wanted)
|
|
if ($editData->getUsernameReplaceAlias() && $aliasNeedsToBeReplaced) {
|
|
$this->aliasRepository->deleteAlias($accountId, $newUsername);
|
|
}
|
|
|
|
// Commit database transaction
|
|
$this->accountRepository->commitTransaction();
|
|
|
|
return $returnData;
|
|
}
|
|
|
|
|
|
// -- /accounts/{id}/delete - Delete account
|
|
|
|
public function getAccountDataForDelete(int $accountId): array
|
|
{
|
|
// Get account data from database
|
|
$account = $this->accountRepository->fetchAccountById($accountId);
|
|
|
|
// Get list of aliases for this account
|
|
$aliases = $this->aliasRepository->fetchAliasesForUserId($accountId);
|
|
|
|
return [
|
|
'id' => $accountId,
|
|
'accountUsername' => $account->getUsername(),
|
|
'aliases' => $aliases,
|
|
];
|
|
}
|
|
|
|
public function deleteAccount(int $accountId): array
|
|
{
|
|
// Check if account exists
|
|
try {
|
|
$account = $this->accountRepository->fetchAccountById($accountId);
|
|
} catch (AccountNotFoundException $e) {
|
|
throw new InputValidationError('Account with ID ' . $accountId . ' does not exist!');
|
|
}
|
|
|
|
// Start database transaction
|
|
$this->accountRepository->beginTransaction();
|
|
|
|
// Delete all aliases associated with this account
|
|
$deleteAliasCount = $this->aliasRepository->deleteAllAliasesForUserId($accountId);
|
|
|
|
// Delete account from database
|
|
$this->accountRepository->deleteAccountWithId($accountId);
|
|
|
|
// Commit database transaction
|
|
$this->accountRepository->commitTransaction();
|
|
|
|
return [
|
|
'username' => $account->getUsername(),
|
|
'deleted_alias_count' => $deleteAliasCount,
|
|
];
|
|
}
|
|
|
|
|
|
// -- /accounts/{id}/addalias - Create a new alias for the account
|
|
|
|
public function addAliasToAccount(int $accountId, AccountAddAliasData $aliasAddData): void
|
|
{
|
|
// Check if account exists
|
|
try {
|
|
$this->accountRepository->fetchAccountById($accountId);
|
|
} catch (AccountNotFoundException $e) {
|
|
throw new InputValidationError('Account with ID ' . $accountId . ' does not exist!');
|
|
}
|
|
|
|
// Check if alias address is still available
|
|
$address = $aliasAddData->getAliasAddress();
|
|
if (!$this->accountRepository->checkUsernameAvailable($address) || !$this->aliasRepository->checkAliasAvailable($address)) {
|
|
throw new InputValidationError("Alias address \"$address\" is not available.");
|
|
}
|
|
|
|
// Create alias in database
|
|
$this->aliasRepository->createNewAlias($accountId, $address);
|
|
}
|
|
}
|