Skip to content

Commit

Permalink
merged branch GromNaN/remember-me (PR silexphp#645)
Browse files Browse the repository at this point in the history
This PR was squashed before being merged into the master branch (closes silexphp#645).

Commits
-------

adc172b Add support for remember-me with RememberMeServiceProvider

Discussion
----------

Add support for remember-me with RememberMeServiceProvider

This is a refactoring of silexphp#464.

I made a separate service provider to avoid adding more complexity to the SecurityServiceProvider.
  • Loading branch information
fabpot committed Mar 8, 2013
2 parents fcd93b3 + adc172b commit fd3ba62
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 1 deletion.
111 changes: 111 additions & 0 deletions src/Silex/Provider/RememberMeServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?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'));
}
}
}
16 changes: 15 additions & 1 deletion src/Silex/Provider/SecurityServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
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;
Expand Down Expand Up @@ -246,7 +247,20 @@ public function register(Application $app)
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
);
}
Expand Down
90 changes: 90 additions & 0 deletions tests/Silex/Tests/Provider/RememberMeServiceProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?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;
}
}

0 comments on commit fd3ba62

Please sign in to comment.