feat: email recipient whitelist

This commit is contained in:
fromme committed 2026-09-26 12:13:37 +02:00
1 parent 4cab57d136
commit 7cf39850e6
4 files changed
+308

No files matched your search

@@ -0,0 +1,147 @@
<?php
declare(strict_types=1);
namespace App\Tests\EventListener;
use App\EventListener\EmailRecipientWhitelistSubscriber;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\Event\MessageEvent;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
/**
* The guard that keeps staging from mailing real teamers: everything not whitelisted has to
* leave the envelope, and a mail with nothing left must not be sent at all.
*/
class EmailRecipientWhitelistSubscriberTest extends TestCase
{
public function testKeepsEnvelopeUntouchedWhenEveryRecipientIsWhitelisted(): void
{
$event = $this->event(['[email protected]', '[email protected]']);
$this->subscriber(['.*@ep-reisen\.de', 'qa@example\.com'])->onMessage($event);
self::assertFalse($event->isRejected());
self::assertSame(
['[email protected]', '[email protected]'],
$this->addresses($event)
);
}
public function testStripsTheRecipientsThatAreNotWhitelisted(): void
{
$event = $this->event(['[email protected]', '[email protected]', '[email protected]']);
$this->subscriber(['.*@ep-reisen\.de'])->onMessage($event);
self::assertFalse($event->isRejected());
self::assertSame(['[email protected]'], $this->addresses($event));
}
public function testRejectsTheMailWhenNoRecipientIsWhitelisted(): void
{
$event = $this->event(['[email protected]', '[email protected]']);
$this->subscriber(['.*@ep-reisen\.de'])->onMessage($event);
self::assertTrue($event->isRejected());
}
public function testDoesNothingWithoutAWhitelist(): void
{
$event = $this->event(['[email protected]']);
$this->subscriber([])->onMessage($event);
self::assertFalse($event->isRejected());
self::assertSame(['[email protected]'], $this->addresses($event));
}
public function testPatternsAreAnchoredToTheWholeAddress(): void
{
$event = $this->event(['[email protected]']);
$this->subscriber(['.*@ep-reisen\.de'])->onMessage($event);
self::assertTrue($event->isRejected());
}
public function testMatchingIsCaseInsensitive(): void
{
$event = $this->event(['[email protected]']);
$this->subscriber(['.*@ep-reisen\.de'])->onMessage($event);
self::assertFalse($event->isRejected());
self::assertSame(['[email protected]'], $this->addresses($event));
}
public function testBlankPatternsAreIgnoredInsteadOfDroppingEveryMail(): void
{
$event = $this->event(['[email protected]']);
$this->subscriber([' ', '', "\t"])->onMessage($event);
self::assertFalse($event->isRejected());
self::assertSame(['[email protected]'], $this->addresses($event));
}
public function testPatternsAreTrimmed(): void
{
$event = $this->event(['[email protected]', '[email protected]']);
$this->subscriber([' .*@ep-reisen\\.de '])->onMessage($event);
self::assertFalse($event->isRejected());
self::assertSame(['[email protected]'], $this->addresses($event));
}
public function testAnInvalidPatternFailsLoudlyOnConstruction(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid recipient whitelist pattern ".*@ep-reisen\\.de("');
$this->subscriber(['.*@ep-reisen\\.de(']);
}
/**
* @param string[] $allowedRecipients
*/
private function subscriber(array $allowedRecipients): EmailRecipientWhitelistSubscriber
{
return new EmailRecipientWhitelistSubscriber($allowedRecipients, new NullLogger());
}
/**
* @param string[] $recipients
*/
private function event(array $recipients): MessageEvent
{
$email = (new Email())
->from('[email protected]')
->to(...$recipients)
->subject('Honorarvertrag fällig')
->text('...')
;
return new MessageEvent(
$email,
new Envelope(new Address('[email protected]'), Address::createArray($recipients)),
'main'
);
}
/**
* @return string[]
*/
private function addresses(MessageEvent $event): array
{
return array_map(
static fn (Address $address) => $address->getAddress(),
$event->getEnvelope()->getRecipients()
);
}
}