diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
index 52624c2cd173..df0d692ebdf7 100644
--- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
+++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
@@ -13,6 +13,8 @@ CHANGELOG
* Add `secrets:reveal` command
* Add `rate_limiter` option to `http_client.default_options` and `http_client.scoped_clients`
* Attach the workflow's configuration to the `workflow` tag
+ * Add the `allowed_recipients` option for mailer to allow some users to receive
+ emails even if `recipients` is defined.
7.0
---
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index 8d54f74ef389..92c20d139da6 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -2117,12 +2117,23 @@ private function addMailerSection(ArrayNodeDefinition $rootNode, callable $enabl
->arrayNode('envelope')
->info('Mailer Envelope configuration')
->fixXmlConfig('recipient')
+ ->fixXmlConfig('allowed_recipient')
->children()
->scalarNode('sender')->end()
->arrayNode('recipients')
->performNoDeepMerging()
->beforeNormalization()
- ->ifArray()
+ ->ifArray()
+ ->then(fn ($v) => array_filter(array_values($v)))
+ ->end()
+ ->prototype('scalar')->end()
+ ->end()
+ ->arrayNode('allowed_recipients')
+ ->info('A list of regular expressions that allow recipients when "recipients" option is defined.')
+ ->example(['.*@example\.com'])
+ ->performNoDeepMerging()
+ ->beforeNormalization()
+ ->ifArray()
->then(fn ($v) => array_filter(array_values($v)))
->end()
->prototype('scalar')->end()
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 1fd7881f3487..86355e5ca1e9 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -2647,6 +2647,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']['allowed_recipients'] ?? []);
if ($config['headers']) {
$headers = new Definition(Headers::class);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
index ad02ec370e45..d8d23168d188 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
@@ -764,6 +764,7 @@
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/mailer_with_dsn.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/mailer_with_dsn.php
index 68387298270a..3357bf354182 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/mailer_with_dsn.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/mailer_with_dsn.php
@@ -13,6 +13,7 @@
'envelope' => [
'sender' => 'sender@example.org',
'recipients' => ['redirected@example.org'],
+ 'allowed_recipients' => ['foobar@example\.org'],
],
'headers' => [
'from' => 'from@example.org',
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/mailer_with_transports.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/mailer_with_transports.php
index 361fe731ccb0..e51fd056b591 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/mailer_with_transports.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/mailer_with_transports.php
@@ -16,6 +16,7 @@
'envelope' => [
'sender' => 'sender@example.org',
'recipients' => ['redirected@example.org', 'redirected1@example.org'],
+ 'allowed_recipients' => ['foobar@example\.org', '.*@example\.com'],
],
'headers' => [
'from' => 'from@example.org',
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/mailer_with_dsn.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/mailer_with_dsn.xml
index 3436cf417caf..d48b7423afb0 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/mailer_with_dsn.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/mailer_with_dsn.xml
@@ -13,6 +13,7 @@
sender@example.org
redirected@example.org
+ foobar@example\.org
from@example.org
bcc1@example.org
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/mailer_with_transports.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/mailer_with_transports.xml
index 1cd8523b680f..9bfd18d9160b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/mailer_with_transports.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/mailer_with_transports.xml
@@ -16,6 +16,8 @@
sender@example.org
redirected@example.org
redirected1@example.org
+ foobar@example\.org
+ .*@example\.com
from@example.org
bcc1@example.org
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/mailer_with_dsn.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/mailer_with_dsn.yml
index e826d6bdcff9..ea703bdad8d1 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/mailer_with_dsn.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/mailer_with_dsn.yml
@@ -10,6 +10,8 @@ framework:
sender: sender@example.org
recipients:
- redirected@example.org
+ allowed_recipients:
+ - foobar@example\.org
headers:
from: from@example.org
bcc: [bcc1@example.org, bcc2@example.org]
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/mailer_with_transports.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/mailer_with_transports.yml
index 59a5f14fd315..ae10f6aee889 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/mailer_with_transports.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/mailer_with_transports.yml
@@ -13,6 +13,9 @@ framework:
recipients:
- redirected@example.org
- redirected1@example.org
+ allowed_recipients:
+ - foobar@example\.org
+ - .*@example\.com
headers:
from: from@example.org
bcc: [bcc1@example.org, bcc2@example.org]
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php
index dd836c06ffd1..2a26e786b436 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php
@@ -2043,6 +2043,7 @@ public static function provideMailer(): iterable
'mailer_with_dsn',
['main' => 'smtp://example.com'],
['redirected@example.org'],
+ ['foobar@example\.org'],
];
yield [
'mailer_with_transports',
@@ -2051,13 +2052,14 @@ public static function provideMailer(): iterable
'transport2' => 'smtp://example2.com',
],
['redirected@example.org', 'redirected1@example.org'],
+ ['foobar@example\.org', '.*@example\.com'],
];
}
/**
* @dataProvider provideMailer
*/
- public function testMailer(string $configFile, array $expectedTransports, array $expectedRecipients)
+ public function testMailer(string $configFile, array $expectedTransports, array $expectedRecipients, array $expectedAllowedRecipients)
{
$container = $this->createContainerFromFile($configFile);
@@ -2070,6 +2072,7 @@ public function testMailer(string $configFile, array $expectedTransports, array
$l = $container->getDefinition('mailer.envelope_listener');
$this->assertSame('sender@example.org', $l->getArgument(0));
$this->assertSame($expectedRecipients, $l->getArgument(1));
+ $this->assertSame($expectedAllowedRecipients, $l->getArgument(2));
$this->assertEquals(new Reference('messenger.default_bus', ContainerInterface::NULL_ON_INVALID_REFERENCE), $container->getDefinition('mailer.mailer')->getArgument(1));
$this->assertTrue($container->hasDefinition('mailer.message_listener'));
diff --git a/src/Symfony/Component/Mailer/CHANGELOG.md b/src/Symfony/Component/Mailer/CHANGELOG.md
index 53b03a0d70fd..54e2a6cde092 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 db9dd723adee..9a1e39c69257 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
{
@@ -32,9 +33,13 @@ class EnvelopeListener implements EventSubscriberInterface
/**
* @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,
+ private array $allowedRecipients = [],
+ ) {
if (null !== $sender) {
$this->sender = Address::create($sender);
}
@@ -57,7 +62,27 @@ public function onMessage(MessageEvent $event): void
}
if ($this->recipients) {
- $event->getEnvelope()->setRecipients($this->recipients);
+ $recipients = $this->recipients;
+ if ($this->allowedRecipients) {
+ foreach ($event->getEnvelope()->getRecipients() as $recipient) {
+ foreach ($this->allowedRecipients as $allowedRecipient) {
+ if (!preg_match('{\A'.$allowedRecipient.'\z}', $recipient->getAddress())) {
+ continue;
+ }
+ // dedup
+ foreach ($recipients as $r) {
+ if ($r->getName() === $recipient->getName() && $r->getAddress() === $recipient->getAddress()) {
+ continue 2;
+ }
+ }
+
+ $recipients[] = $recipient;
+ continue 2;
+ }
+ }
+ }
+
+ $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 000000000000..4a8d8ffc9b24
--- /dev/null
+++ b/src/Symfony/Component/Mailer/Tests/EventListener/EnvelopeListenerTest.php
@@ -0,0 +1,47 @@
+
+ *
+ * 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 [['admin@admin.com', 'r1@example.com'], ['admin@admin.com'], ['.*@example\.com']];
+ yield [['admin@admin.com', 'r1@example.com', 'r2@symfony.com'], ['admin@admin.com'], ['.*@example\.com', '.*@symfony\.com']];
+ yield [['r1@example.com', 'r2@symfony.com'], ['r1@example.com'], ['.*@example\.com', '.*@symfony\.com']];
+ }
+}