Commit fd3ba62d authored by Fabien Potencier's avatar Fabien Potencier

merged branch GromNaN/remember-me (PR #645)

This PR was squashed before being merged into the master branch (closes #645).

Commits
-------

adc172b3 Add support for remember-me with RememberMeServiceProvider

Discussion
----------

Add support for remember-me with RememberMeServiceProvider

This is a refactoring of #464.

I made a separate service provider to avoid adding more complexity to the SecurityServiceProvider.
parents fcd93b34 adc172b3
<?php
/*
* This file is part of the Silex framework.
*
* (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 Silex\Provider;
use Silex\Application;
use Silex\ServiceProviderInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Authentication\Provider\RememberMeAuthenticationProvider;
use Symfony\Component\Security\Http\Firewall\RememberMeListener;
use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices;
use Symfony\Component\Security\Http\RememberMe\ResponseListener;
/**
* Remember-me authentication for the SecurityServiceProvider
*
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class RememberMeServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['security.remember_me.response_listener'] = $app->share(function() {
return new ResponseListener();
});
$app['security.authentication_listener.factory.remember_me'] = $app->protect(function($name, $options) use ($app) {
if (empty($options['key'])) {
$options['key'] = $name;
}
if (!isset($app['security.remember_me.service.'.$name])) {
$app['security.remember_me.service.'.$name] = $app['security.remember_me.service._proto']($name, $options);
}
if (!isset($app['security.authentication_listener.'.$name.'.remember_me'])) {
$app['security.authentication_listener.'.$name.'.remember_me'] = $app['security.authentication_listener.remember_me._proto']($name, $options);
}
if (!isset($app['security.authentication_provider.'.$name.'.remember_me'])) {
$app['security.authentication_provider.'.$name.'.remember_me'] = $app['security.authentication_provider.remember_me._proto']($name, $options);
}
return array(
'security.authentication_provider.'.$name.'.remember_me',
'security.authentication_listener.'.$name.'.remember_me',
null, // entry point
'remember_me'
);
});
$app['security.remember_me.service._proto'] = $app->protect(function($providerKey, $options) use ($app) {
return $app->share(function () use ($providerKey, $options, $app) {
$options = array_replace(array(
'name' => 'REMEMBERME',
'lifetime' => 31536000,
'path' => '/',
'domain' => null,
'secure' => false,
'httponly' => true,
'always_remember_me' => false,
'remember_me_parameter' => '_remember_me',
), $options);
return new TokenBasedRememberMeServices(array($app['security.user_provider.'.$providerKey]), $options['key'], $providerKey, $options, $app['logger']);
});
});
$app['security.authentication_listener.remember_me._proto'] = $app->protect(function ($providerKey) use ($app) {
return $app->share(function () use ($app, $providerKey) {
$listener = new RememberMeListener(
$app['security'],
$app['security.remember_me.service.'.$providerKey],
$app['security.authentication_manager'],
$app['logger']
);
return $listener;
});
});
$app['security.authentication_provider.remember_me._proto'] = $app->protect(function ($name, $options) use ($app) {
return $app->share(function () use ($app, $name, $options) {
return new RememberMeAuthenticationProvider($app['security.user_checker'], $options['key'], $name);
});
});
}
public function boot(Application $app)
{
if (!isset($app['security'])) {
throw new \LogicException('You must register the SecurityServiceProvider to use the RememberMeServiceProvider');
}
// In Symfony 2.2, this is a proper subscriber
if ($app['security.remember_me.response_listener'] instanceof EventSubscriberInterface) {
$app['dispatcher']->addSubscriber($app['security.remember_me.response_listener']);
} else {
$app['dispatcher']->addListener('kernel.response', array($app['security.remember_me.response_listener'], 'onKernelResponse'));
}
}
}
......@@ -34,6 +34,7 @@ use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
use Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Http\FirewallMap;
use Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener;
use Symfony\Component\Security\Http\Firewall\AccessListener;
use Symfony\Component\Security\Http\Firewall\BasicAuthenticationListener;
use Symfony\Component\Security\Http\Firewall\LogoutListener;
......@@ -246,7 +247,20 @@ class SecurityServiceProvider implements ServiceProviderInterface
foreach ($configs as $name => $config) {
$map->add(
is_string($config[0]) ? new RequestMatcher($config[0]) : $config[0],
array_map(function ($listener) use ($app) { return $app[$listener]; }, $config[1]),
array_map(function ($listenerId) use ($app, $name) {
$listener = $app[$listenerId];
if (isset($app['security.remember_me.service.'.$name])) {
if ($listener instanceof AbstractAuthenticationListener) {
$listener->setRememberMeServices($app['security.remember_me.service.'.$name]);
}
if ($listener instanceof LogoutListener) {
$listener->addHandler($app['security.remember_me.service.'.$name]);
}
}
return $listener;
}, $config[1]),
$config[2] ? $app['security.exception_listener.'.$name] : null
);
}
......
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Silex\Tests\Provider;
use Silex\Application;
use Silex\WebTestCase;
use Silex\Provider\RememberMeServiceProvider;
use Silex\Provider\SecurityServiceProvider;
use Silex\Provider\SessionServiceProvider;
use Symfony\Component\HttpKernel\Client;
use Symfony\Component\HttpFoundation\Request;
/**
* SecurityServiceProvider
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class RememberMeServiceProviderTest extends WebTestCase
{
public function testRememberMeAuthentication()
{
$app = $this->createApplication();
$client = new Client($app);
$client->request('get', '/');
$client->request('post', '/login_check', array('_username' => 'fabien', '_password' => 'foo', '_remember_me' => 'true'));
$client->followRedirect();
$this->assertEquals('AUTHENTICATED_FULLY', $client->getResponse()->getContent());
$this->assertNotNull($client->getCookiejar()->get('REMEMBERME'), 'The REMEMBERME cookie is set');
$client->getCookiejar()->expire('MOCKSESSID');
$client->request('get', '/');
$this->assertEquals('AUTHENTICATED_REMEMBERED', $client->getResponse()->getContent());
$client->request('get', '/logout');
$client->followRedirect();
$this->assertNull($client->getCookiejar()->get('REMEMBERME'), 'The REMEMBERME cookie has been removed');
}
public function createApplication($authenticationMethod = 'form')
{
$app = new Application();
$app['debug'] = true;
unset($app['exception_handler']);
$app->register(new SessionServiceProvider(), array(
'session.test' => true,
));
$app->register(new SecurityServiceProvider());
$app->register(new RememberMeServiceProvider());
$app['security.firewalls'] = array(
'http-auth' => array(
'pattern' => '^.*$',
'form' => true,
'remember_me' => array(),
'logout' => true,
'users' => array(
'fabien' => array('ROLE_USER', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='),
),
),
);
$app->get('/', function () use ($app) {
if ($app['security']->isGranted('IS_AUTHENTICATED_FULLY')) {
return 'AUTHENTICATED_FULLY';
} elseif ($app['security']->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
return 'AUTHENTICATED_REMEMBERED';
} else {
return 'AUTHENTICATED_ANONYMOUSLY';
}
});
return $app;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment