bug #36335 [Security] Track session usage whenever a new token is set (wouterj)
This PR was merged into the 4.4 branch.
Discussion
----------
[Security] Track session usage whenever a new token is set
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Tickets | Fix #36208
| License | MIT
| Doc PR | -
When using `anonymous: lazy`, the programatic login using the guard handler is broken. As the `setToken()` does not track usage, the index remains equal.
I tried fixing this more properly in e.g. the `SessionStrategy::onAuthentication` class, but I couldn't get it working (as `$request->hasPreviousSession()` returns false, the session strategy isn't called). `setToken()` can also not be made usage tracking afaics, because it would directly break (`setToken(null)` is called in `ContextListener`).
The current fix does however look really ugly, but I can't find anything better with my minor knowledge of this session usage tracking feature. I'm open for all ideas :)
Commits
-------
8d96dbd08b
Track session usage when setting the token
This commit is contained in:
commit
38cbcc6d4f
@ -23,7 +23,7 @@ class AppCustomAuthenticator extends AbstractGuardAuthenticator
|
||||
{
|
||||
public function supports(Request $request)
|
||||
{
|
||||
return true;
|
||||
return '/manual_login' !== $request->getPathInfo() && '/profile' !== $request->getPathInfo();
|
||||
}
|
||||
|
||||
public function getCredentials(Request $request)
|
||||
|
@ -0,0 +1,38 @@
|
||||
<?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\GuardedBundle;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\User\User;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
|
||||
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
|
||||
|
||||
class AuthenticationController
|
||||
{
|
||||
public function manualLoginAction(GuardAuthenticatorHandler $guardAuthenticatorHandler, Request $request)
|
||||
{
|
||||
$guardAuthenticatorHandler->authenticateWithToken(new PostAuthenticationGuardToken(new User('Jane', 'test', ['ROLE_USER']), 'secure', ['ROLE_USER']), $request, 'secure');
|
||||
|
||||
return new Response('Logged in.');
|
||||
}
|
||||
|
||||
public function profileAction(UserInterface $user = null)
|
||||
{
|
||||
if (null === $user) {
|
||||
return new Response('Not logged in.');
|
||||
}
|
||||
|
||||
return new Response('Username: '.$user->getUsername());
|
||||
}
|
||||
}
|
@ -21,4 +21,14 @@ class GuardedTest extends AbstractWebTestCase
|
||||
|
||||
$this->assertSame(418, $client->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
public function testManualLogin()
|
||||
{
|
||||
$client = $this->createClient(['debug' => true, 'test_case' => 'Guarded', 'root_config' => 'config.yml']);
|
||||
|
||||
$client->request('GET', '/manual_login');
|
||||
$client->request('GET', '/profile');
|
||||
|
||||
$this->assertSame('Username: Jane', $client->getResponse()->getContent());
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,19 @@ framework:
|
||||
services:
|
||||
logger: { class: Psr\Log\NullLogger }
|
||||
Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AppCustomAuthenticator: ~
|
||||
Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController:
|
||||
tags: [controller.service_arguments]
|
||||
|
||||
security:
|
||||
encoders:
|
||||
Symfony\Component\Security\Core\User\User: plaintext
|
||||
|
||||
providers:
|
||||
in_memory:
|
||||
memory:
|
||||
users:
|
||||
Jane: { password: test, roles: [ROLE_USER] }
|
||||
|
||||
firewalls:
|
||||
secure:
|
||||
pattern: ^/
|
||||
|
@ -3,3 +3,12 @@ main:
|
||||
defaults:
|
||||
_controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction
|
||||
path: /app
|
||||
profile:
|
||||
path: /profile
|
||||
defaults:
|
||||
_controller: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController::profileAction
|
||||
|
||||
manual_login:
|
||||
path: /manual_login
|
||||
defaults:
|
||||
_controller: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController::manualLoginAction
|
||||
|
@ -49,6 +49,11 @@ class TokenStorage implements TokenStorageInterface, ResetInterface
|
||||
@trigger_error(sprintf('Not implementing the "%s::getRoleNames()" method in "%s" is deprecated since Symfony 4.3.', TokenInterface::class, \get_class($token)), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if ($token) {
|
||||
// ensure any initializer is called
|
||||
$this->getToken();
|
||||
}
|
||||
|
||||
$this->initializer = null;
|
||||
$this->token = $token;
|
||||
}
|
||||
|
@ -52,6 +52,11 @@ final class UsageTrackingTokenStorage implements TokenStorageInterface, ServiceS
|
||||
public function setToken(TokenInterface $token = null): void
|
||||
{
|
||||
$this->storage->setToken($token);
|
||||
|
||||
if ($token && $this->enableUsageTracking) {
|
||||
// increments the internal session usage index
|
||||
$this->sessionLocator->get('session')->getMetadataBag();
|
||||
}
|
||||
}
|
||||
|
||||
public function enableUsageTracking(): void
|
||||
|
@ -411,9 +411,9 @@ class ContextListenerTest extends TestCase
|
||||
|
||||
private function handleEventWithPreviousSession($userProviders, UserInterface $user = null, RememberMeServicesInterface $rememberMeServices = null)
|
||||
{
|
||||
$user = $user ?: new User('foo', 'bar');
|
||||
$tokenUser = $user ?: new User('foo', 'bar');
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$session->set('_security_context_key', serialize(new UsernamePasswordToken($user, '', 'context_key', ['ROLE_USER'])));
|
||||
$session->set('_security_context_key', serialize(new UsernamePasswordToken($tokenUser, '', 'context_key', ['ROLE_USER'])));
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
@ -442,6 +442,10 @@ class ContextListenerTest extends TestCase
|
||||
$listener(new RequestEvent($this->getMockBuilder(HttpKernelInterface::class)->getMock(), $request, HttpKernelInterface::MASTER_REQUEST));
|
||||
|
||||
if (null !== $usageIndex) {
|
||||
if (null !== $user) {
|
||||
++$usageIndex;
|
||||
}
|
||||
|
||||
$this->assertSame($usageIndex, $session->getUsageIndex());
|
||||
$tokenStorage->getToken();
|
||||
$this->assertSame(1 + $usageIndex, $session->getUsageIndex());
|
||||
|
@ -17,7 +17,7 @@
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1.3",
|
||||
"symfony/security-core": "^4.4.7",
|
||||
"symfony/security-core": "^4.4.8",
|
||||
"symfony/http-foundation": "^3.4.40|^4.4.7|^5.0.7",
|
||||
"symfony/http-kernel": "^4.4",
|
||||
"symfony/property-access": "^3.4|^4.0|^5.0"
|
||||
|
Reference in New Issue
Block a user