diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 1d15c93611f7b..5e9ed8c31e0a7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -2126,6 +2126,15 @@ private function addMailerSection(ArrayNodeDefinition $rootNode, callable $enabl ->end() ->prototype('scalar')->end() ->end() + ->arrayNode('recipients_allowed') + ->info('A list of regular expressions that allow recipients when "recipients" option is defined.') + ->performNoDeepMerging() + ->beforeNormalization() + ->ifArray() + ->then(fn ($v) => array_filter(array_values($v))) + ->end() + ->prototype('scalar')->end() + ->end() ->end() ->end() ->arrayNode('headers') diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index d2a2afadde5a9..3d1bfd3650cbe 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2669,6 +2669,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co $envelopeListener = $container->getDefinition('mailer.envelope_listener'); $envelopeListener->setArgument(0, $config['envelope']['sender'] ?? null); $envelopeListener->setArgument(1, $config['envelope']['recipients'] ?? null); + $envelopeListener->setArgument(2, $config['envelope']['recipients_allowed'] ?? []); if ($config['headers']) { $headers = new Definition(Headers::class); diff --git a/src/Symfony/Component/Mailer/CHANGELOG.md b/src/Symfony/Component/Mailer/CHANGELOG.md index 53b03a0d70fd5..54e2a6cde0927 100644 --- a/src/Symfony/Component/Mailer/CHANGELOG.md +++ b/src/Symfony/Component/Mailer/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Dispatch Postmark's "406 - Inactive recipient" API error code as a `PostmarkDeliveryEvent` instead of throwing an exception * Add DSN param `auto_tls` to disable automatic STARTTLS + * Add support for allowing some users even if `recipients` is defined in `EnvelopeListener` 7.0 --- diff --git a/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php b/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php index db9dd723adee3..f7a7d2b2562f3 100644 --- a/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php +++ b/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php @@ -20,6 +20,7 @@ * Manipulates the Envelope of a Message. * * @author Fabien Potencier + * @author Grégoire Pineau */ class EnvelopeListener implements EventSubscriberInterface { @@ -30,10 +31,16 @@ class EnvelopeListener implements EventSubscriberInterface */ private ?array $recipients = null; + /** + * @var string[] + */ + private array $allowedRecipients; + /** * @param array $recipients + * @param string[] $allowedRecipients An array of regex to match the allowed recipients */ - public function __construct(Address|string|null $sender = null, ?array $recipients = null) + public function __construct(Address|string|null $sender = null, ?array $recipients = null, ?array $allowedRecipients = null) { if (null !== $sender) { $this->sender = Address::create($sender); @@ -41,6 +48,8 @@ public function __construct(Address|string|null $sender = null, ?array $recipien if (null !== $recipients) { $this->recipients = Address::createArray($recipients); } + + $this->allowedRecipients = $allowedRecipients; } public function onMessage(MessageEvent $event): void @@ -57,7 +66,24 @@ public function onMessage(MessageEvent $event): void } if ($this->recipients) { - $event->getEnvelope()->setRecipients($this->recipients); + $recipients = $this->recipients; + if ($this->allowedRecipients) { + $recipients = []; + $addDefaults = false; + foreach ($event->getEnvelope()->getRecipients() as $recipient) { + foreach ($this->allowedRecipients as $allowedRecipient) { + if (preg_match($allowedRecipient, $recipient->getAddress())) { + $recipients[] = $recipient; + continue 2; + } + } + $addDefaults = true; + } + if ($addDefaults) { + $recipients = array_merge($recipients, $this->recipients); + } + } + $event->getEnvelope()->setRecipients($recipients); } } diff --git a/src/Symfony/Component/Mailer/Tests/EventListener/EnvelopeListenerTest.php b/src/Symfony/Component/Mailer/Tests/EventListener/EnvelopeListenerTest.php new file mode 100644 index 0000000000000..fd2118a00f0c6 --- /dev/null +++ b/src/Symfony/Component/Mailer/Tests/EventListener/EnvelopeListenerTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mailer\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\Event\MessageEvent; +use Symfony\Component\Mailer\EventListener\EnvelopeListener; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\RawMessage; + +class EnvelopeListenerTest extends TestCase +{ + /** + * @dataProvider provideRecipientsTests + */ + public function testRecipients(array $expected, ?array $recipients = null, array $allowedRecipients = []) + { + $listener = new EnvelopeListener(null, $recipients, $allowedRecipients); + $message = new RawMessage('message'); + $envelope = new Envelope(new Address('sender@example.com'), [new Address('r1@example.com'), new Address('r2@symfony.com')]); + $event = new MessageEvent($message, $envelope, 'default'); + + $listener->onMessage($event); + + $recipients = array_map(fn (Address $a): string => $a->getAddress(), $event->getEnvelope()->getRecipients()); + $this->assertSame($expected, $recipients); + } + + public static function provideRecipientsTests(): iterable + { + yield [['r1@example.com', 'r2@symfony.com'], null, []]; + yield [['admin@admin.com'], ['admin@admin.com'], []]; + yield [['r1@example.com', 'admin@admin.com'], ['admin@admin.com'], ['/@example.com$/']]; + yield [['r1@example.com', 'r2@symfony.com'], ['admin@admin.com'], ['/@example.com$/', '/@symfony.com$/']]; + } +}