Skip to content

Commit

Permalink
Migrated SF AdvancedUserInterface to FOS UserInterface (#2815)
Browse files Browse the repository at this point in the history
* Migrated SF AdvancedUserInterface to FOS UserInterface

AdvancedUserInterface is deprecated since Symfony 4.1
- symfony/symfony#23508

Issue:
- #2803 Deprecation with Symfony 4.1 - AdvancedUserInterface

* Code style fixed and using `getMockBuilder` instead of `createMock`

* Code style fixed and using attributes instead of `$this->expectException`

* Change to restart travis

* EquatableInterface added to `UserInterface` and implementation added to `User`

* Tests after merge of master fixed

* Tests after merge of master fixed

* Update README.md

* Added compatibility for apps that check against AdvancedUserInterface

* Code style fixed to pass all travis tests

* fos_user.user_checker Service marked as non-public
  • Loading branch information
devtronic authored and XWB committed Sep 19, 2018
1 parent f328299 commit b93d732
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 6 deletions.
25 changes: 25 additions & 0 deletions Model/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Security\Core\User\UserInterface as BaseUserInterface;

/**
* Storage agnostic user object.
Expand Down Expand Up @@ -554,4 +555,28 @@ public function removeGroup(GroupInterface $group)

return $this;
}

/**
* {@inheritdoc}
*/
public function isEqualTo(BaseUserInterface $user)
{
if (!$user instanceof self) {
return false;
}

if ($this->password !== $user->getPassword()) {
return false;
}

if ($this->salt !== $user->getSalt()) {
return false;
}

if ($this->username !== $user->getUsername()) {
return false;
}

return true;
}
}
78 changes: 74 additions & 4 deletions Model/UserInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@

namespace FOS\UserBundle\Model;

use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\UserInterface as BaseUserInterface;

/**
* @author Thibault Duplessis <[email protected]>
* @author Johannes M. Schmitt <[email protected]>
* @internal Only for back compatibility. Remove / merge when dropping support for Symfony 4
*/
interface UserInterface extends AdvancedUserInterface, \Serializable
interface FosUserInterface extends \Serializable
{
const ROLE_DEFAULT = 'ROLE_USER';

Expand Down Expand Up @@ -227,4 +227,74 @@ public function addRole($role);
* @return static
*/
public function removeRole($role);

/**
* Checks whether the user's account has expired.
*
* Internally, if this method returns false, the authentication system
* will throw an AccountExpiredException and prevent login.
*
* @return bool true if the user's account is non expired, false otherwise
*
* @see AccountExpiredException
*/
public function isAccountNonExpired();

/**
* Checks whether the user is locked.
*
* Internally, if this method returns false, the authentication system
* will throw a LockedException and prevent login.
*
* @return bool true if the user is not locked, false otherwise
*
* @see LockedException
*/
public function isAccountNonLocked();

/**
* Checks whether the user's credentials (password) has expired.
*
* Internally, if this method returns false, the authentication system
* will throw a CredentialsExpiredException and prevent login.
*
* @return bool true if the user's credentials are non expired, false otherwise
*
* @see CredentialsExpiredException
*/
public function isCredentialsNonExpired();

/**
* Checks whether the user is enabled.
*
* Internally, if this method returns false, the authentication system
* will throw a DisabledException and prevent login.
*
* @return bool true if the user is enabled, false otherwise
*
* @see DisabledException
*/
public function isEnabled();
}

// This is required to support apps that explicitly check if a user is an instance of AdvancedUserInterface
if (interface_exists('\Symfony\Component\Security\Core\User\AdvancedUserInterface')) {
/**
* @author Thibault Duplessis <[email protected]>
* @author Johannes M. Schmitt <[email protected]>
*
* @deprecated since Symfony 4.1. Remove in Nov 2023 (End of support for security fixes SF 4.4)
*/
interface UserInterface extends FosUserInterface, \Symfony\Component\Security\Core\User\AdvancedUserInterface
{
}
} else {
/**
* @author Thibault Duplessis <[email protected]>
* @author Johannes M. Schmitt <[email protected]>
* @author Julian Finkler <[email protected]>
*/
interface UserInterface extends FosUserInterface, BaseUserInterface, EquatableInterface
{
}
}
2 changes: 2 additions & 0 deletions Resources/config/security.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
</service>

<service id="FOS\UserBundle\Controller\SecurityController" alias="fos_user.security.controller" public="true" />

<service id="fos_user.user_checker" class="FOS\UserBundle\Security\UserChecker" public="false" />
</services>

