Skip to content

Commit

Permalink
Specify an listener to update the encoded password on login
Browse files Browse the repository at this point in the history
  • Loading branch information
pamil committed Jan 10, 2019
1 parent 400cba3 commit d05d8aa
Show file tree
Hide file tree
Showing 4 changed files with 321 additions and 0 deletions.
@@ -0,0 +1,80 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\UserBundle\EventListener;

use Doctrine\Common\Persistence\ObjectManager;
use Sylius\Component\User\Model\UserInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

final class UpdateUserEncoderListener
{
/** @var ObjectManager */
private $objectManager;

/** @var string */
private $recommendedEncoderName;

/** @var string */
private $className;

/** @var string */
private $interfaceName;

/** @var string */
private $passwordParameter;

public function __construct(
ObjectManager $objectManager,
string $recommendedEncoderName,
string $className,
string $interfaceName,
string $passwordParameter
) {
$this->objectManager = $objectManager;
$this->recommendedEncoderName = $recommendedEncoderName;
$this->className = $className;
$this->interfaceName = $interfaceName;
$this->passwordParameter = $passwordParameter;
}

public function onSecurityInteractiveLogin(InteractiveLoginEvent $event): void
{
$user = $event->getAuthenticationToken()->getUser();

if (!$user instanceof UserInterface) {
return;
}

if (!$user instanceof $this->className || !$user instanceof $this->interfaceName) {
return;
}

if ($user->getEncoderName() === $this->recommendedEncoderName) {
return;
}

$request = $event->getRequest();

$plainPassword = $request->request->get($this->passwordParameter);
if (null === $plainPassword || '' === $plainPassword) {
return;
}

$user->setEncoderName($this->recommendedEncoderName);
$user->setPlainPassword($plainPassword);

$this->objectManager->persist($user);
$this->objectManager->flush();
}
}
@@ -0,0 +1,201 @@
<?php

declare(strict_types=1);

namespace spec\Sylius\Bundle\UserBundle\EventListener;

use Doctrine\Common\Persistence\ObjectManager;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\UserBundle\spec\Fixture\FixtureUser;
use Sylius\Bundle\UserBundle\spec\Fixture\FixtureUserInterface;
use Sylius\Component\User\Model\User;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

