Implement account deletion
This commit is contained in:
parent
2ccee2169b
commit
930726432e
|
|
@ -24,7 +24,7 @@ class ActionResult
|
|||
return new self(self::STATUS_SUCCESS, $successMessage);
|
||||
}
|
||||
|
||||
public static function createErrorResult(string $errorMessage, array $inputData): self
|
||||
public static function createErrorResult(string $errorMessage, ?array $inputData = null): self
|
||||
{
|
||||
return new self(self::STATUS_ERROR, $errorMessage, $inputData);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,19 +3,21 @@ declare(strict_types=1);
|
|||
|
||||
namespace MailAccountAdmin\Common;
|
||||
|
||||
use RuntimeException;
|
||||
use MailAccountAdmin\Exceptions\UnauthenticatedException;
|
||||
use MailAccountAdmin\Models\AdminUser;
|
||||
use MailAccountAdmin\Repositories\AdminUserRepository;
|
||||
|
||||
class UserHelper
|
||||
{
|
||||
private SessionHelper $sessionHelper;
|
||||
private AdminUserRepository $adminUserRepository;
|
||||
private SessionHelper $sessionHelper;
|
||||
private PasswordHelper $passwordHelper;
|
||||
|
||||
public function __construct(SessionHelper $sessionHelper, AdminUserRepository $adminUserRepository)
|
||||
public function __construct(AdminUserRepository $adminUserRepository, SessionHelper $sessionHelper, PasswordHelper $passwordHelper)
|
||||
{
|
||||
$this->sessionHelper = $sessionHelper;
|
||||
$this->adminUserRepository = $adminUserRepository;
|
||||
$this->sessionHelper = $sessionHelper;
|
||||
$this->passwordHelper = $passwordHelper;
|
||||
}
|
||||
|
||||
public function isLoggedIn(): bool
|
||||
|
|
@ -27,8 +29,17 @@ class UserHelper
|
|||
{
|
||||
$userId = $this->sessionHelper->getUserId();
|
||||
if (empty($userId)) {
|
||||
throw new RuntimeException('Not logged in!');
|
||||
throw new UnauthenticatedException('Not logged in!');
|
||||
}
|
||||
return $this->adminUserRepository->getUserById($userId);
|
||||
}
|
||||
|
||||
public function confirmActionByAdminPassword(string $enteredPassword): void
|
||||
{
|
||||
$currentUser = $this->getCurrentUser();
|
||||
|
||||
if (!$this->passwordHelper->verifyPassword($enteredPassword, $currentUser->getPasswordHash())) {
|
||||
throw new UnauthenticatedException('Admin password wrong.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,8 +98,9 @@ class Dependencies
|
|||
|
||||
$container->set(UserHelper::class, function (ContainerInterface $c) {
|
||||
return new UserHelper(
|
||||
$c->get(SessionHelper::class),
|
||||
$c->get(AdminUserRepository::class),
|
||||
$c->get(SessionHelper::class),
|
||||
$c->get(PasswordHelper::class),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MailAccountAdmin\Exceptions;
|
||||
|
||||
class UnauthenticatedException extends AppException
|
||||
{
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ namespace MailAccountAdmin\Frontend\Accounts;
|
|||
use MailAccountAdmin\Common\ActionResult;
|
||||
use MailAccountAdmin\Common\SessionHelper;
|
||||
use MailAccountAdmin\Common\UserHelper;
|
||||
use MailAccountAdmin\Exceptions\AppException;
|
||||
use MailAccountAdmin\Exceptions\InputValidationError;
|
||||
use MailAccountAdmin\Frontend\BaseController;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
|
|
@ -31,7 +32,12 @@ class AccountController extends BaseController
|
|||
$queryParams = $request->getQueryParams();
|
||||
$filterByDomain = $queryParams['domain'] ?? '';
|
||||
|
||||
// Get list of all accounts
|
||||
$renderData = $this->accountHandler->listAccounts($filterByDomain);
|
||||
|
||||
// If the form has been submitted, add the result message to the render data array
|
||||
$renderData = $this->addLastActionResultToRenderData($renderData);
|
||||
|
||||
return $this->view->render($response, 'accounts.html.twig', $renderData);
|
||||
}
|
||||
|
||||
|
|
@ -55,10 +61,7 @@ class AccountController extends BaseController
|
|||
$renderData = $this->accountHandler->getPageDataForCreate();
|
||||
|
||||
// If the form has been submitted, add the result message and form input data to the render data array
|
||||
$lastActionResult = $this->sessionHelper->getLastActionResult();
|
||||
if ($lastActionResult !== null) {
|
||||
$renderData = array_merge($renderData, $lastActionResult->getRenderData());
|
||||
}
|
||||
$renderData = $this->addLastActionResultToRenderData($renderData);
|
||||
|
||||
return $this->view->render($response, 'account_create.html.twig', $renderData);
|
||||
}
|
||||
|
|
@ -100,10 +103,7 @@ class AccountController extends BaseController
|
|||
$renderData = $this->accountHandler->getAccountDataForEdit($accountId);
|
||||
|
||||
// If the form has been submitted, add the result message and form input data to the render data array
|
||||
$lastActionResult = $this->sessionHelper->getLastActionResult();
|
||||
if ($lastActionResult !== null) {
|
||||
$renderData = array_merge($renderData, $lastActionResult->getRenderData());
|
||||
}
|
||||
$renderData = $this->addLastActionResultToRenderData($renderData);
|
||||
|
||||
return $this->view->render($response, 'account_edit.html.twig', $renderData);
|
||||
}
|
||||
|
|
@ -145,13 +145,47 @@ class AccountController extends BaseController
|
|||
// Get account data and list of aliases from database
|
||||
$renderData = $this->accountHandler->getAccountDataForDelete($accountId);
|
||||
|
||||
// If the form has been submitted, add the result message to the render data array
|
||||
$renderData = $this->addLastActionResultToRenderData($renderData);
|
||||
|
||||
return $this->view->render($response, 'account_delete.html.twig', $renderData);
|
||||
}
|
||||
|
||||
public function deleteAccount(Request $request, Response $response, array $args): Response
|
||||
{
|
||||
// TODO: just a placeholder
|
||||
$this->view->getEnvironment()->addGlobal('error', 'Not implemented yet!');
|
||||
return $this->showAccountDelete($request, $response, $args);
|
||||
// Parse URL arguments and form data
|
||||
$accountId = (int)$args['id'];
|
||||
$formData = $request->getParsedBody();
|
||||
|
||||
try {
|
||||
// Confirm action by entering the admin password
|
||||
$this->userHelper->confirmActionByAdminPassword($formData['admin_password'] ?? '');
|
||||
|
||||
// Delete account
|
||||
$deleteResult = $this->accountHandler->deleteAccount($accountId);
|
||||
|
||||
// Save success result
|
||||
$successMessage = "Account <i>{$deleteResult['username']}</i> ";
|
||||
$deletedAliasCount = $deleteResult['deleted_alias_count'];
|
||||
if ($deletedAliasCount > 0) {
|
||||
$aliasWordPlural = $deletedAliasCount > 1 ? 'aliases' : 'alias';
|
||||
$successMessage .= "and {$deletedAliasCount} {$aliasWordPlural} were deleted.";
|
||||
} else {
|
||||
$successMessage .= "was deleted.";
|
||||
}
|
||||
$this->sessionHelper->setLastActionResult(ActionResult::createSuccessResult($successMessage));
|
||||
|
||||
// Redirect to account list (where the success message will be displayed)
|
||||
$redirectTarget = '/accounts';
|
||||
} catch (AppException $e) {
|
||||
// Save error result
|
||||
$this->sessionHelper->setLastActionResult(ActionResult::createErrorResult($e->getMessage()));
|
||||
|
||||
// Stay on delete page
|
||||
$redirectTarget = '/accounts/' . $accountId . '/delete';
|
||||
}
|
||||
|
||||
// Redirect to edit form page via GET (PRG)
|
||||
return $response->withHeader('Location', $redirectTarget)->withStatus(303);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ class AccountHandler
|
|||
|
||||
// Remove existing alias for new username (if wanted)
|
||||
if ($editData->getUsernameReplaceAlias() && $aliasNeedsToBeReplaced) {
|
||||
$this->aliasRepository->removeAlias($accountId, $newUsername);
|
||||
$this->aliasRepository->deleteAlias($accountId, $newUsername);
|
||||
}
|
||||
|
||||
// Commit database transaction
|
||||
|
|
@ -238,4 +238,31 @@ class AccountHandler
|
|||
'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,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,4 +23,13 @@ class BaseController
|
|||
$twigEnv = $view->getEnvironment();
|
||||
$twigEnv->addGlobal('current_user_name', $userHelper->isLoggedIn() ? $userHelper->getCurrentUser()->getUsername() : null);
|
||||
}
|
||||
|
||||
protected function addLastActionResultToRenderData(array $renderData): array
|
||||
{
|
||||
$lastActionResult = $this->sessionHelper->getLastActionResult();
|
||||
if ($lastActionResult !== null) {
|
||||
$renderData = array_merge($renderData, $lastActionResult->getRenderData());
|
||||
}
|
||||
return $renderData;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,4 +124,10 @@ class AccountRepository extends BaseRepository
|
|||
$statement = $this->pdo->prepare($query);
|
||||
$statement->execute($queryParams);
|
||||
}
|
||||
|
||||
public function deleteAccountWithId(int $accountId): void
|
||||
{
|
||||
$statement = $this->pdo->prepare('DELETE FROM mail_users WHERE user_id = :user_id LIMIT 1');
|
||||
$statement->execute(['user_id' => $accountId]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class AliasRepository extends BaseRepository
|
|||
]);
|
||||
}
|
||||
|
||||
public function removeAlias(int $userId, string $mailAddress): void
|
||||
public function deleteAlias(int $userId, string $mailAddress): void
|
||||
{
|
||||
// Check user ID in WHERE clause to make sure we don't accidentally delete someone else's alias
|
||||
$statement = $this->pdo->prepare('DELETE FROM mail_aliases WHERE user_id = :user_id AND mail_address = :mail_address LIMIT 1');
|
||||
|
|
@ -61,4 +61,13 @@ class AliasRepository extends BaseRepository
|
|||
'mail_address' => $mailAddress,
|
||||
]);
|
||||
}
|
||||
|
||||
public function deleteAllAliasesForUserId(int $userId): int
|
||||
{
|
||||
$statement = $this->pdo->prepare('DELETE FROM mail_aliases WHERE user_id = :user_id');
|
||||
$statement->execute([
|
||||
'user_id' => $userId,
|
||||
]);
|
||||
return $statement->rowCount();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,21 +15,16 @@
|
|||
|
||||
<h3>Delete account</h3>
|
||||
|
||||
<p>You are about to delete the mail account "{{ accountUsername }}" including the following aliases:</p>
|
||||
<p>You are about to delete the mail account "{{ accountUsername }}" <b>including</b> the following aliases:</p>
|
||||
<ul>
|
||||
{% for alias in aliases %}
|
||||
<li>{{ alias.getMailAddress() }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<p><b>Note:</b> This will only delete the user entry from the database. Mail data will not be deleted!</p>
|
||||
<p><b>Note:</b> This will only delete the account entry from the database. Mail data will not be deleted!</p>
|
||||
|
||||
{% if error is defined %}
|
||||
<div class="error_box">
|
||||
<h4>Error:</h4>
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ include('includes/form_result_box.html.twig') }}
|
||||
|
||||
<div class="confirmation_box">
|
||||
<p><b>Please confirm that you want to delete this account by entering your password.</b></p>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
<h3>List of accounts</h3>
|
||||
|
||||
{{ include('includes/form_result_box.html.twig') }}
|
||||
|
||||
<div class="filter_options">
|
||||
<form action="/accounts" method="GET">
|
||||
<h4>Filter:</h4>
|
||||
|
|
|
|||
Loading…
Reference in New Issue