</container>
1 change: 1 addition & 0 deletions Resources/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ in your application:
firewalls:
main:
pattern: ^/
user_checker: fos_user.user_checker
form_login:
provider: fos_userbundle
csrf_token_generator: security.csrf.token_manager
Expand Down
63 changes: 63 additions & 0 deletions Security/UserChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/*
* This file is part of the FOSUserBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FOS\UserBundle\Security;

use Symfony\Component\Security\Core\Exception\AccountExpiredException;
use Symfony\Component\Security\Core\Exception\CredentialsExpiredException;
use Symfony\Component\Security\Core\Exception\DisabledException;
use Symfony\Component\Security\Core\Exception\LockedException;
use Symfony\Component\Security\Core\User\UserChecker as BaseUserChecker;
use Symfony\Component\Security\Core\User\UserInterface as BaseUserInterface;

/**
* UserChecker checks the user account flags.
*
* @author Julian Finkler (Devtronic) <[email protected]>
*/
class UserChecker extends BaseUserChecker
{
/**
* {@inheritdoc}
*/
public function checkPreAuth(BaseUserInterface $user)
{
if (!$user->isAccountNonLocked()) {
$ex = new LockedException('User account is locked.');
$ex->setUser($user);
throw $ex;
}

if (!$user->isEnabled()) {
$ex = new DisabledException('User account is disabled.');
$ex->setUser($user);
throw $ex;
}

if (!$user->isAccountNonExpired()) {
$ex = new AccountExpiredException('User account has expired.');
$ex->setUser($user);
throw $ex;
}
}

/**
* {@inheritdoc}
*/
public function checkPostAuth(BaseUserInterface $user)
{
if (!$user->isCredentialsNonExpired()) {
$ex = new CredentialsExpiredException('User credentials have expired.');
$ex->setUser($user);
throw $ex;
}
}
}
3 changes: 1 addition & 2 deletions Tests/Mailer/MailerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

use FOS\UserBundle\Mailer\Mailer;
use PHPUnit\Framework\TestCase;
use Swift_Events_EventDispatcher;
use Swift_Mailer;
use Swift_Transport_NullTransport;

Expand Down Expand Up @@ -84,7 +83,7 @@ private function getMailer()
return new Mailer(
new Swift_Mailer(
new Swift_Transport_NullTransport(
$this->getMockBuilder(Swift_Events_EventDispatcher::class)->getMock()
$this->getMockBuilder('\Swift_Events_EventDispatcher')->getMock()
)
),
$this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->getMock(),
Expand Down
19 changes: 19 additions & 0 deletions Tests/Model/UserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,25 @@ public function testFalseHasRole()
$this->assertTrue($user->hasRole($newrole));
}

public function testIsEqualTo()
{
$user = $this->getUser();
$this->assertTrue($user->isEqualTo($user));
$this->assertFalse($user->isEqualTo($this->getMockBuilder('FOS\UserBundle\Model\UserInterface')->getMock()));

$user2 = $this->getUser();
$user2->setPassword('secret');
$this->assertFalse($user->isEqualTo($user2));

$user3 = $this->getUser();
$user3->setSalt('pepper');
$this->assertFalse($user->isEqualTo($user3));

$user4 = $this->getUser();
$user4->setUsername('f00b4r');
$this->assertFalse($user->isEqualTo($user4));
}

/**
* @return User
*/
Expand Down
105 changes: 105 additions & 0 deletions Tests/Security/UserCheckerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

/*
* This file is part of the FOSUserBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FOS\UserBundle\Tests\Security;

use FOS\UserBundle\Security\UserChecker;
use PHPUnit\Framework\TestCase;

class UserCheckerTest extends TestCase
{
/**
* @expectedException \Symfony\Component\Security\Core\Exception\LockedException
* @expectedExceptionMessage User account is locked.
*/
public function testCheckPreAuthFailsLockedOut()
{
$userMock = $this->getUser(false, false, false, false);
$checker = new UserChecker();
$checker->checkPreAuth($userMock);
}

/**
* @expectedException \Symfony\Component\Security\Core\Exception\DisabledException
* @expectedExceptionMessage User account is disabled.
*/
public function testCheckPreAuthFailsIsEnabled()
{
$userMock = $this->getUser(true, false, false, false);
$checker = new UserChecker();
$checker->checkPreAuth($userMock);
}

/**
* @expectedException \Symfony\Component\Security\Core\Exception\AccountExpiredException
* @expectedExceptionMessage User account has expired.
*/
public function testCheckPreAuthFailsIsAccountNonExpired()
{
$userMock = $this->getUser(true, true, false, false);
$checker = new UserChecker();
$checker->checkPreAuth($userMock);
}

public function testCheckPreAuthSuccess()
{
$userMock = $this->getUser(true, true, true, false);
$checker = new UserChecker();

try {
$this->assertNull($checker->checkPreAuth($userMock));
} catch (\Exception $ex) {
$this->fail();
}
}

/**
* @expectedException \Symfony\Component\Security\Core\Exception\CredentialsExpiredException
* @expectedExceptionMessage User credentials have expired.
*/
public function testCheckPostAuthFailsIsCredentialsNonExpired()
{
$userMock = $this->getUser(true, true, true, false);
$checker = new UserChecker();
$checker->checkPostAuth($userMock);
}

public function testCheckPostAuthSuccess()
{
$userMock = $this->getUser(true, true, true, true);
$checker = new UserChecker();

try {
$this->assertNull($checker->checkPostAuth($userMock));
} catch (\Exception $ex) {
$this->fail();
}
}

private function getUser($isAccountNonLocked, $isEnabled, $isAccountNonExpired, $isCredentialsNonExpired)
{
$userMock = $this->getMockBuilder('FOS\UserBundle\Model\User')->getMock();
$userMock
->method('isAccountNonLocked')
->willReturn($isAccountNonLocked);
$userMock
->method('isEnabled')
->willReturn($isEnabled);
$userMock
->method('isAccountNonExpired')
->willReturn($isAccountNonExpired);
$userMock
->method('isCredentialsNonExpired')
->willReturn($isCredentialsNonExpired);

return $userMock;
}
}

0 comments on commit b93d732

Please sign in to comment.