mirror of
https://github.com/wallabag/wallabag.git
synced 2025-07-22 17:18:37 +00:00
Merge pull request #1484 from wallabag/v2-2factor-auth
2factor authentication via email
This commit is contained in:
commit
3d3ed955f1
15 changed files with 451 additions and 111 deletions
|
@ -13,6 +13,7 @@ class UserInformationType extends AbstractType
|
|||
$builder
|
||||
->add('name', 'text')
|
||||
->add('email', 'email')
|
||||
->add('twoFactorAuthentication', 'checkbox', array('required' => false))
|
||||
->add('save', 'submit')
|
||||
->remove('username')
|
||||
->remove('plainPassword')
|
||||
|
|
|
@ -100,6 +100,16 @@
|
|||
</div>
|
||||
</fieldset>
|
||||
|
||||
{% if twofactor_auth %}
|
||||
<fieldset class="w500p inline">
|
||||
<div class="row">
|
||||
{{ form_label(form.user.twoFactorAuthentication) }}
|
||||
{{ form_errors(form.user.twoFactorAuthentication) }}
|
||||
{{ form_widget(form.user.twoFactorAuthentication) }}
|
||||
</div>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
|
||||
{{ form_rest(form.user) }}
|
||||
</form>
|
||||
|
||||
|
|
|
@ -132,6 +132,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{% if twofactor_auth %}
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
{{ form_widget(form.user.twoFactorAuthentication) }}
|
||||
{{ form_label(form.user.twoFactorAuthentication) }}
|
||||
{{ form_errors(form.user.twoFactorAuthentication) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="hidden">{{ form_rest(form.user) }}</div>
|
||||
<button class="btn waves-effect waves-light" type="submit" name="action">
|
||||
{% trans %}Save{% endtrans %}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace Wallabag\CoreBundle\Tests\Controller;
|
||||
|
||||
use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
|
||||
|
||||
class SecurityControllerTest extends WallabagCoreTestCase
|
||||
{
|
||||
public function testLoginWithout2Factor()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$client = $this->getClient();
|
||||
$client->followRedirects();
|
||||
|
||||
$client->request('GET', '/config');
|
||||
$this->assertContains('RSS', $client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
public function testLoginWith2Factor()
|
||||
{
|
||||
$client = $this->getClient();
|
||||
|
||||
if ($client->getContainer()->getParameter('twofactor_auth')) {
|
||||
$client->followRedirects();
|
||||
|
||||
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||
$user = $em
|
||||
->getRepository('WallabagUserBundle:User')
|
||||
->findOneByUsername('admin');
|
||||
$user->setTwoFactorAuthentication(true);
|
||||
$em->persist($user);
|
||||
$em->flush();
|
||||
|
||||
$this->logInAs('admin');
|
||||
$client->request('GET', '/config');
|
||||
$this->assertContains('trusted computer', $client->getResponse()->getContent());
|
||||
|
||||
// restore user
|
||||
$user = $em
|
||||
->getRepository('WallabagUserBundle:User')
|
||||
->findOneByUsername('admin');
|
||||
$user->setTwoFactorAuthentication(false);
|
||||
$em->persist($user);
|
||||
$em->flush();
|
||||
}
|
||||
}
|
||||
|
||||
public function testTrustedComputer()
|
||||
{
|
||||
$client = $this->getClient();
|
||||
|
||||
if ($client->getContainer()->getParameter('twofactor_auth')) {
|
||||
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||
$user = $em
|
||||
->getRepository('WallabagUserBundle:User')
|
||||
->findOneByUsername('admin');
|
||||
|
||||
$date = new \DateTime();
|
||||
$user->addTrustedComputer('ABCDEF', $date->add(new \DateInterval('P1M')));
|
||||
$this->assertTrue($user->isTrustedComputer('ABCDEF'));
|
||||
$this->assertFalse($user->isTrustedComputer('FEDCBA'));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ namespace Wallabag\UserBundle\Entity;
|
|||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface;
|
||||
use Scheb\TwoFactorBundle\Model\TrustedComputerInterface;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use JMS\Serializer\Annotation\ExclusionPolicy;
|
||||
|
@ -24,7 +26,7 @@ use Wallabag\CoreBundle\Entity\Tag;
|
|||
* @UniqueEntity("email")
|
||||
* @UniqueEntity("username")
|
||||
*/
|
||||
class User extends BaseUser
|
||||
class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
|
@ -72,6 +74,22 @@ class User extends BaseUser
|
|||
*/
|
||||
protected $tags;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", nullable=true)
|
||||
*/
|
||||
private $authCode;
|
||||
|
||||
/**
|
||||
* @var bool Enabled yes/no
|
||||
* @ORM\Column(type="boolean")
|
||||
*/
|
||||
private $twoFactorAuthentication = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="json_array", nullable=true)
|
||||
*/
|
||||
private $trusted;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
@ -201,4 +219,52 @@ class User extends BaseUser
|
|||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isTwoFactorAuthentication()
|
||||
{
|
||||
return $this->twoFactorAuthentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $twoFactorAuthentication
|
||||
*/
|
||||
public function setTwoFactorAuthentication($twoFactorAuthentication)
|
||||
{
|
||||
$this->twoFactorAuthentication = $twoFactorAuthentication;
|
||||
}
|
||||
|
||||
public function isEmailAuthEnabled()
|
||||
{
|
||||
return $this->twoFactorAuthentication;
|
||||
}
|
||||
|
||||
public function getEmailAuthCode()
|
||||
{
|
||||
return $this->authCode;
|
||||
}
|
||||
|
||||
public function setEmailAuthCode($authCode)
|
||||
{
|
||||
$this->authCode = $authCode;
|
||||
}
|
||||
|
||||
public function addTrustedComputer($token, \DateTime $validUntil)
|
||||
{
|
||||
$this->trusted[$token] = $validUntil->format('r');
|
||||
}
|
||||
|
||||
public function isTrustedComputer($token)
|
||||
{
|
||||
if (isset($this->trusted[$token])) {
|
||||
$now = new \DateTime();
|
||||
$validUntil = new \DateTime($this->trusted[$token]);
|
||||
|
||||
return $now < $validUntil;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
{% extends "WallabagUserBundle::layout.html.twig" %}
|
||||
|
||||
{% block fos_user_content %}
|
||||
<form class="form" action="" method="post">
|
||||
<fieldset class="w500p center">
|
||||
{% for flashMessage in app.session.flashbag.get("two_factor") %}
|
||||
<p class="error">{{ flashMessage|trans }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<div class="row">
|
||||
<label for="_auth_code">{{ "scheb_two_factor.auth_code"|trans }}</label>
|
||||
<input id="_auth_code" type="text" autocomplete="off" name="_auth_code" />
|
||||
</div>
|
||||
|
||||
{% if useTrustedOption %}
|
||||
<div class="row">
|
||||
<input id="_trusted" type="checkbox" name="_trusted" />
|
||||
<label for="_trusted">{{ "scheb_two_factor.trusted"|trans }}</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row mts txtcenter">
|
||||
<a href="{{ path('fos_user_security_logout') }}" class="waves-effect waves-light grey btn"><i class="material-icons left"></i> {% trans %}Cancel{% endtrans %}</a>
|
||||
<button type="submit" name="send">
|
||||
{{ "scheb_two_factor.login"|trans }}
|
||||
<i class="mdi-content-send right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -0,0 +1,33 @@
|
|||
{% extends "WallabagUserBundle::layout.html.twig" %}
|
||||
|
||||
{% block fos_user_content %}
|
||||
<form class="form" action="" method="post">
|
||||
<div class="card-content">
|
||||
<div class="row">
|
||||
|
||||
{% for flashMessage in app.session.flashbag.get("two_factor") %}
|
||||
<p class="error">{{ flashMessage|trans }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<div class="input-field col s12">
|
||||
<label for="_auth_code">{{ "scheb_two_factor.auth_code"|trans }}</label>
|
||||
<input id="_auth_code" type="text" autocomplete="off" name="_auth_code" />
|
||||
</div>
|
||||
|
||||
{% if useTrustedOption %}
|
||||
<div class="input-field col s12">
|
||||
<input id="_trusted" type="checkbox" name="_trusted" />
|
||||
<label for="_trusted">{{ "scheb_two_factor.trusted"|trans }}</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-action center">
|
||||
<a href="{{ path('fos_user_security_logout') }}" class="waves-effect waves-light grey btn"><i class="material-icons left"></i> {% trans %}Cancel{% endtrans %}</a>
|
||||
<button class="btn waves-effect waves-light" type="submit" name="send">
|
||||
{{ "scheb_two_factor.login"|trans }}
|
||||
<i class="mdi-content-send right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue