Implement wildcard aliases
This commit is contained in:
parent
a01ddd9b22
commit
6400962a44
9 changed files with 141 additions and 25 deletions
|
|
@ -44,25 +44,42 @@ abstract class FormData
|
|||
|
||||
// Input validation - Application specific validators
|
||||
|
||||
protected static function validateUsername(string $username, bool $required = true, string $fieldName = 'Username'): ?string
|
||||
private static function validateMailAddress(string $address, bool $required = true, string $fieldName = 'Mail address'): ?string
|
||||
{
|
||||
if (!$required && $username === '') {
|
||||
// Note: This validator allows '%' as wildcard character inside mail addresses.
|
||||
if (!$required && $address === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$username = strtolower(
|
||||
self::validateString($username, 3, 100, $fieldName)
|
||||
$address = strtolower(
|
||||
self::validateString($address, 3, 100, $fieldName)
|
||||
);
|
||||
|
||||
if (!preg_match('/^[a-z0-9._+-]+@[a-z0-9.-]+$/', $username) || preg_match('/^\\.|\\.\\.|\\.@|@\\.|\\.$/', $username)) {
|
||||
if (!preg_match('/^[a-z0-9%._+-]+@[a-z0-9%.-]+$/', $address) || preg_match('/^\\.|\\.\\.|\\.@|@\\.|\\.$/', $address)) {
|
||||
throw new InputValidationError("$fieldName is not valid (must be a valid mail address).");
|
||||
}
|
||||
return $address;
|
||||
}
|
||||
|
||||
protected static function validateUsername(string $username, bool $required = true): ?string
|
||||
{
|
||||
$username = self::validateMailAddress($username, $required, 'Username');
|
||||
|
||||
if (strpos($username, '%') !== false) {
|
||||
throw new InputValidationError('Username must not contain the wildcard character "%" (use a wildcard alias instead).');
|
||||
}
|
||||
return $username;
|
||||
}
|
||||
|
||||
protected static function validateAliasAddress(string $aliasAddress): string
|
||||
protected static function validateAliasAddress(string $aliasAddress, bool $isWildcard): string
|
||||
{
|
||||
return self::validateUsername($aliasAddress, true, 'Alias address');
|
||||
$aliasAddress = self::validateMailAddress($aliasAddress, true, 'Alias address');
|
||||
|
||||
// Check if the address contains a wildcard character
|
||||
if (!$isWildcard && strpos($aliasAddress, '%') !== false) {
|
||||
throw new InputValidationError('Non-wildcard alias address must not contain "%" character.');
|
||||
}
|
||||
return $aliasAddress;
|
||||
}
|
||||
|
||||
protected static function validatePassword(string $password, string $passwordRepeat, bool $required = true): ?string
|
||||
|
|
|
|||
|
|
@ -4,20 +4,34 @@ declare(strict_types=1);
|
|||
namespace MailAccountAdmin\Frontend\Accounts;
|
||||
|
||||
use MailAccountAdmin\Common\FormData;
|
||||
use MailAccountAdmin\Exceptions\InputValidationError;
|
||||
|
||||
class AccountAddAliasData extends FormData
|
||||
{
|
||||
private string $aliasAddress;
|
||||
private bool $isWildcard;
|
||||
private int $wildcardPriority;
|
||||
|
||||
private function __construct(string $aliasAddress)
|
||||
private function __construct(string $aliasAddress, bool $isWildcard, int $wildcardPriority)
|
||||
{
|
||||
if ($isWildcard && $wildcardPriority === 0) {
|
||||
throw new InputValidationError('Wildcard alias must have a wildcard priority other than 0.');
|
||||
} elseif (!$isWildcard) {
|
||||
$wildcardPriority = 0;
|
||||
}
|
||||
|
||||
$this->aliasAddress = $aliasAddress;
|
||||
$this->isWildcard = $isWildcard;
|
||||
$this->wildcardPriority = $wildcardPriority;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $raw): self
|
||||
{
|
||||
$isWildcard = self::validateBoolOption(trim($raw['is_wildcard'] ?? ''));
|
||||
return new self(
|
||||
self::validateAliasAddress(trim($raw['alias_address'] ?? '')),
|
||||
self::validateAliasAddress(trim($raw['alias_address'] ?? ''), $isWildcard),
|
||||
$isWildcard,
|
||||
self::validateInteger(trim($raw['wildcard_priority'] ?? '')),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -25,4 +39,14 @@ class AccountAddAliasData extends FormData
|
|||
{
|
||||
return $this->aliasAddress;
|
||||
}
|
||||
|
||||
public function isWildcard(): bool
|
||||
{
|
||||
return $this->isWildcard;
|
||||
}
|
||||
|
||||
public function getWildcardPriority(): int
|
||||
{
|
||||
return $this->wildcardPriority;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -278,14 +278,22 @@ class AccountHandler
|
|||
// Check if account exists
|
||||
$this->ensureAccountExists($accountId);
|
||||
|
||||
$unescapedAddress = $address = $aliasAddData->getAliasAddress();
|
||||
$wildcardPriority = 0;
|
||||
|
||||
if ($aliasAddData->isWildcard()) {
|
||||
// If it's a wildcard alias, escape underscores
|
||||
$address = str_replace('_', '\\_', $address);
|
||||
$wildcardPriority = $aliasAddData->getWildcardPriority();
|
||||
}
|
||||
|
||||
// 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.");
|
||||
throw new InputValidationError("Alias address \"$unescapedAddress\" is not available.");
|
||||
}
|
||||
|
||||
// Create alias in database
|
||||
$this->aliasRepository->createNewAlias($accountId, $address);
|
||||
$this->aliasRepository->createNewAlias($accountId, $address, $wildcardPriority);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,14 +10,23 @@ class Alias
|
|||
private int $id;
|
||||
private int $userId;
|
||||
private string $mailAddress;
|
||||
private int $wildcardPriority;
|
||||
private DateTimeImmutable $createdAt;
|
||||
private DateTimeImmutable $modifiedAt;
|
||||
|
||||
private function __construct(int $id, int $userId, string $mailAddress, DateTimeImmutable $createdAt, DateTimeImmutable $modifiedAt)
|
||||
private function __construct(
|
||||
int $id,
|
||||
int $userId,
|
||||
string $mailAddress,
|
||||
int $wildcardPriority,
|
||||
DateTimeImmutable $createdAt,
|
||||
DateTimeImmutable $modifiedAt
|
||||
)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->userId = $userId;
|
||||
$this->mailAddress = $mailAddress;
|
||||
$this->wildcardPriority = $wildcardPriority;
|
||||
$this->createdAt = $createdAt;
|
||||
$this->modifiedAt = $modifiedAt;
|
||||
}
|
||||
|
|
@ -28,6 +37,7 @@ class Alias
|
|||
(int)$data['alias_id'],
|
||||
(int)$data['user_id'],
|
||||
$data['mail_address'],
|
||||
(int)$data['wildcard_priority'],
|
||||
new DateTimeImmutable($data['created_at']),
|
||||
new DateTimeImmutable($data['modified_at']),
|
||||
);
|
||||
|
|
@ -43,11 +53,24 @@ class Alias
|
|||
return $this->userId;
|
||||
}
|
||||
|
||||
public function getMailAddress(): string
|
||||
public function getMailAddress(bool $unescape = true): string
|
||||
{
|
||||
if ($this->isWildcard() && $unescape) {
|
||||
return str_replace('\\_', '_', $this->mailAddress);
|
||||
}
|
||||
return $this->mailAddress;
|
||||
}
|
||||
|
||||
public function isWildcard(): bool
|
||||
{
|
||||
return $this->wildcardPriority !== 0;
|
||||
}
|
||||
|
||||
public function getWildcardPriority(): int
|
||||
{
|
||||
return $this->wildcardPriority;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
|
|
|
|||
|
|
@ -43,12 +43,20 @@ class AliasRepository extends BaseRepository
|
|||
return $statement->rowCount() === 0;
|
||||
}
|
||||
|
||||
public function createNewAlias(int $userId, string $mailAddress): void
|
||||
public function createNewAlias(int $userId, string $mailAddress, int $wildcardPriority = 0): void
|
||||
{
|
||||
$statement = $this->pdo->prepare('INSERT INTO mail_aliases (user_id, mail_address) VALUES (:user_id, :mail_address)');
|
||||
$query = '
|
||||
INSERT INTO mail_aliases
|
||||
(user_id, mail_address, wildcard_priority)
|
||||
VALUES
|
||||
(:user_id, :mail_address, :wildcard_priority)
|
||||
';
|
||||
|
||||
$statement = $this->pdo->prepare($query);
|
||||
$statement->execute([
|
||||
'user_id' => $userId,
|
||||
'mail_address' => $mailAddress,
|
||||
'wildcard_priority' => $wildcardPriority,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue