[Security][Notifier] Added integration of Login Link with the Notifier component

This commit is contained in:
Wouter de Jong 2020-10-13 17:39:17 +02:00 committed by Fabien Potencier
parent ffbb9883bd
commit 04ef565895
5 changed files with 124 additions and 3 deletions

View File

@ -10,6 +10,7 @@ CHANGELOG
* added the `t()` function to easily create `TranslatableMessage` objects
* Added support for extracting messages from the `t()` function
* Added `field_*` Twig functions to access string values from Form fields
* changed the `importance` context option of `NotificationEmail` to allow `null`
5.0.0
-----

View File

@ -37,6 +37,7 @@ class NotificationEmail extends TemplatedEmail
'action_url' => null,
'markdown' => false,
'raw' => false,
'footer_text' => 'Notification e-mail sent by Symfony',
];
public function __construct(Headers $headers = null, AbstractPart $body = null)
@ -57,6 +58,18 @@ class NotificationEmail extends TemplatedEmail
parent::__construct($headers, $body);
}
/**
* Creates a NotificationEmail instance that is appropriate to send to normal (non-admin) users.
*/
public static function asPublicEmail(Headers $headers = null, AbstractPart $body = null): self
{
$email = new static($headers, $body);
$email->context['importance'] = null;
$email->context['footer_text'] = null;
return $email;
}
/**
* @return $this
*/
@ -166,7 +179,9 @@ class NotificationEmail extends TemplatedEmail
$importance = $this->context['importance'] ?? self::IMPORTANCE_LOW;
$this->priority($this->determinePriority($importance));
$headers->setHeaderBody('Text', 'Subject', sprintf('[%s] %s', strtoupper($importance), $this->getSubject()));
if ($this->context['importance']) {
$headers->setHeaderBody('Text', 'Subject', sprintf('[%s] %s', strtoupper($importance), $this->getSubject()));
}
return $headers;
}

View File

@ -16,7 +16,7 @@
<row>
<columns large="12" small="12">
{% block lead %}
<small><strong>{{ importance|upper }}</strong></small>
{% if importance is not null %}<small><strong>{{ importance|upper }}</strong></small>{% endif %}
<p class="lead">
{{ email.subject }}
</p>
@ -49,13 +49,15 @@
<wrapper class="secondary">
<spacer size="16"></spacer>
{% block footer %}
{% if footer_text is defined and footer_text is not null %}
<row>
<columns small="12" large="6">
{% block footer_content %}
<p><small>Notification e-mail sent by Symfony</small></p>
<p><small>{{ footer_text }}</small></p>
{% endblock %}
</columns>
</row>
{% endif %}
{% endblock %}
</wrapper>
</container>

View File

@ -26,6 +26,7 @@ class NotificationEmailTest extends TestCase
'markdown' => true,
'raw' => false,
'a' => 'b',
'footer_text' => 'Notification e-mail sent by Symfony',
], $email->getContext());
}
@ -47,6 +48,7 @@ class NotificationEmailTest extends TestCase
'markdown' => false,
'raw' => true,
'a' => 'b',
'footer_text' => 'Notification e-mail sent by Symfony',
], $email->getContext());
}
@ -63,4 +65,32 @@ class NotificationEmailTest extends TestCase
$headers = $email->getPreparedHeaders();
$this->assertSame('[LOW] Foo', $headers->get('Subject')->getValue());
}
public function testPublicMail()
{
$email = NotificationEmail::asPublicEmail()
->markdown('Foo')
->action('Bar', 'http://example.com/')
->context(['a' => 'b'])
;
$this->assertEquals([
'importance' => null,
'content' => 'Foo',
'exception' => false,
'action_text' => 'Bar',
'action_url' => 'http://example.com/',
'markdown' => true,
'raw' => false,
'a' => 'b',
'footer_text' => null,
], $email->getContext());
}
public function testPublicMailSubject()
{
$email = NotificationEmail::asPublicEmail()->from('me@example.com')->subject('Foo');
$headers = $email->getPreparedHeaders();
$this->assertSame('Foo', $headers->get('Subject')->getValue());
}
}

View File

@ -0,0 +1,73 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\LoginLink;
use Symfony\Bridge\Twig\Mime\NotificationEmail;
use Symfony\Component\Notifier\Message\EmailMessage;
use Symfony\Component\Notifier\Message\SmsMessage;
use Symfony\Component\Notifier\Notification\EmailNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Notification\SmsNotificationInterface;
use Symfony\Component\Notifier\Recipient\EmailRecipientInterface;
use Symfony\Component\Notifier\Recipient\SmsRecipientInterface;
/**
* Use this notification to ease sending login link
* emails/SMS using the Notifier component.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*
* @experimental in 5.2
*/
class LoginLinkNotification extends Notification implements EmailNotificationInterface, SmsNotificationInterface
{
private $loginLinkDetails;
public function __construct(LoginLinkDetails $loginLinkDetails, string $subject, array $channels = [])
{
parent::__construct($subject, $channels);
$this->loginLinkDetails = $loginLinkDetails;
}
public function asEmailMessage(EmailRecipientInterface $recipient, string $transport = null): ?EmailMessage
{
if (!class_exists(NotificationEmail::class)) {
throw new \LogicException(sprintf('The "%s" method requires "symfony/twig-bridge:>4.4".', __METHOD__));
}
$email = NotificationEmail::asPublicEmail()
->to($recipient->getEmail())
->subject($this->getSubject())
->content($this->getContent() ?: $this->getDefaultContent('button below'))
->action('Sign in', $this->loginLinkDetails->getUrl())
;
return new EmailMessage($email);
}
public function asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage
{
return new SmsMessage($recipient->getPhone(), $this->getDefaultContent('link').' '.$this->loginLinkDetails->getUrl());
}
private function getDefaultContent(string $target): string
{
$duration = $this->loginLinkDetails->getExpiresAt()->getTimestamp() - time();
$durationString = floor($duration / 60).' minute'.($duration > 60 ? 's' : '');
if (($hours = $duration / 3600) >= 1) {
$durationString = floor($hours).' hour'.($hours >= 2 ? 's' : '');
}
return sprintf('Click on the %s to confirm you want to sign in. This link will expire in %s.', $target, $durationString);
}
}