mirror of
https://github.com/wallabag/wallabag.git
synced 2024-11-26 11:01:04 +00:00
Merge pull request #1544 from wallabag/2fa-email
v2 – Add custom email for 2FA
This commit is contained in:
commit
e6a228c43b
17 changed files with 266 additions and 6 deletions
|
@ -32,7 +32,6 @@ matrix:
|
||||||
- php: hhvm
|
- php: hhvm
|
||||||
env: DB=pgsql # driver for PostgreSQL currently unsupported by HHVM, requires 3rd party dependency
|
env: DB=pgsql # driver for PostgreSQL currently unsupported by HHVM, requires 3rd party dependency
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- php: 7.0
|
|
||||||
- php: hhvm
|
- php: hhvm
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
|
|
|
@ -198,6 +198,7 @@ scheb_two_factor:
|
||||||
sender_email: %twofactor_sender%
|
sender_email: %twofactor_sender%
|
||||||
digits: 6
|
digits: 6
|
||||||
template: WallabagUserBundle:Authentication:form.html.twig
|
template: WallabagUserBundle:Authentication:form.html.twig
|
||||||
|
mailer: wallabag_user.auth_code_mailer
|
||||||
|
|
||||||
kphoen_rulerz:
|
kphoen_rulerz:
|
||||||
executors:
|
executors:
|
||||||
|
|
|
@ -52,10 +52,11 @@ parameters:
|
||||||
export_mobi: true
|
export_mobi: true
|
||||||
export_pdf: true
|
export_pdf: true
|
||||||
wallabag_url: http://v2.wallabag.org
|
wallabag_url: http://v2.wallabag.org
|
||||||
|
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
|
||||||
|
|
||||||
# default user config
|
# default user config
|
||||||
items_on_page: 12
|
items_on_page: 12
|
||||||
theme: material
|
theme: material
|
||||||
language: en_US
|
language: en
|
||||||
from_email: no-reply@wallabag.org
|
from_email: no-reply@wallabag.org
|
||||||
rss_limit: 50
|
rss_limit: 50
|
||||||
|
|
|
@ -52,6 +52,7 @@ parameters:
|
||||||
export_mobi: true
|
export_mobi: true
|
||||||
export_pdf: true
|
export_pdf: true
|
||||||
wallabag_url: http://v2.wallabag.org
|
wallabag_url: http://v2.wallabag.org
|
||||||
|
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
|
||||||
|
|
||||||
# default user config
|
# default user config
|
||||||
items_on_page: 12
|
items_on_page: 12
|
||||||
|
|
|
@ -52,6 +52,7 @@ parameters:
|
||||||
export_mobi: true
|
export_mobi: true
|
||||||
export_pdf: true
|
export_pdf: true
|
||||||
wallabag_url: http://v2.wallabag.org
|
wallabag_url: http://v2.wallabag.org
|
||||||
|
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
|
||||||
|
|
||||||
# default user config
|
# default user config
|
||||||
items_on_page: 12
|
items_on_page: 12
|
||||||
|
|
|
@ -52,6 +52,7 @@ parameters:
|
||||||
export_mobi: true
|
export_mobi: true
|
||||||
export_pdf: true
|
export_pdf: true
|
||||||
wallabag_url: http://v2.wallabag.org
|
wallabag_url: http://v2.wallabag.org
|
||||||
|
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
|
||||||
|
|
||||||
# default user config
|
# default user config
|
||||||
items_on_page: 12
|
items_on_page: 12
|
||||||
|
|
|
@ -25,7 +25,7 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
|
||||||
|
|
||||||
$adminConfig->setTheme('material');
|
$adminConfig->setTheme('material');
|
||||||
$adminConfig->setItemsPerPage(30);
|
$adminConfig->setItemsPerPage(30);
|
||||||
$adminConfig->setLanguage('en_US');
|
$adminConfig->setLanguage('en');
|
||||||
|
|
||||||
$manager->persist($adminConfig);
|
$manager->persist($adminConfig);
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
|
||||||
$bobConfig = new Config($this->getReference('bob-user'));
|
$bobConfig = new Config($this->getReference('bob-user'));
|
||||||
$bobConfig->setTheme('default');
|
$bobConfig->setTheme('default');
|
||||||
$bobConfig->setItemsPerPage(10);
|
$bobConfig->setItemsPerPage(10);
|
||||||
$bobConfig->setLanguage('fr_FR');
|
$bobConfig->setLanguage('fr');
|
||||||
|
|
||||||
$manager->persist($bobConfig);
|
$manager->persist($bobConfig);
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,10 @@
|
||||||
{{ form_start(form.rss) }}
|
{{ form_start(form.rss) }}
|
||||||
{{ form_errors(form.rss) }}
|
{{ form_errors(form.rss) }}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{% trans %}RSS feeds provided by wallabag allow you to read your saved articles with your favourite RSS reader.{% endtrans %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<fieldset class="w500p inline">
|
<fieldset class="w500p inline">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label>Rss token</label>
|
<label>Rss token</label>
|
||||||
|
@ -101,6 +105,10 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
{% if twofactor_auth %}
|
{% if twofactor_auth %}
|
||||||
|
<div class="row">
|
||||||
|
{% trans %}Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion{% endtrans %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<fieldset class="w500p inline">
|
<fieldset class="w500p inline">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{{ form_label(form.user.twoFactorAuthentication) }}
|
{{ form_label(form.user.twoFactorAuthentication) }}
|
||||||
|
|
|
@ -131,6 +131,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if twofactor_auth %}
|
{% if twofactor_auth %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="input-field col s12">
|
||||||
|
{% trans %}Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion{% endtrans %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="input-field col s12">
|
<div class="input-field col s12">
|
||||||
{{ form_widget(form.user.twoFactorAuthentication) }}
|
{{ form_widget(form.user.twoFactorAuthentication) }}
|
||||||
|
|
|
@ -44,7 +44,7 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
$form = $crawler->filter('button[id=config_save]')->form();
|
$form = $crawler->filter('button[id=config_save]')->form();
|
||||||
|
|
||||||
$data = array(
|
$data = array(
|
||||||
'config[theme]' => 0,
|
'config[theme]' => 'baggy',
|
||||||
'config[items_per_page]' => '30',
|
'config[items_per_page]' => '30',
|
||||||
'config[language]' => 'en',
|
'config[language]' => 'en',
|
||||||
);
|
);
|
||||||
|
@ -63,7 +63,7 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
array(array(
|
array(array(
|
||||||
'config[theme]' => 0,
|
'config[theme]' => 'baggy',
|
||||||
'config[items_per_page]' => '',
|
'config[items_per_page]' => '',
|
||||||
'config[language]' => 'en',
|
'config[language]' => 'en',
|
||||||
)),
|
)),
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wallabag\UserBundle\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||||
|
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||||
|
|
||||||
|
class Configuration implements ConfigurationInterface
|
||||||
|
{
|
||||||
|
public function getConfigTreeBuilder()
|
||||||
|
{
|
||||||
|
$treeBuilder = new TreeBuilder();
|
||||||
|
$rootNode = $treeBuilder->root('wallabag_user');
|
||||||
|
|
||||||
|
return $treeBuilder;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wallabag\UserBundle\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\Config\FileLocator;
|
||||||
|
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||||
|
use Symfony\Component\DependencyInjection\Loader;
|
||||||
|
|
||||||
|
class WallabagUserExtension extends Extension
|
||||||
|
{
|
||||||
|
public function load(array $configs, ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$configuration = new Configuration();
|
||||||
|
$config = $this->processConfiguration($configuration, $configs);
|
||||||
|
|
||||||
|
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||||
|
$loader->load('services.yml');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAlias()
|
||||||
|
{
|
||||||
|
return 'wallabag_user';
|
||||||
|
}
|
||||||
|
}
|
93
src/Wallabag/UserBundle/Mailer/AuthCodeMailer.php
Normal file
93
src/Wallabag/UserBundle/Mailer/AuthCodeMailer.php
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wallabag\UserBundle\Mailer;
|
||||||
|
|
||||||
|
use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface;
|
||||||
|
use Scheb\TwoFactorBundle\Mailer\AuthCodeMailerInterface;
|
||||||
|
use Symfony\Component\Translation\DataCollectorTranslator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom mailer for TwoFactorBundle email.
|
||||||
|
* It adds a custom template to the email so user won't get a lonely authentication code but a complete email.
|
||||||
|
*/
|
||||||
|
class AuthCodeMailer implements AuthCodeMailerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* SwiftMailer.
|
||||||
|
*
|
||||||
|
* @var \Swift_Mailer
|
||||||
|
*/
|
||||||
|
private $mailer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translator for email content.
|
||||||
|
*
|
||||||
|
* @var DataCollectorTranslator
|
||||||
|
*/
|
||||||
|
private $translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sender email address.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $senderEmail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sender name.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $senderName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support URL to report any bugs.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $supportUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the auth code mailer with the SwiftMailer object.
|
||||||
|
*
|
||||||
|
* @param \Swift_Mailer $mailer
|
||||||
|
* @param DataCollectorTranslator $translator
|
||||||
|
* @param string $senderEmail
|
||||||
|
* @param string $senderName
|
||||||
|
* @param string $supportUrl
|
||||||
|
*/
|
||||||
|
public function __construct(\Swift_Mailer $mailer, DataCollectorTranslator $translator, $senderEmail, $senderName, $supportUrl)
|
||||||
|
{
|
||||||
|
$this->mailer = $mailer;
|
||||||
|
$this->translator = $translator;
|
||||||
|
$this->senderEmail = $senderEmail;
|
||||||
|
$this->senderName = $senderName;
|
||||||
|
$this->supportUrl = $supportUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the auth code to the user via email.
|
||||||
|
*
|
||||||
|
* @param TwoFactorInterface $user
|
||||||
|
*/
|
||||||
|
public function sendAuthCode(TwoFactorInterface $user)
|
||||||
|
{
|
||||||
|
$message = new \Swift_Message();
|
||||||
|
$message
|
||||||
|
->setTo($user->getEmail())
|
||||||
|
->setFrom($this->senderEmail, $this->senderName)
|
||||||
|
->setSubject($this->translator->trans('auth_code.mailer.subject', array(), 'wallabag_user'))
|
||||||
|
->setBody($this->translator->trans(
|
||||||
|
'auth_code.mailer.body',
|
||||||
|
[
|
||||||
|
'%user%' => $user->getName(),
|
||||||
|
'%code%' => $user->getEmailAuthCode(),
|
||||||
|
'%support%' => $this->supportUrl,
|
||||||
|
],
|
||||||
|
'wallabag_user'
|
||||||
|
))
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->mailer->send($message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
services:
|
||||||
|
wallabag_user.auth_code_mailer:
|
||||||
|
class: Wallabag\UserBundle\Mailer\AuthCodeMailer
|
||||||
|
arguments:
|
||||||
|
- "@mailer"
|
||||||
|
- "@translator"
|
||||||
|
- "%scheb_two_factor.email.sender_email%"
|
||||||
|
- "%scheb_two_factor.email.sender_name%"
|
||||||
|
- "%wallabag_support_url%"
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Two factor mail
|
||||||
|
auth_code.mailer.subject: 'Wallabag authentication Code'
|
||||||
|
auth_code.mailer.body: |
|
||||||
|
Hi %user%,
|
||||||
|
|
||||||
|
Since you enable two factor authentication on your wallabag account and you just logged in from a new device (computer, phone, etc.), we send you a code to validate your connection.
|
||||||
|
Here is the code: %code%
|
||||||
|
|
||||||
|
Please don't hesitate to contact us if you have any problems: %support%
|
||||||
|
The wallabag team
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Two factor mail
|
||||||
|
auth_code.mailer.subject: "Code d'authentification wallabag"
|
||||||
|
auth_code.mailer.body: |
|
||||||
|
Bonjour %user%,
|
||||||
|
|
||||||
|
Comme vous avez activé la double authentification sur votre compte wallabag et que vous venez de vous connecter depuis un nouvel appareil (ordinateur, téléphone, etc.), nous vous envoyons un code pour valider votre connexion.
|
||||||
|
Voici le code à renseigner: %code%
|
||||||
|
|
||||||
|
Si vous avez un problème de connexion, n'hésitez pas à contacter le support: %support%
|
||||||
|
L'équipe wallabag
|
78
src/Wallabag/UserBundle/Tests/Mailer/AuthCodeMailerTest.php
Normal file
78
src/Wallabag/UserBundle/Tests/Mailer/AuthCodeMailerTest.php
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wallabag\UserBundle\Tests\Mailer;
|
||||||
|
|
||||||
|
use Wallabag\UserBundle\Entity\User;
|
||||||
|
use Wallabag\UserBundle\Mailer\AuthCodeMailer;
|
||||||
|
use Symfony\Component\Translation\Translator;
|
||||||
|
use Symfony\Component\Translation\Loader\ArrayLoader;
|
||||||
|
use Symfony\Component\Translation\DataCollectorTranslator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://www.pmg.com/blog/integration-testing-swift-mailer/
|
||||||
|
*/
|
||||||
|
final class CountableMemorySpool extends \Swift_MemorySpool implements \Countable
|
||||||
|
{
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
return count($this->messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessages()
|
||||||
|
{
|
||||||
|
return $this->messages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthCodeMailerTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
protected $mailer;
|
||||||
|
protected $spool;
|
||||||
|
protected $dataCollector;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->spool = new CountableMemorySpool();
|
||||||
|
$transport = new \Swift_Transport_SpoolTransport(
|
||||||
|
new \Swift_Events_SimpleEventDispatcher(),
|
||||||
|
$this->spool
|
||||||
|
);
|
||||||
|
$this->mailer = new \Swift_Mailer($transport);
|
||||||
|
|
||||||
|
$translator = new Translator('en');
|
||||||
|
$translator->addLoader('array', new ArrayLoader());
|
||||||
|
$translator->addResource('array', array(
|
||||||
|
'auth_code.mailer.subject' => 'auth_code subject',
|
||||||
|
'auth_code.mailer.body' => 'Hi %user%, here is the code: %code% and the support: %support%',
|
||||||
|
), 'en', 'wallabag_user');
|
||||||
|
|
||||||
|
$this->dataCollector = new DataCollectorTranslator($translator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendEmail()
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$user->setTwoFactorAuthentication(true);
|
||||||
|
$user->setEmailAuthCode(666666);
|
||||||
|
$user->setEmail('test@wallabag.io');
|
||||||
|
$user->setName('Bob');
|
||||||
|
|
||||||
|
$authCodeMailer = new AuthCodeMailer(
|
||||||
|
$this->mailer,
|
||||||
|
$this->dataCollector,
|
||||||
|
'nobody@test.io',
|
||||||
|
'wallabag test',
|
||||||
|
'http://0.0.0.0'
|
||||||
|
);
|
||||||
|
|
||||||
|
$authCodeMailer->sendAuthCode($user);
|
||||||
|
|
||||||
|
$this->assertCount(1, $this->spool);
|
||||||
|
|
||||||
|
$msg = $this->spool->getMessages()[0];
|
||||||
|
$this->assertArrayHasKey('test@wallabag.io', $msg->getTo());
|
||||||
|
$this->assertEquals(array('nobody@test.io' => 'wallabag test'), $msg->getFrom());
|
||||||
|
$this->assertEquals('auth_code subject', $msg->getSubject());
|
||||||
|
$this->assertContains('Hi Bob, here is the code: 666666 and the support: http://0.0.0.0', $msg->toString());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue