-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrated SF AdvancedUserInterface to FOS UserInterface #2815
Changes from 14 commits
017a2bb
f4d3275
340db13
e6fcbfe
ddc0b08
6beb423
41ab05c
6512e71
e594ae5
450b2d4
1f10a7f
815f35c
d02f9c7
3fd0e97
6b8d62b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 with UserInterface in Nov 2023 (End of support for security fixes SF 4.4) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would say |
||
*/ | ||
interface UserInterface extends AdvancedUserInterface, \Serializable | ||
interface FosUserInterface extends \Serializable | ||
{ | ||
const ROLE_DEFAULT = 'ROLE_USER'; | ||
|
||
|
@@ -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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we remove this method, some people can get in trouble ;-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's true as long as compatibility is granted. When dropping sf4 support, maybe, this should be removed and users should, for example, implement own There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @DonCallisto , I don't understand why introducing account locking in "FOS user" is such a shame? Does this bundle not manage user account? perhaps this is a missing feature from the bundle and it could permit to be fully compliant with next major symfony version and even avoid BC break... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yonisolo see https://symfony.com/blog/new-in-symfony-4-1-deprecated-the-advanceduserinterface I suppose we provided it only as a a "compatibility feature" for symfony. Don't know if it wise to keep it, instead in a new FOSUserBundle version we should show users how to implement this feature on their own if the need to keep compatibility. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes i saw that deprecation and the link to create a custom user checker but preauth check for account status could be implemented as an interface to provide compatibility and with a basic usage which could be overridden later by the user for its own business logic. Presently, if FOSUserBundle remove this functionality, most user will be lost between the Symfony security component and the FOsUser User management, please don't break this chain... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yonisolo No one proposed to remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah ok, my mistakes, sorry!! I understand better, thank you!! |
||
|
||
/** | ||
* 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')) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will always return true in Symfony 4.x as the interface was just deprecated and not removed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes that's true, but if we do that way, the user bundle is compatible to SF5. Do you have an alternative proposal how we can solve this problem? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Solution can be: use Symfony\Component\HttpKernel\Kernel;
if ( Kernel::VERSION_ID >= 40100 ){
} Not sure if this is the best we can do, but it's working and nobody receive deprecation notices. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that won't work fine, as it will detect the version of the wrong component There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @XWB probably I'm missing the point but this looks fine to me as long as this version is compliant with sf4 AND sf5. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @DonCallisto Well, my point was that extending the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's impossible unless you create a new major version, still compatible with sf4, and explain that |
||
/** | ||
* @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 | ||
{ | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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="true" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to make it public, as it is meant to be referenced in the SecurityBundle config, which supports private services There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moreover I would use FQCN as |
||
</services> | ||
|
||
</container> |
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()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO, the FOSUserBundle checker should check only the methods that belong to FOSUserBundle (account locking is not part of FOSUserBundle) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue is that some people might be using these methods. Removing it would break their application. |
||
$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; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,6 @@ | |
|
||
use FOS\UserBundle\Mailer\Mailer; | ||
use PHPUnit\Framework\TestCase; | ||
use Swift_Events_EventDispatcher; | ||
use Swift_Mailer; | ||
use Swift_Transport_NullTransport; | ||
|
||
|
@@ -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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change is not required. Travis currently fails due to a memory error, not because of this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is required, because this this test fails: https://travis-ci.org/FriendsOfSymfony/FOSUserBundle/jobs/411741460 Only this test fails due to a memory error: https://travis-ci.org/FriendsOfSymfony/FOSUserBundle/jobs/411741470 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which is quite strange because the test already passed on master branch. |
||
) | ||
), | ||
$this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->getMock(), | ||
|
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; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm totally missing the point here. What this method suppose to do and where is used/useful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The FOS
UserInterface
extends fromEquatableInterface
https://api.symfony.com/master/Symfony/Component/Security/Core/User/EquatableInterface.html
afaik this is required for user switching and re-authentication
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not totally sure of this but I really don't know it properly. Just wondering why
AdvantageUserInterface
didn't extends that interface aswell so that's the reason of my question.