final class UpdateUserEncoderListenerSpec extends ObjectBehavior
{
function let(ObjectManager $objectManager): void
{
$this->beConstructedWith($objectManager, 'newalgo', FixtureUser::class, FixtureUserInterface::class, '_password');
}

function it_does_nothing_if_user_does_not_implement_user_interface(
ObjectManager $objectManager,
InteractiveLoginEvent $event,
TokenInterface $token
): void {
$event->getAuthenticationToken()->willReturn($token);

$user = new \stdClass();

$token->getUser()->willReturn($user);

$objectManager->persist($user)->shouldNotBeCalled();
$objectManager->flush()->shouldNotBeCalled();

$this->onSecurityInteractiveLogin($event);
}

function it_does_nothing_if_user_does_not_implement_specified_class_or_interface(
ObjectManager $objectManager,
InteractiveLoginEvent $event,
TokenInterface $token
): void {
$event->getAuthenticationToken()->willReturn($token);

$user = new User();

$token->getUser()->willReturn($user);

$objectManager->persist($user)->shouldNotBeCalled();
$objectManager->flush()->shouldNotBeCalled();

$this->onSecurityInteractiveLogin($event);
}

function it_does_nothing_if_user_uses_the_recommended_encoder(
ObjectManager $objectManager,
InteractiveLoginEvent $event,
TokenInterface $token
): void {
$event->getAuthenticationToken()->willReturn($token);

$user = new FixtureUser();
$user->setEncoderName('newalgo');

$token->getUser()->willReturn($user);

$objectManager->persist($user)->shouldNotBeCalled();
$objectManager->flush()->shouldNotBeCalled();

$this->onSecurityInteractiveLogin($event);

assert($user->getEncoderName() === 'newalgo');
assert($user->getPlainPassword() === null);
}

function it_does_nothing_if_plain_password_could_not_be_resolved(
ObjectManager $objectManager,
InteractiveLoginEvent $event,
TokenInterface $token
): void {
$request = new Request();

$event->getAuthenticationToken()->willReturn($token);
$event->getRequest()->willReturn($request);

$user = new FixtureUser();
$user->setEncoderName('oldalgo');

$token->getUser()->willReturn($user);

$objectManager->persist($user)->shouldNotBeCalled();
$objectManager->flush()->shouldNotBeCalled();

$this->onSecurityInteractiveLogin($event);

assert($user->getEncoderName() === 'oldalgo');
assert($user->getPlainPassword() === null);
}

function it_does_nothing_if_resolved_plain_password_is_null(
ObjectManager $objectManager,
InteractiveLoginEvent $event,
TokenInterface $token
): void {
$request = new Request();
$request->request->set('_password', null);

$event->getAuthenticationToken()->willReturn($token);
$event->getRequest()->willReturn($request);

$user = new FixtureUser();
$user->setEncoderName('oldalgo');

$token->getUser()->willReturn($user);

$objectManager->persist($user)->shouldNotBeCalled();
$objectManager->flush()->shouldNotBeCalled();

$this->onSecurityInteractiveLogin($event);

assert($user->getEncoderName() === 'oldalgo');
assert($user->getPlainPassword() === null);
}

function it_does_nothing_if_resolved_plain_password_is_empty(
ObjectManager $objectManager,
InteractiveLoginEvent $event,
TokenInterface $token
): void {
$request = new Request();
$request->request->set('_password', '');

$event->getAuthenticationToken()->willReturn($token);
$event->getRequest()->willReturn($request);

$user = new FixtureUser();
$user->setEncoderName('oldalgo');

$token->getUser()->willReturn($user);

$objectManager->persist($user)->shouldNotBeCalled();
$objectManager->flush()->shouldNotBeCalled();

$this->onSecurityInteractiveLogin($event);

assert($user->getEncoderName() === 'oldalgo');
assert($user->getPlainPassword() === null);
}

function it_updates_the_encoder_and_plain_password_if_using_old_encoder_and_plain_password_could_be_resolved(
ObjectManager $objectManager,
InteractiveLoginEvent $event,
TokenInterface $token
): void {
$request = new Request();
$request->request->set('_password', 'plainpassword');

$event->getAuthenticationToken()->willReturn($token);
$event->getRequest()->willReturn($request);

$user = new FixtureUser();
$user->setEncoderName('oldalgo');

$token->getUser()->willReturn($user);

$objectManager->persist($user)->shouldBeCalled();
$objectManager->flush()->shouldBeCalled();

$this->onSecurityInteractiveLogin($event);

assert($user->getEncoderName() === 'newalgo');
assert($user->getPlainPassword() === 'plainpassword');
}

function it_updates_the_encoder_and_plain_password_if_using_default_null_encoder_and_plain_password_could_be_resolved(
ObjectManager $objectManager,
InteractiveLoginEvent $event,
TokenInterface $token
): void {
$request = new Request();
$request->request->set('_password', 'plainpassword');

$event->getAuthenticationToken()->willReturn($token);
$event->getRequest()->willReturn($request);

$user = new FixtureUser();
$user->setEncoderName(null);

$token->getUser()->willReturn($user);

$objectManager->persist($user)->shouldBeCalled();
$objectManager->flush()->shouldBeCalled();

$this->onSecurityInteractiveLogin($event);

assert($user->getEncoderName() === 'newalgo');
assert($user->getPlainPassword() === 'plainpassword');
}
}
20 changes: 20 additions & 0 deletions src/Sylius/Bundle/UserBundle/spec/Fixture/FixtureUser.php
@@ -0,0 +1,20 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\UserBundle\spec\Fixture;

use Sylius\Component\User\Model\User;

final class FixtureUser extends User implements FixtureUserInterface
{
}
20 changes: 20 additions & 0 deletions src/Sylius/Bundle/UserBundle/spec/Fixture/FixtureUserInterface.php
@@ -0,0 +1,20 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\UserBundle\spec\Fixture;

use Sylius\Component\User\Model\UserInterface;

interface FixtureUserInterface extends UserInterface
{
}

0 comments on commit d05d8aa

Please sign in to comment.