Lazily load the user during the check passport event
This commit is contained in:
parent
53a8f7d490
commit
907ef311bf
@ -41,6 +41,7 @@ use Symfony\Component\Security\Core\User\ChainUserProvider;
|
|||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
use Symfony\Component\Security\Http\Controller\UserValueResolver;
|
use Symfony\Component\Security\Http\Controller\UserValueResolver;
|
||||||
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
|
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
|
||||||
|
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -342,6 +343,12 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
|||||||
throw new InvalidConfigurationException(sprintf('Invalid firewall "%s": user provider "%s" not found.', $id, $firewall['provider']));
|
throw new InvalidConfigurationException(sprintf('Invalid firewall "%s": user provider "%s" not found.', $id, $firewall['provider']));
|
||||||
}
|
}
|
||||||
$defaultProvider = $providerIds[$normalizedName];
|
$defaultProvider = $providerIds[$normalizedName];
|
||||||
|
|
||||||
|
if ($this->authenticatorManagerEnabled) {
|
||||||
|
$container->setDefinition('security.listener.'.$id.'.user_provider', new ChildDefinition('security.listener.user_provider.abstract'))
|
||||||
|
->addTag('kernel.event_listener', ['event' => CheckPassportEvent::class, 'priority' => 2048, 'method' => 'checkPassport'])
|
||||||
|
->replaceArgument(0, new Reference($defaultProvider));
|
||||||
|
}
|
||||||
} elseif (1 === \count($providerIds)) {
|
} elseif (1 === \count($providerIds)) {
|
||||||
$defaultProvider = reset($providerIds);
|
$defaultProvider = reset($providerIds);
|
||||||
}
|
}
|
||||||
@ -632,7 +639,7 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
|||||||
return $userProvider;
|
return $userProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('remember_me' === $factoryKey || 'anonymous' === $factoryKey) {
|
if ('remember_me' === $factoryKey || 'anonymous' === $factoryKey || 'custom_authenticators' === $factoryKey) {
|
||||||
return 'security.user_providers';
|
return 'security.user_providers';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,11 +23,13 @@ use Symfony\Component\Security\Http\Authenticator\JsonLoginAuthenticator;
|
|||||||
use Symfony\Component\Security\Http\Authenticator\RememberMeAuthenticator;
|
use Symfony\Component\Security\Http\Authenticator\RememberMeAuthenticator;
|
||||||
use Symfony\Component\Security\Http\Authenticator\RemoteUserAuthenticator;
|
use Symfony\Component\Security\Http\Authenticator\RemoteUserAuthenticator;
|
||||||
use Symfony\Component\Security\Http\Authenticator\X509Authenticator;
|
use Symfony\Component\Security\Http\Authenticator\X509Authenticator;
|
||||||
|
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
|
||||||
use Symfony\Component\Security\Http\EventListener\CheckCredentialsListener;
|
use Symfony\Component\Security\Http\EventListener\CheckCredentialsListener;
|
||||||
use Symfony\Component\Security\Http\EventListener\PasswordMigratingListener;
|
use Symfony\Component\Security\Http\EventListener\PasswordMigratingListener;
|
||||||
use Symfony\Component\Security\Http\EventListener\RememberMeListener;
|
use Symfony\Component\Security\Http\EventListener\RememberMeListener;
|
||||||
use Symfony\Component\Security\Http\EventListener\SessionStrategyListener;
|
use Symfony\Component\Security\Http\EventListener\SessionStrategyListener;
|
||||||
use Symfony\Component\Security\Http\EventListener\UserCheckerListener;
|
use Symfony\Component\Security\Http\EventListener\UserCheckerListener;
|
||||||
|
use Symfony\Component\Security\Http\EventListener\UserProviderListener;
|
||||||
use Symfony\Component\Security\Http\Firewall\AuthenticatorManagerListener;
|
use Symfony\Component\Security\Http\Firewall\AuthenticatorManagerListener;
|
||||||
|
|
||||||
return static function (ContainerConfigurator $container) {
|
return static function (ContainerConfigurator $container) {
|
||||||
@ -73,6 +75,18 @@ return static function (ContainerConfigurator $container) {
|
|||||||
])
|
])
|
||||||
->tag('kernel.event_subscriber')
|
->tag('kernel.event_subscriber')
|
||||||
|
|
||||||
|
->set('security.listener.user_provider', UserProviderListener::class)
|
||||||
|
->args([
|
||||||
|
service('security.user_providers'),
|
||||||
|
])
|
||||||
|
->tag('kernel.event_listener', ['event' => CheckPassportEvent::class, 'priority' => 1024, 'method' => 'checkPassport'])
|
||||||
|
|
||||||
|
->set('security.listener.user_provider.abstract', UserProviderListener::class)
|
||||||
|
->abstract()
|
||||||
|
->args([
|
||||||
|
abstract_arg('user provider'),
|
||||||
|
])
|
||||||
|
|
||||||
->set('security.listener.password_migrating', PasswordMigratingListener::class)
|
->set('security.listener.password_migrating', PasswordMigratingListener::class)
|
||||||
->args([
|
->args([
|
||||||
service('security.encoder_factory'),
|
service('security.encoder_factory'),
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
|
||||||
|
|
||||||
|
class AuthenticatorTest extends AbstractWebTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider provideEmails
|
||||||
|
*/
|
||||||
|
public function testGlobalUserProvider($email)
|
||||||
|
{
|
||||||
|
$client = $this->createClient(['test_case' => 'Authenticator', 'root_config' => 'implicit_user_provider.yml']);
|
||||||
|
|
||||||
|
$client->request('GET', '/profile', [], [], [
|
||||||
|
'HTTP_X-USER-EMAIL' => $email,
|
||||||
|
]);
|
||||||
|
$this->assertJsonStringEqualsJsonString('{"email":"'.$email.'"}', $client->getResponse()->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideEmails
|
||||||
|
*/
|
||||||
|
public function testFirewallUserProvider($email, $withinFirewall)
|
||||||
|
{
|
||||||
|
$client = $this->createClient(['test_case' => 'Authenticator', 'root_config' => 'firewall_user_provider.yml']);
|
||||||
|
|
||||||
|
$client->request('GET', '/profile', [], [], [
|
||||||
|
'HTTP_X-USER-EMAIL' => $email,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($withinFirewall) {
|
||||||
|
$this->assertJsonStringEqualsJsonString('{"email":"'.$email.'"}', $client->getResponse()->getContent());
|
||||||
|
} else {
|
||||||
|
$this->assertJsonStringEqualsJsonString('{"error":"Username could not be found."}', $client->getResponse()->getContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideEmails()
|
||||||
|
{
|
||||||
|
yield ['jane@example.org', true];
|
||||||
|
yield ['john@example.org', false];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
|
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
|
|
||||||
|
class ApiAuthenticator extends AbstractAuthenticator
|
||||||
|
{
|
||||||
|
public function supports(Request $request): ?bool
|
||||||
|
{
|
||||||
|
return $request->headers->has('X-USER-EMAIL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authenticate(Request $request): PassportInterface
|
||||||
|
{
|
||||||
|
$email = $request->headers->get('X-USER-EMAIL');
|
||||||
|
if (false === strpos($email, '@')) {
|
||||||
|
throw new BadCredentialsException('Email is not a valid email address.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SelfValidatingPassport(new UserBadge($email));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
|
||||||
|
{
|
||||||
|
return new JsonResponse([
|
||||||
|
'error' => $exception->getMessageKey(),
|
||||||
|
], JsonResponse::HTTP_FORBIDDEN);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
|
||||||
|
class ProfileController extends AbstractController
|
||||||
|
{
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||||
|
|
||||||
|
return $this->json(['email' => $this->getUser()->getUsername()]);
|
||||||
|
}
|
||||||
|
}
|
@ -51,10 +51,6 @@ class CsrfFormLoginTest extends AbstractWebTestCase
|
|||||||
$client = $this->createClient($options);
|
$client = $this->createClient($options);
|
||||||
|
|
||||||
$form = $client->request('GET', '/login')->selectButton('login')->form();
|
$form = $client->request('GET', '/login')->selectButton('login')->form();
|
||||||
if ($options['enable_authenticator_manager'] ?? false) {
|
|
||||||
$form['user_login[username]'] = 'johannes';
|
|
||||||
$form['user_login[password]'] = 'test';
|
|
||||||
}
|
|
||||||
$form['user_login[_token]'] = '';
|
$form['user_login[_token]'] = '';
|
||||||
$client->submit($form);
|
$client->submit($form);
|
||||||
|
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
|
||||||
|
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
|
||||||
|
];
|
@ -0,0 +1,33 @@
|
|||||||
|
framework:
|
||||||
|
secret: test
|
||||||
|
router: { resource: "%kernel.project_dir%/%kernel.test_case%/routing.yml", utf8: true }
|
||||||
|
test: ~
|
||||||
|
default_locale: en
|
||||||
|
profiler: false
|
||||||
|
session:
|
||||||
|
storage_id: session.storage.mock_file
|
||||||
|
|
||||||
|
services:
|
||||||
|
logger: { class: Psr\Log\NullLogger }
|
||||||
|
Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ProfileController:
|
||||||
|
public: true
|
||||||
|
calls:
|
||||||
|
- ['setContainer', ['@Psr\Container\ContainerInterface']]
|
||||||
|
tags: [container.service_subscriber]
|
||||||
|
Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ApiAuthenticator: ~
|
||||||
|
|
||||||
|
security:
|
||||||
|
enable_authenticator_manager: true
|
||||||
|
|
||||||
|
encoders:
|
||||||
|
Symfony\Component\Security\Core\User\User: plaintext
|
||||||
|
|
||||||
|
providers:
|
||||||
|
in_memory:
|
||||||
|
memory:
|
||||||
|
users:
|
||||||
|
'jane@example.org': { password: test, roles: [ROLE_USER] }
|
||||||
|
in_memory2:
|
||||||
|
memory:
|
||||||
|
users:
|
||||||
|
'john@example.org': { password: test, roles: [ROLE_USER] }
|
@ -0,0 +1,10 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: ./config.yml }
|
||||||
|
|
||||||
|
security:
|
||||||
|
firewalls:
|
||||||
|
api:
|
||||||
|
pattern: /
|
||||||
|
provider: in_memory
|
||||||
|
custom_authenticator: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ApiAuthenticator
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: ./config.yml }
|
||||||
|
|
||||||
|
security:
|
||||||
|
firewalls:
|
||||||
|
api:
|
||||||
|
pattern: /
|
||||||
|
custom_authenticator: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ApiAuthenticator
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
profile:
|
||||||
|
path: /profile
|
||||||
|
defaults:
|
||||||
|
_controller: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ProfileController
|
@ -27,6 +27,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
|||||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||||
use Symfony\Component\Security\Core\User\User;
|
use Symfony\Component\Security\Core\User\User;
|
||||||
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
@ -64,14 +65,13 @@ class CheckLdapCredentialsListenerTest extends TestCase
|
|||||||
$this->markTestSkipped('This test requires symfony/security-http:^5.1');
|
$this->markTestSkipped('This test requires symfony/security-http:^5.1');
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = new User('Wouter', null, ['ROLE_USER']);
|
|
||||||
// no LdapBadge
|
// no LdapBadge
|
||||||
yield [new TestAuthenticator(), new Passport($user, new PasswordCredentials('s3cret'))];
|
yield [new TestAuthenticator(), new Passport(new UserBadge('test'), new PasswordCredentials('s3cret'))];
|
||||||
|
|
||||||
// ldap already resolved
|
// ldap already resolved
|
||||||
$badge = new LdapBadge('app.ldap');
|
$badge = new LdapBadge('app.ldap');
|
||||||
$badge->markResolved();
|
$badge->markResolved();
|
||||||
yield [new TestAuthenticator(), new Passport($user, new PasswordCredentials('s3cret'), [$badge])];
|
yield [new TestAuthenticator(), new Passport(new UserBadge('test'), new PasswordCredentials('s3cret'), [$badge])];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPasswordCredentialsAlreadyResolvedThrowsException()
|
public function testPasswordCredentialsAlreadyResolvedThrowsException()
|
||||||
@ -81,8 +81,7 @@ class CheckLdapCredentialsListenerTest extends TestCase
|
|||||||
|
|
||||||
$badge = new PasswordCredentials('s3cret');
|
$badge = new PasswordCredentials('s3cret');
|
||||||
$badge->markResolved();
|
$badge->markResolved();
|
||||||
$user = new User('Wouter', null, ['ROLE_USER']);
|
$passport = new Passport(new UserBadge('test'), $badge, [new LdapBadge('app.ldap')]);
|
||||||
$passport = new Passport($user, $badge, [new LdapBadge('app.ldap')]);
|
|
||||||
|
|
||||||
$listener = $this->createListener();
|
$listener = $this->createListener();
|
||||||
$listener->onCheckPassport(new CheckPassportEvent(new TestAuthenticator(), $passport));
|
$listener->onCheckPassport(new CheckPassportEvent(new TestAuthenticator(), $passport));
|
||||||
@ -116,7 +115,7 @@ class CheckLdapCredentialsListenerTest extends TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
// no password credentials
|
// no password credentials
|
||||||
yield [new SelfValidatingPassport(new User('Wouter', null, ['ROLE_USER']), [new LdapBadge('app.ldap')])];
|
yield [new SelfValidatingPassport(new UserBadge('test'), [new LdapBadge('app.ldap')])];
|
||||||
|
|
||||||
// no user passport
|
// no user passport
|
||||||
$passport = $this->createMock(PassportInterface::class);
|
$passport = $this->createMock(PassportInterface::class);
|
||||||
@ -181,7 +180,7 @@ class CheckLdapCredentialsListenerTest extends TestCase
|
|||||||
{
|
{
|
||||||
return new CheckPassportEvent(
|
return new CheckPassportEvent(
|
||||||
new TestAuthenticator(),
|
new TestAuthenticator(),
|
||||||
new Passport(new User('Wouter', null, ['ROLE_USER']), new PasswordCredentials($password), [$ldapBadge ?? new LdapBadge('app.ldap')])
|
new Passport(new UserBadge('Wouter', function () { return new User('Wouter', null, ['ROLE_USER']); }), new PasswordCredentials($password), [$ldapBadge ?? new LdapBadge('app.ldap')])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
|
|||||||
use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
|
use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
@ -62,14 +63,11 @@ class GuardBridgeAuthenticator implements InteractiveAuthenticatorInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get the user from the GuardAuthenticator
|
// get the user from the GuardAuthenticator
|
||||||
$user = $this->guard->getUser($credentials, $this->userProvider);
|
if (class_exists(UserBadge::class)) {
|
||||||
|
$user = new UserBadge('guard_authenticator_'.md5(serialize($credentials)), function () use ($credentials) { return $this->getUser($credentials); });
|
||||||
if (null === $user) {
|
} else {
|
||||||
throw new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($this->guard)));
|
// BC with symfony/security-http:5.1
|
||||||
}
|
$user = $this->getUser($credentials);
|
||||||
|
|
||||||
if (!$user instanceof UserInterface) {
|
|
||||||
throw new \UnexpectedValueException(sprintf('The "%s::getUser()" method must return a UserInterface. You returned "%s".', get_debug_type($this->guard), get_debug_type($user)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$passport = new Passport($user, new CustomCredentials([$this->guard, 'checkCredentials'], $credentials));
|
$passport = new Passport($user, new CustomCredentials([$this->guard, 'checkCredentials'], $credentials));
|
||||||
@ -84,6 +82,21 @@ class GuardBridgeAuthenticator implements InteractiveAuthenticatorInterface
|
|||||||
return $passport;
|
return $passport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getUser($credentials): UserInterface
|
||||||
|
{
|
||||||
|
$user = $this->guard->getUser($credentials, $this->userProvider);
|
||||||
|
|
||||||
|
if (null === $user) {
|
||||||
|
throw new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($this->guard)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user instanceof UserInterface) {
|
||||||
|
throw new \UnexpectedValueException(sprintf('The "%s::getUser()" method must return a UserInterface. You returned "%s".', get_debug_type($this->guard), get_debug_type($user)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
|
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
|
||||||
{
|
{
|
||||||
if (!$passport instanceof UserPassportInterface) {
|
if (!$passport instanceof UserPassportInterface) {
|
||||||
|
@ -22,6 +22,7 @@ use Symfony\Component\Security\Guard\Authenticator\GuardBridgeAuthenticator;
|
|||||||
use Symfony\Component\Security\Guard\AuthenticatorInterface;
|
use Symfony\Component\Security\Guard\AuthenticatorInterface;
|
||||||
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
|
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
|
|
||||||
@ -83,6 +84,7 @@ class GuardBridgeAuthenticatorTest extends TestCase
|
|||||||
->willReturn($user);
|
->willReturn($user);
|
||||||
|
|
||||||
$passport = $this->authenticator->authenticate($request);
|
$passport = $this->authenticator->authenticate($request);
|
||||||
|
$this->assertEquals($user, $passport->getUser());
|
||||||
$this->assertTrue($passport->hasBadge(CustomCredentials::class));
|
$this->assertTrue($passport->hasBadge(CustomCredentials::class));
|
||||||
|
|
||||||
$this->guardAuthenticator->expects($this->once())
|
$this->guardAuthenticator->expects($this->once())
|
||||||
@ -110,7 +112,8 @@ class GuardBridgeAuthenticatorTest extends TestCase
|
|||||||
->with($credentials, $this->userProvider)
|
->with($credentials, $this->userProvider)
|
||||||
->willReturn(null);
|
->willReturn(null);
|
||||||
|
|
||||||
$this->authenticator->authenticate($request);
|
$passport = $this->authenticator->authenticate($request);
|
||||||
|
$passport->getUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,12 +129,6 @@ class GuardBridgeAuthenticatorTest extends TestCase
|
|||||||
->with($request)
|
->with($request)
|
||||||
->willReturn($credentials);
|
->willReturn($credentials);
|
||||||
|
|
||||||
$user = new User('test', null, ['ROLE_USER']);
|
|
||||||
$this->guardAuthenticator->expects($this->once())
|
|
||||||
->method('getUser')
|
|
||||||
->with($credentials, $this->userProvider)
|
|
||||||
->willReturn($user);
|
|
||||||
|
|
||||||
$this->guardAuthenticator->expects($this->once())
|
$this->guardAuthenticator->expects($this->once())
|
||||||
->method('supportsRememberMe')
|
->method('supportsRememberMe')
|
||||||
->willReturn($rememberMeSupported);
|
->willReturn($rememberMeSupported);
|
||||||
@ -156,7 +153,7 @@ class GuardBridgeAuthenticatorTest extends TestCase
|
|||||||
->with($user, 'main')
|
->with($user, 'main')
|
||||||
->willReturn($token);
|
->willReturn($token);
|
||||||
|
|
||||||
$this->assertSame($token, $this->authenticator->createAuthenticatedToken(new SelfValidatingPassport($user), 'main'));
|
$this->assertSame($token, $this->authenticator->createAuthenticatedToken(new SelfValidatingPassport(new UserBadge('test', function () use ($user) { return $user; })), 'main'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHandleSuccess()
|
public function testHandleSuccess()
|
||||||
|
@ -24,6 +24,7 @@ use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
|||||||
use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
|
use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\AnonymousPassport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\AnonymousPassport;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
use Symfony\Component\Security\Http\Event\AuthenticationTokenCreatedEvent;
|
use Symfony\Component\Security\Http\Event\AuthenticationTokenCreatedEvent;
|
||||||
@ -69,7 +70,7 @@ class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthent
|
|||||||
public function authenticateUser(UserInterface $user, AuthenticatorInterface $authenticator, Request $request, array $badges = []): ?Response
|
public function authenticateUser(UserInterface $user, AuthenticatorInterface $authenticator, Request $request, array $badges = []): ?Response
|
||||||
{
|
{
|
||||||
// create an authenticated token for the User
|
// create an authenticated token for the User
|
||||||
$token = $authenticator->createAuthenticatedToken($passport = new SelfValidatingPassport($user, $badges), $this->firewallName);
|
$token = $authenticator->createAuthenticatedToken($passport = new SelfValidatingPassport(new UserBadge($user->getUsername(), function () use ($user) { return $user; }), $badges), $this->firewallName);
|
||||||
|
|
||||||
// announce the authenticated token
|
// announce the authenticated token
|
||||||
$token = $this->eventDispatcher->dispatch(new AuthenticationTokenCreatedEvent($token))->getAuthenticatedToken();
|
$token = $this->eventDispatcher->dispatch(new AuthenticationTokenCreatedEvent($token))->getAuthenticatedToken();
|
||||||
|
@ -21,6 +21,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
|||||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PreAuthenticatedUserBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PreAuthenticatedUserBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
|
|
||||||
@ -86,10 +87,9 @@ abstract class AbstractPreAuthenticatedAuthenticator implements InteractiveAuthe
|
|||||||
|
|
||||||
public function authenticate(Request $request): PassportInterface
|
public function authenticate(Request $request): PassportInterface
|
||||||
{
|
{
|
||||||
$username = $request->attributes->get('_pre_authenticated_username');
|
return new SelfValidatingPassport(new UserBadge($request->attributes->get('_pre_authenticated_username'), function ($username) {
|
||||||
$user = $this->userProvider->loadUserByUsername($username);
|
return $this->userProvider->loadUserByUsername($username);
|
||||||
|
}), [new PreAuthenticatedUserBadge()]);
|
||||||
return new SelfValidatingPassport($user, [new PreAuthenticatedUserBadge()]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
|
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
|
||||||
|
@ -28,6 +28,7 @@ use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerI
|
|||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
@ -80,12 +81,15 @@ class FormLoginAuthenticator extends AbstractLoginFormAuthenticator
|
|||||||
public function authenticate(Request $request): PassportInterface
|
public function authenticate(Request $request): PassportInterface
|
||||||
{
|
{
|
||||||
$credentials = $this->getCredentials($request);
|
$credentials = $this->getCredentials($request);
|
||||||
$user = $this->userProvider->loadUserByUsername($credentials['username']);
|
|
||||||
|
$passport = new Passport(new UserBadge($credentials['username'], function ($username) {
|
||||||
|
$user = $this->userProvider->loadUserByUsername($username);
|
||||||
if (!$user instanceof UserInterface) {
|
if (!$user instanceof UserInterface) {
|
||||||
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
|
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$passport = new Passport($user, new PasswordCredentials($credentials['password']), [new RememberMeBadge()]);
|
return $user;
|
||||||
|
}), new PasswordCredentials($credentials['password']), [new RememberMeBadge()]);
|
||||||
if ($this->options['enable_csrf']) {
|
if ($this->options['enable_csrf']) {
|
||||||
$passport->addBadge(new CsrfTokenBadge($this->options['csrf_token_id'], $credentials['csrf_token']));
|
$passport->addBadge(new CsrfTokenBadge($this->options['csrf_token_id'], $credentials['csrf_token']));
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
|||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
@ -66,12 +67,14 @@ class HttpBasicAuthenticator implements AuthenticatorInterface, AuthenticationEn
|
|||||||
$username = $request->headers->get('PHP_AUTH_USER');
|
$username = $request->headers->get('PHP_AUTH_USER');
|
||||||
$password = $request->headers->get('PHP_AUTH_PW', '');
|
$password = $request->headers->get('PHP_AUTH_PW', '');
|
||||||
|
|
||||||
|
$passport = new Passport(new UserBadge($username, function ($username) {
|
||||||
$user = $this->userProvider->loadUserByUsername($username);
|
$user = $this->userProvider->loadUserByUsername($username);
|
||||||
if (!$user instanceof UserInterface) {
|
if (!$user instanceof UserInterface) {
|
||||||
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
|
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$passport = new Passport($user, new PasswordCredentials($password));
|
return $user;
|
||||||
|
}), new PasswordCredentials($password));
|
||||||
if ($this->userProvider instanceof PasswordUpgraderInterface) {
|
if ($this->userProvider instanceof PasswordUpgraderInterface) {
|
||||||
$passport->addBadge(new PasswordUpgradeBadge($password, $this->userProvider));
|
$passport->addBadge(new PasswordUpgradeBadge($password, $this->userProvider));
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ use Symfony\Component\Security\Core\User\UserProviderInterface;
|
|||||||
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
|
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
|
||||||
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
|
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
@ -87,12 +88,14 @@ class JsonLoginAuthenticator implements InteractiveAuthenticatorInterface
|
|||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->userProvider->loadUserByUsername($credentials['username']);
|
$passport = new Passport(new UserBadge($credentials['username'], function ($username) {
|
||||||
|
$user = $this->userProvider->loadUserByUsername($username);
|
||||||
if (!$user instanceof UserInterface) {
|
if (!$user instanceof UserInterface) {
|
||||||
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
|
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$passport = new Passport($user, new PasswordCredentials($credentials['password']));
|
return $user;
|
||||||
|
}), new PasswordCredentials($credentials['password']));
|
||||||
if ($this->userProvider instanceof PasswordUpgraderInterface) {
|
if ($this->userProvider instanceof PasswordUpgraderInterface) {
|
||||||
$passport->addBadge(new PasswordUpgradeBadge($credentials['password'], $this->userProvider));
|
$passport->addBadge(new PasswordUpgradeBadge($credentials['password'], $this->userProvider));
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Security\Http\Authenticator\Passport\Badge;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Http\EventListener\UserProviderListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the user in the authentication process.
|
||||||
|
*
|
||||||
|
* It uses an identifier (e.g. email, or username) and
|
||||||
|
* "user loader" to load the related User object.
|
||||||
|
*
|
||||||
|
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||||
|
*
|
||||||
|
* @experimental in 5.2
|
||||||
|
*/
|
||||||
|
class UserBadge implements BadgeInterface
|
||||||
|
{
|
||||||
|
private $userIdentifier;
|
||||||
|
private $userLoader;
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the user badge.
|
||||||
|
*
|
||||||
|
* You must provide a $userIdentifier. This is a unique string representing the
|
||||||
|
* user for this authentication (e.g. the email if authentication is done using
|
||||||
|
* email + password; or a string combining email+company if authentication is done
|
||||||
|
* based on email *and* company name). This string can be used for e.g. login throttling.
|
||||||
|
*
|
||||||
|
* Optionally, you may pass a user loader. This callable receives the $userIdentifier
|
||||||
|
* as argument and must return a UserInterface object (otherwise a UsernameNotFoundException
|
||||||
|
* is thrown). If this is not set, the default user provider will be used with
|
||||||
|
* $userIdentifier as username.
|
||||||
|
*/
|
||||||
|
public function __construct(string $userIdentifier, ?callable $userLoader = null)
|
||||||
|
{
|
||||||
|
$this->userIdentifier = $userIdentifier;
|
||||||
|
$this->userLoader = $userLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUser(): UserInterface
|
||||||
|
{
|
||||||
|
if (null === $this->user) {
|
||||||
|
if (null === $this->userLoader) {
|
||||||
|
throw new \LogicException(sprintf('No user loader is configured, did you forget to register the "%s" listener?', UserProviderListener::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->user = ($this->userLoader)($this->userIdentifier);
|
||||||
|
if (!$this->user instanceof UserInterface) {
|
||||||
|
throw new UsernameNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserLoader(): ?callable
|
||||||
|
{
|
||||||
|
return $this->userLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUserLoader(callable $userLoader): void
|
||||||
|
{
|
||||||
|
$this->userLoader = $userLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isResolved(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Http\Authenticator\Passport;
|
|||||||
|
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CredentialsInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CredentialsInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,13 +32,22 @@ class Passport implements UserPassportInterface
|
|||||||
private $attributes = [];
|
private $attributes = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param UserBadge $userBadge
|
||||||
* @param CredentialsInterface $credentials the credentials to check for this authentication, use
|
* @param CredentialsInterface $credentials the credentials to check for this authentication, use
|
||||||
* SelfValidatingPassport if no credentials should be checked
|
* SelfValidatingPassport if no credentials should be checked
|
||||||
* @param BadgeInterface[] $badges
|
* @param BadgeInterface[] $badges
|
||||||
*/
|
*/
|
||||||
public function __construct(UserInterface $user, CredentialsInterface $credentials, array $badges = [])
|
public function __construct($userBadge, CredentialsInterface $credentials, array $badges = [])
|
||||||
{
|
{
|
||||||
$this->user = $user;
|
if ($userBadge instanceof UserInterface) {
|
||||||
|
trigger_deprecation('symfony/security-http', '5.2', 'The 1st argument of "%s" must be an instance of "%s", support for "%s" will be removed in symfony/security-http 5.3.', __CLASS__, UserBadge::class, UserInterface::class);
|
||||||
|
|
||||||
|
$this->user = $userBadge;
|
||||||
|
} elseif ($userBadge instanceof UserBadge) {
|
||||||
|
$this->addBadge($userBadge);
|
||||||
|
} else {
|
||||||
|
throw new \TypeError(sprintf('Argument 1 of "%s" must be an instance of "%s", "%s" given.', __METHOD__, UserBadge::class, get_debug_type($userBadge)));
|
||||||
|
}
|
||||||
|
|
||||||
$this->addBadge($credentials);
|
$this->addBadge($credentials);
|
||||||
foreach ($badges as $badge) {
|
foreach ($badges as $badge) {
|
||||||
@ -47,6 +57,14 @@ class Passport implements UserPassportInterface
|
|||||||
|
|
||||||
public function getUser(): UserInterface
|
public function getUser(): UserInterface
|
||||||
{
|
{
|
||||||
|
if (null === $this->user) {
|
||||||
|
if (!$this->hasBadge(UserBadge::class)) {
|
||||||
|
throw new \LogicException('Cannot get the Security user, no username or UserBadge configured for this passport.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->user = $this->getBadge(UserBadge::class)->getUser();
|
||||||
|
}
|
||||||
|
|
||||||
return $this->user;
|
return $this->user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Http\Authenticator\Passport;
|
|||||||
|
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation used when there are no credentials to be checked (e.g.
|
* An implementation used when there are no credentials to be checked (e.g.
|
||||||
@ -25,11 +26,20 @@ use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
|
|||||||
class SelfValidatingPassport extends Passport
|
class SelfValidatingPassport extends Passport
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
* @param UserBadge $userBadge
|
||||||
* @param BadgeInterface[] $badges
|
* @param BadgeInterface[] $badges
|
||||||
*/
|
*/
|
||||||
public function __construct(UserInterface $user, array $badges = [])
|
public function __construct($userBadge, array $badges = [])
|
||||||
{
|
{
|
||||||
$this->user = $user;
|
if ($userBadge instanceof UserInterface) {
|
||||||
|
trigger_deprecation('symfony/security-http', '5.2', 'The 1st argument of "%s" must be an instance of "%s", support for "%s" will be removed in symfony/security-http 5.3.', __CLASS__, UserBadge::class, UserInterface::class);
|
||||||
|
|
||||||
|
$this->user = $userBadge;
|
||||||
|
} elseif ($userBadge instanceof UserBadge) {
|
||||||
|
$this->addBadge($userBadge);
|
||||||
|
} else {
|
||||||
|
throw new \TypeError(sprintf('Argument 1 of "%s" must be an instance of "%s", "%s" given.', __METHOD__, UserBadge::class, get_debug_type($userBadge)));
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($badges as $badge) {
|
foreach ($badges as $badge) {
|
||||||
$this->addBadge($badge);
|
$this->addBadge($badge);
|
||||||
|
@ -17,6 +17,7 @@ use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken;
|
|||||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
||||||
@ -74,7 +75,7 @@ class RememberMeAuthenticator implements InteractiveAuthenticatorInterface
|
|||||||
throw new \LogicException('No remember me token is set.');
|
throw new \LogicException('No remember me token is set.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SelfValidatingPassport($token->getUser());
|
return new SelfValidatingPassport(new UserBadge($token->getUsername(), [$token, 'getUser']));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
|
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
|
||||||
|
@ -57,6 +57,6 @@ class CsrfProtectionListener implements EventSubscriberInterface
|
|||||||
|
|
||||||
public static function getSubscribedEvents(): array
|
public static function getSubscribedEvents(): array
|
||||||
{
|
{
|
||||||
return [CheckPassportEvent::class => ['checkPassport', 128]];
|
return [CheckPassportEvent::class => ['checkPassport', 512]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Security\Http\EventListener;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
|
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
* @experimental in 5.2
|
||||||
|
*/
|
||||||
|
class UserProviderListener
|
||||||
|
{
|
||||||
|
private $userProvider;
|
||||||
|
|
||||||
|
public function __construct(UserProviderInterface $userProvider)
|
||||||
|
{
|
||||||
|
$this->userProvider = $userProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkPassport(CheckPassportEvent $event): void
|
||||||
|
{
|
||||||
|
$passport = $event->getPassport();
|
||||||
|
if (!$passport->hasBadge(UserBadge::class)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var UserBadge $badge */
|
||||||
|
$badge = $passport->getBadge(UserBadge::class);
|
||||||
|
if (null !== $badge->getUserLoader()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$badge->setUserLoader([$this->userProvider, 'loadUserByUsername']);
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
|||||||
use Symfony\Component\Security\Core\User\User;
|
use Symfony\Component\Security\Core\User\User;
|
||||||
use Symfony\Component\Security\Http\Authentication\AuthenticatorManager;
|
use Symfony\Component\Security\Http\Authentication\AuthenticatorManager;
|
||||||
use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
|
use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
@ -93,7 +94,7 @@ class AuthenticatorManagerTest extends TestCase
|
|||||||
|
|
||||||
$authenticators[($matchingAuthenticatorIndex + 1) % 2]->expects($this->never())->method('authenticate');
|
$authenticators[($matchingAuthenticatorIndex + 1) % 2]->expects($this->never())->method('authenticate');
|
||||||
|
|
||||||
$matchingAuthenticator->expects($this->any())->method('authenticate')->willReturn(new SelfValidatingPassport($this->user));
|
$matchingAuthenticator->expects($this->any())->method('authenticate')->willReturn(new SelfValidatingPassport(new UserBadge('wouter', function () { return $this->user; })));
|
||||||
|
|
||||||
$listenerCalled = false;
|
$listenerCalled = false;
|
||||||
$this->eventDispatcher->addListener(CheckPassportEvent::class, function (CheckPassportEvent $event) use (&$listenerCalled, $matchingAuthenticator) {
|
$this->eventDispatcher->addListener(CheckPassportEvent::class, function (CheckPassportEvent $event) use (&$listenerCalled, $matchingAuthenticator) {
|
||||||
@ -121,7 +122,7 @@ class AuthenticatorManagerTest extends TestCase
|
|||||||
$authenticator = $this->createAuthenticator();
|
$authenticator = $this->createAuthenticator();
|
||||||
$this->request->attributes->set('_security_authenticators', [$authenticator]);
|
$this->request->attributes->set('_security_authenticators', [$authenticator]);
|
||||||
|
|
||||||
$authenticator->expects($this->any())->method('authenticate')->willReturn(new Passport($this->user, new PasswordCredentials('pass')));
|
$authenticator->expects($this->any())->method('authenticate')->willReturn(new Passport(new UserBadge('wouter', function () { return $this->user; }), new PasswordCredentials('pass')));
|
||||||
|
|
||||||
$authenticator->expects($this->once())
|
$authenticator->expects($this->once())
|
||||||
->method('onAuthenticationFailure')
|
->method('onAuthenticationFailure')
|
||||||
@ -139,7 +140,7 @@ class AuthenticatorManagerTest extends TestCase
|
|||||||
$authenticator = $this->createAuthenticator();
|
$authenticator = $this->createAuthenticator();
|
||||||
$this->request->attributes->set('_security_authenticators', [$authenticator]);
|
$this->request->attributes->set('_security_authenticators', [$authenticator]);
|
||||||
|
|
||||||
$authenticator->expects($this->any())->method('authenticate')->willReturn(new SelfValidatingPassport($this->user));
|
$authenticator->expects($this->any())->method('authenticate')->willReturn(new SelfValidatingPassport(new UserBadge('wouter', function () { return $this->user; })));
|
||||||
|
|
||||||
$authenticator->expects($this->any())->method('createAuthenticatedToken')->willReturn($this->token);
|
$authenticator->expects($this->any())->method('createAuthenticatedToken')->willReturn($this->token);
|
||||||
|
|
||||||
@ -160,7 +161,7 @@ class AuthenticatorManagerTest extends TestCase
|
|||||||
$authenticator = $this->createAuthenticator();
|
$authenticator = $this->createAuthenticator();
|
||||||
$this->request->attributes->set('_security_authenticators', [$authenticator]);
|
$this->request->attributes->set('_security_authenticators', [$authenticator]);
|
||||||
|
|
||||||
$authenticator->expects($this->any())->method('authenticate')->willReturn(new SelfValidatingPassport($this->user));
|
$authenticator->expects($this->any())->method('authenticate')->willReturn(new SelfValidatingPassport(new UserBadge('wouter', function () { return $this->user; })));
|
||||||
|
|
||||||
$authenticator->expects($this->any())->method('createAuthenticatedToken')->willReturn($this->token);
|
$authenticator->expects($this->any())->method('createAuthenticatedToken')->willReturn($this->token);
|
||||||
|
|
||||||
@ -216,7 +217,7 @@ class AuthenticatorManagerTest extends TestCase
|
|||||||
$authenticator->expects($this->any())->method('isInteractive')->willReturn(true);
|
$authenticator->expects($this->any())->method('isInteractive')->willReturn(true);
|
||||||
$this->request->attributes->set('_security_authenticators', [$authenticator]);
|
$this->request->attributes->set('_security_authenticators', [$authenticator]);
|
||||||
|
|
||||||
$authenticator->expects($this->any())->method('authenticate')->willReturn(new SelfValidatingPassport($this->user));
|
$authenticator->expects($this->any())->method('authenticate')->willReturn(new SelfValidatingPassport(new UserBadge('wouter', function () { return $this->user; })));
|
||||||
$authenticator->expects($this->any())->method('createAuthenticatedToken')->willReturn($this->token);
|
$authenticator->expects($this->any())->method('createAuthenticatedToken')->willReturn($this->token);
|
||||||
|
|
||||||
$this->tokenStorage->expects($this->once())->method('setToken')->with($this->token);
|
$this->tokenStorage->expects($this->once())->method('setToken')->with($this->token);
|
||||||
|
@ -16,7 +16,6 @@ use Symfony\Component\HttpFoundation\Request;
|
|||||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
use Symfony\Component\Security\Core\User\User;
|
|
||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\JsonLoginAuthenticator;
|
use Symfony\Component\Security\Http\Authenticator\JsonLoginAuthenticator;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
||||||
@ -72,8 +71,6 @@ class JsonLoginAuthenticatorTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->setUpAuthenticator();
|
$this->setUpAuthenticator();
|
||||||
|
|
||||||
$this->userProvider->expects($this->once())->method('loadUserByUsername')->with('dunglas')->willReturn(new User('dunglas', 'pa$$'));
|
|
||||||
|
|
||||||
$request = new Request([], [], [], [], [], ['HTTP_CONTENT_TYPE' => 'application/json'], '{"username": "dunglas", "password": "foo"}');
|
$request = new Request([], [], [], [], [], ['HTTP_CONTENT_TYPE' => 'application/json'], '{"username": "dunglas", "password": "foo"}');
|
||||||
$passport = $this->authenticator->authenticate($request);
|
$passport = $this->authenticator->authenticate($request);
|
||||||
$this->assertEquals('foo', $passport->getBadge(PasswordCredentials::class)->getPassword());
|
$this->assertEquals('foo', $passport->getBadge(PasswordCredentials::class)->getPassword());
|
||||||
@ -86,8 +83,6 @@ class JsonLoginAuthenticatorTest extends TestCase
|
|||||||
'password_path' => 'authentication.password',
|
'password_path' => 'authentication.password',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->userProvider->expects($this->once())->method('loadUserByUsername')->with('dunglas')->willReturn(new User('dunglas', 'pa$$'));
|
|
||||||
|
|
||||||
$request = new Request([], [], [], [], [], ['HTTP_CONTENT_TYPE' => 'application/json'], '{"authentication": {"username": "dunglas", "password": "foo"}}');
|
$request = new Request([], [], [], [], [], ['HTTP_CONTENT_TYPE' => 'application/json'], '{"authentication": {"username": "dunglas", "password": "foo"}}');
|
||||||
$passport = $this->authenticator->authenticate($request);
|
$passport = $this->authenticator->authenticate($request);
|
||||||
$this->assertEquals('foo', $passport->getBadge(PasswordCredentials::class)->getPassword());
|
$this->assertEquals('foo', $passport->getBadge(PasswordCredentials::class)->getPassword());
|
||||||
|
@ -32,11 +32,11 @@ class X509AuthenticatorTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @dataProvider provideServerVars
|
* @dataProvider provideServerVars
|
||||||
*/
|
*/
|
||||||
public function testAuthentication($user, $credentials)
|
public function testAuthentication($username, $credentials)
|
||||||
{
|
{
|
||||||
$serverVars = [];
|
$serverVars = [];
|
||||||
if ('' !== $user) {
|
if ('' !== $username) {
|
||||||
$serverVars['SSL_CLIENT_S_DN_Email'] = $user;
|
$serverVars['SSL_CLIENT_S_DN_Email'] = $username;
|
||||||
}
|
}
|
||||||
if ('' !== $credentials) {
|
if ('' !== $credentials) {
|
||||||
$serverVars['SSL_CLIENT_S_DN'] = $credentials;
|
$serverVars['SSL_CLIENT_S_DN'] = $credentials;
|
||||||
@ -45,12 +45,13 @@ class X509AuthenticatorTest extends TestCase
|
|||||||
$request = $this->createRequest($serverVars);
|
$request = $this->createRequest($serverVars);
|
||||||
$this->assertTrue($this->authenticator->supports($request));
|
$this->assertTrue($this->authenticator->supports($request));
|
||||||
|
|
||||||
$this->userProvider->expects($this->once())
|
$this->userProvider->expects($this->any())
|
||||||
->method('loadUserByUsername')
|
->method('loadUserByUsername')
|
||||||
->with($user)
|
->with($username)
|
||||||
->willReturn(new User($user, null));
|
->willReturn(new User($username, null));
|
||||||
|
|
||||||
$this->authenticator->authenticate($request);
|
$passport = $this->authenticator->authenticate($request);
|
||||||
|
$this->assertEquals($username, $passport->getUser()->getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function provideServerVars()
|
public static function provideServerVars()
|
||||||
@ -73,7 +74,8 @@ class X509AuthenticatorTest extends TestCase
|
|||||||
->with($emailAddress)
|
->with($emailAddress)
|
||||||
->willReturn(new User($emailAddress, null));
|
->willReturn(new User($emailAddress, null));
|
||||||
|
|
||||||
$this->authenticator->authenticate($request);
|
$passport = $this->authenticator->authenticate($request);
|
||||||
|
$this->assertEquals($emailAddress, $passport->getUser()->getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function provideServerVarsNoUser()
|
public static function provideServerVarsNoUser()
|
||||||
@ -108,7 +110,8 @@ class X509AuthenticatorTest extends TestCase
|
|||||||
->with('TheUser')
|
->with('TheUser')
|
||||||
->willReturn(new User('TheUser', null));
|
->willReturn(new User('TheUser', null));
|
||||||
|
|
||||||
$authenticator->authenticate($request);
|
$passport = $this->authenticator->authenticate($request);
|
||||||
|
$this->assertEquals('TheUser', $passport->getUser()->getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAuthenticationCustomCredentialsKey()
|
public function testAuthenticationCustomCredentialsKey()
|
||||||
@ -125,7 +128,8 @@ class X509AuthenticatorTest extends TestCase
|
|||||||
->with('cert@example.com')
|
->with('cert@example.com')
|
||||||
->willReturn(new User('cert@example.com', null));
|
->willReturn(new User('cert@example.com', null));
|
||||||
|
|
||||||
$authenticator->authenticate($request);
|
$passport = $authenticator->authenticate($request);
|
||||||
|
$this->assertEquals('cert@example.com', $passport->getUser()->getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createRequest(array $server)
|
private function createRequest(array $server)
|
||||||
|
@ -17,6 +17,7 @@ use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
|
|||||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||||
use Symfony\Component\Security\Core\User\User;
|
use Symfony\Component\Security\Core\User\User;
|
||||||
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||||
@ -53,7 +54,7 @@ class CheckCredentialsListenerTest extends TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
$credentials = new PasswordCredentials($password);
|
$credentials = new PasswordCredentials($password);
|
||||||
$this->listener->checkPassport($this->createEvent(new Passport($this->user, $credentials)));
|
$this->listener->checkPassport($this->createEvent(new Passport(new UserBadge('wouter', function () { return $this->user; }), $credentials)));
|
||||||
|
|
||||||
if (true === $result) {
|
if (true === $result) {
|
||||||
$this->assertTrue($credentials->isResolved());
|
$this->assertTrue($credentials->isResolved());
|
||||||
@ -73,7 +74,7 @@ class CheckCredentialsListenerTest extends TestCase
|
|||||||
|
|
||||||
$this->encoderFactory->expects($this->never())->method('getEncoder');
|
$this->encoderFactory->expects($this->never())->method('getEncoder');
|
||||||
|
|
||||||
$event = $this->createEvent(new Passport($this->user, new PasswordCredentials('')));
|
$event = $this->createEvent(new Passport(new UserBadge('wouter', function () { return $this->user; }), new PasswordCredentials('')));
|
||||||
$this->listener->checkPassport($event);
|
$this->listener->checkPassport($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +92,7 @@ class CheckCredentialsListenerTest extends TestCase
|
|||||||
$credentials = new CustomCredentials(function () use ($result) {
|
$credentials = new CustomCredentials(function () use ($result) {
|
||||||
return $result;
|
return $result;
|
||||||
}, ['password' => 'foo']);
|
}, ['password' => 'foo']);
|
||||||
$this->listener->checkPassport($this->createEvent(new Passport($this->user, $credentials)));
|
$this->listener->checkPassport($this->createEvent(new Passport(new UserBadge('wouter', function () { return $this->user; }), $credentials)));
|
||||||
|
|
||||||
if (true === $result) {
|
if (true === $result) {
|
||||||
$this->assertTrue($credentials->isResolved());
|
$this->assertTrue($credentials->isResolved());
|
||||||
@ -108,7 +109,7 @@ class CheckCredentialsListenerTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->encoderFactory->expects($this->never())->method('getEncoder');
|
$this->encoderFactory->expects($this->never())->method('getEncoder');
|
||||||
|
|
||||||
$event = $this->createEvent(new SelfValidatingPassport($this->user));
|
$event = $this->createEvent(new SelfValidatingPassport(new UserBadge('wouter', function () { return $this->user; })));
|
||||||
$this->listener->checkPassport($event);
|
$this->listener->checkPassport($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ use Symfony\Component\Security\Csrf\CsrfToken;
|
|||||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
|
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
|
||||||
use Symfony\Component\Security\Http\EventListener\CsrfProtectionListener;
|
use Symfony\Component\Security\Http\EventListener\CsrfProtectionListener;
|
||||||
@ -75,7 +76,7 @@ class CsrfProtectionListenerTest extends TestCase
|
|||||||
|
|
||||||
private function createPassport(?CsrfTokenBadge $badge)
|
private function createPassport(?CsrfTokenBadge $badge)
|
||||||
{
|
{
|
||||||
$passport = new SelfValidatingPassport(new User('wouter', 'pass'));
|
$passport = new SelfValidatingPassport(new UserBadge('wouter', function ($username) { return new User($username, 'pass'); }));
|
||||||
if ($badge) {
|
if ($badge) {
|
||||||
$passport->addBadge($badge);
|
$passport->addBadge($badge);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
|||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||||
@ -51,10 +52,10 @@ class PasswordMigratingListenerTest extends TestCase
|
|||||||
public function provideUnsupportedEvents()
|
public function provideUnsupportedEvents()
|
||||||
{
|
{
|
||||||
// no password upgrade badge
|
// no password upgrade badge
|
||||||
yield [$this->createEvent(new SelfValidatingPassport($this->createMock(UserInterface::class)))];
|
yield [$this->createEvent(new SelfValidatingPassport(new UserBadge('test', function () { return $this->createMock(UserInterface::class); })))];
|
||||||
|
|
||||||
// blank password
|
// blank password
|
||||||
yield [$this->createEvent(new SelfValidatingPassport($this->createMock(UserInterface::class), [new PasswordUpgradeBadge('', $this->createPasswordUpgrader())]))];
|
yield [$this->createEvent(new SelfValidatingPassport(new UserBadge('test', function () { return $this->createMock(UserInterface::class); }), [new PasswordUpgradeBadge('', $this->createPasswordUpgrader())]))];
|
||||||
|
|
||||||
// no user
|
// no user
|
||||||
yield [$this->createEvent($this->createMock(PassportInterface::class))];
|
yield [$this->createEvent($this->createMock(PassportInterface::class))];
|
||||||
@ -76,7 +77,7 @@ class PasswordMigratingListenerTest extends TestCase
|
|||||||
->with($this->user, 'new-encoded-password')
|
->with($this->user, 'new-encoded-password')
|
||||||
;
|
;
|
||||||
|
|
||||||
$event = $this->createEvent(new SelfValidatingPassport($this->user, [new PasswordUpgradeBadge('pa$$word', $passwordUpgrader)]));
|
$event = $this->createEvent(new SelfValidatingPassport(new UserBadge('test', function () { return $this->user; }), [new PasswordUpgradeBadge('pa$$word', $passwordUpgrader)]));
|
||||||
$this->listener->onLoginSuccess($event);
|
$this->listener->onLoginSuccess($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
|||||||
use Symfony\Component\Security\Core\User\User;
|
use Symfony\Component\Security\Core\User\User;
|
||||||
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
use Symfony\Component\Security\Http\Event\LoginFailureEvent;
|
use Symfony\Component\Security\Http\Event\LoginFailureEvent;
|
||||||
@ -47,7 +48,7 @@ class RememberMeListenerTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->rememberMeServices->expects($this->never())->method('loginSuccess');
|
$this->rememberMeServices->expects($this->never())->method('loginSuccess');
|
||||||
|
|
||||||
$event = $this->createLoginSuccessfulEvent('main_firewall', $this->response, new SelfValidatingPassport(new User('wouter', null)));
|
$event = $this->createLoginSuccessfulEvent('main_firewall', $this->response, new SelfValidatingPassport(new UserBadge('wouter', function ($username) { return new User($username, null); })));
|
||||||
$this->listener->onSuccessfulLogin($event);
|
$this->listener->onSuccessfulLogin($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +79,7 @@ class RememberMeListenerTest extends TestCase
|
|||||||
private function createLoginSuccessfulEvent($firewallName, $response, PassportInterface $passport = null)
|
private function createLoginSuccessfulEvent($firewallName, $response, PassportInterface $passport = null)
|
||||||
{
|
{
|
||||||
if (null === $passport) {
|
if (null === $passport) {
|
||||||
$passport = new SelfValidatingPassport(new User('test', null), [new RememberMeBadge()]);
|
$passport = new SelfValidatingPassport(new UserBadge('test', function ($username) { return new User($username, null); }), [new RememberMeBadge()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), $passport, $this->token, $this->request, $response, $firewallName);
|
return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), $passport, $this->token, $this->request, $response, $firewallName);
|
||||||
|
@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\Request;
|
|||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\User\User;
|
use Symfony\Component\Security\Core\User\User;
|
||||||
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||||
use Symfony\Component\Security\Http\EventListener\SessionStrategyListener;
|
use Symfony\Component\Security\Http\EventListener\SessionStrategyListener;
|
||||||
@ -62,7 +63,7 @@ class SessionStrategyListenerTest extends TestCase
|
|||||||
|
|
||||||
private function createEvent($firewallName)
|
private function createEvent($firewallName)
|
||||||
{
|
{
|
||||||
return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), new SelfValidatingPassport(new User('test', null)), $this->token, $this->request, null, $firewallName);
|
return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), new SelfValidatingPassport(new UserBadge('test', function ($username) { return new User($username, null); })), $this->token, $this->request, null, $firewallName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function configurePreviousSession()
|
private function configurePreviousSession()
|
||||||
|
@ -18,6 +18,7 @@ use Symfony\Component\Security\Core\User\User;
|
|||||||
use Symfony\Component\Security\Core\User\UserCheckerInterface;
|
use Symfony\Component\Security\Core\User\UserCheckerInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PreAuthenticatedUserBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PreAuthenticatedUserBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
|
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
|
||||||
@ -55,7 +56,7 @@ class UserCheckerListenerTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->userChecker->expects($this->never())->method('checkPreAuth');
|
$this->userChecker->expects($this->never())->method('checkPreAuth');
|
||||||
|
|
||||||
$this->listener->preCheckCredentials($this->createCheckPassportEvent(new SelfValidatingPassport($this->user, [new PreAuthenticatedUserBadge()])));
|
$this->listener->preCheckCredentials($this->createCheckPassportEvent(new SelfValidatingPassport(new UserBadge('test', function () { return $this->user; }), [new PreAuthenticatedUserBadge()])));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPostAuthValidCredentials()
|
public function testPostAuthValidCredentials()
|
||||||
@ -75,7 +76,7 @@ class UserCheckerListenerTest extends TestCase
|
|||||||
private function createCheckPassportEvent($passport = null)
|
private function createCheckPassportEvent($passport = null)
|
||||||
{
|
{
|
||||||
if (null === $passport) {
|
if (null === $passport) {
|
||||||
$passport = new SelfValidatingPassport($this->user);
|
$passport = new SelfValidatingPassport(new UserBadge('test', function () { return $this->user; }));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CheckPassportEvent($this->createMock(AuthenticatorInterface::class), $passport);
|
return new CheckPassportEvent($this->createMock(AuthenticatorInterface::class), $passport);
|
||||||
@ -84,7 +85,7 @@ class UserCheckerListenerTest extends TestCase
|
|||||||
private function createLoginSuccessEvent($passport = null)
|
private function createLoginSuccessEvent($passport = null)
|
||||||
{
|
{
|
||||||
if (null === $passport) {
|
if (null === $passport) {
|
||||||
$passport = new SelfValidatingPassport($this->user);
|
$passport = new SelfValidatingPassport(new UserBadge('test', function () { return $this->user; }));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), $passport, $this->createMock(TokenInterface::class), new Request(), null, 'main');
|
return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), $passport, $this->createMock(TokenInterface::class), new Request(), null, 'main');
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Security\Http\Tests\EventListener;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Security\Core\User\User;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\AnonymousPassport;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||||
|
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
|
||||||
|
use Symfony\Component\Security\Http\EventListener\UserProviderListener;
|
||||||
|
|
||||||
|
class UserProviderListenerTest extends TestCase
|
||||||
|
{
|
||||||
|
private $userProvider;
|
||||||
|
private $listener;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->userProvider = $this->createMock(UserProviderInterface::class);
|
||||||
|
$this->listener = new UserProviderListener($this->userProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetUserProvider()
|
||||||
|
{
|
||||||
|
$passport = new SelfValidatingPassport(new UserBadge('wouter'));
|
||||||
|
|
||||||
|
$this->listener->checkPassport(new CheckPassportEvent($this->createMock(AuthenticatorInterface::class), $passport));
|
||||||
|
|
||||||
|
$badge = $passport->getBadge(UserBadge::class);
|
||||||
|
$this->assertEquals([$this->userProvider, 'loadUserByUsername'], $badge->getUserLoader());
|
||||||
|
|
||||||
|
$user = new User('wouter', null);
|
||||||
|
$this->userProvider->expects($this->once())->method('loadUserByUsername')->with('wouter')->willReturn($user);
|
||||||
|
$this->assertSame($user, $passport->getUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideCompletePassports
|
||||||
|
*/
|
||||||
|
public function testNotOverrideUserLoader($passport)
|
||||||
|
{
|
||||||
|
$badgeBefore = $passport->hasBadge(UserBadge::class) ? $passport->getBadge(UserBadge::class) : null;
|
||||||
|
$this->listener->checkPassport(new CheckPassportEvent($this->createMock(AuthenticatorInterface::class), $passport));
|
||||||
|
|
||||||
|
$this->assertEquals($passport->hasBadge(UserBadge::class) ? $passport->getBadge(UserBadge::class) : null, $badgeBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideCompletePassports()
|
||||||
|
{
|
||||||
|
yield [new AnonymousPassport()];
|
||||||
|
yield [new SelfValidatingPassport(new UserBadge('wouter', function () {}))];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group legacy
|
||||||
|
*/
|
||||||
|
public function testLegacyUserPassport()
|
||||||
|
{
|
||||||
|
$passport = new SelfValidatingPassport($user = $this->createMock(UserInterface::class));
|
||||||
|
$this->listener->checkPassport(new CheckPassportEvent($this->createMock(AuthenticatorInterface::class), $passport));
|
||||||
|
|
||||||
|
$this->assertFalse($passport->hasBadge(UserBadge::class));
|
||||||
|
$this->assertSame($user, $passport->getUser());
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user