8000 feature #59479 [Mailer] [Smtp] Add DSN param to enforce TLS/STARTTLS … · symfonyaml/symfony@af2df09 · GitHub
[go: up one dir, main page]

Skip to content

Commit af2df09

Browse files
feature symfony#59479 [Mailer] [Smtp] Add DSN param to enforce TLS/STARTTLS (ssddanbrown)
This PR was merged into the 7.3 branch. Discussion ---------- [Mailer] [Smtp] Add DSN param to enforce TLS/STARTTLS | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | Fix symfony#48297 | License | MIT Adds 'require_tls' param which can be set to true to enforce the use of TLS/STARTTLS within the ESMTP transport. This was discussed in symfony#48297. These changes are based upon patches [I've been maintaining](ssddanbrown/symfony-mailer@e9de8dc) for my own projects. This is my first PR to Symfony, I've tried to follow the guide as best as possible, and I was also using symfony#53621 as a general guide. There are some other ways I could have gone about things, but I've tried to avoid touching as much existing Symfony code as possible. In symfony#48297, nicolas-grekas mentioned unifying such an option with `auto_tls` under a `tls` option, but I think these are distinct options which may not be as clear combined (in addition to any expectations of such an option disabling/enabling TLS in general). Commits ------- a93d5f6 [Mailer] [Smtp] Add DSN param to enforce TLS/STARTTLS
2 parents 802940e + a93d5f6 commit af2df09

File tree

5 files changed

+53
-0
lines changed

5 files changed

+53
-0
lines changed

src/Symfony/Component/Mailer/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Add DSN param `retry_period` to override default email transport retry period
88
* Add `Dsn::getBooleanOption()`
99
* Add DSN param `source_ip` to allow binding to a (specific) IPv4 or IPv6 address.
10+
* Add DSN param `require_tls` to enforce use of TLS/STARTTLS
1011
* Add `DkimSignedMessageListener`, `SmimeEncryptedMessageListener`, and `SmimeSignedMessageListener`
1112

1213
7.2

src/Symfony/Component/Mailer/Tests/Transport/Smtp/EsmtpTransportFactoryTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,18 @@ public static function createProvider(): iterable
194194
Dsn::fromString('smtps://:@example.com:465?source_ip=[2606:4700:20::681a:5fb]'),
195195
$transport,
196196
];
197+
198+
$transport = new EsmtpTransport('example.com', 465, true, null, $logger);
199+
$transport->setRequireTls(true);
200+
201+
yield [
202+
new Dsn('smtps', 'example.com', '', '', 465, ['require_tls' => true]),
203+
$transport,
204+
];
205+
yield [
206+
Dsn::fromString('smtps://:@example.com?require_tls=true'),
207+
$transport,
208+
];
197209
}
198210

199211
public static function unsupportedSchemeProvider(): iterable

src/Symfony/Component/Mailer/Tests/Transport/Smtp/EsmtpTransportTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,23 @@ public function testSocketTimeout()
297297
$stream->getCommands()
298298
);
299299
}
300+
301+
public function testRequireTls()
302+
{
303+
$stream = new DummyStream();
304+
$transport = new EsmtpTransport(stream: $stream);
305+
$transport->setRequireTls(true);
306+
307+
$message = new Email();
308+
$message->from('sender@example.org');
309+
$message->addTo('recipient@example.org');
310+
$message->text('.');
311+
312+
$this->expectException(TransportException::class);
313+
$this->expectExceptionMessage('TLS required but neither TLS or STARTTLS are in use.');
314+
315+
$transport->send($message);
316+
}
300317
}
301318

302319
class CustomEsmtpTransport extends EsmtpTransport

src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class EsmtpTransport extends SmtpTransport
3333
private string $password = '';
3434
private array $capabilities;
3535
private bool $autoTls = true;
36+
private bool $requireTls = false;
3637

3738
public function __construct(string $host = 'localhost', int $port = 0, ?bool $tls = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null, ?AbstractStream $stream = null, ?array $authenticators = null)
3839
{
@@ -116,6 +117,21 @@ public function isAutoTls(): bool
116117
return $this->autoTls;
117118
}
118119

120+
/**
121+
* @return $this
122+
*/
123+
public function setRequireTls(bool $requireTls): static
124+
{
125+
$this->requireTls = $requireTls;
126+
127+
return $this;
128+
}
129+
130+
public function isTlsRequired(): bool
131+
{
132+
return $this->requireTls;
133+
}
134+
119135
public function setAuthenticators(array $authenticators): void
120136
{
121137
$this->authenticators = [];
@@ -159,6 +175,7 @@ private function doEhloCommand(): string
159175

160176
/** @var SocketStream $stream */
161177
$stream = $this->getStream();
178+
$tlsStarted = $stream->isTls();
162179
// WARNING: !$stream->isTLS() is right, 100% sure :)
163180
// if you think that the ! should be removed, read the code again
164181
// if doing so "fixes" your issue then it probably means your SMTP server behaves incorrectly or is wrongly configured
@@ -169,10 +186,15 @@ private function doEhloCommand(): string
169186
throw new TransportException('Unable to connect with STARTTLS.');
170187
}
171188

189+
$tlsStarted = true;
172190
$response = $this->executeCommand(\sprintf("EHLO %s\r\n", $this->getLocalDomain()), [250]);
173191
$this->capabilities = $this->parseCapabilities($response);
174192
}
175193

194+
if (!$tlsStarted && $this->isTlsRequired()) {
195+
throw new TransportException('TLS required but neither TLS or STARTTLS are in use.');
196+
}
197+
176198
if (\array_key_exists('AUTH', $this->capabilities)) {
177199
$this->handleAuth($this->capabilities['AUTH']);
178200
}

src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransportFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public function create(Dsn $dsn): TransportInterface
3535

3636
$transport = new EsmtpTransport($host, $port, $tls, $this->dispatcher, $this->logger);
3737
$transport->setAutoTls($autoTls);
38+
$transport->setRequireTls($dsn->getBooleanOption('require_tls'));
3839

3940
/** @var SocketStream $stream */
4041
$stream = $transport->getStream();

0 commit comments

Comments
 (0)
0