[Security] many improvements, and fixes
This commit is contained in:
parent
fb889a2eee
commit
57ae50e894
@ -65,6 +65,14 @@ class EntityUserProvider implements UserProviderInterface
|
||||
throw new UnsupportedAccountException(sprintf('Instances of "%s" are not supported.', get_class($account)));
|
||||
}
|
||||
|
||||
return $this->loadUserByUsername((string) $account);
|
||||
return $this->loadUserByUsername($account->getUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function supportsClass($class)
|
||||
{
|
||||
return $class === $this->class;
|
||||
}
|
||||
}
|
||||
|
@ -72,8 +72,6 @@ EOT
|
||||
}
|
||||
}
|
||||
|
||||
$paths = array_filter($paths, 'is_dir');
|
||||
|
||||
$loader = new \Doctrine\Common\DataFixtures\Loader();
|
||||
foreach ($paths as $path) {
|
||||
$loader->loadFromDirectory($path);
|
||||
|
@ -65,6 +65,14 @@ class DocumentUserProvider implements UserProviderInterface
|
||||
throw new UnsupportedAccountException(sprintf('Instances of "%s" are not supported.', get_class($account)));
|
||||
}
|
||||
|
||||
return $this->loadUserByUsername((string) $account);
|
||||
return $this->loadUserByUsername($account->getUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function supportsClass($class)
|
||||
{
|
||||
return $class === $this->class;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class AddAuthenticationProvidersPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('security.authentication.manager')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$providers = array();
|
||||
foreach ($container->findTaggedServiceIds('security.authentication_provider') as $id => $attributes) {
|
||||
$providers[] = new Reference($id);
|
||||
}
|
||||
|
||||
$container
|
||||
->getDefinition('security.authentication.manager')
|
||||
->setArguments(array($providers))
|
||||
;
|
||||
}
|
||||
}
|
@ -26,17 +26,28 @@ class FormLoginFactory implements SecurityFactoryInterface
|
||||
$provider = 'security.authentication.provider.dao.'.$id;
|
||||
$container
|
||||
->register($provider, '%security.authentication.provider.dao.class%')
|
||||
->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), new Reference('security.encoder_factory')))
|
||||
->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), $id, new Reference('security.encoder_factory')))
|
||||
->setPublic(false)
|
||||
->addTag('security.authentication_provider')
|
||||
;
|
||||
|
||||
// listener
|
||||
$listenerId = 'security.authentication.listener.form.'.$id;
|
||||
$listener = $container->setDefinition($listenerId, clone $container->getDefinition('security.authentication.listener.form'));
|
||||
$arguments = $listener->getArguments();
|
||||
$arguments[1] = new Reference($provider);
|
||||
$listener->setArguments($arguments);
|
||||
$listener->setArgument(3, $id);
|
||||
|
||||
// add remember-me tag
|
||||
$rememberMe = true;
|
||||
if (isset($config['remember-me']) && false === $config['remember-me']) {
|
||||
$rememberMe = false;
|
||||
} else if (isset($config['remember_me']) && false === $config['remember_me']) {
|
||||
$rememberMe = false;
|
||||
}
|
||||
if ($rememberMe) {
|
||||
$listener->addTag('security.remember_me_aware', array('id' => $id, 'provider' => $userProvider));
|
||||
}
|
||||
|
||||
// generate options
|
||||
$options = array(
|
||||
'check_path' => '/login_check',
|
||||
'login_path' => '/login',
|
||||
@ -53,11 +64,29 @@ class FormLoginFactory implements SecurityFactoryInterface
|
||||
$options[$key] = $config[$key];
|
||||
}
|
||||
}
|
||||
$container->setParameter('security.authentication.form.options', $options);
|
||||
$container->setParameter('security.authentication.form.login_path', $options['login_path']);
|
||||
$container->setParameter('security.authentication.form.use_forward', $options['use_forward']);
|
||||
$listener->setArgument(4, $options);
|
||||
|
||||
return array($provider, $listenerId, 'security.authentication.form_entry_point');
|
||||
// success handler
|
||||
if (isset($config['success_handler'])) {
|
||||
$config['success-handler'] = $config['success_handler'];
|
||||
}
|
||||
if (isset($config['success-handler'])) {
|
||||
$listener->setArgument(5, new Reference($config['success-handler']));
|
||||
}
|
||||
|
||||
// failure handler
|
||||
if (isset($config['failure_handler'])) {
|
||||
$config['failure-handler'] = $config['failure_handler'];
|
||||
}
|
||||
if (isset($config['failure-handler'])) {
|
||||
$listener->setArgument(6, new Reference($config['failure-handler']));
|
||||
}
|
||||
|
||||
// form entry point
|
||||
$entryPoint = $container->setDefinition($entryPointId = 'security.authentication.form_entry_point.'.$id, clone $container->getDefinition('security.authentication.form_entry_point'));
|
||||
$entryPoint->setArguments(array($options['login_path'], $options['use_forward']));
|
||||
|
||||
return array($provider, $listenerId, $entryPointId);
|
||||
}
|
||||
|
||||
public function getPosition()
|
||||
|
@ -26,16 +26,15 @@ class HttpBasicFactory implements SecurityFactoryInterface
|
||||
$provider = 'security.authentication.provider.dao.'.$id;
|
||||
$container
|
||||
->register($provider, '%security.authentication.provider.dao.class%')
|
||||
->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), new Reference('security.encoder_factory')))
|
||||
->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), $id, new Reference('security.encoder_factory')))
|
||||
->setPublic(false)
|
||||
->addTag('security.authentication_provider')
|
||||
;
|
||||
|
||||
// listener
|
||||
$listenerId = 'security.authentication.listener.basic.'.$id;
|
||||
$listener = $container->setDefinition($listenerId, clone $container->getDefinition('security.authentication.listener.basic'));
|
||||
$arguments = $listener->getArguments();
|
||||
$arguments[1] = new Reference($provider);
|
||||
$listener->setArguments($arguments);
|
||||
$listener->setArgument(2, $id);
|
||||
|
||||
if (isset($config['path'])) {
|
||||
$container->setParameter('security.authentication.form.path', $config['path']);
|
||||
|
@ -26,16 +26,15 @@ class HttpDigestFactory implements SecurityFactoryInterface
|
||||
$provider = 'security.authentication.provider.dao.'.$id;
|
||||
$container
|
||||
->register($provider, '%security.authentication.provider.dao.class%')
|
||||
->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), new Reference('security.encoder_factory')))
|
||||
->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), $id, new Reference('security.encoder_factory')))
|
||||
->setPublic(false)
|
||||
->addTag('security.authentication_provider')
|
||||
;
|
||||
|
||||
// listener
|
||||
$listenerId = 'security.authentication.listener.digest.'.$id;
|
||||
$listener = $container->setDefinition($listenerId, clone $container->getDefinition('security.authentication.listener.digest'));
|
||||
$arguments = $listener->getArguments();
|
||||
$arguments[1] = new Reference($userProvider);
|
||||
$listener->setArguments($arguments);
|
||||
$listener->setArgument(2, $id);
|
||||
|
||||
if (null === $defaultEntryPoint) {
|
||||
$defaultEntryPoint = 'security.authentication.digest_entry_point';
|
||||
|
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Security\Factory;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Parameter;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class RememberMeFactory implements SecurityFactoryInterface
|
||||
{
|
||||
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
|
||||
{
|
||||
if (!isset($config['key']) || empty($config['key'])) {
|
||||
throw new \RuntimeException('A "key" must be defined for each remember-me section.');
|
||||
}
|
||||
|
||||
if (isset($config['provider'])) {
|
||||
throw new \RuntimeException('You must not set a user provider for remember-me.');
|
||||
}
|
||||
|
||||
// authentication provider
|
||||
$authenticationProviderId = 'security.authentication.provider.rememberme.'.$id;
|
||||
$container
|
||||
->register($authenticationProviderId, '%security.authentication.provider.rememberme.class%')
|
||||
->setArguments(array(new Reference('security.account_checker'), $config['key'], $id))
|
||||
->setPublic(false)
|
||||
->addTag('security.authentication_provider')
|
||||
;
|
||||
|
||||
// remember me services
|
||||
if (isset($config['token_provider'])) {
|
||||
$config['token-provider'] = $config['token_provider'];
|
||||
}
|
||||
if (isset($config['token-provider'])) {
|
||||
$templateId = 'security.authentication.rememberme.services.persistent';
|
||||
$rememberMeServicesId = $templateId.'.'.$id;
|
||||
} else {
|
||||
$templateId = 'security.authentication.rememberme.services.simplehash';
|
||||
$rememberMeServicesId = $templateId.'.'.$id;
|
||||
}
|
||||
|
||||
if ($container->hasDefinition('security.logout_listener.'.$id)) {
|
||||
$container
|
||||
->getDefinition('security.logout_listener.'.$id)
|
||||
->addMethodCall('addHandler', array(new Reference($rememberMeServicesId)))
|
||||
;
|
||||
}
|
||||
|
||||
$rememberMeServices = $container->setDefinition($rememberMeServicesId, clone $container->getDefinition($templateId));
|
||||
$arguments = $rememberMeServices->getArguments();
|
||||
$arguments[1] = $config['key'];
|
||||
$arguments[2] = $id;
|
||||
|
||||
if (isset($config['token-provider'])) {
|
||||
// FIXME: make the naming assumption more flexible
|
||||
$rememberMeServices->addMethodCall('setTokenProvider', array(
|
||||
new Reference('security.rememberme.token.provider.'.$config['token-provider'])
|
||||
));
|
||||
}
|
||||
|
||||
// remember-me options
|
||||
foreach ($arguments[3] as $name => $option) {
|
||||
if (array_key_exists($name, $config)) {
|
||||
$arguments[3][$name] = $config[$name];
|
||||
}
|
||||
}
|
||||
$rememberMeServices->setArguments($arguments);
|
||||
|
||||
// attach to remember-me aware listeners
|
||||
$userProviders = array();
|
||||
foreach ($container->findTaggedServiceIds('security.remember_me_aware') as $serviceId => $attributes) {
|
||||
foreach ($attributes as $attribute) {
|
||||
if (!isset($attribute['id']) || $attribute['id'] !== $id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($attribute['provider'])) {
|
||||
throw new \RuntimeException('Each "security.remember_me_aware" tag must have a provider attribute.');
|
||||
}
|
||||
|
||||
$userProviders[] = new Reference($attribute['provider']);
|
||||
$container
|
||||
->getDefinition($serviceId)
|
||||
->addMethodCall('setRememberMeServices', array(new Reference($rememberMeServicesId)))
|
||||
;
|
||||
}
|
||||
}
|
||||
if (count($userProviders) === 0) {
|
||||
throw new \RuntimeException('You must configure at least one remember-me aware listener (such as form-login) for each firewall that has remember-me enabled.');
|
||||
}
|
||||
$rememberMeServices->setArgument(0, $userProviders);
|
||||
|
||||
// remember-me listener
|
||||
$listenerId = 'security.authentication.listener.rememberme.'.$id;
|
||||
$listener = $container->setDefinition($listenerId, clone $container->getDefinition('security.authentication.listener.rememberme'));
|
||||
$listener->setArgument(1, new Reference($rememberMeServicesId));
|
||||
|
||||
return array($authenticationProviderId, $listenerId, $defaultEntryPoint);
|
||||
}
|
||||
|
||||
public function getPosition()
|
||||
{
|
||||
return 'remember_me';
|
||||
}
|
||||
|
||||
public function getKey()
|
||||
{
|
||||
return 'remember-me';
|
||||
}
|
||||
}
|
@ -26,15 +26,16 @@ class X509Factory implements SecurityFactoryInterface
|
||||
$provider = 'security.authentication.provider.pre_authenticated.'.$id;
|
||||
$container
|
||||
->register($provider, '%security.authentication.provider.pre_authenticated.class%')
|
||||
->setArguments(array(new Reference($userProvider), new Reference('security.account_checker')))
|
||||
->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), $id))
|
||||
->setPublic(false)
|
||||
->addTag('security.authentication_provider')
|
||||
;
|
||||
|
||||
// listener
|
||||
$listenerId = 'security.authentication.listener.x509.'.$id;
|
||||
$listener = $container->setDefinition($listenerId, clone $container->getDefinition('security.authentication.listener.x509'));
|
||||
$arguments = $listener->getArguments();
|
||||
$arguments[1] = new Reference($provider);
|
||||
$arguments[2] = $id;
|
||||
$listener->setArguments($arguments);
|
||||
|
||||
return array($provider, $listenerId, $defaultEntryPoint);
|
||||
|
@ -12,12 +12,12 @@
|
||||
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Alias;
|
||||
use Symfony\Component\DependencyInjection\Parameter;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Resource\FileResource;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Parameter;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Component\HttpFoundation\RequestMatcher;
|
||||
@ -26,10 +26,12 @@ use Symfony\Component\HttpFoundation\RequestMatcher;
|
||||
* SecurityExtension.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class SecurityExtension extends Extension
|
||||
{
|
||||
protected $requestMatchers = array();
|
||||
protected $contextListeners = array();
|
||||
|
||||
public function configLoad(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
@ -59,7 +61,15 @@ class SecurityExtension extends Extension
|
||||
}
|
||||
|
||||
if (isset($config['access-denied-url'])) {
|
||||
$container->setParameter('security.access_denied.url', $config['access-denied-url']);
|
||||
$container->setParameter('security.access.denied_url', $config['access-denied-url']);
|
||||
}
|
||||
|
||||
// session fixation protection
|
||||
if (isset($config['session_fixation_protection'])) {
|
||||
$config['session-fixation-protection'] = $config['session_fixation_protection'];
|
||||
}
|
||||
if (isset($config['session-fixation-protection'])) {
|
||||
$container->setParameter('security.authentication.session_strategy.strategy', $config['session-fixation-protection']);
|
||||
}
|
||||
|
||||
$this->createFirewalls($config, $container);
|
||||
@ -208,7 +218,13 @@ class SecurityExtension extends Extension
|
||||
// Matcher
|
||||
$i = 0;
|
||||
$matcher = null;
|
||||
if (isset($firewall['pattern'])) {
|
||||
if (isset($firewall['request_matcher'])) {
|
||||
$firewall['request-matcher'] = $firewall['request_matcher'];
|
||||
}
|
||||
|
||||
if (isset($firewall['request-matcher'])) {
|
||||
$matcher = new Reference($firewall['request-matcher']);
|
||||
} else if (isset($firewall['pattern'])) {
|
||||
$matcher = $this->createRequestMatcher($container, $firewall['pattern']);
|
||||
}
|
||||
|
||||
@ -236,7 +252,12 @@ class SecurityExtension extends Extension
|
||||
|
||||
// Context serializer listener
|
||||
if (!isset($firewall['stateless']) || !$firewall['stateless']) {
|
||||
$listeners[] = new Reference('security.context_listener');
|
||||
$contextKey = $id;
|
||||
if (isset($firewall['context'])) {
|
||||
$contextKey = $firewall['context'];
|
||||
}
|
||||
|
||||
$listeners[] = new Reference($this->createContextListener($container, $contextKey));
|
||||
}
|
||||
|
||||
// Logout listener
|
||||
@ -246,6 +267,10 @@ class SecurityExtension extends Extension
|
||||
|
||||
$listeners[] = new Reference($listenerId);
|
||||
|
||||
if (!is_array($firewall['logout'])) {
|
||||
$firewall['logout'] = array();
|
||||
}
|
||||
|
||||
$arguments = $listener->getArguments();
|
||||
if (isset($firewall['logout']['path'])) {
|
||||
$arguments[1] = $firewall['logout']['path'];
|
||||
@ -256,10 +281,19 @@ class SecurityExtension extends Extension
|
||||
}
|
||||
$listener->setArguments($arguments);
|
||||
|
||||
if (!isset($firewall['stateless']) || !$firewall['stateless']) {
|
||||
// add session logout handler
|
||||
$invalidateSession = true;
|
||||
if (array_key_exists('invalidate_session', $firewall['logout'])) {
|
||||
$firewall['logout']['invalidate-session'] = $firewall['logout']['invalidate_session'];
|
||||
}
|
||||
if (array_key_exists('invalidate-session', $firewall['logout'])) {
|
||||
$invalidateSession = (Boolean) $invalidateSession;
|
||||
}
|
||||
if (true === $invalidateSession && (!isset($firewall['stateless']) || !$firewall['stateless'])) {
|
||||
$listener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session')));
|
||||
}
|
||||
|
||||
// add cookie logout handler
|
||||
if (count($cookies = $this->normalizeConfig($firewall['logout'], 'cookie')) > 0) {
|
||||
$cookieHandlerId = 'security.logout.handler.cookie_clearing.'.$id;
|
||||
$cookieHandler = $container->setDefinition($cookieHandlerId, clone $container->getDefinition('security.logout.handler.cookie_clearing'));
|
||||
@ -275,7 +309,7 @@ class SecurityExtension extends Extension
|
||||
$listeners = array_merge($listeners, $authListeners);
|
||||
|
||||
// Access listener
|
||||
$listeners[] = new Reference($this->createAccessListener($container, $id, $providers));
|
||||
$listeners[] = new Reference('security.access_listener');
|
||||
|
||||
// Switch user listener
|
||||
if (array_key_exists('switch_user', $firewall)) {
|
||||
@ -285,12 +319,35 @@ class SecurityExtension extends Extension
|
||||
$listeners[] = new Reference($this->createSwitchUserListener($container, $id, $firewall['switch-user'], $defaultProvider));
|
||||
}
|
||||
|
||||
// Determine default entry point
|
||||
if (isset($firewall['entry_point'])) {
|
||||
$firewall['entry-point'] = $firewall['entry_point'];
|
||||
}
|
||||
if (isset($firewall['entry-point'])) {
|
||||
$defaultEntryPoint = $firewall['entry-point'];
|
||||
}
|
||||
|
||||
// Exception listener
|
||||
$exceptionListener = new Reference($this->createExceptionListener($container, $id, $defaultEntryPoint));
|
||||
$exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $defaultEntryPoint));
|
||||
|
||||
return array($matcher, $listeners, $exceptionListener);
|
||||
}
|
||||
|
||||
protected function createContextListener($container, $contextKey)
|
||||
{
|
||||
if (isset($this->contextListeners[$contextKey])) {
|
||||
return $this->contextListeners[$contextKey];
|
||||
}
|
||||
|
||||
$listenerId = 'security.context_listener.'.count($this->contextListeners);
|
||||
$listener = $container->setDefinition($listenerId, clone $container->getDefinition('security.context_listener'));
|
||||
$arguments = $listener->getArguments();
|
||||
$arguments[2] = $contextKey;
|
||||
$listener->setArguments($arguments);
|
||||
|
||||
return $this->contextListeners[$contextKey] = $listenerId;
|
||||
}
|
||||
|
||||
protected function createAuthenticationListeners($container, $id, $firewall, $defaultProvider)
|
||||
{
|
||||
$listeners = array();
|
||||
@ -298,7 +355,7 @@ class SecurityExtension extends Extension
|
||||
$hasListeners = false;
|
||||
$defaultEntryPoint = null;
|
||||
|
||||
$positions = array('pre_auth', 'form', 'http');
|
||||
$positions = array('pre_auth', 'form', 'http', 'remember_me');
|
||||
|
||||
$tags = $container->findTaggedServiceIds('security.listener.factory');
|
||||
$factories = array();
|
||||
@ -323,9 +380,9 @@ class SecurityExtension extends Extension
|
||||
if (array_key_exists($key, $firewall) && $firewall[$key] !== false) {
|
||||
$userProvider = isset($firewall[$key]['provider']) ? $this->getUserProviderId($firewall[$key]['provider']) : $defaultProvider;
|
||||
|
||||
list($provider, $listener, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
|
||||
list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
|
||||
|
||||
$listeners[] = new Reference($listener);
|
||||
$listeners[] = new Reference($listenerId);
|
||||
$providers[] = new Reference($provider);
|
||||
$hasListeners = true;
|
||||
}
|
||||
@ -553,32 +610,27 @@ class SecurityExtension extends Extension
|
||||
return 'security.authentication.provider.'.$name;
|
||||
}
|
||||
|
||||
protected function createAccessListener($container, $id, $providers)
|
||||
protected function createExceptionListener($container, $config, $id, $defaultEntryPoint)
|
||||
{
|
||||
// Authentication manager
|
||||
$authManager = 'security.authentication.manager.'.$id;
|
||||
$container
|
||||
->register($authManager, '%security.authentication.manager.class%')
|
||||
->addArgument($providers)
|
||||
->setPublic(false)
|
||||
;
|
||||
if (isset($config['access_denied_handler'])) {
|
||||
$config['access-denied-handler'] = $config['access_denied_handler'];
|
||||
}
|
||||
if (isset($config['access_denied_url'])) {
|
||||
$config['access-denied-url'] = $config['access_denied_url'];
|
||||
}
|
||||
|
||||
// Access listener
|
||||
$listenerId = 'security.access_listener.'.$id;
|
||||
$listener = $container->setDefinition($listenerId, clone $container->getDefinition('security.access_listener'));
|
||||
$arguments = $listener->getArguments();
|
||||
$arguments[3] = new Reference($authManager);
|
||||
$listener->setArguments($arguments);
|
||||
|
||||
return $listenerId;
|
||||
}
|
||||
|
||||
protected function createExceptionListener($container, $id, $defaultEntryPoint)
|
||||
{
|
||||
$exceptionListenerId = 'security.exception_listener.'.$id;
|
||||
$listener = $container->setDefinition($exceptionListenerId, clone $container->getDefinition('security.exception_listener'));
|
||||
$arguments = $listener->getArguments();
|
||||
$arguments[2] = null === $defaultEntryPoint ? null : new Reference($defaultEntryPoint);
|
||||
|
||||
// access denied handler setup
|
||||
if (isset($config['access-denied-handler'])) {
|
||||
$arguments[4] = new Reference($config['access-denied-handler']);
|
||||
} else if (isset($config['access-denied-url'])) {
|
||||
$arguments[3] = $config['access-denied-url'];
|
||||
}
|
||||
|
||||
$listener->setArguments($arguments);
|
||||
|
||||
return $exceptionListenerId;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAuthenticationProvidersPass;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddFieldFactoryGuessersPass;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
|
||||
@ -67,6 +68,7 @@ class FrameworkBundle extends Bundle
|
||||
$container->addCompilerPass(new AddClassesToCachePass());
|
||||
$container->addCompilerPass(new TranslatorPass());
|
||||
$container->addCompilerPass(new AddCacheWarmerPass());
|
||||
$container->addCompilerPass(new AddAuthenticationProvidersPass());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
<parameters>
|
||||
<parameter key="security.context.class">Symfony\Component\Security\SecurityContext</parameter>
|
||||
<parameter key="security.context.always_authenticate">false</parameter>
|
||||
|
||||
<parameter key="security.account_checker.class">Symfony\Component\Security\User\AccountChecker</parameter>
|
||||
|
||||
@ -23,14 +24,16 @@
|
||||
|
||||
<parameter key="security.authentication.provider.dao.class">Symfony\Component\Security\Authentication\Provider\DaoAuthenticationProvider</parameter>
|
||||
<parameter key="security.authentication.provider.pre_authenticated.class">Symfony\Component\Security\Authentication\Provider\PreAuthenticatedAuthenticationProvider</parameter>
|
||||
<parameter key="security.authentication.provider.rememberme.class">Symfony\Component\Security\Authentication\Provider\RememberMeAuthenticationProvider</parameter>
|
||||
|
||||
<parameter key="security.authentication.manager.class">Symfony\Component\Security\Authentication\AuthenticationProviderManager</parameter>
|
||||
|
||||
|
||||
<parameter key="security.authentication.retry_entry_point.class">Symfony\Component\HttpKernel\Security\EntryPoint\RetryAuthenticationEntryPoint</parameter>
|
||||
|
||||
<parameter key="security.authentication.session_strategy.class">Symfony\Component\HttpKernel\Security\Session\SessionAuthenticationStrategy</parameter>
|
||||
<parameter key="security.authentication.session_strategy.strategy">migrate</parameter>
|
||||
|
||||
<parameter key="security.authentication.form_entry_point.class">Symfony\Component\HttpKernel\Security\EntryPoint\FormAuthenticationEntryPoint</parameter>
|
||||
<parameter key="security.authentication.form.login_path">/login</parameter>
|
||||
<parameter key="security.authentication.form.use_forward">false</parameter>
|
||||
<parameter key="security.authentication.listener.form.class">Symfony\Component\HttpKernel\Security\Firewall\UsernamePasswordFormAuthenticationListener</parameter>
|
||||
<parameter key="security.authentication.form.options" type="collection" />
|
||||
|
||||
@ -48,8 +51,24 @@
|
||||
<parameter key="security.authentication.listener.basic.class">Symfony\Component\HttpKernel\Security\Firewall\BasicAuthenticationListener</parameter>
|
||||
<parameter key="security.authentication.listener.digest.class">Symfony\Component\HttpKernel\Security\Firewall\DigestAuthenticationListener</parameter>
|
||||
<parameter key="security.authentication.listener.anonymous.class">Symfony\Component\HttpKernel\Security\Firewall\AnonymousAuthenticationListener</parameter>
|
||||
|
||||
<parameter key="security.authentication.provider.anonymous">Symfony\Component\Security\Authentication\Provider\AnonymousAuthenticationProvider</parameter>
|
||||
<parameter key="security.anonymous.key">SomeRandomValue</parameter>
|
||||
|
||||
<parameter key="security.authentication.listener.rememberme.class">Symfony\Component\HttpKernel\Security\Firewall\RememberMeListener</parameter>
|
||||
<parameter key="security.rembemerme.token.provider.in_memory.class">Symfony\Component\Security\Authentication\RememberMe\InMemoryTokenProvider</parameter>
|
||||
|
||||
<parameter key="security.authentication.rememberme.services.persistent.class">Symfony\Component\HttpKernel\Security\RememberMe\PersistentTokenBasedRememberMeServices</parameter>
|
||||
<parameter key="security.authentication.rememberme.services.simplehash.class">Symfony\Component\HttpKernel\Security\RememberMe\TokenBasedRememberMeServices</parameter>
|
||||
<parameter key="security.authentication.rememberme.services.options.name">SYMFONY_REMEMBERME</parameter>
|
||||
<parameter key="security.authentication.rememberme.services.options.lifetime">31536000</parameter>
|
||||
<parameter key="security.authentication.rememberme.services.options.path">/</parameter>
|
||||
<parameter key="security.authentication.rememberme.services.options.domain" />
|
||||
<parameter key="security.authentication.rememberme.services.options.secure">false</parameter>
|
||||
<parameter key="security.authentication.rememberme.services.options.httponly">true</parameter>
|
||||
<parameter key="security.authentication.rememberme.services.options.always_remember_me">false</parameter>
|
||||
<parameter key="security.authentication.rememberme.services.options.remember_me_parameter">_remember_me</parameter>
|
||||
|
||||
<parameter key="security.channel_listener.class">Symfony\Component\HttpKernel\Security\Firewall\ChannelListener</parameter>
|
||||
|
||||
<parameter key="security.logout_listener.class">Symfony\Component\HttpKernel\Security\Firewall\LogoutListener</parameter>
|
||||
@ -67,6 +86,8 @@
|
||||
<parameter key="security.access.decision_manager.allow_if_all_abstain">false</parameter>
|
||||
<parameter key="security.access.decision_manager.allow_if_equal_granted_denied">true</parameter>
|
||||
|
||||
<parameter key="security.access.denied_url">/access_denied</parameter>
|
||||
|
||||
<parameter key="security.access.simple_role_voter.class">Symfony\Component\Security\Authorization\Voter\RoleVoter</parameter>
|
||||
<parameter key="security.access.authenticated_voter.class">Symfony\Component\Security\Authorization\Voter\AuthenticatedVoter</parameter>
|
||||
<parameter key="security.access.role_hierarchy_voter.class">Symfony\Component\Security\Authorization\Voter\RoleHierarchyVoter</parameter>
|
||||
@ -85,7 +106,9 @@
|
||||
|
||||
<services>
|
||||
<service id="security.context" class="%security.context.class%">
|
||||
<argument type="service" id="security.authentication.manager" />
|
||||
<argument type="service" id="security.access.decision_manager" />
|
||||
<argument>%security.context.always_authenticate%</argument>
|
||||
</service>
|
||||
|
||||
<service id="security.role_hierarchy" class="%security.role_hierarchy.class%" public="false">
|
||||
@ -108,18 +131,28 @@
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.manager" class="%security.authentication.manager.class%" public="false">
|
||||
<argument type="collection" />
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.provider.anonymous" class="%security.authentication.provider.anonymous%" public="false">
|
||||
<argument>%security.anonymous.key%</argument>
|
||||
<tag name="security.authentication_provider" />
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.trust_resolver" class="%security.authentication.trust_resolver.class%" public="false">
|
||||
<argument>%security.authentication.trust_resolver.anonymous_class%</argument>
|
||||
<argument>%security.authentication.trust_resolver.rememberme_class%</argument>
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.retry_entry_point" class="%security.authentication.retry_entry_point.class%" public="false" />
|
||||
|
||||
<service id="security.authentication.form_entry_point" class="%security.authentication.form_entry_point.class%" public="false">
|
||||
<argument>%security.authentication.form.login_path%</argument>
|
||||
<argument>%security.authentication.form.use_forward%</argument>
|
||||
|
||||
<service id="security.authentication.session_strategy" class="%security.authentication.session_strategy.class%" public="false">
|
||||
<argument>%security.authentication.session_strategy.strategy%</argument>
|
||||
</service>
|
||||
|
||||
<service id="security.rememberme.token.provider.in_memory" class="%security.rembemerme.token.provider.in_memory.class%" public="false"></service>
|
||||
|
||||
<service id="security.authentication.retry_entry_point" class="%security.authentication.retry_entry_point.class%" public="false" />
|
||||
|
||||
<service id="security.authentication.basic_entry_point" class="%security.authentication.basic_entry_point.class%" public="false">
|
||||
<argument>%security.authentication.basic_entry_point.realm%</argument>
|
||||
</service>
|
||||
@ -168,6 +201,7 @@
|
||||
<service id="security.context_listener" class="%security.context_listener.class%" public="false">
|
||||
<argument type="service" id="security.context" />
|
||||
<argument type="collection"></argument>
|
||||
<argument />
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
</services>
|
||||
|
@ -21,26 +21,47 @@
|
||||
<tag name="security.listener.factory" />
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.factory.remember_me" class="Symfony\Bundle\FrameworkBundle\DependencyInjection\Security\Factory\RememberMeFactory" public="false">
|
||||
<tag name="security.listener.factory" />
|
||||
</service>
|
||||
|
||||
<service id="security.logout_listener" class="%security.logout_listener.class%" public="false">
|
||||
<argument type="service" id="security.context" />
|
||||
<argument>%security.logout.path%</argument>
|
||||
<argument>%security.logout.target_path%</argument>
|
||||
</service>
|
||||
|
||||
|
||||
<service id="security.logout.handler.cookie_clearing" class="%security.logout.handler.cookie_clearing.class%" public="false">
|
||||
<argument type="collection"></argument>
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.form_entry_point" class="%security.authentication.form_entry_point.class%" public="false">
|
||||
<argument/>
|
||||
<argument/>
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.listener.form" class="%security.authentication.listener.form.class%" public="false">
|
||||
<argument type="service" id="security.context" />
|
||||
<argument type="service" id="security.authentication.manager" />
|
||||
<argument>%security.authentication.form.options%</argument>
|
||||
<argument type="service" id="security.authentication.session_strategy" />
|
||||
<argument />
|
||||
<argument type="collection"></argument>
|
||||
<argument type="service" id="security.authentication.success_handler" on-invalid="null" />
|
||||
<argument type="service" id="security.authentication.failure_handler" on-invalid="null" />
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.listener.rememberme" class="%security.authentication.listener.rememberme.class%" public="false">
|
||||
<argument type="service" id="security.context" />
|
||||
<argument type="service" id="security.authentication.rememberme" />
|
||||
<argument type="service" id="security.authentication.manager" />
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.listener.x509" class="%security.authentication.listener.x509.class%" public="false">
|
||||
<argument type="service" id="security.context" />
|
||||
<argument type="service" id="security.authentication.manager" />
|
||||
<argument />
|
||||
<argument>%security.authentication.x509.user%</argument>
|
||||
<argument>%security.authentication.x509.credentials%</argument>
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
@ -49,6 +70,7 @@
|
||||
<service id="security.authentication.listener.basic" class="%security.authentication.listener.basic.class%" public="false">
|
||||
<argument type="service" id="security.context" />
|
||||
<argument type="service" id="security.authentication.manager" />
|
||||
<argument></argument>
|
||||
<argument type="service" id="security.authentication.basic_entry_point" />
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
@ -56,18 +78,55 @@
|
||||
<service id="security.authentication.listener.digest" class="%security.authentication.listener.digest.class%" public="false">
|
||||
<argument type="service" id="security.context" />
|
||||
<argument type="service" id="security.user.provider.in_memory" />
|
||||
<argument></argument>
|
||||
<argument type="service" id="security.authentication.digest_entry_point" />
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.rememberme.services.simplehash" class="%security.authentication.rememberme.services.simplehash.class%" public="false">
|
||||
<argument type="collection" />
|
||||
<argument />
|
||||
<argument />
|
||||
<argument type="collection">
|
||||
<argument key="name">%security.authentication.rememberme.services.options.name%</argument>
|
||||
<argument key="lifetime">%security.authentication.rememberme.services.options.lifetime%</argument>
|
||||
<argument key="path">%security.authentication.rememberme.services.options.path%</argument>
|
||||
<argument key="domain">%security.authentication.rememberme.services.options.domain%</argument>
|
||||
<argument key="secure">%security.authentication.rememberme.services.options.secure%</argument>
|
||||
<argument key="httponly">%security.authentication.rememberme.services.options.httponly%</argument>
|
||||
<argument key="always_remember_me">%security.authentication.rememberme.services.options.always_remember_me%</argument>
|
||||
<argument key="remember_me_parameter">%security.authentication.rememberme.services.options.remember_me_parameter%</argument>
|
||||
</argument>
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<service id="security.authentication.rememberme.services.persistent" class="%security.authentication.rememberme.services.persistent.class%" public="false">
|
||||
<argument type="collection" />
|
||||
<argument />
|
||||
<argument />
|
||||
<argument type="collection">
|
||||
<argument key="name">%security.authentication.rememberme.services.options.name%</argument>
|
||||
<argument key="lifetime">%security.authentication.rememberme.services.options.lifetime%</argument>
|
||||
<argument key="path">%security.authentication.rememberme.services.options.path%</argument>
|
||||
<argument key="domain">%security.authentication.rememberme.services.options.domain%</argument>
|
||||
<argument key="secure">%security.authentication.rememberme.services.options.secure%</argument>
|
||||
<argument key="httponly">%security.authentication.rememberme.services.options.httponly%</argument>
|
||||
<argument key="always_remember_me">%security.authentication.rememberme.services.options.always_remember_me%</argument>
|
||||
<argument key="remember_me_parameter">%security.authentication.rememberme.services.options.remember_me_parameter%</argument>
|
||||
</argument>
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<service id="security.exception_listener" class="%security.exception_listener.class%" public="false">
|
||||
<argument type="service" id="security.context" />
|
||||
<argument type="service" id="security.authentication.trust_resolver" />
|
||||
<argument type="service" id="security.authentication.entry_point" on-invalid="null" />
|
||||
<argument>%security.access_denied.url%</argument>
|
||||
<argument>%security.access.denied_url%</argument>
|
||||
<argument type="service" id="security.access.denied_handler" on-invalid="null" />
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
|
||||
<service id="security.authentication.switchuser_listener" class="%security.authentication.switchuser_listener.class%" public="false">
|
||||
<argument type="service" id="security.context" />
|
||||
<argument type="service" id="security.user.provider.in_memory" />
|
||||
|
@ -35,7 +35,7 @@ abstract class SecurityExtensionTest extends TestCase
|
||||
|
||||
$providers = array_values(array_filter($container->getServiceIds(), function ($key) { return 0 === strpos($key, 'security.authentication.provider.'); }));
|
||||
|
||||
$this->assertEquals(array(
|
||||
$expectedProviders = array(
|
||||
'security.authentication.provider.digest',
|
||||
'security.authentication.provider.digest_0ff1b54f2a4b7f71b2b9d6604fcca4b8',
|
||||
'security.authentication.provider.basic',
|
||||
@ -43,7 +43,11 @@ abstract class SecurityExtensionTest extends TestCase
|
||||
'security.authentication.provider.basic_98e44377704554700e68c22094b51ca4',
|
||||
'security.authentication.provider.doctrine',
|
||||
'security.authentication.provider.service',
|
||||
), $providers);
|
||||
'security.authentication.provider.anonymous',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(), array_diff($expectedProviders, $providers));
|
||||
$this->assertEquals(array(), array_diff($providers, $expectedProviders));
|
||||
}
|
||||
|
||||
public function testFirewalls()
|
||||
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\Authentication;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
interface AuthenticationFailureHandlerInterface
|
||||
{
|
||||
function onAuthenticationFailure(EventInterface $event, Request $request, \Exception $exception);
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\Authentication;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventInterface;
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
interface AuthenticationSuccessHandlerInterface
|
||||
{
|
||||
function onAuthenticationSuccess(EventInterface $event, Request $request, TokenInterface $token);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\ExceptionTranslation;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\EventDispatcher\EventInterface;
|
||||
use Symfony\Component\Security\Exception\AccessDeniedException;
|
||||
|
||||
/**
|
||||
* This is used by the ExceptionListener to translate an AccessDeniedException
|
||||
* to a Response object.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
interface AccessDeniedHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Handles an access denied failure.
|
||||
*
|
||||
* @param EventInterface $event
|
||||
* @param Request $request
|
||||
* @param AccessDeniedException $accessDeniedException
|
||||
*
|
||||
* @return Response may return null
|
||||
*/
|
||||
function handle(EventInterface $event, Request $request, AccessDeniedException $accessDeniedException);
|
||||
}
|
@ -11,6 +11,12 @@
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\Firewall;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
use Symfony\Component\HttpKernel\Security\Session\SessionAuthenticationStrategyInterface;
|
||||
use Symfony\Component\HttpKernel\Security\Authentication\AuthenticationFailureHandlerInterface;
|
||||
use Symfony\Component\HttpKernel\Security\Authentication\AuthenticationSuccessHandlerInterface;
|
||||
use Symfony\Component\HttpKernel\Security\RememberMe\RememberMeServicesInterface;
|
||||
use Symfony\Component\Security\SecurityContext;
|
||||
use Symfony\Component\Security\Authentication\AuthenticationManagerInterface;
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
@ -23,29 +29,55 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
|
||||
/**
|
||||
* FormAuthenticationListener implements authentication via a form.
|
||||
* The AbstractAuthenticationListener is the preferred base class for all
|
||||
* browser-/HTTP-based authentication requests.
|
||||
*
|
||||
* Subclasses likely have to implement the following:
|
||||
* - an TokenInterface to hold authentication related data
|
||||
* - an AuthenticationProvider to perform the actual authentication of the
|
||||
* token, retrieve the AccountInterface implementation from a database, and
|
||||
* perform the specific account checks using the AccountChecker
|
||||
*
|
||||
* By default, this listener only is active for a specific path, e.g.
|
||||
* /login_check. If you want to change this behavior, you can overwrite the
|
||||
* requiresAuthentication() method.
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
abstract class FormAuthenticationListener
|
||||
abstract class AbstractAuthenticationListener implements ListenerInterface
|
||||
{
|
||||
protected $securityContext;
|
||||
protected $authenticationManager;
|
||||
protected $sessionStrategy;
|
||||
protected $providerKey;
|
||||
protected $eventDispatcher;
|
||||
protected $options;
|
||||
protected $successHandler;
|
||||
protected $failureHandler;
|
||||
protected $logger;
|
||||
protected $rememberMeServices;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param SecurityContext $securityContext A SecurityContext instance
|
||||
* @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
|
||||
* @param array $options An array of options
|
||||
* @param array $options An array of options for the processing of a successful, or failed authentication attempt
|
||||
* @param LoggerInterface $logger A LoggerInterface instance
|
||||
*/
|
||||
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, array $options = array(), LoggerInterface $logger = null)
|
||||
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null)
|
||||
{
|
||||
if (empty($providerKey)) {
|
||||
throw new \InvalidArgumentException('$providerKey must not be empty.');
|
||||
}
|
||||
|
||||
$this->securityContext = $securityContext;
|
||||
$this->authenticationManager = $authenticationManager;
|
||||
$this->sessionStrategy = $sessionStrategy;
|
||||
$this->providerKey = $providerKey;
|
||||
$this->successHandler = $successHandler;
|
||||
$this->failureHandler = $failureHandler;
|
||||
$this->options = array_merge(array(
|
||||
'check_path' => '/login_check',
|
||||
'login_path' => '/login',
|
||||
@ -60,14 +92,26 @@ abstract class FormAuthenticationListener
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the RememberMeServices implementation to use
|
||||
*
|
||||
* @param RememberMeServicesInterface $rememberMeServices
|
||||
*/
|
||||
public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
|
||||
{
|
||||
$this->rememberMeServices = $rememberMeServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to the core.security event
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
|
||||
* @param integer $priority The priority
|
||||
* @param EventDispatcher $dispatcher An EventDispatcher instance
|
||||
* @param integer $priority The priority
|
||||
*/
|
||||
public function register(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$dispatcher->connect('core.security', array($this, 'handle'), 0);
|
||||
|
||||
$this->eventDispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,32 +124,56 @@ abstract class FormAuthenticationListener
|
||||
/**
|
||||
* Handles form based authentication.
|
||||
*
|
||||
* @param EventInterface $event An EventInterface instance
|
||||
* @param Event $event An Event instance
|
||||
*/
|
||||
public function handle(EventInterface $event)
|
||||
{
|
||||
$request = $event->get('request');
|
||||
|
||||
if ($this->options['check_path'] !== $request->getPathInfo()) {
|
||||
if (!$this->requiresAuthentication($request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (null === $token = $this->attemptAuthentication($request)) {
|
||||
if (null === $returnValue = $this->attemptAuthentication($request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$response = $this->onSuccess($request, $token);
|
||||
if ($returnValue instanceof TokenInterface) {
|
||||
$this->sessionStrategy->onAuthentication($request, $returnValue);
|
||||
|
||||
$response = $this->onSuccess($event, $request, $returnValue);
|
||||
} else if ($returnValue instanceof Response) {
|
||||
$response = $returnValue;
|
||||
} else {
|
||||
throw new \RuntimeException('attemptAuthentication() must either return a Response, an implementation of TokenInterface, or null.');
|
||||
}
|
||||
} catch (AuthenticationException $failed) {
|
||||
$response = $this->onFailure($event->getSubject(), $request, $failed);
|
||||
$response = $this->onFailure($event, $request, $failed);
|
||||
}
|
||||
|
||||
$event->setProcessed();
|
||||
$event->setReturnValue($response);
|
||||
|
||||
return $response;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function onFailure($kernel, Request $request, \Exception $failed)
|
||||
/**
|
||||
* Whether this request requires authentication.
|
||||
*
|
||||
* The default implementation only processed requests to a specific path,
|
||||
* but a subclass could change this to only authenticate requests where a
|
||||
* certain parameters is present.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
protected function requiresAuthentication(Request $request)
|
||||
{
|
||||
return $this->options['check_path'] === $request->getPathInfo();
|
||||
}
|
||||
|
||||
protected function onFailure($event, Request $request, \Exception $failed)
|
||||
{
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(sprintf('Authentication request failed: %s', $failed->getMessage()));
|
||||
@ -113,6 +181,10 @@ abstract class FormAuthenticationListener
|
||||
|
||||
$this->securityContext->setToken(null);
|
||||
|
||||
if (null !== $this->failureHandler) {
|
||||
return $this->failureHandler->onAuthenticationFailure($event, $request, $failed);
|
||||
}
|
||||
|
||||
if (null === $this->options['failure_path']) {
|
||||
$this->options['failure_path'] = $this->options['login_path'];
|
||||
}
|
||||
@ -125,7 +197,7 @@ abstract class FormAuthenticationListener
|
||||
$subRequest = Request::create($this->options['failure_path']);
|
||||
$subRequest->attributes->set(SecurityContext::AUTHENTICATION_ERROR, $failed->getMessage());
|
||||
|
||||
return $kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
|
||||
return $event->getSubject()->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
|
||||
} else {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(sprintf('Redirecting to %s', $this->options['failure_path']));
|
||||
@ -140,7 +212,7 @@ abstract class FormAuthenticationListener
|
||||
}
|
||||
}
|
||||
|
||||
protected function onSuccess(Request $request, TokenInterface $token)
|
||||
protected function onSuccess(EventInterface $event, Request $request, TokenInterface $token)
|
||||
{
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('User has been authenticated successfully');
|
||||
@ -152,15 +224,29 @@ abstract class FormAuthenticationListener
|
||||
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
|
||||
$session->remove(SecurityContext::LAST_USERNAME);
|
||||
|
||||
$this->eventDispatcher->notify(new Event($this, 'security.login_success', array('request' => $request, 'token' => $token)));
|
||||
|
||||
if (null !== $this->successHandler) {
|
||||
return $this->successHandler->onAuthenticationSuccess($request, $token);
|
||||
}
|
||||
|
||||
$response = new Response();
|
||||
$path = $this->determineTargetUrl($request);
|
||||
$response->setRedirect(0 !== strpos($path, 'http') ? $request->getUriForPath($path) : $path, 302);
|
||||
|
||||
if (null !== $this->rememberMeServices) {
|
||||
$this->rememberMeServices->loginSuccess($request, $response, $token);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the target URL according to the defined options.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function determineTargetUrl(Request $request)
|
||||
{
|
||||
@ -180,7 +266,6 @@ abstract class FormAuthenticationListener
|
||||
}
|
||||
|
||||
if ($this->options['use_referer'] && $targetUrl = $request->headers->get('Referer')) {
|
||||
|
||||
return $targetUrl;
|
||||
}
|
||||
|
@ -29,21 +29,27 @@ class BasicAuthenticationListener implements ListenerInterface
|
||||
{
|
||||
protected $securityContext;
|
||||
protected $authenticationManager;
|
||||
protected $providerKey;
|
||||
protected $authenticationEntryPoint;
|
||||
protected $logger;
|
||||
protected $ignoreFailure;
|
||||
|
||||
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null)
|
||||
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null)
|
||||
{
|
||||
if (empty($providerKey)) {
|
||||
throw new \InvalidArgumentException('$providerKey must not be empty.');
|
||||
}
|
||||
|
||||
$this->securityContext = $securityContext;
|
||||
$this->authenticationManager = $authenticationManager;
|
||||
$this->providerKey = $providerKey;
|
||||
$this->authenticationEntryPoint = $authenticationEntryPoint;
|
||||
$this->logger = $logger;
|
||||
$this->ignoreFailure = false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
|
||||
* @param integer $priority The priority
|
||||
@ -52,7 +58,7 @@ class BasicAuthenticationListener implements ListenerInterface
|
||||
{
|
||||
$dispatcher->connect('core.security', array($this, 'handle'), 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@ -88,7 +94,7 @@ class BasicAuthenticationListener implements ListenerInterface
|
||||
}
|
||||
|
||||
try {
|
||||
$token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->server->get('PHP_AUTH_PW')));
|
||||
$token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->server->get('PHP_AUTH_PW'), $this->providerKey));
|
||||
$this->securityContext->setToken($token);
|
||||
} catch (AuthenticationException $failed) {
|
||||
$this->securityContext->setToken(null);
|
||||
|
@ -32,13 +32,19 @@ use Symfony\Component\Security\User\AccountInterface;
|
||||
class ContextListener implements ListenerInterface
|
||||
{
|
||||
protected $context;
|
||||
protected $contextKey;
|
||||
protected $logger;
|
||||
protected $userProviders;
|
||||
|
||||
public function __construct(SecurityContext $context, array $userProviders, LoggerInterface $logger = null)
|
||||
public function __construct(SecurityContext $context, array $userProviders, $contextKey, LoggerInterface $logger = null)
|
||||
{
|
||||
if (empty($contextKey)) {
|
||||
throw new \InvalidArgumentException('$contextKey must not be empty.');
|
||||
}
|
||||
|
||||
$this->context = $context;
|
||||
$this->userProviders = $userProviders;
|
||||
$this->contextKey = $contextKey;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
@ -74,7 +80,7 @@ class ContextListener implements ListenerInterface
|
||||
|
||||
$session = $request->hasSession() ? $request->getSession() : null;
|
||||
|
||||
if (null === $session || null === $token = $session->get('_security')) {
|
||||
if (null === $session || null === $token = $session->get('_security_'.$this->contextKey)) {
|
||||
$this->context->setToken(null);
|
||||
} else {
|
||||
if (null !== $this->logger) {
|
||||
@ -114,7 +120,7 @@ class ContextListener implements ListenerInterface
|
||||
$this->logger->debug('Write SecurityContext in the session');
|
||||
}
|
||||
|
||||
$event->get('request')->getSession()->set('_security', serialize($token));
|
||||
$event->get('request')->getSession()->set('_security_'.$this->contextKey, serialize($token));
|
||||
|
||||
return $response;
|
||||
}
|
||||
@ -148,10 +154,18 @@ class ContextListener implements ListenerInterface
|
||||
$token->setAuthenticated(false);
|
||||
}
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(sprintf('Username "%s" was reloaded from user provider.', $user));
|
||||
}
|
||||
|
||||
return $token;
|
||||
} catch (UnsupportedAccountException $unsupported) {
|
||||
// let's try the next user provider
|
||||
} catch (UsernameNotFoundException $notFound) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(sprintf('Username "%s" could not be found.', $user));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -34,19 +34,25 @@ class DigestAuthenticationListener implements ListenerInterface
|
||||
{
|
||||
protected $securityContext;
|
||||
protected $provider;
|
||||
protected $providerKey;
|
||||
protected $authenticationEntryPoint;
|
||||
protected $logger;
|
||||
|
||||
public function __construct(SecurityContext $securityContext, UserProviderInterface $provider, DigestAuthenticationEntryPoint $authenticationEntryPoint, LoggerInterface $logger = null)
|
||||
public function __construct(SecurityContext $securityContext, UserProviderInterface $provider, $providerKey, DigestAuthenticationEntryPoint $authenticationEntryPoint, LoggerInterface $logger = null)
|
||||
{
|
||||
if (empty($providerKey)) {
|
||||
throw new \InvalidArgumentException('$providerKey must not be empty.');
|
||||
}
|
||||
|
||||
$this->securityContext = $securityContext;
|
||||
$this->provider = $provider;
|
||||
$this->providerKey = $providerKey;
|
||||
$this->authenticationEntryPoint = $authenticationEntryPoint;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
|
||||
* @param integer $priority The priority
|
||||
@ -55,7 +61,7 @@ class DigestAuthenticationListener implements ListenerInterface
|
||||
{
|
||||
$dispatcher->connect('core.security', array($this, 'handle'), 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@ -134,7 +140,7 @@ class DigestAuthenticationListener implements ListenerInterface
|
||||
$this->logger->debug(sprintf('Authentication success for user "%s" with response "%s"', $digestAuth->getUsername(), $digestAuth->getResponse()));
|
||||
}
|
||||
|
||||
$this->securityContext->setToken(new UsernamePasswordToken($user, $user->getPassword()));
|
||||
$this->securityContext->setToken(new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey));
|
||||
}
|
||||
|
||||
protected function fail(Request $request, AuthenticationException $failed)
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\Firewall;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Security\AccessDeniedHandler;
|
||||
use Symfony\Component\HttpKernel\Security\ExceptionTranslation\AccessDeniedHandlerInterface;
|
||||
use Symfony\Component\Security\SecurityContext;
|
||||
use Symfony\Component\Security\Authentication\AuthenticationTrustResolverInterface;
|
||||
use Symfony\Component\Security\Authentication\EntryPoint\AuthenticationEntryPointInterface;
|
||||
@ -33,14 +36,16 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
class ExceptionListener implements ListenerInterface
|
||||
{
|
||||
protected $context;
|
||||
protected $accessDeniedHandler;
|
||||
protected $authenticationEntryPoint;
|
||||
protected $authenticationTrustResolver;
|
||||
protected $errorPage;
|
||||
protected $logger;
|
||||
|
||||
public function __construct(SecurityContext $context, AuthenticationTrustResolverInterface $trustResolver, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, LoggerInterface $logger = null)
|
||||
public function __construct(SecurityContext $context, AuthenticationTrustResolverInterface $trustResolver, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null)
|
||||
{
|
||||
$this->context = $context;
|
||||
$this->accessDeniedHandler = $accessDeniedHandler;
|
||||
$this->authenticationEntryPoint = $authenticationEntryPoint;
|
||||
$this->authenticationTrustResolver = $trustResolver;
|
||||
$this->errorPage = $errorPage;
|
||||
@ -57,7 +62,7 @@ class ExceptionListener implements ListenerInterface
|
||||
{
|
||||
$dispatcher->connect('core.exception', array($this, 'handleException'), 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@ -107,15 +112,26 @@ class ExceptionListener implements ListenerInterface
|
||||
$this->logger->info('Access is denied (and user is neither anonymous, nor remember-me)');
|
||||
}
|
||||
|
||||
if (null === $this->errorPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subRequest = Request::create($this->errorPage);
|
||||
$subRequest->attributes->set(SecurityContext::ACCESS_DENIED_ERROR, $exception->getMessage());
|
||||
|
||||
try {
|
||||
$response = $event->getSubject()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true);
|
||||
if (null !== $this->accessDeniedHandler) {
|
||||
$response = $this->accessDeniedHandler->handle($event, $request, $exception);
|
||||
|
||||
if (!$response instanceof Response) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (null === $this->errorPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subRequest = Request::create($this->errorPage);
|
||||
$subRequest->attributes->set(SecurityContext::ACCESS_DENIED_ERROR, $exception->getMessage());
|
||||
|
||||
$response = $event->getSubject()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true);
|
||||
$response->setStatusCode(403);
|
||||
|
||||
return $response;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->err(sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $e->getMessage()));
|
||||
@ -125,7 +141,6 @@ class ExceptionListener implements ListenerInterface
|
||||
|
||||
return;
|
||||
}
|
||||
$response->setStatusCode(403);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
|
@ -89,6 +89,7 @@ class LogoutListener implements ListenerInterface
|
||||
$response = new Response();
|
||||
$response->setRedirect(0 !== strpos($this->targetUrl, 'http') ? $request->getUriForPath($this->targetUrl) : $this->targetUrl, 302);
|
||||
|
||||
// handle multiple logout attempts gracefully
|
||||
if ($token = $this->securityContext->getToken()) {
|
||||
foreach ($this->handlers as $handler) {
|
||||
$handler->logout($request, $response, $token);
|
||||
|
@ -31,17 +31,19 @@ abstract class PreAuthenticatedListener implements ListenerInterface
|
||||
{
|
||||
protected $securityContext;
|
||||
protected $authenticationManager;
|
||||
protected $providerKey;
|
||||
protected $logger;
|
||||
|
||||
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger = null)
|
||||
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, LoggerInterface $logger = null)
|
||||
{
|
||||
$this->securityContext = $securityContext;
|
||||
$this->authenticationManager = $authenticationManager;
|
||||
$this->providerKey = $providerKey;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
|
||||
* @param integer $priority The priority
|
||||
@ -50,14 +52,14 @@ abstract class PreAuthenticatedListener implements ListenerInterface
|
||||
{
|
||||
$dispatcher->connect('core.security', array($this, 'handle'), 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function unregister(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles X509 authentication.
|
||||
*
|
||||
@ -88,7 +90,7 @@ abstract class PreAuthenticatedListener implements ListenerInterface
|
||||
}
|
||||
|
||||
try {
|
||||
$token = $this->authenticationManager->authenticate(new PreAuthenticatedToken($user, $credentials));
|
||||
$token = $this->authenticationManager->authenticate(new PreAuthenticatedToken($user, $credentials, $this->providerKey));
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(sprintf('Authentication success: %s', $token));
|
||||
|
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\Firewall;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Exception\CookieTheftException;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
use Symfony\Component\Security\Authentication\AuthenticationManagerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\SecurityContext;
|
||||
use Symfony\Component\HttpKernel\Security\RememberMe\RememberMeServicesInterface;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* RememberMeListener implements authentication capabilities via a cookie
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class RememberMeListener implements ListenerInterface
|
||||
{
|
||||
protected $securityContext;
|
||||
protected $rememberMeServices;
|
||||
protected $authenticationManager;
|
||||
protected $logger;
|
||||
protected $lastState;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param SecurityContext $securityContext
|
||||
* @param RememberMeServicesInterface $rememberMeServices
|
||||
* @param AuthenticationManagerInterface $authenticationManager
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function __construct(SecurityContext $securityContext, RememberMeServicesInterface $rememberMeServices, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger = null)
|
||||
{
|
||||
$this->securityContext = $securityContext;
|
||||
$this->rememberMeServices = $rememberMeServices;
|
||||
$this->authenticationManager = $authenticationManager;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to core.security, and core.response event
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
|
||||
* @param integer $priority The priority
|
||||
*/
|
||||
public function register(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$dispatcher->connect('core.security', array($this, 'checkCookies'), 0);
|
||||
$dispatcher->connect('core.response', array($this, 'updateCookies'), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function unregister(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$dispatcher->disconnect('core.response', array($this, 'updateCookies'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles remember-me cookie based authentication.
|
||||
*
|
||||
* @param Event $event An Event instance
|
||||
*/
|
||||
public function checkCookies(EventInterface $event)
|
||||
{
|
||||
$this->lastState = null;
|
||||
|
||||
if (null !== $this->securityContext->getToken()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (null === $token = $this->rememberMeServices->autoLogin($event->get('request'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (null === $token = $this->authenticationManager->authenticate($token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->securityContext->setToken($token);
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('SecurityContext populated with remember-me token.');
|
||||
}
|
||||
|
||||
$this->lastState = $token;
|
||||
} catch (AuthenticationException $failed) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(
|
||||
'SecurityContext not populated with remember-me token as the'
|
||||
.' AuthenticationManager rejected the AuthenticationToken returned'
|
||||
.' by the RememberMeServices: '.$failed->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
$this->lastState = $failed;
|
||||
}
|
||||
} catch (AuthenticationException $cookieInvalid) {
|
||||
$this->lastState = $cookieInvalid;
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('The presented cookie was invalid: '.$cookieInvalid->getMessage());
|
||||
}
|
||||
|
||||
// silently ignore everything except a cookie theft exception
|
||||
if ($cookieInvalid instanceof CookieTheftException) {
|
||||
throw $cookieInvalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cookies
|
||||
* @param Event $event
|
||||
*/
|
||||
public function updateCookies(EventInterface $event, Response $response)
|
||||
{
|
||||
if (HttpKernelInterface::MASTER_REQUEST !== $event->get('request_type')) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($this->lastState instanceof TokenInterface) {
|
||||
$this->rememberMeServices->loginSuccess($event->get('request'), $response, $this->lastState);
|
||||
} else if ($this->lastState instanceof AuthenticationException) {
|
||||
$this->rememberMeServices->loginFail($event->get('request'), $response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ class SwitchUserListener implements ListenerInterface
|
||||
protected $securityContext;
|
||||
protected $provider;
|
||||
protected $accountChecker;
|
||||
protected $providerKey;
|
||||
protected $accessDecisionManager;
|
||||
protected $usernameParameter;
|
||||
protected $role;
|
||||
@ -45,11 +46,16 @@ class SwitchUserListener implements ListenerInterface
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(SecurityContext $securityContext, UserProviderInterface $provider, AccountCheckerInterface $accountChecker, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, $usernameParameter = '_switch_user', $role = 'ROLE_ALLOWED_TO_SWITCH')
|
||||
public function __construct(SecurityContext $securityContext, UserProviderInterface $provider, AccountCheckerInterface $accountChecker, $providerKey, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, $usernameParameter = '_switch_user', $role = 'ROLE_ALLOWED_TO_SWITCH')
|
||||
{
|
||||
if (empty($providerKey)) {
|
||||
throw new \InvalidArgumentException('$providerKey must not be empty.');
|
||||
}
|
||||
|
||||
$this->securityContext = $securityContext;
|
||||
$this->provider = $provider;
|
||||
$this->accountChecker = $accountChecker;
|
||||
$this->providerKey = $providerKey;
|
||||
$this->accessDecisionManager = $accessDecisionManager;
|
||||
$this->usernameParameter = $usernameParameter;
|
||||
$this->role = $role;
|
||||
@ -57,7 +63,7 @@ class SwitchUserListener implements ListenerInterface
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
|
||||
* @param integer $priority The priority
|
||||
@ -66,14 +72,14 @@ class SwitchUserListener implements ListenerInterface
|
||||
{
|
||||
$dispatcher->connect('core.security', array($this, 'handle'), 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function unregister(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles digest authentication.
|
||||
*
|
||||
@ -136,7 +142,7 @@ class SwitchUserListener implements ListenerInterface
|
||||
$roles = $user->getRoles();
|
||||
$roles[] = new SwitchUserRole('ROLE_PREVIOUS_ADMIN', $this->securityContext->getToken());
|
||||
|
||||
$token = new UsernamePasswordToken($user, $user->getPassword(), $roles);
|
||||
$token = new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey, $roles);
|
||||
$token->setImmutable(true);
|
||||
|
||||
return $token;
|
||||
|
@ -11,6 +11,12 @@
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\Firewall;
|
||||
|
||||
use Symfony\Component\HttpKernel\Security\Authentication\AuthenticationFailureHandlerInterface;
|
||||
|
||||
use Symfony\Component\HttpKernel\Security\Authentication\AuthenticationSuccessHandlerInterface;
|
||||
|
||||
use Symfony\Component\HttpKernel\Security\Session\SessionAuthenticationStrategyInterface;
|
||||
|
||||
use Symfony\Component\Security\SecurityContext;
|
||||
use Symfony\Component\Security\Authentication\AuthenticationManagerInterface;
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
@ -23,18 +29,18 @@ use Symfony\Component\Security\Authentication\Token\UsernamePasswordToken;
|
||||
*
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class UsernamePasswordFormAuthenticationListener extends FormAuthenticationListener
|
||||
class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationListener
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, array $options = array(), LoggerInterface $logger = null)
|
||||
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null)
|
||||
{
|
||||
parent::__construct($securityContext, $authenticationManager, array_merge(array(
|
||||
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $providerKey, array_merge(array(
|
||||
'username_parameter' => '_username',
|
||||
'password_parameter' => '_password',
|
||||
'post_only' => true,
|
||||
), $options), $logger);
|
||||
), $options), $successHandler, $failureHandler, $logger);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,6 +61,6 @@ class UsernamePasswordFormAuthenticationListener extends FormAuthenticationListe
|
||||
|
||||
$request->getSession()->set(SecurityContext::LAST_USERNAME, $username);
|
||||
|
||||
return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password));
|
||||
return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
|
||||
}
|
||||
}
|
||||
|
@ -27,9 +27,9 @@ class X509AuthenticationListener extends PreAuthenticatedListener
|
||||
protected $userKey;
|
||||
protected $credentialKey;
|
||||
|
||||
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, $userKey = 'SSL_CLIENT_S_DN_Email', $credentialKey = 'SSL_CLIENT_S_DN', LoggerInterface $logger = null)
|
||||
public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, $userKey = 'SSL_CLIENT_S_DN_Email', $credentialKey = 'SSL_CLIENT_S_DN', LoggerInterface $logger = null)
|
||||
{
|
||||
parent::__construct($securityContext, $authenticationManager, $logger);
|
||||
parent::__construct($securityContext, $authenticationManager, $providerKey, $logger);
|
||||
|
||||
$this->userKey = $userKey;
|
||||
$this->credentialKey = $credentialKey;
|
||||
|
@ -56,4 +56,4 @@ class CookieClearingLogoutHandler implements LogoutHandlerInterface
|
||||
$response->headers->clearCookie($cookieName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Interface that needs to be implemented by LogoutHandlers.
|
||||
*
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
interface LogoutHandlerInterface
|
||||
@ -26,7 +26,7 @@ interface LogoutHandlerInterface
|
||||
* This method is called by the LogoutListener when a user has requested
|
||||
* to be logged out. Usually, you would unset session variables, or remove
|
||||
* cookies, etc.
|
||||
*
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param TokenInterface $token
|
||||
|
@ -17,14 +17,14 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Handler for clearing invalidating the current session.
|
||||
*
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class SessionLogoutHandler implements LogoutHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Invalidate the current session
|
||||
*
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param TokenInterface $token
|
||||
@ -33,5 +33,5 @@ class SessionLogoutHandler implements LogoutHandlerInterface
|
||||
public function logout(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
$request->getSession()->invalidate();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\Authentication\RememberMe\TokenProviderInterface;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Security\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Exception\CookieTheftException;
|
||||
use Symfony\Component\Security\Authentication\RememberMe\PersistentToken;
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Authentication\Token\RememberMeToken;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Concrete implementation of the RememberMeServicesInterface which needs
|
||||
* an implementation of TokenProviderInterface for providing remember-me
|
||||
* capabilities.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class PersistentTokenBasedRememberMeServices extends RememberMeServices
|
||||
{
|
||||
protected $tokenProvider;
|
||||
|
||||
/**
|
||||
* Sets the token provider
|
||||
*
|
||||
* @param TokenProviderInterface $tokenProvider
|
||||
* @return void
|
||||
*/
|
||||
public function setTokenProvider(TokenProviderInterface $tokenProvider)
|
||||
{
|
||||
$this->tokenProvider = $tokenProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function processAutoLoginCookie(array $cookieParts, Request $request)
|
||||
{
|
||||
if (count($cookieParts) !== 2) {
|
||||
throw new AuthenticationException('The cookie is invalid.');
|
||||
}
|
||||
|
||||
list($series, $tokenValue) = $cookieParts;
|
||||
$persistentToken = $this->tokenProvider->loadTokenBySeries($series);
|
||||
|
||||
if ($persistentToken->getTokenValue() !== $tokenValue) {
|
||||
$this->tokenProvider->deleteTokenBySeries($series);
|
||||
|
||||
throw new CookieTheftException('This token was already used. The account is possibly compromised.');
|
||||
}
|
||||
|
||||
if ($persistentToken->getLastUsed()->getTimestamp() + $this->options['lifetime'] < time()) {
|
||||
throw new AuthenticationException('The cookie has expired.');
|
||||
}
|
||||
|
||||
$user = $this->getUserProvider($persistentToken->getClass())->loadUserByUsername($persistentToken->getUsername());
|
||||
$authenticationToken = new RememberMeToken($user, $this->providerKey, $this->key);
|
||||
$authenticationToken->setPersistentToken($persistentToken);
|
||||
|
||||
return $authenticationToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
if ($token instanceof RememberMeToken) {
|
||||
if (null === $persistentToken = $token->getPersistentToken()) {
|
||||
throw new \RuntimeException('RememberMeToken must contain a PersistentTokenInterface implementation when used as login.');
|
||||
}
|
||||
|
||||
$series = $persistentToken->getSeries();
|
||||
$tokenValue = $this->generateRandomValue();
|
||||
|
||||
$this->tokenProvider->updateToken($series, $tokenValue, new \DateTime());
|
||||
} else {
|
||||
$series = $this->generateRandomValue();
|
||||
$tokenValue = $this->generateRandomValue();
|
||||
|
||||
$this->tokenProvider->createNewToken(
|
||||
new PersistentToken(
|
||||
get_class($user = $token->getUser()),
|
||||
$user->getUsername(),
|
||||
$series,
|
||||
$tokenValue,
|
||||
new \DateTime()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$response->headers->setCookie(
|
||||
new Cookie(
|
||||
$this->options['name'],
|
||||
$this->generateCookieValue($series, $tokenValue),
|
||||
time() + $this->options['lifetime'],
|
||||
$this->options['path'],
|
||||
$this->options['domain'],
|
||||
$this->options['secure'],
|
||||
$this->options['httponly']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function logout(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
parent::logout($request, $response, $token);
|
||||
|
||||
if (null !== ($cookie = $request->cookies->get($this->options['name']))
|
||||
&& count($parts = $this->decodeCookie($cookie)) === 2
|
||||
) {
|
||||
list($series, $tokenValue) = $parts;
|
||||
$this->tokenProvider->deleteTokenBySeries($series);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the value for the cookie
|
||||
*
|
||||
* @param string $series
|
||||
* @param string $tokenValue
|
||||
* @return string
|
||||
*/
|
||||
protected function generateCookieValue($series, $tokenValue)
|
||||
{
|
||||
return $this->encodeCookie(array($series, $tokenValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a cryptographically strong random value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateRandomValue()
|
||||
{
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
$bytes = openssl_random_pseudo_bytes(32, $strong);
|
||||
|
||||
if (true === $strong && false !== $bytes) {
|
||||
return base64_encode($bytes);
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->warn('Could not produce a cryptographically strong random value. Please install/update the OpenSSL extension.');
|
||||
}
|
||||
|
||||
return base64_encode(hash('sha256', uniqid(mt_rand(), true), true));
|
||||
}
|
||||
}
|
@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\User\AccountInterface;
|
||||
use Symfony\Component\Security\Authentication\Token\RememberMeToken;
|
||||
use Symfony\Component\HttpKernel\Security\Logout\LogoutHandlerInterface;
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Security\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\Authentication\AuthenticationManagerInterface;
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class implementing the RememberMeServicesInterface
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
abstract class RememberMeServices implements RememberMeServicesInterface, LogoutHandlerInterface
|
||||
{
|
||||
const COOKIE_DELIMITER = ':';
|
||||
|
||||
protected $userProviders;
|
||||
protected $options;
|
||||
protected $logger;
|
||||
protected $key;
|
||||
protected $providerKey;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $userProviders
|
||||
* @param array $options
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function __construct(array $userProviders, $key, $providerKey, array $options = array(), LoggerInterface $logger = null)
|
||||
{
|
||||
if (empty($key)) {
|
||||
throw new \InvalidArgumentException('$key must not be empty.');
|
||||
}
|
||||
if (empty($providerKey)) {
|
||||
throw new \InvalidArgumentException('$providerKey must not be empty.');
|
||||
}
|
||||
if (0 === count($userProviders)) {
|
||||
throw new \InvalidArgumentException('You must provide at least one user provider.');
|
||||
}
|
||||
|
||||
$this->userProviders = $userProviders;
|
||||
$this->key = $key;
|
||||
$this->providerKey = $providerKey;
|
||||
$this->options = $options;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of RememberMeServicesInterface. Detects whether a remember-me
|
||||
* cookie was set, decodes it, and hands it to subclasses for further processing.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return TokenInterface
|
||||
*/
|
||||
public function autoLogin(Request $request)
|
||||
{
|
||||
if (null === $cookie = $request->cookies->get($this->options['name'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me cookie detected.');
|
||||
}
|
||||
|
||||
$cookieParts = $this->decodeCookie($cookie);
|
||||
$token = $this->processAutoLoginCookie($cookieParts, $request);
|
||||
|
||||
if (!$token instanceof TokenInterface) {
|
||||
throw new \RuntimeException('processAutoLoginCookie() must return a TokenInterface implementation.');
|
||||
}
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me cookie accepted.');
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation for LogoutHandlerInterface. Deletes the cookie.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param TokenInterface $token
|
||||
* @return void
|
||||
*/
|
||||
public function logout(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
$this->cancelCookie($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation for RememberMeServicesInterface. Deletes the cookie when
|
||||
* an attempted authentication fails.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @return void
|
||||
*/
|
||||
public function loginFail(Request $request, Response $response)
|
||||
{
|
||||
$this->cancelCookie($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation for RememberMeServicesInterface. This is called when an
|
||||
* authentication is successful.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param TokenInterface $token The token that resulted in a successful authentication
|
||||
* @return void
|
||||
*/
|
||||
public function loginSuccess(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
if (!$token instanceof RememberMeToken) {
|
||||
if (!$token->getUser() instanceof AccountInterface) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me ignores token since it does not contain an AccountInterface implementation.');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->isRememberMeRequested($request)) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me was not requested.');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me was requested; setting cookie.');
|
||||
}
|
||||
} else if (null !== $this->logger) {
|
||||
$this->logger->debug('Re-newing remember-me token; setting cookie.');
|
||||
}
|
||||
|
||||
$this->onLoginSuccess($request, $response, $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should validate the cookie and do any additional processing
|
||||
* that is required. This is called from autoLogin().
|
||||
*
|
||||
* @param array $cookieParts
|
||||
* @param Request $request
|
||||
* @return TokenInterface
|
||||
*/
|
||||
abstract protected function processAutoLoginCookie(array $cookieParts, Request $request);
|
||||
|
||||
/**
|
||||
* This is called after a user has been logged in successfully, and has
|
||||
* requested remember-me capabilities. The implementation usually sets a
|
||||
* cookie and possibly stores a persistent record of it.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param TokenInterface $token
|
||||
* @return void
|
||||
*/
|
||||
abstract protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token);
|
||||
|
||||
protected function getUserProvider($class)
|
||||
{
|
||||
foreach ($this->userProviders as $provider) {
|
||||
if ($provider->supportsClass($class)) {
|
||||
return $provider;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException(sprintf('There is no user provider that supports class "%s".', $class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the raw cookie value
|
||||
*
|
||||
* @param string $rawCookie
|
||||
* @return array
|
||||
*/
|
||||
protected function decodeCookie($rawCookie)
|
||||
{
|
||||
return explode(self::COOKIE_DELIMITER, base64_decode($rawCookie));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the cookie parts
|
||||
*
|
||||
* @param array $cookieParts
|
||||
* @return string
|
||||
*/
|
||||
protected function encodeCookie(array $cookieParts)
|
||||
{
|
||||
return base64_encode(implode(self::COOKIE_DELIMITER, $cookieParts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the remember-me cookie
|
||||
*
|
||||
* @param Response $response
|
||||
* @return void
|
||||
*/
|
||||
protected function cancelCookie(Response $response)
|
||||
{
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(sprintf('Clearing remember-me cookie "%s"', $this->options['name']));
|
||||
}
|
||||
|
||||
$response->headers->clearCookie($this->options['name'], $this->options['path'], $this->options['domain']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether remember-me capabilities where requested
|
||||
*
|
||||
* @param Request $request
|
||||
* @return Boolean
|
||||
*/
|
||||
protected function isRememberMeRequested(Request $request)
|
||||
{
|
||||
if (true === $this->options['always_remember_me']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$parameter = $request->request->get($this->options['remember_me_parameter']);
|
||||
|
||||
if ($parameter === null && null !== $this->logger) {
|
||||
$this->logger->debug(sprintf('Did not send remember-me cookie (remember-me parameter "%s" was not sent).', $this->options['remember_me_parameter']));
|
||||
}
|
||||
|
||||
return $parameter === 'true' || $parameter === 'on' || $parameter === '1' || $parameter === 'yes';
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace Symfony\Component\HttpKernel\Security\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface that needs to be implemented by classes which provide remember-me
|
||||
* capabilities.
|
||||
*
|
||||
* We provide two implementations out-of-the-box:
|
||||
* - TokenBasedRememberMeServices (does not require a TokenProvider)
|
||||
* - PersistentTokenBasedRememberMeServices (requires a TokenProvider)
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
interface RememberMeServicesInterface
|
||||
{
|
||||
/**
|
||||
* This method will be called whenever the SecurityContext does not contain
|
||||
* an TokenInterface object and the framework wishes to provide an implementation
|
||||
* with an opportunity to authenticate the request using remember-me capabilities.
|
||||
*
|
||||
* No attempt whatsoever is made to determine whether the browser has requested
|
||||
* remember-me services or presented a valid cookie. Any and all such determinations
|
||||
* are left to the implementation of this method.
|
||||
*
|
||||
* If a browser has presented an unauthorised cookie for whatever reason,
|
||||
* make sure to throw an AuthenticationException as this will consequentially
|
||||
* result in a call to loginFail() and therefore an invalidation of the cookie.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return TokenInterface
|
||||
*/
|
||||
function autoLogin(Request $request);
|
||||
|
||||
/**
|
||||
* Called whenever an authentication attempt was made, but the credentials
|
||||
* supplied by the user were missing or otherwise invalid.
|
||||
*
|
||||
* This method needs to take care of invalidating the cookie.
|
||||
*/
|
||||
function loginFail(Request $request, Response $response);
|
||||
|
||||
/**
|
||||
* Called whenever authentication attempt is successful (e.g. a form login).
|
||||
*
|
||||
* An implementation may always set a remember-me cookie in the Response,
|
||||
* although this is not recommended.
|
||||
*
|
||||
* Instead, implementations should typically look for a request parameter
|
||||
* (such as a HTTP POST parameter) that indicates the browser has explicitly
|
||||
* requested for the authentication to be remembered.
|
||||
*/
|
||||
function loginSuccess(Request $request, Response $response, TokenInterface $token);
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\RememberMe;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Authentication\Token\RememberMeToken;
|
||||
use Symfony\Component\Security\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\User\AccountInterface;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Concrete implementation of the RememberMeServicesInterface providing
|
||||
* remember-me capabilities without requiring a TokenProvider.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class TokenBasedRememberMeServices extends RememberMeServices
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function processAutoLoginCookie(array $cookieParts, Request $request)
|
||||
{
|
||||
if (count($cookieParts) !== 4) {
|
||||
throw new AuthenticationException('The cookie is invalid.');
|
||||
}
|
||||
|
||||
list($class, $username, $expires, $hash) = $cookieParts;
|
||||
if (false === $username = base64_decode($username, true)) {
|
||||
throw new AuthenticationException('$username contains a character from outside the base64 alphabet.');
|
||||
}
|
||||
try {
|
||||
$user = $this->getUserProvider($class)->loadUserByUsername($username);
|
||||
} catch (\Exception $ex) {
|
||||
if (!$ex instanceof AuthenticationException) {
|
||||
$ex = new AuthenticationException($ex->getMessage(), null, $ex->getCode(), $ex);
|
||||
}
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
if (!$user instanceof AccountInterface) {
|
||||
throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of AccountInterface, but returned "%s".', get_class($user)));
|
||||
}
|
||||
|
||||
if (true !== $this->compareHashes($hash, $this->generateCookieHash($class, $username, $expires, $user->getPassword()))) {
|
||||
throw new AuthenticationException('The cookie\'s hash is invalid.');
|
||||
}
|
||||
|
||||
if ($expires < time()) {
|
||||
throw new AuthenticationException('The cookie has expired.');
|
||||
}
|
||||
|
||||
return new RememberMeToken($user, $this->providerKey, $this->key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two hashes using a constant-time algorithm to avoid (remote)
|
||||
* timing attacks.
|
||||
*
|
||||
* This is the same implementation as used in the BasePasswordEncoder.
|
||||
*
|
||||
* @param string $hash1 The first hash
|
||||
* @param string $hash2 The second hash
|
||||
*
|
||||
* @return Boolean true if the two hashes are the same, false otherwise
|
||||
*/
|
||||
protected function compareHashes($hash1, $hash2)
|
||||
{
|
||||
if (strlen($hash1) !== $c = strlen($hash2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = 0;
|
||||
for ($i = 0; $i < $c; $i++) {
|
||||
$result |= ord($hash1[$i]) ^ ord($hash2[$i]);
|
||||
}
|
||||
|
||||
return 0 === $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
if ($token instanceof RememberMeToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $token->getUser();
|
||||
$expires = time() + $this->options['lifetime'];
|
||||
$value = $this->generateCookieValue(get_class($user), $user->getUsername(), $expires, $user->getPassword());
|
||||
|
||||
$response->headers->setCookie(
|
||||
new Cookie(
|
||||
$this->options['name'],
|
||||
$value,
|
||||
$expires,
|
||||
$this->options['path'],
|
||||
$this->options['domain'],
|
||||
$this->options['secure'],
|
||||
$this->options['httponly']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the cookie value
|
||||
*
|
||||
* @param strign $class
|
||||
* @param string $username The username
|
||||
* @param integer $expires The unixtime when the cookie expires
|
||||
* @param string $password The encoded password
|
||||
* @throws \RuntimeException if username contains invalid chars
|
||||
* @return string
|
||||
*/
|
||||
protected function generateCookieValue($class, $username, $expires, $password)
|
||||
{
|
||||
return $this->encodeCookie(array(
|
||||
$class,
|
||||
base64_encode($username),
|
||||
$expires,
|
||||
$this->generateCookieHash($class, $username, $expires, $password)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a hash for the cookie to ensure it is not being tempered with
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $username The username
|
||||
* @param integer $expires The unixtime when the cookie expires
|
||||
* @param string $password The encoded password
|
||||
* @throws \RuntimeException when the private key is empty
|
||||
* @return string
|
||||
*/
|
||||
protected function generateCookieHash($class, $username, $expires, $password)
|
||||
{
|
||||
return hash('sha256', $class.$username.$expires.$password.$this->key);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\Session;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class SessionAuthenticationStrategy implements SessionAuthenticationStrategyInterface
|
||||
{
|
||||
const NONE = 'none';
|
||||
const MIGRATE = 'migrate';
|
||||
const INVALIDATE = 'invalidate';
|
||||
|
||||
protected $strategy;
|
||||
|
||||
public function __construct($strategy)
|
||||
{
|
||||
$this->strategy = $strategy;
|
||||
}
|
||||
|
||||
public function onAuthentication(Request $request, TokenInterface $token)
|
||||
{
|
||||
switch ($this->strategy) {
|
||||
case self::NONE:
|
||||
return;
|
||||
|
||||
case self::MIGRATE:
|
||||
$request->getSession()->migrate();
|
||||
break;
|
||||
|
||||
case self::INVALIDATE:
|
||||
$request->getSession()->invalidate();
|
||||
|
||||
default:
|
||||
throw new \RuntimeException(sprintf('Invalid session authentication strategy "%s"', $this->strategy));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Security\Session;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
interface SessionAuthenticationStrategyInterface
|
||||
{
|
||||
function onAuthentication(Request $request, TokenInterface $token);
|
||||
}
|
@ -11,6 +11,8 @@
|
||||
|
||||
namespace Symfony\Component\Security\Acl\Domain;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\AnonymousToken;
|
||||
|
||||
use Symfony\Component\Security\User\AccountInterface;
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface;
|
||||
@ -49,9 +51,12 @@ class SecurityIdentityRetrievalStrategy implements SecurityIdentityRetrievalStra
|
||||
$sids = array();
|
||||
|
||||
// add user security identity
|
||||
$user = $token->getUser();
|
||||
if ($user instanceof AccountInterface) {
|
||||
$sids[] = UserSecurityIdentity::fromAccount($user);
|
||||
if (!$token instanceof AnonymousToken) {
|
||||
try {
|
||||
$sids[] = UserSecurityIdentity::fromToken($token);
|
||||
} catch (\InvalidArgumentException $invalid) {
|
||||
// ignore, user has no user security identity
|
||||
}
|
||||
}
|
||||
|
||||
// add all reachable roles
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Security\Acl\Domain;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\User\AccountInterface;
|
||||
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
|
||||
|
||||
@ -51,7 +52,24 @@ class UserSecurityIdentity implements SecurityIdentityInterface
|
||||
*/
|
||||
public static function fromAccount(AccountInterface $user)
|
||||
{
|
||||
return new self((string) $user, get_class($user));
|
||||
return new self($user->getUsername(), get_class($user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a user security identity from a TokenInterface
|
||||
*
|
||||
* @param TokenInterface $token
|
||||
* @return UserSecurityIdentity
|
||||
*/
|
||||
public static function fromToken(TokenInterface $token)
|
||||
{
|
||||
$user = $token->getUser();
|
||||
|
||||
if ($user instanceof AccountInterface) {
|
||||
return self::fromAccount($user);
|
||||
}
|
||||
|
||||
return new self((string) $user, is_object($user)? get_class($user) : get_class($token));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Security\Authentication\Provider;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Encoder\EncoderFactoryInterface;
|
||||
use Symfony\Component\Security\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\User\AccountCheckerInterface;
|
||||
@ -38,9 +39,9 @@ class DaoAuthenticationProvider extends UserAuthenticationProvider
|
||||
* @param AccountCheckerInterface $accountChecker An AccountCheckerInterface instance
|
||||
* @param EncoderFactoryInterface $encoderFactory A EncoderFactoryInterface instance
|
||||
*/
|
||||
public function __construct(UserProviderInterface $userProvider, AccountCheckerInterface $accountChecker, EncoderFactoryInterface $encoderFactory, $hideUserNotFoundExceptions = true)
|
||||
public function __construct(UserProviderInterface $userProvider, AccountCheckerInterface $accountChecker, $providerKey, EncoderFactoryInterface $encoderFactory, $hideUserNotFoundExceptions = true)
|
||||
{
|
||||
parent::__construct($accountChecker, $hideUserNotFoundExceptions);
|
||||
parent::__construct($accountChecker, $providerKey, $hideUserNotFoundExceptions);
|
||||
|
||||
$this->encoderFactory = $encoderFactory;
|
||||
$this->userProvider = $userProvider;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Security\Authentication\Provider;
|
||||
|
||||
use Symfony\Component\Security\User\AccountInterface;
|
||||
use Symfony\Component\Security\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\User\AccountCheckerInterface;
|
||||
use Symfony\Component\Security\Exception\BadCredentialsException;
|
||||
@ -31,6 +32,7 @@ class PreAuthenticatedAuthenticationProvider implements AuthenticationProviderIn
|
||||
{
|
||||
protected $userProvider;
|
||||
protected $accountChecker;
|
||||
protected $providerKey;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -38,10 +40,11 @@ class PreAuthenticatedAuthenticationProvider implements AuthenticationProviderIn
|
||||
* @param UserProviderInterface $userProvider A UserProviderInterface instance
|
||||
* @param AccountCheckerInterface $accountChecker An AccountCheckerInterface instance
|
||||
*/
|
||||
public function __construct(UserProviderInterface $userProvider, AccountCheckerInterface $accountChecker)
|
||||
public function __construct(UserProviderInterface $userProvider, AccountCheckerInterface $accountChecker, $providerKey)
|
||||
{
|
||||
$this->userProvider = $userProvider;
|
||||
$this->accountChecker = $accountChecker;
|
||||
$this->providerKey = $providerKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,6 +76,6 @@ class PreAuthenticatedAuthenticationProvider implements AuthenticationProviderIn
|
||||
*/
|
||||
public function supports(TokenInterface $token)
|
||||
{
|
||||
return $token instanceof PreAuthenticatedToken;
|
||||
return $token instanceof PreAuthenticatedToken && $this->providerKey === $token->getProviderKey();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace Symfony\Component\Security\Authentication\Provider;
|
||||
|
||||
use Symfony\Component\Security\User\AccountCheckerInterface;
|
||||
use Symfony\Component\Security\User\AccountInterface;
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Authentication\Token\RememberMeToken;
|
||||
use Symfony\Component\Security\Exception\BadCredentialsException;
|
||||
|
||||
class RememberMeAuthenticationProvider implements AuthenticationProviderInterface
|
||||
{
|
||||
protected $accountChecker;
|
||||
protected $key;
|
||||
protected $providerKey;
|
||||
|
||||
public function __construct(AccountCheckerInterface $accountChecker, $key, $providerKey)
|
||||
{
|
||||
$this->accountChecker = $accountChecker;
|
||||
$this->key = $key;
|
||||
$this->providerKey = $providerKey;
|
||||
}
|
||||
|
||||
public function authenticate(TokenInterface $token)
|
||||
{
|
||||
if (!$this->supports($token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->key !== $token->getKey()) {
|
||||
throw new BadCredentialsException('The presented key does not match.');
|
||||
}
|
||||
|
||||
$user = $token->getUser();
|
||||
$this->accountChecker->checkPreAuth($user);
|
||||
$this->accountChecker->checkPostAuth($user);
|
||||
$token->setAuthenticated(true);
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
public function supports(TokenInterface $token)
|
||||
{
|
||||
return $token instanceof RememberMeToken && $token->getProviderKey() === $this->providerKey;
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter
|
||||
{
|
||||
protected $hideUserNotFoundExceptions;
|
||||
protected $accountChecker;
|
||||
protected $providerKey;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -36,9 +37,14 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter
|
||||
* @param AccountCheckerInterface $accountChecker An AccountCheckerInterface interface
|
||||
* @param Boolean $hideUserNotFoundExceptions Whether to hide user not found exception or not
|
||||
*/
|
||||
public function __construct(AccountCheckerInterface $accountChecker, $hideUserNotFoundExceptions = true)
|
||||
public function __construct(AccountCheckerInterface $accountChecker, $providerKey, $hideUserNotFoundExceptions = true)
|
||||
{
|
||||
if (empty($providerKey)) {
|
||||
throw new \InvalidArgumentException('$providerKey must not be empty.');
|
||||
}
|
||||
|
||||
$this->accountChecker = $accountChecker;
|
||||
$this->providerKey = $providerKey;
|
||||
$this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions;
|
||||
}
|
||||
|
||||
@ -64,7 +70,7 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter
|
||||
$this->checkAuthentication($user, $token);
|
||||
$this->accountChecker->checkPostAuth($user);
|
||||
|
||||
return new UsernamePasswordToken($user, $token->getCredentials(), $user->getRoles());
|
||||
return new UsernamePasswordToken($user, $token->getCredentials(), $this->providerKey, $user->getRoles());
|
||||
} catch (UsernameNotFoundException $notFound) {
|
||||
if ($this->hideUserNotFoundExceptions) {
|
||||
throw new BadCredentialsException('Bad credentials', 0, $notFound);
|
||||
@ -79,7 +85,7 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter
|
||||
*/
|
||||
public function supports(TokenInterface $token)
|
||||
{
|
||||
return $token instanceof UsernamePasswordToken;
|
||||
return $token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Authentication\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\Exception\TokenNotFoundException;
|
||||
|
||||
/**
|
||||
* This class is used for testing purposes, and is not really suited for production.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class InMemoryTokenProvider implements TokenProviderInterface
|
||||
{
|
||||
protected $tokens = array();
|
||||
|
||||
public function loadTokenBySeries($series)
|
||||
{
|
||||
if (!isset($this->tokens[$series])) {
|
||||
throw new TokenNotFoundException('No token found.');
|
||||
}
|
||||
|
||||
return $this->tokens[$series];
|
||||
}
|
||||
|
||||
public function updateToken($series, $tokenValue, \DateTime $lastUsed)
|
||||
{
|
||||
if (!isset($this->tokens[$series])) {
|
||||
throw new TokenNotFoundException('No token found.');
|
||||
}
|
||||
|
||||
$token = new PersistentToken(
|
||||
$this->tokens[$series]->getClass(),
|
||||
$this->tokens[$series]->getUsername(),
|
||||
$series,
|
||||
$tokenValue,
|
||||
$lastUsed
|
||||
);
|
||||
$this->tokens[$series] = $token;
|
||||
}
|
||||
|
||||
public function deleteTokenBySeries($series)
|
||||
{
|
||||
unset($this->tokens[$series]);
|
||||
}
|
||||
|
||||
public function createNewToken(PersistentTokenInterface $token)
|
||||
{
|
||||
$this->tokens[$token->getSeries()] = $token;
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Authentication\RememberMe;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class is only used by PersistentTokenRememberMeServices internally.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
final class PersistentToken implements PersistentTokenInterface
|
||||
{
|
||||
private $class;
|
||||
private $username;
|
||||
private $series;
|
||||
private $tokenValue;
|
||||
private $lastUsed;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $username
|
||||
* @param string $series
|
||||
* @param string $tokenValue
|
||||
* @param DateTime $lastUsed
|
||||
*/
|
||||
public function __construct($class, $username, $series, $tokenValue, \DateTime $lastUsed)
|
||||
{
|
||||
if (empty($class)) {
|
||||
throw new \InvalidArgumentException('$class must not be empty.');
|
||||
}
|
||||
if (empty($username)) {
|
||||
throw new \InvalidArgumentException('$username must not be empty.');
|
||||
}
|
||||
if (empty($series)) {
|
||||
throw new \InvalidArgumentException('$series must not be empty.');
|
||||
}
|
||||
if (empty($tokenValue)) {
|
||||
throw new \InvalidArgumentException('$tokenValue must not be empty.');
|
||||
}
|
||||
|
||||
$this->class = $class;
|
||||
$this->username = $username;
|
||||
$this->series = $series;
|
||||
$this->tokenValue = $tokenValue;
|
||||
$this->lastUsed = $lastUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class of the user
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClass()
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the series
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSeries()
|
||||
{
|
||||
return $this->series;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTokenValue()
|
||||
{
|
||||
return $this->tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time the token was last used
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getLastUsed()
|
||||
{
|
||||
return $this->lastUsed;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Authentication\RememberMe;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface to be implemented by persistent token classes (such as
|
||||
* Doctrine entities representing a remember-me token)
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
interface PersistentTokenInterface
|
||||
{
|
||||
/**
|
||||
* Returns the username
|
||||
* @return string
|
||||
*/
|
||||
function getUsername();
|
||||
|
||||
/**
|
||||
* Returns the series
|
||||
* @return string
|
||||
*/
|
||||
function getSeries();
|
||||
|
||||
/**
|
||||
* Returns the token value
|
||||
* @return string
|
||||
*/
|
||||
function getTokenValue();
|
||||
|
||||
/**
|
||||
* Returns the last time the cookie was used
|
||||
* @return \DateTime
|
||||
*/
|
||||
function getLastUsed();
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Authentication\RememberMe;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for TokenProviders
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
interface TokenProviderInterface
|
||||
{
|
||||
/**
|
||||
* Loads the active token for the given series
|
||||
*
|
||||
* @throws TokenNotFoundException if the token is not found
|
||||
*
|
||||
* @param string $series
|
||||
* @return PersistentTokenInterface
|
||||
*/
|
||||
function loadTokenBySeries($series);
|
||||
|
||||
/**
|
||||
* Deletes all tokens belonging to series
|
||||
* @param string $series
|
||||
*/
|
||||
function deleteTokenBySeries($series);
|
||||
|
||||
/**
|
||||
* Updates the token according to this data
|
||||
*
|
||||
* @param string $series
|
||||
* @param string $tokenValue
|
||||
* @param DateTime $lastUsed
|
||||
*/
|
||||
function updateToken($series, $tokenValue, \DateTime $lastUsed);
|
||||
|
||||
/**
|
||||
* Creates a new token
|
||||
* @param PersistentTokenInterface $token
|
||||
*/
|
||||
function createNewToken(PersistentTokenInterface $token);
|
||||
}
|
@ -18,10 +18,12 @@ namespace Symfony\Component\Security\Authentication\Token;
|
||||
*/
|
||||
class PreAuthenticatedToken extends Token
|
||||
{
|
||||
protected $providerKey;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct($user, $credentials, array $roles = null)
|
||||
public function __construct($user, $credentials, $providerKey, array $roles = null)
|
||||
{
|
||||
parent::__construct(null === $roles ? array() : $roles);
|
||||
if (null !== $roles) {
|
||||
@ -30,6 +32,12 @@ class PreAuthenticatedToken extends Token
|
||||
|
||||
$this->user = $user;
|
||||
$this->credentials = $credentials;
|
||||
$this->providerKey = $providerKey;
|
||||
}
|
||||
|
||||
public function getProviderKey()
|
||||
{
|
||||
return $this->providerKey;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,6 +22,7 @@ use Symfony\Component\Security\User\AccountInterface;
|
||||
class RememberMeToken extends Token
|
||||
{
|
||||
protected $key;
|
||||
protected $providerKey;
|
||||
|
||||
/**
|
||||
* The persistent token which resulted in this authentication token.
|
||||
@ -36,30 +37,39 @@ class RememberMeToken extends Token
|
||||
* @param string $username
|
||||
* @param string $key
|
||||
*/
|
||||
public function __construct(AccountInterface $user, $key) {
|
||||
public function __construct(AccountInterface $user, $providerKey, $key) {
|
||||
parent::__construct($user->getRoles());
|
||||
|
||||
if (0 === strlen($key)) {
|
||||
throw new \InvalidArgumentException('$key cannot be empty.');
|
||||
if (empty($key)) {
|
||||
throw new \InvalidArgumentException('$key must not be empty.');
|
||||
}
|
||||
if (empty($providerKey)) {
|
||||
throw new \InvalidArgumentException('$providerKey must not be empty.');
|
||||
}
|
||||
|
||||
$this->user = $user;
|
||||
$this->setUser($user);
|
||||
$this->providerKey = $providerKey;
|
||||
$this->key = $key;
|
||||
$this->setAuthenticated(true);
|
||||
}
|
||||
|
||||
public function getProviderKey()
|
||||
{
|
||||
return $this->providerKey;
|
||||
}
|
||||
|
||||
public function getKey()
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function setPersistentToken(PersistentTokenInterface $persistentToken)
|
||||
{
|
||||
$this->persistentToken = $persistentToken;
|
||||
}
|
||||
|
||||
public function getPersistentToken()
|
||||
{
|
||||
return $this->persistentToken;
|
||||
}
|
||||
}
|
||||
|
||||
public function setPersistentToken(PersistentTokenInterface $persistentToken)
|
||||
{
|
||||
$this->persistentToken = $persistentToken;
|
||||
}
|
||||
}
|
@ -18,19 +18,30 @@ namespace Symfony\Component\Security\Authentication\Token;
|
||||
*/
|
||||
class UsernamePasswordToken extends Token
|
||||
{
|
||||
protected $providerKey;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $user The username (like a nickname, email address, etc.)
|
||||
* @param string $credentials This usually is the password of the user
|
||||
*/
|
||||
public function __construct($user, $credentials, array $roles = array())
|
||||
public function __construct($user, $credentials, $providerKey, array $roles = array())
|
||||
{
|
||||
parent::__construct($roles);
|
||||
|
||||
$this->setUser($user);
|
||||
$this->credentials = $credentials;
|
||||
$this->providerKey = $providerKey;
|
||||
|
||||
parent::setAuthenticated((Boolean) count($roles));
|
||||
}
|
||||
|
||||
public function getProviderKey()
|
||||
{
|
||||
return $this->providerKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Exception;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This exception is thrown when the RememberMeServices implementation
|
||||
* detects that a presented cookie has already been used by someone else.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class CookieTheftException extends AuthenticationException
|
||||
{
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace Symfony\Component\Security\Exception;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TokenNotFoundException is thrown if a Token cannot be found.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class TokenNotFoundException extends AuthenticationException
|
||||
{
|
||||
}
|
@ -11,8 +11,9 @@
|
||||
|
||||
namespace Symfony\Component\Security;
|
||||
|
||||
use Symfony\Component\Security\Authorization\AccessDecisionManagerInterface;
|
||||
use Symfony\Component\Security\Authentication\AuthenticationManagerInterface;
|
||||
use Symfony\Component\Security\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Authorization\AccessDecisionManager;
|
||||
use Symfony\Component\Security\Acl\Voter\FieldVote;
|
||||
|
||||
/**
|
||||
@ -30,15 +31,19 @@ class SecurityContext
|
||||
|
||||
protected $token;
|
||||
protected $accessDecisionManager;
|
||||
protected $authenticationManager;
|
||||
protected $alwaysAuthenticate;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param AccessDecisionManager|null $accessDecisionManager An AccessDecisionManager instance
|
||||
* @param AccessDecisionManagerInterface|null $accessDecisionManager An AccessDecisionManager instance
|
||||
*/
|
||||
public function __construct(AccessDecisionManager $accessDecisionManager = null)
|
||||
public function __construct(AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager = null, $alwaysAuthenticate = false)
|
||||
{
|
||||
$this->authenticationManager = $authenticationManager;
|
||||
$this->accessDecisionManager = $accessDecisionManager;
|
||||
$this->alwaysAuthenticate = $alwaysAuthenticate;
|
||||
}
|
||||
|
||||
public function getUser()
|
||||
@ -60,6 +65,10 @@ class SecurityContext
|
||||
$object = new FieldVote($object, $field);
|
||||
}
|
||||
|
||||
if ($this->alwaysAuthenticate || !$this->token->isAuthenticated()) {
|
||||
$this->token = $this->authenticationManager->authenticate($this->token);
|
||||
}
|
||||
|
||||
return $this->accessDecisionManager->decide($this->token, (array) $attributes, $object);
|
||||
}
|
||||
|
||||
|
@ -87,4 +87,12 @@ class InMemoryUserProvider implements UserProviderInterface
|
||||
|
||||
return $this->loadUserByUsername((string) $account);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function supportsClass($class)
|
||||
{
|
||||
return $class === 'Symfony\Component\Security\User\User';
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +119,6 @@ class User implements AdvancedAccountInterface
|
||||
*/
|
||||
public function eraseCredentials()
|
||||
{
|
||||
$this->password = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,13 +36,22 @@ interface UserProviderInterface
|
||||
* Loads the user for the account interface.
|
||||
*
|
||||
* It is up to the implementation if it decides to reload the user data
|
||||
* from the database, or if it simply merges the passed User into the
|
||||
* from the database, or if it simply merges the passed User into the
|
||||
* identity map of an entity manager.
|
||||
*
|
||||
* @throws UnsupportedAccountException if the account is not supported
|
||||
* @param AccountInterface $user
|
||||
* @param AccountInterface $account
|
||||
*
|
||||
* @return AccountInterface
|
||||
*/
|
||||
function loadUserByAccount(AccountInterface $user);
|
||||
function loadUserByAccount(AccountInterface $account);
|
||||
|
||||
/**
|
||||
* Whether this provider supports the given user class
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
function supportsClass($class);
|
||||
}
|
@ -0,0 +1,396 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\HttpKernel\Security\Firewall;
|
||||
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Exception\CookieTheftException;
|
||||
use Symfony\Component\Security\Exception\AuthenticationException;
|
||||
use Symfony\Component\HttpKernel\Security\Firewall\RememberMeListener;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testRegister()
|
||||
{
|
||||
list($listener,,,,) = $this->getListener();
|
||||
|
||||
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcher');
|
||||
$dispatcher
|
||||
->expects($this->at(0))
|
||||
->method('connect')
|
||||
->with($this->equalTo('core.security'))
|
||||
;
|
||||
$dispatcher
|
||||
->expects($this->at(1))
|
||||
->method('connect')
|
||||
->with($this->equalTo('core.response'))
|
||||
;
|
||||
|
||||
$listener->register($dispatcher);
|
||||
}
|
||||
|
||||
public function testCheckCookiesDoesNotTryToPopulateNonEmptySecurityContext()
|
||||
{
|
||||
list($listener, $context, $service,,) = $this->getListener();
|
||||
|
||||
$context
|
||||
->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue($this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface')))
|
||||
;
|
||||
|
||||
$context
|
||||
->expects($this->never())
|
||||
->method('setToken')
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($this->getEvent()));
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
}
|
||||
|
||||
public function testCheckCookiesDoesNothingWhenNoCookieIsSet()
|
||||
{
|
||||
list($listener, $context, $service,,) = $this->getListener();
|
||||
|
||||
$context
|
||||
->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('autoLogin')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($event));
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
}
|
||||
|
||||
public function testCheckCookiesIgnoresAuthenticationExceptionThrownByTheRememberMeServicesImplementation()
|
||||
{
|
||||
list($listener, $context, $service,,) = $this->getListener();
|
||||
|
||||
$context
|
||||
->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$exception = new AuthenticationException('cookie invalid.');
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('autoLogin')
|
||||
->will($this->throwException($exception))
|
||||
;
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($event));
|
||||
$this->assertSame($exception, $this->getLastState($listener));
|
||||
}
|
||||
|
||||
public function testCheckCookiesThrowsCookieTheftExceptionIfThrownByTheRememberMeServicesImplementation()
|
||||
{
|
||||
list($listener, $context, $service,,) = $this->getListener();
|
||||
|
||||
$context
|
||||
->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$exception = new CookieTheftException('cookie was stolen.');
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('autoLogin')
|
||||
->will($this->throwException($exception))
|
||||
;
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
try {
|
||||
$listener->checkCookies($event);
|
||||
}
|
||||
catch (CookieTheftException $theft) {
|
||||
$this->assertSame($theft, $this->getLastState($listener));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fail('Expected CookieTheftException was not thrown.');
|
||||
}
|
||||
|
||||
public function testCheckCookiesAuthenticationManagerDoesNotChangeListenerStateWhenTokenIsNotSupported()
|
||||
{
|
||||
list($listener, $context, $service, $manager,) = $this->getListener();
|
||||
|
||||
$context
|
||||
->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('autoLogin')
|
||||
->will($this->returnValue($this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface')))
|
||||
;
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($event));
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
}
|
||||
|
||||
public function testCheckCookiesIgnoresAuthenticationExceptionThrownByAuthenticationManagerImplementation()
|
||||
{
|
||||
list($listener, $context, $service, $manager,) = $this->getListener();
|
||||
|
||||
$context
|
||||
->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('autoLogin')
|
||||
->will($this->returnValue($this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface')))
|
||||
;
|
||||
|
||||
$exception = new AuthenticationException('Authentication failed.');
|
||||
$manager
|
||||
->expects($this->once())
|
||||
->method('authenticate')
|
||||
->will($this->throwException($exception))
|
||||
;
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($event));
|
||||
$this->assertSame($exception, $this->getLastState($listener));
|
||||
}
|
||||
|
||||
public function testCheckCookies()
|
||||
{
|
||||
list($listener, $context, $service, $manager,) = $this->getListener();
|
||||
|
||||
$context
|
||||
->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('autoLogin')
|
||||
->will($this->returnValue($token))
|
||||
;
|
||||
|
||||
$context
|
||||
->expects($this->once())
|
||||
->method('setToken')
|
||||
->with($this->equalTo($token))
|
||||
;
|
||||
|
||||
$manager
|
||||
->expects($this->once())
|
||||
->method('authenticate')
|
||||
->will($this->returnValue($token))
|
||||
;
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($event));
|
||||
$this->assertSame($token, $this->getLastState($listener));
|
||||
}
|
||||
|
||||
public function testUpdateCookiesIgnoresAnythingButMasterRequests()
|
||||
{
|
||||
list($listener,,,,) = $this->getListener();
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with($this->equalTo('request_type'))
|
||||
->will($this->returnValue('foo'))
|
||||
;
|
||||
|
||||
$response = $this->getMock('Symfony\Component\HttpFoundation\Response');
|
||||
|
||||
$this->assertSame($response, $listener->updateCookies($event, $response));
|
||||
}
|
||||
|
||||
public function testUpdateCookiesCallsLoginSuccessOnRememberMeServicesImplementationWhenAuthenticationWasSuccessful()
|
||||
{
|
||||
list($listener,, $service,,) = $this->getListener();
|
||||
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
$this->setLastState($listener, $token);
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->at(0))
|
||||
->method('get')
|
||||
->with('request_type')
|
||||
->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST))
|
||||
;
|
||||
$event
|
||||
->expects($this->at(1))
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue($request))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('loginSuccess')
|
||||
->with($this->equalTo($request), $this->equalTo($response), $this->equalTo($token))
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$this->assertSame($response, $listener->updateCookies($event, $response));
|
||||
}
|
||||
|
||||
public function testUpdateCookiesCallsLoginFailOnRememberMeServicesImplementationWhenAuthenticationWasNotSuccessful()
|
||||
{
|
||||
list($listener,, $service,,) = $this->getListener();
|
||||
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
|
||||
$exception = new AuthenticationException('foo');
|
||||
$this->setLastState($listener, $exception);
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->at(0))
|
||||
->method('get')
|
||||
->with('request_type')
|
||||
->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST))
|
||||
;
|
||||
$event
|
||||
->expects($this->at(1))
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue($request))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('loginFail')
|
||||
->with($this->equalTo($request), $this->equalTo($response))
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$this->assertSame($response, $listener->updateCookies($event, $response));
|
||||
}
|
||||
|
||||
protected function setLastState($listener, $state)
|
||||
{
|
||||
$r = new \ReflectionObject($listener);
|
||||
$p = $r->getProperty('lastState');
|
||||
$p->setAccessible(true);
|
||||
$p->setValue($listener, $state);
|
||||
}
|
||||
|
||||
protected function getLastState($listener)
|
||||
{
|
||||
$r = new \ReflectionObject($listener);
|
||||
$p = $r->getProperty('lastState');
|
||||
$p->setAccessible(true);
|
||||
|
||||
return $p->getValue($listener);
|
||||
}
|
||||
|
||||
protected function getEvent()
|
||||
{
|
||||
return $this->getMock('Symfony\Component\EventDispatcher\Event', array(), array(), '', false);
|
||||
}
|
||||
|
||||
protected function getListener()
|
||||
{
|
||||
$listener = new RememberMeListener(
|
||||
$context = $this->getContext(),
|
||||
$service = $this->getService(),
|
||||
$manager = $this->getManager(),
|
||||
$logger = $this->getLogger()
|
||||
);
|
||||
|
||||
return array($listener, $context, $service, $manager, $logger);
|
||||
}
|
||||
|
||||
protected function getLogger()
|
||||
{
|
||||
return $this->getMock('Symfony\Component\HttpKernel\Log\LoggerInterface');
|
||||
}
|
||||
|
||||
protected function getManager()
|
||||
{
|
||||
return $this->getMock('Symfony\Component\Security\Authentication\AuthenticationManagerInterface');
|
||||
}
|
||||
|
||||
protected function getService()
|
||||
{
|
||||
return $this->getMock('Symfony\Component\HttpKernel\Security\RememberMe\RememberMeServicesInterface');
|
||||
}
|
||||
|
||||
protected function getContext()
|
||||
{
|
||||
return $this->getMockBuilder('Symfony\Component\Security\SecurityContext')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
}
|
||||
}
|
@ -49,4 +49,4 @@ class CookieClearingLogoutHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertStringStartsWith('foo2', $cookie->getName());
|
||||
$this->assertTrue($cookie->isCleared());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,22 +19,22 @@ class SessionLogoutHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
public function testLogout()
|
||||
{
|
||||
$handler = new SessionLogoutHandler();
|
||||
|
||||
|
||||
$request = $this->getMock('Symfony\Component\HttpFoundation\Request');
|
||||
$response = new Response();
|
||||
$session = $this->getMock('Symfony\Component\HttpFoundation\Session', array(), array(), '', false);
|
||||
|
||||
|
||||
$request
|
||||
->expects($this->once())
|
||||
->method('getSession')
|
||||
->will($this->returnValue($session))
|
||||
;
|
||||
|
||||
|
||||
$session
|
||||
->expects($this->once())
|
||||
->method('invalidate')
|
||||
;
|
||||
|
||||
|
||||
$handler->logout($request, $response, $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface'));
|
||||
}
|
||||
}
|
@ -0,0 +1,403 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\HttpKernel\Security\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\RememberMeToken;
|
||||
use Symfony\Component\HttpFoundation\HeaderBag;
|
||||
use Symfony\Component\Security\Exception\UsernameNotFoundException;
|
||||
use Symfony\Component\Security\Authentication\RememberMe\PersistentToken;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Security\RememberMe\PersistentTokenBasedRememberMeServices;
|
||||
use Symfony\Component\Security\Exception\TokenNotFoundException;
|
||||
use Symfony\Component\Security\Exception\CookieTheftException;
|
||||
|
||||
class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testAutoLoginReturnsNullWhenNoCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo'));
|
||||
|
||||
$this->assertNull($service->autoLogin(new Request()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\AuthenticationException
|
||||
* @expectedMessage The cookie is invalid.
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionOnInvalidCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
|
||||
$request = new Request;
|
||||
$request->request->set('foo', 'true');
|
||||
$request->cookies->set('foo', 'foo');
|
||||
|
||||
$service->autoLogin($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\TokenNotFoundException
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionOnNonExistentToken()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
|
||||
$request = new Request;
|
||||
$request->request->set('foo', 'true');
|
||||
$request->cookies->set('foo', $this->encodeCookie(array(
|
||||
$series = 'fooseries',
|
||||
$tokenValue = 'foovalue',
|
||||
)));
|
||||
|
||||
$tokenProvider = $this->getMock('Symfony\Component\Security\Authentication\RememberMe\TokenProviderInterface');
|
||||
$tokenProvider
|
||||
->expects($this->once())
|
||||
->method('loadTokenBySeries')
|
||||
->will($this->throwException(new TokenNotFoundException('Token not found.')))
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$service->autoLogin($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\UsernameNotFoundException
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionOnNonExistentUser()
|
||||
{
|
||||
$userProvider = $this->getProvider();
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
|
||||
|
||||
$tokenProvider = $this->getMock('Symfony\Component\Security\Authentication\RememberMe\TokenProviderInterface');
|
||||
$tokenProvider
|
||||
->expects($this->once())
|
||||
->method('loadTokenBySeries')
|
||||
->will($this->returnValue(new PersistentToken('fooclass', 'fooname', 'fooseries', 'foovalue', new \DateTime())))
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$userProvider
|
||||
->expects($this->once())
|
||||
->method('loadUserByUsername')
|
||||
->will($this->throwException(new UsernameNotFoundException('user not found')))
|
||||
;
|
||||
|
||||
$service->autoLogin($request);
|
||||
}
|
||||
|
||||
public function testAutoLoginThrowsExceptionOnStolenCookieAndRemovesItFromThePersistentBackend()
|
||||
{
|
||||
$userProvider = $this->getProvider();
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
|
||||
|
||||
$tokenProvider = $this->getMock('Symfony\Component\Security\Authentication\RememberMe\TokenProviderInterface');
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$tokenProvider
|
||||
->expects($this->once())
|
||||
->method('loadTokenBySeries')
|
||||
->will($this->returnValue(new PersistentToken('fooclass', 'foouser', 'fooseries', 'anotherFooValue', new \DateTime())))
|
||||
;
|
||||
|
||||
$tokenProvider
|
||||
->expects($this->once())
|
||||
->method('deleteTokenBySeries')
|
||||
->with($this->equalTo('fooseries'))
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
try {
|
||||
$service->autoLogin($request);
|
||||
} catch (CookieTheftException $theft) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fail('Expected CookieTheftException was not thrown.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\AuthenticationException
|
||||
* @expectedMessage The cookie has expired.
|
||||
*/
|
||||
public function testAutoLoginDoesNotAcceptAnExpiredCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
|
||||
|
||||
$tokenProvider = $this->getMock('Symfony\Component\Security\Authentication\RememberMe\TokenProviderInterface');
|
||||
$tokenProvider
|
||||
->expects($this->once())
|
||||
->method('loadTokenBySeries')
|
||||
->with($this->equalTo('fooseries'))
|
||||
->will($this->returnValue(new PersistentToken('fooclass', 'username', 'fooseries', 'newFooValue', new \DateTime('yesterday'))))
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$service->autoLogin($request);
|
||||
}
|
||||
|
||||
public function testAutoLogin()
|
||||
{
|
||||
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array('ROLE_FOO')))
|
||||
;
|
||||
|
||||
$userProvider = $this->getProvider();
|
||||
$userProvider
|
||||
->expects($this->once())
|
||||
->method('loadUserByUsername')
|
||||
->with($this->equalTo('foouser'))
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
|
||||
|
||||
$tokenProvider = $this->getMock('Symfony\Component\Security\Authentication\RememberMe\TokenProviderInterface');
|
||||
$tokenProvider
|
||||
->expects($this->once())
|
||||
->method('loadTokenBySeries')
|
||||
->with($this->equalTo('fooseries'))
|
||||
->will($this->returnValue(new PersistentToken('fooclass', 'foouser', 'fooseries', 'foovalue', new \DateTime())))
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$returnedToken = $service->autoLogin($request);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Security\Authentication\Token\RememberMeToken', $returnedToken);
|
||||
$this->assertInstanceOf('Symfony\Component\Security\Authentication\RememberMe\PersistentTokenInterface', $returnedToken->getPersistentToken());
|
||||
$this->assertSame($user, $returnedToken->getUser());
|
||||
$this->assertEquals('fookey', $returnedToken->getKey());
|
||||
}
|
||||
|
||||
public function testLogout()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => '/foo', 'domain' => 'foodomain.foo'));
|
||||
$request = new Request();
|
||||
$request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
|
||||
$response = new Response();
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
|
||||
$tokenProvider = $this->getMock('Symfony\Component\Security\Authentication\RememberMe\TokenProviderInterface');
|
||||
$tokenProvider
|
||||
->expects($this->once())
|
||||
->method('deleteTokenBySeries')
|
||||
->with($this->equalTo('fooseries'))
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->logout($request, $response, $token);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$this->assertTrue($cookie->isCleared());
|
||||
$this->assertEquals('/foo', $cookie->getPath());
|
||||
$this->assertEquals('foodomain.foo', $cookie->getDomain());
|
||||
}
|
||||
|
||||
public function testLogoutSimplyIgnoresNonSetRequestCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
|
||||
$tokenProvider = $this->getMock('Symfony\Component\Security\Authentication\RememberMe\TokenProviderInterface');
|
||||
$tokenProvider
|
||||
->expects($this->never())
|
||||
->method('deleteTokenBySeries')
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
$service->logout($request, $response, $token);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$this->assertTrue($cookie->isCleared());
|
||||
$this->assertNull($cookie->getPath());
|
||||
$this->assertNull($cookie->getDomain());
|
||||
}
|
||||
|
||||
public function testLogoutSimplyIgnoresInvalidCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', 'somefoovalue');
|
||||
$response = new Response;
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
|
||||
$tokenProvider = $this->getMock('Symfony\Component\Security\Authentication\RememberMe\TokenProviderInterface');
|
||||
$tokenProvider
|
||||
->expects($this->never())
|
||||
->method('deleteTokenBySeries')
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->logout($request, $response, $token);
|
||||
|
||||
$this->assertTrue($response->headers->getCookie('foo')->isCleared());
|
||||
}
|
||||
|
||||
public function testLoginFail()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginFail($request, $response);
|
||||
|
||||
$this->assertTrue($response->headers->getCookie('foo')->isCleared());
|
||||
}
|
||||
|
||||
public function testLoginSuccessRenewsRememberMeTokenWhenUsedForLogin()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
|
||||
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array('ROLE_FOO')))
|
||||
;
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\RememberMeToken', array(), array($user, 'fookey', 'fookey'));
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getPersistentToken')
|
||||
->will($this->returnValue(new PersistentToken('fooclass', 'foouser', 'fooseries', 'foovalue', new \DateTime())))
|
||||
;
|
||||
|
||||
$tokenProvider = $this->getMock('Symfony\Component\Security\Authentication\RememberMe\TokenProviderInterface');
|
||||
$tokenProvider
|
||||
->expects($this->once())
|
||||
->method('updateToken')
|
||||
->with($this->equalTo('fooseries'))
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$this->assertFalse($cookie->isCleared());
|
||||
$this->assertTrue($cookie->isSecure());
|
||||
$this->assertTrue($cookie->isHttponly());
|
||||
$this->assertTrue($cookie->getExpire() > time() + 3590 && $cookie->getExpire() < time() + 3610);
|
||||
$this->assertEquals('myfoodomain.foo', $cookie->getDomain());
|
||||
$this->assertEquals('/foo/path', $cookie->getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
* @expectedMessage RememberMeToken must contain a PersistentTokenInterface implementation when used as login.
|
||||
*/
|
||||
public function testLoginSuccessThrowsExceptionWhenRememberMeTokenDoesNotContainPersistentTokenImplementation()
|
||||
{
|
||||
$service = $this->getService(null, array('always_remember_me' => true, 'name' => 'foo'));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
|
||||
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array('ROLE_FOO')))
|
||||
;
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\RememberMeToken', array(), array($user, 'fookey', 'fookey'));
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getPersistentToken')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
}
|
||||
|
||||
public function testLoginSuccessSetsCookieWhenLoggedInWithNonRememberMeTokenInterfaceImplementation()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600, 'always_remember_me' => true));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
|
||||
$account = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$account
|
||||
->expects($this->once())
|
||||
->method('getUsername')
|
||||
->will($this->returnValue('foo'))
|
||||
;
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
$token
|
||||
->expects($this->any())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($account))
|
||||
;
|
||||
|
||||
$tokenProvider = $this->getMock('Symfony\Component\Security\Authentication\RememberMe\TokenProviderInterface');
|
||||
$tokenProvider
|
||||
->expects($this->once())
|
||||
->method('createNewToken')
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$this->assertFalse($cookie->isCleared());
|
||||
$this->assertTrue($cookie->isSecure());
|
||||
$this->assertTrue($cookie->isHttponly());
|
||||
$this->assertTrue($cookie->getExpire() > time() + 3590 && $cookie->getExpire() < time() + 3610);
|
||||
$this->assertEquals('myfoodomain.foo', $cookie->getDomain());
|
||||
$this->assertEquals('/foo/path', $cookie->getPath());
|
||||
}
|
||||
|
||||
protected function encodeCookie(array $parts)
|
||||
{
|
||||
$service = $this->getService();
|
||||
$r = new \ReflectionMethod($service, 'encodeCookie');
|
||||
$r->setAccessible(true);
|
||||
|
||||
return $r->invoke($service, $parts);
|
||||
}
|
||||
|
||||
protected function getService($userProvider = null, $options = array(), $logger = null)
|
||||
{
|
||||
if (null === $userProvider) {
|
||||
$userProvider = $this->getProvider();
|
||||
}
|
||||
|
||||
return new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger);
|
||||
}
|
||||
|
||||
protected function getProvider()
|
||||
{
|
||||
$provider = $this->getMock('Symfony\Component\Security\User\UserProviderInterface');
|
||||
$provider
|
||||
->expects($this->any())
|
||||
->method('supportsClass')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
return $provider;
|
||||
}
|
||||
}
|
@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\HttpKernel\Security\RememberMe;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class RememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testAutoLoginReturnsNullWhenNoCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo'));
|
||||
|
||||
$this->assertNull($service->autoLogin(new Request()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedMessage processAutoLoginCookie() must return a TokenInterface implementation.
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionWhenImplementationDoesNotReturnTokenInterface()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo'));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', 'foo');
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('processAutoLoginCookie')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$service->autoLogin($request);
|
||||
}
|
||||
|
||||
public function testAutoLogin()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo'));
|
||||
$request = new Request();
|
||||
$request->cookies->set('foo', 'foo');
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('processAutoLoginCookie')
|
||||
->will($this->returnValue($token))
|
||||
;
|
||||
|
||||
$returnedToken = $service->autoLogin($request);
|
||||
|
||||
$this->assertSame($token, $returnedToken);
|
||||
}
|
||||
|
||||
public function testLogout()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->logout($request, $response, $token);
|
||||
|
||||
$this->assertTrue($response->headers->getCookie('foo')->isCleared());
|
||||
}
|
||||
|
||||
public function testLoginFail()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginFail($request, $response);
|
||||
|
||||
$this->assertTrue($response->headers->getCookie('foo')->isCleared());
|
||||
}
|
||||
|
||||
public function testLoginSuccessIsNotProcessedWhenTokenDoesNotContainAccountInterfaceImplementation()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
$account = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getUser')
|
||||
->will($this->returnValue('foo'))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->never())
|
||||
->method('onLoginSuccess')
|
||||
;
|
||||
|
||||
$this->assertFalse($request->request->has('foo'));
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
}
|
||||
|
||||
public function testLoginSuccessIsNotProcessedWhenRememberMeIsNotRequested()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
$account = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($account))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->never())
|
||||
->method('onLoginSuccess')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$this->assertFalse($request->request->has('foo'));
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
}
|
||||
|
||||
public function testLoginSuccessWhenRememberMeAlwaysIsTrue()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
$account = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($account))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('onLoginSuccess')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPositiveRememberMeParameterValues
|
||||
*/
|
||||
public function testLoginSuccessWhenRememberMeParameterIsPositive($value)
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
|
||||
|
||||
$request = new Request;
|
||||
$request->request->set('foo', $value);
|
||||
$response = new Response;
|
||||
$account = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($account))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('onLoginSuccess')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
}
|
||||
|
||||
public function getPositiveRememberMeParameterValues()
|
||||
{
|
||||
return array(
|
||||
array('true'),
|
||||
array('1'),
|
||||
array('on'),
|
||||
array('yes'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testLoginSuccessRenewsRememberMeCookie()
|
||||
{
|
||||
$service = $this->getService();
|
||||
|
||||
$token = $this->getMock(
|
||||
'Symfony\Component\Security\Authentication\Token\RememberMeToken',
|
||||
array(),
|
||||
array(),
|
||||
'NonFunctionalRememberMeTokenMockClass',
|
||||
false
|
||||
);
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('onLoginSuccess')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$service->loginSuccess(new Request(), new Response(), $token);
|
||||
}
|
||||
|
||||
protected function getService($userProvider = null, $options = array(), $logger = null)
|
||||
{
|
||||
if (null === $userProvider) {
|
||||
$userProvider = $this->getProvider();
|
||||
}
|
||||
|
||||
return $this->getMockForAbstractClass('Symfony\Component\HttpKernel\Security\RememberMe\RememberMeServices', array(
|
||||
array($userProvider), 'fookey', 'fookey', $options, $logger
|
||||
));
|
||||
}
|
||||
|
||||
protected function getProvider()
|
||||
{
|
||||
$provider = $this->getMock('Symfony\Component\Security\User\UserProviderInterface');
|
||||
$provider
|
||||
->expects($this->any())
|
||||
->method('supportsClass')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
return $provider;
|
||||
}
|
||||
}
|
@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\HttpKernel\Security\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\RememberMeToken;
|
||||
use Symfony\Component\Security\Authentication\Token\Token;
|
||||
use Symfony\Component\Security\Exception\UsernameNotFoundException;
|
||||
use Symfony\Component\Security\Authentication\RememberMe\PersistentToken;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Security\RememberMe\TokenBasedRememberMeServices;
|
||||
use Symfony\Component\Security\Exception\TokenNotFoundException;
|
||||
use Symfony\Component\Security\Exception\CookieTheftException;
|
||||
|
||||
class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testAutoLoginReturnsNullWhenNoCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo'));
|
||||
|
||||
$this->assertNull($service->autoLogin(new Request()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\AuthenticationException
|
||||
* @expectedMessage The cookie is invalid.
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionOnInvalidCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
|
||||
$request = new Request;
|
||||
$request->request->set('foo', 'true');
|
||||
$request->cookies->set('foo', 'foo');
|
||||
|
||||
$service->autoLogin($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\UsernameNotFoundException
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionOnNonExistentUser()
|
||||
{
|
||||
$userProvider = $this->getProvider();
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time()+3600, 'foopass'));
|
||||
|
||||
$userProvider
|
||||
->expects($this->once())
|
||||
->method('loadUserByUsername')
|
||||
->will($this->throwException(new UsernameNotFoundException('user not found')))
|
||||
;
|
||||
|
||||
$service->autoLogin($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\AuthenticationException
|
||||
* @expectedMessage The cookie's hash is invalid.
|
||||
*/
|
||||
public function testAutoLoginDoesNotAcceptCookieWithInvalidHash()
|
||||
{
|
||||
$userProvider = $this->getProvider();
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', base64_encode('class:'.base64_encode('foouser').':123456789:fooHash'));
|
||||
|
||||
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getPassword')
|
||||
->will($this->returnValue('foopass'))
|
||||
;
|
||||
|
||||
$userProvider
|
||||
->expects($this->once())
|
||||
->method('loadUserByUsername')
|
||||
->with($this->equalTo('foouser'))
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
|
||||
$service->autoLogin($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\AuthenticationException
|
||||
* @expectedMessage The cookie has expired.
|
||||
*/
|
||||
public function testAutoLoginDoesNotAcceptAnExpiredCookie()
|
||||
{
|
||||
$userProvider = $this->getProvider();
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time() - 1, 'foopass'));
|
||||
|
||||
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getPassword')
|
||||
->will($this->returnValue('foopass'))
|
||||
;
|
||||
|
||||
$userProvider
|
||||
->expects($this->once())
|
||||
->method('loadUserByUsername')
|
||||
->with($this->equalTo('foouser'))
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
|
||||
$service->autoLogin($request);
|
||||
}
|
||||
|
||||
public function testAutoLogin()
|
||||
{
|
||||
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array('ROLE_FOO')))
|
||||
;
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getPassword')
|
||||
->will($this->returnValue('foopass'))
|
||||
;
|
||||
|
||||
$userProvider = $this->getProvider();
|
||||
$userProvider
|
||||
->expects($this->once())
|
||||
->method('loadUserByUsername')
|
||||
->with($this->equalTo('foouser'))
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time()+3600, 'foopass'));
|
||||
|
||||
$returnedToken = $service->autoLogin($request);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Security\Authentication\Token\RememberMeToken', $returnedToken);
|
||||
$this->assertSame($user, $returnedToken->getUser());
|
||||
$this->assertEquals('fookey', $returnedToken->getKey());
|
||||
}
|
||||
|
||||
public function testLogout()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->logout($request, $response, $token);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$this->assertTrue($cookie->isCleared());
|
||||
$this->assertNull($cookie->getPath());
|
||||
$this->assertNull($cookie->getDomain());
|
||||
}
|
||||
|
||||
public function testLoginFail()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => '/foo', 'domain' => 'foodomain.foo'));
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginFail($request, $response);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$this->assertTrue($cookie->isCleared());
|
||||
$this->assertEquals('/foo', $cookie->getPath());
|
||||
$this->assertEquals('foodomain.foo', $cookie->getDomain());
|
||||
}
|
||||
|
||||
public function testLoginSuccessDoesNotRenewRememberMeToken()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
|
||||
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array('ROLE_FOO')))
|
||||
;
|
||||
|
||||
$token = new RememberMeToken($user, 'fookey', 'foo');
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
}
|
||||
|
||||
public function testLoginSuccessIgnoresTokensWhichDoNotContainAnAccountInterfaceImplementation()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getUser')
|
||||
->will($this->returnValue('foo'))
|
||||
;
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
}
|
||||
|
||||
public function testLoginSuccess()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600, 'always_remember_me' => true));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getPassword')
|
||||
->will($this->returnValue('foopass'))
|
||||
;
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getUsername')
|
||||
->will($this->returnValue('foouser'))
|
||||
;
|
||||
$token
|
||||
->expects($this->atLeastOnce())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$this->assertFalse($cookie->isCleared());
|
||||
$this->assertTrue($cookie->isSecure());
|
||||
$this->assertTrue($cookie->isHttponly());
|
||||
$this->assertTrue($cookie->getExpire() > time() + 3590 && $cookie->getExpire() < time() + 3610);
|
||||
$this->assertEquals('myfoodomain.foo', $cookie->getDomain());
|
||||
$this->assertEquals('/foo/path', $cookie->getPath());
|
||||
}
|
||||
|
||||
protected function getCookie($class, $username, $expires, $password)
|
||||
{
|
||||
$service = $this->getService();
|
||||
$r = new \ReflectionMethod($service, 'generateCookieValue');
|
||||
$r->setAccessible(true);
|
||||
|
||||
return $r->invoke($service, $class, $username, $expires, $password);
|
||||
}
|
||||
|
||||
protected function encodeCookie(array $parts)
|
||||
{
|
||||
$service = $this->getService();
|
||||
$r = new \ReflectionMethod($service, 'encodeCookie');
|
||||
$r->setAccessible(true);
|
||||
|
||||
return $r->invoke($service, $parts);
|
||||
}
|
||||
|
||||
protected function getService($userProvider = null, $options = array(), $logger = null)
|
||||
{
|
||||
if (null === $userProvider) {
|
||||
$userProvider = $this->getProvider();
|
||||
}
|
||||
|
||||
$service = new TokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger);
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
protected function getProvider()
|
||||
{
|
||||
$provider = $this->getMock('Symfony\Component\Security\User\UserProviderInterface');
|
||||
$provider
|
||||
->expects($this->any())
|
||||
->method('supportsClass')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
return $provider;
|
||||
}
|
||||
}
|
@ -25,27 +25,27 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
|
||||
protected $insertOidAncestorStmt;
|
||||
protected $insertOidStmt;
|
||||
protected $insertEntryStmt;
|
||||
|
||||
|
||||
public function testFindAcls()
|
||||
{
|
||||
// $this->generateTestData();
|
||||
|
||||
|
||||
// get some random test object identities from the database
|
||||
$oids = array();
|
||||
$stmt = $this->con->executeQuery("SELECT object_identifier, class_type FROM acl_object_identities o INNER JOIN acl_classes c ON c.id = o.class_id ORDER BY RAND() LIMIT 25");
|
||||
foreach ($stmt->fetchAll() as $oid) {
|
||||
$oids[] = new ObjectIdentity($oid['object_identifier'], $oid['class_type']);
|
||||
}
|
||||
|
||||
|
||||
$provider = $this->getProvider();
|
||||
|
||||
|
||||
$start = microtime(true);
|
||||
$provider->findAcls($oids);
|
||||
$time = microtime(true) - $start;
|
||||
echo "Total Time: ".$time."s\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
// comment the following line, and run only this test, if you need to benchmark
|
||||
@ -58,12 +58,12 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
|
||||
'dbname' => 'testdb',
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->con = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This generates a huge amount of test data to be used mainly for benchmarking
|
||||
* purposes, not so much for testing. That's why it's not called by default.
|
||||
@ -73,7 +73,7 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
|
||||
$sm = $this->con->getSchemaManager();
|
||||
$sm->dropAndCreateDatabase('testdb');
|
||||
$this->con->exec("USE testdb");
|
||||
|
||||
|
||||
// import the schema
|
||||
$schema = new Schema($options = $this->getOptions());
|
||||
foreach ($schema->toSql($this->con->getDatabasePlatform()) as $sql) {
|
||||
@ -86,49 +86,49 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
|
||||
$this->insertOidStmt = $this->con->prepare('INSERT INTO acl_object_identities (id, class_id, object_identifier, parent_object_identity_id, entries_inheriting) VALUES (?, ?, ?, ?, ?)');
|
||||
$this->insertEntryStmt = $this->con->prepare('INSERT INTO acl_entries (id, class_id, object_identity_id, field_name, ace_order, security_identity_id, mask, granting, granting_strategy, audit_success, audit_failure) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
||||
$this->insertOidAncestorStmt = $this->con->prepare('INSERT INTO acl_object_identity_ancestors (object_identity_id, ancestor_id) VALUES (?, ?)');
|
||||
|
||||
|
||||
for ($i=0; $i<40000; $i++) {
|
||||
$this->generateAclHierarchy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function generateAclHierarchy()
|
||||
{
|
||||
$rootId = $this->generateAcl($this->chooseClassId(), null, array());
|
||||
|
||||
|
||||
$this->generateAclLevel(rand(1, 15), $rootId, array($rootId));
|
||||
}
|
||||
|
||||
|
||||
protected function generateAclLevel($depth, $parentId, $ancestors)
|
||||
{
|
||||
$level = count($ancestors);
|
||||
for ($i=0,$t=rand(1, 10); $i<$t; $i++) {
|
||||
$id = $this->generateAcl($this->chooseClassId(), $parentId, $ancestors);
|
||||
|
||||
|
||||
if ($level < $depth) {
|
||||
$this->generateAclLevel($depth, $id, array_merge($ancestors, array($id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function chooseClassId()
|
||||
{
|
||||
static $id = 1000;
|
||||
|
||||
|
||||
if ($id === 1000 || ($id < 1500 && rand(0, 1))) {
|
||||
$this->insertClassStmt->execute(array($id, $this->getRandomString(rand(20, 100), 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\\_')));
|
||||
$id += 1;
|
||||
|
||||
return $id-1;
|
||||
}
|
||||
|
||||
return rand(1000, $id-1);
|
||||
return $id-1;
|
||||
} else {
|
||||
return rand(1000, $id-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function generateAcl($classId, $parentId, $ancestors)
|
||||
{
|
||||
static $id = 1000;
|
||||
|
||||
|
||||
$this->insertOidStmt->execute(array(
|
||||
$id,
|
||||
$classId,
|
||||
@ -136,57 +136,57 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
|
||||
$parentId,
|
||||
rand(0, 1),
|
||||
));
|
||||
|
||||
|
||||
$this->insertOidAncestorStmt->execute(array($id, $id));
|
||||
foreach ($ancestors as $ancestor) {
|
||||
$this->insertOidAncestorStmt->execute(array($id, $ancestor));
|
||||
}
|
||||
|
||||
|
||||
$this->generateAces($classId, $id);
|
||||
$id += 1;
|
||||
|
||||
|
||||
return $id-1;
|
||||
}
|
||||
|
||||
|
||||
protected function chooseSid()
|
||||
{
|
||||
static $id = 1000;
|
||||
|
||||
|
||||
if ($id === 1000 || ($id < 11000 && rand(0, 1))) {
|
||||
$this->insertSidStmt->execute(array(
|
||||
$id,
|
||||
$this->getRandomString(rand(5, 30)),
|
||||
$id,
|
||||
$this->getRandomString(rand(5, 30)),
|
||||
rand(0, 1)
|
||||
));
|
||||
$id += 1;
|
||||
|
||||
return $id-1;
|
||||
}
|
||||
|
||||
return rand(1000, $id-1);
|
||||
return $id-1;
|
||||
} else {
|
||||
return rand(1000, $id-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function generateAces($classId, $objectId)
|
||||
{
|
||||
static $id = 1000;
|
||||
|
||||
|
||||
$sids = array();
|
||||
$fieldOrder = array();
|
||||
|
||||
|
||||
for ($i=0; $i<=30; $i++) {
|
||||
$fieldName = rand(0, 1) ? null : $this->getRandomString(rand(10, 20));
|
||||
|
||||
|
||||
do {
|
||||
$sid = $this->chooseSid();
|
||||
}
|
||||
while (array_key_exists($sid, $sids) && in_array($fieldName, $sids[$sid], true));
|
||||
|
||||
|
||||
$fieldOrder[$fieldName] = array_key_exists($fieldName, $fieldOrder) ? $fieldOrder[$fieldName]+1 : 0;
|
||||
if (!isset($sids[$sid])) {
|
||||
$sids[$sid] = array();
|
||||
}
|
||||
$sids[$sid][] = $fieldName;
|
||||
|
||||
|
||||
$strategy = rand(0, 2);
|
||||
if ($strategy === 0) {
|
||||
$strategy = PermissionGrantingStrategy::ALL;
|
||||
@ -195,7 +195,7 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
|
||||
} else {
|
||||
$strategy = PermissionGrantingStrategy::EQUAL;
|
||||
}
|
||||
|
||||
|
||||
// id, cid, oid, field, order, sid, mask, granting, strategy, a success, a failure
|
||||
$this->insertEntryStmt->execute(array(
|
||||
$id,
|
||||
@ -210,36 +210,36 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
|
||||
rand(0, 1),
|
||||
rand(0, 1),
|
||||
));
|
||||
|
||||
|
||||
$id += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function generateMask()
|
||||
{
|
||||
$i = rand(1, 30);
|
||||
$mask = 0;
|
||||
|
||||
|
||||
while ($i <= 30) {
|
||||
$mask |= 1 << rand(0, 30);
|
||||
$i++;
|
||||
}
|
||||
|
||||
|
||||
return $mask;
|
||||
}
|
||||
|
||||
|
||||
protected function getRandomString($length, $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
|
||||
{
|
||||
$s = '';
|
||||
$cLength = strlen($chars);
|
||||
|
||||
|
||||
while (strlen($s) < $length) {
|
||||
$s .= $chars[mt_rand(0, $cLength-1)];
|
||||
}
|
||||
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
protected function getOptions()
|
||||
{
|
||||
return array(
|
||||
@ -249,15 +249,15 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase
|
||||
'sid_table_name' => 'acl_security_identities',
|
||||
'entry_table_name' => 'acl_entries',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected function getStrategy()
|
||||
{
|
||||
return new PermissionGrantingStrategy();
|
||||
}
|
||||
|
||||
|
||||
protected function getProvider()
|
||||
{
|
||||
return new AclProvider($this->con, $this->getStrategy(), $this->getOptions());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,17 +24,37 @@ class SecurityIdentityRetrievalStrategyTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$strategy = $this->getStrategy($roles, $authenticationStatus);
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
if ('anonymous' === $authenticationStatus) {
|
||||
$token = $this->getMockBuilder('Symfony\Component\Security\Authentication\Token\AnonymousToken')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
} else {
|
||||
$class = '';
|
||||
if (is_string($user)) {
|
||||
$class = 'MyCustomTokenImpl';
|
||||
}
|
||||
|
||||
$token = $this->getMockBuilder('Symfony\Component\Security\Authentication\Token\TokenInterface')
|
||||
->setMockClassName($class)
|
||||
->getMock();
|
||||
}
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array('foo')))
|
||||
;
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
if ('anonymous' === $authenticationStatus) {
|
||||
$token
|
||||
->expects($this->never())
|
||||
->method('getUser')
|
||||
;
|
||||
} else {
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
}
|
||||
|
||||
$extractedSids = $strategy->getSecurityIdentities($token);
|
||||
|
||||
@ -60,6 +80,20 @@ class SecurityIdentityRetrievalStrategyTest extends \PHPUnit_Framework_TestCase
|
||||
new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'),
|
||||
new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'),
|
||||
)),
|
||||
array('johannes', array('ROLE_FOO'), 'fullFledged', array(
|
||||
new UserSecurityIdentity('johannes', 'MyCustomTokenImpl'),
|
||||
new RoleSecurityIdentity('ROLE_FOO'),
|
||||
new RoleSecurityIdentity('IS_AUTHENTICATED_FULLY'),
|
||||
new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'),
|
||||
new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'),
|
||||
)),
|
||||
array(new CustomUserImpl('johannes'), array('ROLE_FOO'), 'fullFledged', array(
|
||||
new UserSecurityIdentity('johannes', 'Symfony\Tests\Component\Security\Acl\Domain\CustomUserImpl'),
|
||||
new RoleSecurityIdentity('ROLE_FOO'),
|
||||
new RoleSecurityIdentity('IS_AUTHENTICATED_FULLY'),
|
||||
new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'),
|
||||
new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'),
|
||||
)),
|
||||
array($this->getAccount('foo', 'FooBarUser'), array('ROLE_FOO'), 'rememberMe', array(
|
||||
new UserSecurityIdentity('foo', 'FooBarUser'),
|
||||
new RoleSecurityIdentity('ROLE_FOO'),
|
||||
@ -77,11 +111,12 @@ class SecurityIdentityRetrievalStrategyTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$account = $this->getMock('Symfony\Component\Security\User\AccountInterface', array(), array(), $class);
|
||||
$account
|
||||
->expects($this->once())
|
||||
->method('__toString')
|
||||
->expects($this->any())
|
||||
->method('getUsername')
|
||||
->will($this->returnValue($username))
|
||||
;
|
||||
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
@ -145,4 +180,19 @@ class SecurityIdentityRetrievalStrategyTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
return new SecurityIdentityRetrievalStrategy($roleHierarchy, $trustResolver);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomUserImpl
|
||||
{
|
||||
protected $name;
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
@ -29,22 +29,27 @@ class UserSecurityIdentityTest extends \PHPUnit_Framework_TestCase
|
||||
*/
|
||||
public function testEquals($id1, $id2, $equal)
|
||||
{
|
||||
if ($equal) {
|
||||
$this->assertTrue($id1->equals($id2));
|
||||
} else {
|
||||
$this->assertFalse($id1->equals($id2));
|
||||
}
|
||||
$this->assertSame($equal, $id1->equals($id2));
|
||||
}
|
||||
|
||||
public function getCompareData()
|
||||
{
|
||||
$account = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$account = $this->getMockBuilder('Symfony\Component\Security\User\AccountInterface')
|
||||
->setMockClassName('USI_AccountImpl')
|
||||
->getMock();
|
||||
$account
|
||||
->expects($this->once())
|
||||
->method('__toString')
|
||||
->expects($this->any())
|
||||
->method('getUsername')
|
||||
->will($this->returnValue('foo'))
|
||||
;
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
$token
|
||||
->expects($this->any())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($account))
|
||||
;
|
||||
|
||||
return array(
|
||||
array(new UserSecurityIdentity('foo', 'Foo'), new UserSecurityIdentity('foo', 'Foo'), true),
|
||||
array(new UserSecurityIdentity('foo', 'Bar'), new UserSecurityIdentity('foo', 'Foo'), false),
|
||||
@ -52,6 +57,8 @@ class UserSecurityIdentityTest extends \PHPUnit_Framework_TestCase
|
||||
array(new UserSecurityIdentity('foo', 'Foo'), UserSecurityIdentity::fromAccount($account), false),
|
||||
array(new UserSecurityIdentity('bla', 'Foo'), new UserSecurityIdentity('blub', 'Foo'), false),
|
||||
array(new UserSecurityIdentity('foo', 'Foo'), new RoleSecurityIdentity('foo'), false),
|
||||
array(new UserSecurityIdentity('foo', 'Foo'), UserSecurityIdentity::fromToken($token), false),
|
||||
array(new UserSecurityIdentity('foo', 'USI_AccountImpl'), UserSecurityIdentity::fromToken($token), true),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -105,14 +105,14 @@ class AuthenticationProviderManagerTest extends \PHPUnit_Framework_TestCase
|
||||
public function testEraseCredentialFlag()
|
||||
{
|
||||
$manager = new AuthenticationProviderManager(array(
|
||||
$this->getAuthenticationProvider(true, $token = new UsernamePasswordToken('foo', 'bar')),
|
||||
$this->getAuthenticationProvider(true, $token = new UsernamePasswordToken('foo', 'bar', 'key')),
|
||||
));
|
||||
|
||||
$token = $manager->authenticate($this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface'));
|
||||
$this->assertEquals('', $token->getCredentials());
|
||||
|
||||
$manager = new AuthenticationProviderManager(array(
|
||||
$this->getAuthenticationProvider(true, $token = new UsernamePasswordToken('foo', 'bar')),
|
||||
$this->getAuthenticationProvider(true, $token = new UsernamePasswordToken('foo', 'bar', 'key')),
|
||||
), false);
|
||||
|
||||
$token = $manager->authenticate($this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface'));
|
||||
|
@ -42,7 +42,7 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->throwException($this->getMock('Symfony\Component\Security\Exception\UsernameNotFoundException', null, array(), '', false)))
|
||||
;
|
||||
|
||||
$provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
|
||||
$provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), 'key', $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
|
||||
$method = new \ReflectionMethod($provider, 'retrieveUser');
|
||||
$method->setAccessible(true);
|
||||
|
||||
@ -60,7 +60,7 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->throwException($this->getMock('RuntimeException', null, array(), '', false)))
|
||||
;
|
||||
|
||||
$provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
|
||||
$provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), 'key', $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
|
||||
$method = new \ReflectionMethod($provider, 'retrieveUser');
|
||||
$method->setAccessible(true);
|
||||
|
||||
@ -81,7 +81,7 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
|
||||
$provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
|
||||
$provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), 'key', $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
|
||||
$reflection = new \ReflectionMethod($provider, 'retrieveUser');
|
||||
$reflection->setAccessible(true);
|
||||
$result = $reflection->invoke($provider, null, $token);
|
||||
@ -99,7 +99,7 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
|
||||
$provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
|
||||
$provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), 'key', $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
|
||||
$method = new \ReflectionMethod($provider, 'retrieveUser');
|
||||
$method->setAccessible(true);
|
||||
|
||||
@ -224,7 +224,14 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
protected function getSupportedToken()
|
||||
{
|
||||
return $this->getMock('Symfony\Component\Security\Authentication\Token\UsernamePasswordToken', array('getCredentials', 'getUser'), array(), '', false);
|
||||
$mock = $this->getMock('Symfony\Component\Security\Authentication\Token\UsernamePasswordToken', array('getCredentials', 'getUser'), array(), '', false);
|
||||
$mock
|
||||
->expects($this->any())
|
||||
->method('getProviderKey')
|
||||
->will($this->returnValue('key'))
|
||||
;
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function getProvider($user = false, $userChecker = false, $passwordEncoder = null)
|
||||
@ -252,6 +259,6 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->returnValue($passwordEncoder))
|
||||
;
|
||||
|
||||
return new DaoAuthenticationProvider($userProvider, $userChecker, $encoderFactory);
|
||||
return new DaoAuthenticationProvider($userProvider, $userChecker, 'key', $encoderFactory);
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,17 @@ class PreAuthenticatedAuthenticationProviderTest extends \PHPUnit_Framework_Test
|
||||
|
||||
$this->assertTrue($provider->supports($this->getSupportedToken()));
|
||||
$this->assertFalse($provider->supports($this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface')));
|
||||
|
||||
$token = $this->getMockBuilder('Symfony\Component\Security\Authentication\Token\PreAuthenticatedToken')
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getProviderKey')
|
||||
->will($this->returnValue('foo'))
|
||||
;
|
||||
$this->assertFalse($provider->supports($token));
|
||||
}
|
||||
|
||||
public function testAuthenticateWhenTokenIsNotSupported()
|
||||
@ -71,7 +82,7 @@ class PreAuthenticatedAuthenticationProviderTest extends \PHPUnit_Framework_Test
|
||||
|
||||
protected function getSupportedToken($user = false, $credentials = false)
|
||||
{
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\PreAuthenticatedToken', array('getUser', 'getCredentials'), array(), '', false);
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\PreAuthenticatedToken', array('getUser', 'getCredentials', 'getProviderKey'), array(), '', false);
|
||||
if (false !== $user) {
|
||||
$token->expects($this->once())
|
||||
->method('getUser')
|
||||
@ -85,6 +96,12 @@ class PreAuthenticatedAuthenticationProviderTest extends \PHPUnit_Framework_Test
|
||||
;
|
||||
}
|
||||
|
||||
$token
|
||||
->expects($this->any())
|
||||
->method('getProviderKey')
|
||||
->will($this->returnValue('key'))
|
||||
;
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
@ -102,6 +119,6 @@ class PreAuthenticatedAuthenticationProviderTest extends \PHPUnit_Framework_Test
|
||||
$userChecker = $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface');
|
||||
}
|
||||
|
||||
return new PreAuthenticatedAuthenticationProvider($userProvider, $userChecker);
|
||||
return new PreAuthenticatedAuthenticationProvider($userProvider, $userChecker, 'key');
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\Security\Authentication\Provider;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Provider\RememberMeAuthenticationProvider;
|
||||
use Symfony\Component\Security\Authentication\Token\RememberMeToken;
|
||||
use Symfony\Component\Security\Role\Role;
|
||||
|
||||
class RememberMeAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testSupports()
|
||||
{
|
||||
$provider = $this->getProvider();
|
||||
|
||||
$this->assertTrue($provider->supports($this->getSupportedToken()));
|
||||
$this->assertFalse($provider->supports($this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface')));
|
||||
}
|
||||
|
||||
public function testAuthenticateWhenTokenIsNotSupported()
|
||||
{
|
||||
$provider = $this->getProvider();
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface');
|
||||
$this->assertNull($provider->authenticate($token));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\BadCredentialsException
|
||||
*/
|
||||
public function testAuthenticateWhenKeysDoNotMatch()
|
||||
{
|
||||
$provider = $this->getProvider(null, 'key1');
|
||||
$token = $this->getSupportedToken(null, 'key2');
|
||||
|
||||
$provider->authenticate($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\CredentialsExpiredException
|
||||
*/
|
||||
public function testAuthenticateWhenPreChecksFails()
|
||||
{
|
||||
$userChecker = $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface');
|
||||
$userChecker->expects($this->once())
|
||||
->method('checkPreAuth')
|
||||
->will($this->throwException($this->getMock('Symfony\Component\Security\Exception\CredentialsExpiredException', null, array(), '', false)))
|
||||
;
|
||||
|
||||
$provider = $this->getProvider($userChecker);
|
||||
|
||||
$provider->authenticate($this->getSupportedToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\AccountExpiredException
|
||||
*/
|
||||
public function testAuthenticateWhenPostChecksFails()
|
||||
{
|
||||
$userChecker = $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface');
|
||||
$userChecker->expects($this->once())
|
||||
->method('checkPostAuth')
|
||||
->will($this->throwException($this->getMock('Symfony\Component\Security\Exception\AccountExpiredException', null, array(), '', false)))
|
||||
;
|
||||
|
||||
$provider = $this->getProvider($userChecker);
|
||||
|
||||
$provider->authenticate($this->getSupportedToken());
|
||||
}
|
||||
|
||||
public function testAuthenticate()
|
||||
{
|
||||
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$user->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array('ROLE_FOO')))
|
||||
;
|
||||
|
||||
$provider = $this->getProvider();
|
||||
|
||||
$token = $this->getSupportedToken($user);
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getCredentials')
|
||||
->will($this->returnValue('foo'))
|
||||
;
|
||||
|
||||
$authToken = $provider->authenticate($token);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Security\Authentication\Token\RememberMeToken', $authToken);
|
||||
$this->assertSame($user, $authToken->getUser());
|
||||
$this->assertEquals(array(new Role('ROLE_FOO')), $authToken->getRoles());
|
||||
$this->assertEquals('foo', $authToken->getCredentials());
|
||||
}
|
||||
|
||||
protected function getSupportedToken($user = null, $key = 'test')
|
||||
{
|
||||
if (null === $user) {
|
||||
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$user
|
||||
->expects($this->any())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array()))
|
||||
;
|
||||
}
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Authentication\Token\RememberMeToken', array('getCredentials', 'getProviderKey'), array($user, 'foo', $key));
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getProviderKey')
|
||||
->will($this->returnValue('foo'))
|
||||
;
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
protected function getProvider($userChecker = null, $key = 'test')
|
||||
{
|
||||
if (null === $userChecker) {
|
||||
$userChecker = $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface');
|
||||
}
|
||||
|
||||
return new RememberMeAuthenticationProvider($userChecker, $key, 'foo');
|
||||
}
|
||||
}
|
@ -161,7 +161,14 @@ class UserAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
protected function getSupportedToken()
|
||||
{
|
||||
return $this->getMock('Symfony\Component\Security\Authentication\Token\UsernamePasswordToken', array('getCredentials'), array(), '', false);
|
||||
$mock = $this->getMock('Symfony\Component\Security\Authentication\Token\UsernamePasswordToken', array('getCredentials', 'getProviderKey'), array(), '', false);
|
||||
$mock
|
||||
->expects($this->any())
|
||||
->method('getProviderKey')
|
||||
->will($this->returnValue('key'))
|
||||
;
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function getProvider($userChecker = false, $hide = true)
|
||||
@ -170,6 +177,6 @@ class UserAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
|
||||
$userChecker = $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface');
|
||||
}
|
||||
|
||||
return $this->getMockForAbstractClass('Symfony\Component\Security\Authentication\Provider\UserAuthenticationProvider', array($userChecker, $hide));
|
||||
return $this->getMockForAbstractClass('Symfony\Component\Security\Authentication\Provider\UserAuthenticationProvider', array($userChecker, 'key', $hide));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\Security\Authentication\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\Authentication\RememberMe\PersistentToken;
|
||||
use Symfony\Component\Security\Authentication\RememberMe\InMemoryTokenProvider;
|
||||
|
||||
class InMemoryTokenProviderTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testCreateNewToken()
|
||||
{
|
||||
$provider = new InMemoryTokenProvider();
|
||||
|
||||
$token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTime());
|
||||
$provider->createNewToken($token);
|
||||
|
||||
$this->assertSame($provider->loadTokenBySeries('foo'), $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\TokenNotFoundException
|
||||
*/
|
||||
public function testLoadTokenBySeriesThrowsNotFoundException()
|
||||
{
|
||||
$provider = new InMemoryTokenProvider();
|
||||
$provider->loadTokenBySeries('foo');
|
||||
}
|
||||
|
||||
public function testUpdateToken()
|
||||
{
|
||||
$provider = new InMemoryTokenProvider();
|
||||
|
||||
$token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTime());
|
||||
$provider->createNewToken($token);
|
||||
$provider->updateToken('foo', 'newFoo', $lastUsed = new \DateTime());
|
||||
$token = $provider->loadTokenBySeries('foo');
|
||||
|
||||
$this->assertEquals('newFoo', $token->getTokenValue());
|
||||
$this->assertSame($token->getLastUsed(), $lastUsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Exception\TokenNotFoundException
|
||||
*/
|
||||
public function testDeleteToken()
|
||||
{
|
||||
$provider = new InMemoryTokenProvider();
|
||||
|
||||
$token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTime());
|
||||
$provider->createNewToken($token);
|
||||
$provider->deleteTokenBySeries('foo');
|
||||
$provider->loadTokenBySeries('foo');
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\Security\Authentication\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\Authentication\RememberMe\PersistentToken;
|
||||
|
||||
class PersistentTokenTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testConstructor()
|
||||
{
|
||||
$lastUsed = new \DateTime();
|
||||
$token = new PersistentToken('fooclass', 'fooname', 'fooseries', 'footokenvalue', $lastUsed);
|
||||
|
||||
$this->assertEquals('fooclass', $token->getClass());
|
||||
$this->assertEquals('fooname', $token->getUsername());
|
||||
$this->assertEquals('fooseries', $token->getSeries());
|
||||
$this->assertEquals('footokenvalue', $token->getTokenValue());
|
||||
$this->assertSame($lastUsed, $token->getLastUsed());
|
||||
}
|
||||
}
|
@ -18,29 +18,30 @@ class PreAuthenticatedTokenTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testConstructor()
|
||||
{
|
||||
$token = new PreAuthenticatedToken('foo', 'bar');
|
||||
$token = new PreAuthenticatedToken('foo', 'bar', 'key');
|
||||
$this->assertFalse($token->isAuthenticated());
|
||||
|
||||
$token = new PreAuthenticatedToken('foo', 'bar', array('ROLE_FOO'));
|
||||
$token = new PreAuthenticatedToken('foo', 'bar', 'key', array('ROLE_FOO'));
|
||||
$this->assertTrue($token->isAuthenticated());
|
||||
$this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles());
|
||||
$this->assertEquals('key', $token->getProviderKey());
|
||||
}
|
||||
|
||||
public function testGetCredentials()
|
||||
{
|
||||
$token = new PreAuthenticatedToken('foo', 'bar');
|
||||
$token = new PreAuthenticatedToken('foo', 'bar', 'key');
|
||||
$this->assertEquals('bar', $token->getCredentials());
|
||||
}
|
||||
|
||||
public function testGetUser()
|
||||
{
|
||||
$token = new PreAuthenticatedToken('foo', 'bar');
|
||||
$token = new PreAuthenticatedToken('foo', 'bar', 'key');
|
||||
$this->assertEquals('foo', $token->getUser());
|
||||
}
|
||||
|
||||
public function testEraseCredentials()
|
||||
{
|
||||
$token = new PreAuthenticatedToken('foo', 'bar');
|
||||
$token = new PreAuthenticatedToken('foo', 'bar', 'key');
|
||||
$token->eraseCredentials();
|
||||
$this->assertEquals('', $token->getCredentials());
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\Security\Authentication\Token;
|
||||
|
||||
use Symfony\Component\Security\Authentication\Token\RememberMeToken;
|
||||
use Symfony\Component\Security\Role\Role;
|
||||
|
||||
class RememberMeTokenTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testConstructor()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$token = new RememberMeToken($user, 'fookey', 'foo');
|
||||
|
||||
$this->assertEquals('fookey', $token->getProviderKey());
|
||||
$this->assertEquals('foo', $token->getKey());
|
||||
$this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles());
|
||||
$this->assertSame($user, $token->getUser());
|
||||
$this->assertTrue($token->isAuthenticated());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testConstructorKeyCannotBeNull()
|
||||
{
|
||||
new RememberMeToken(
|
||||
$this->getUser(),
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testConstructorKeyCannotBeEmptyString()
|
||||
{
|
||||
new RememberMeToken(
|
||||
$this->getUser(),
|
||||
'',
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException PHPUnit_Framework_Error
|
||||
* @dataProvider getUserArguments
|
||||
*/
|
||||
public function testConstructorUserCannotBeNull($user)
|
||||
{
|
||||
new RememberMeToken($user, 'foo', 'foo');
|
||||
}
|
||||
|
||||
public function getUserArguments()
|
||||
{
|
||||
return array(
|
||||
array(null),
|
||||
array('foo'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testPersistentToken()
|
||||
{
|
||||
$token = new RememberMeToken($this->getUser(), 'fookey', 'foo');
|
||||
$persistentToken = $this->getMock('Symfony\Component\Security\Authentication\RememberMe\PersistentTokenInterface');
|
||||
|
||||
$this->assertNull($token->getPersistentToken());
|
||||
$token->setPersistentToken($persistentToken);
|
||||
$this->assertSame($persistentToken, $token->getPersistentToken());
|
||||
}
|
||||
|
||||
protected function getUser($roles = array('ROLE_FOO'))
|
||||
{
|
||||
$user = $this->getMock('Symfony\Component\Security\User\AccountInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue($roles))
|
||||
;
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
@ -18,12 +18,13 @@ class UsernamePasswordTokenTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testConstructor()
|
||||
{
|
||||
$token = new UsernamePasswordToken('foo', 'bar');
|
||||
$token = new UsernamePasswordToken('foo', 'bar', 'key');
|
||||
$this->assertFalse($token->isAuthenticated());
|
||||
|
||||
$token = new UsernamePasswordToken('foo', 'bar', array('ROLE_FOO'));
|
||||
$token = new UsernamePasswordToken('foo', 'bar', 'key', array('ROLE_FOO'));
|
||||
$this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles());
|
||||
$this->assertTrue($token->isAuthenticated());
|
||||
$this->assertEquals('key', $token->getProviderKey());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -31,20 +32,20 @@ class UsernamePasswordTokenTest extends \PHPUnit_Framework_TestCase
|
||||
*/
|
||||
public function testSetAuthenticatedToTrue()
|
||||
{
|
||||
$token = new UsernamePasswordToken('foo', 'bar');
|
||||
$token = new UsernamePasswordToken('foo', 'bar', 'key');
|
||||
$token->setAuthenticated(true);
|
||||
}
|
||||
|
||||
public function testSetAuthenticatedToFalse()
|
||||
{
|
||||
$token = new UsernamePasswordToken('foo', 'bar');
|
||||
$token = new UsernamePasswordToken('foo', 'bar', 'key');
|
||||
$token->setAuthenticated(false);
|
||||
$this->assertFalse($token->isAuthenticated());
|
||||
}
|
||||
|
||||
public function testEraseCredentials()
|
||||
{
|
||||
$token = new UsernamePasswordToken('foo', 'bar');
|
||||
$token = new UsernamePasswordToken('foo', 'bar', 'key');
|
||||
$token->eraseCredentials();
|
||||
$this->assertEquals('', $token->getCredentials());
|
||||
}
|
||||
|
@ -11,13 +11,14 @@
|
||||
|
||||
namespace Symfony\Tests\Component\Security;
|
||||
|
||||
use Symfony\Component\Security\Authentication\AuthenticationProviderManager;
|
||||
use Symfony\Component\Security\SecurityContext;
|
||||
|
||||
class SecurityContextTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testIsAuthenticated()
|
||||
{
|
||||
$context = new SecurityContext();
|
||||
$context = new SecurityContext($this->getMock('Symfony\Component\Security\Authentication\AuthenticationManagerInterface'));
|
||||
|
||||
$this->assertFalse($context->isAuthenticated());
|
||||
|
||||
@ -34,7 +35,7 @@ class SecurityContextTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testGetUser()
|
||||
{
|
||||
$context = new SecurityContext();
|
||||
$context = new SecurityContext($this->getMock('Symfony\Component\Security\Authentication\AuthenticationManagerInterface'));
|
||||
|
||||
$this->assertNull($context->getUser());
|
||||
|
||||
@ -44,33 +45,64 @@ class SecurityContextTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals('foo', $context->getUser());
|
||||
}
|
||||
|
||||
public function testVoteAuthenticatesTokenIfNecessary()
|
||||
{
|
||||
$authManager = $this->getMock('Symfony\Component\Security\Authentication\AuthenticationManagerInterface');
|
||||
$decisionManager = $this->getMock('Symfony\Component\Security\Authorization\AccessDecisionManagerInterface');
|
||||
|
||||
$context = new SecurityContext($authManager, $decisionManager);
|
||||
$context->setToken($token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface'));
|
||||
|
||||
$authManager
|
||||
->expects($this->once())
|
||||
->method('authenticate')
|
||||
->with($this->equalTo($token))
|
||||
->will($this->returnValue($newToken = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface')))
|
||||
;
|
||||
|
||||
$decisionManager
|
||||
->expects($this->once())
|
||||
->method('decide')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertTrue($context->vote('foo'));
|
||||
$this->assertSame($newToken, $context->getToken());
|
||||
}
|
||||
|
||||
public function testVote()
|
||||
{
|
||||
$context = new SecurityContext();
|
||||
|
||||
$context = new SecurityContext($this->getMock('Symfony\Component\Security\Authentication\AuthenticationManagerInterface'));
|
||||
$this->assertFalse($context->vote('ROLE_FOO'));
|
||||
|
||||
$context->setToken($token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface'));
|
||||
$this->assertFalse($context->vote('ROLE_FOO'));
|
||||
|
||||
$manager = $this->getMock('Symfony\Component\Security\Authorization\AccessDecisionManager');
|
||||
$manager = $this->getMock('Symfony\Component\Security\Authorization\AccessDecisionManagerInterface');
|
||||
$manager->expects($this->once())->method('decide')->will($this->returnValue(false));
|
||||
$context = new SecurityContext($manager);
|
||||
$context = new SecurityContext($this->getMock('Symfony\Component\Security\Authentication\AuthenticationManagerInterface'), $manager);
|
||||
$context->setToken($token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface'));
|
||||
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('isAuthenticated')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
$this->assertFalse($context->vote('ROLE_FOO'));
|
||||
|
||||
$manager = $this->getMock('Symfony\Component\Security\Authorization\AccessDecisionManager');
|
||||
$manager = $this->getMock('Symfony\Component\Security\Authorization\AccessDecisionManagerInterface');
|
||||
$manager->expects($this->once())->method('decide')->will($this->returnValue(true));
|
||||
$context = new SecurityContext($manager);
|
||||
$context = new SecurityContext($this->getMock('Symfony\Component\Security\Authentication\AuthenticationManagerInterface'), $manager);
|
||||
$context->setToken($token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface'));
|
||||
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('isAuthenticated')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
$this->assertTrue($context->vote('ROLE_FOO'));
|
||||
}
|
||||
|
||||
public function testGetSetToken()
|
||||
{
|
||||
$context = new SecurityContext();
|
||||
$context = new SecurityContext($this->getMock('Symfony\Component\Security\Authentication\AuthenticationManagerInterface'));
|
||||
$this->assertNull($context->getToken());
|
||||
|
||||
$context->setToken($token = $this->getMock('Symfony\Component\Security\Authentication\Token\TokenInterface'));
|
||||
|
@ -121,7 +121,7 @@ class UserTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$user = new User('fabien', 'superpass');
|
||||
$user->eraseCredentials();
|
||||
$this->assertNull($user->getPassword());
|
||||
$this->assertEquals('superpass', $user->getPassword());
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user