[Security] Added a REMOTE_USER based listener to security firewalls

This commit is contained in:
Maxime Douailin 2014-04-12 11:42:42 +02:00 committed by Fabien Potencier
parent 2aea588ceb
commit a2872f21b9
9 changed files with 223 additions and 0 deletions

View File

@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* RemoteUserFactory creates services for REMOTE_USER based authentication.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Maxime Douailin <maxime.douailin@gmail.com>
*/
class RemoteUserFactory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.pre_authenticated.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.pre_authenticated'))
->replaceArgument(0, new Reference($userProvider))
->addArgument($id)
;
$listenerId = 'security.authentication.listener.remote_user.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.remote_user'));
$listener->replaceArgument(2, $id);
$listener->replaceArgument(3, $config['user']);
return array($providerId, $listenerId, $defaultEntryPoint);
}
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'remote-user';
}
public function addConfiguration(NodeDefinition $node)
{
$node
->children()
->scalarNode('provider')->end()
->scalarNode('user')->defaultValue('REMOTE_USER')->end()
->end()
;
}
}

View File

@ -24,6 +24,8 @@
<parameter key="security.authentication.listener.x509.class">Symfony\Component\Security\Http\Firewall\X509AuthenticationListener</parameter> <parameter key="security.authentication.listener.x509.class">Symfony\Component\Security\Http\Firewall\X509AuthenticationListener</parameter>
<parameter key="security.authentication.listener.remote_user.class">Symfony\Component\Security\Http\Firewall\RemoteUserAuthenticationListener</parameter>
<parameter key="security.authentication.listener.anonymous.class">Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener</parameter> <parameter key="security.authentication.listener.anonymous.class">Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener</parameter>
<parameter key="security.authentication.switchuser_listener.class">Symfony\Component\Security\Http\Firewall\SwitchUserListener</parameter> <parameter key="security.authentication.switchuser_listener.class">Symfony\Component\Security\Http\Firewall\SwitchUserListener</parameter>
@ -173,6 +175,16 @@
<argument type="service" id="event_dispatcher" on-invalid="null"/> <argument type="service" id="event_dispatcher" on-invalid="null"/>
</service> </service>
<service id="security.authentication.listener.remote_user" class="%security.authentication.listener.remote_user.class%" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.context" />
<argument type="service" id="security.authentication.manager" />
<argument /> <!-- Provider-shared Key -->
<argument /> <!-- REMOTE_USER server env var -->
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="event_dispatcher" on-invalid="null"/>
</service>
<service id="security.authentication.listener.basic" class="%security.authentication.listener.basic.class%" public="false" abstract="true"> <service id="security.authentication.listener.basic" class="%security.authentication.listener.basic.class%" public="false" abstract="true">
<tag name="monolog.logger" channel="security" /> <tag name="monolog.logger" channel="security" />
<argument type="service" id="security.context" /> <argument type="service" id="security.context" />

View File

@ -19,6 +19,7 @@ use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasic
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpDigestFactory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpDigestFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\X509Factory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\X509Factory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RemoteUserFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimplePreAuthenticationFactory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimplePreAuthenticationFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimpleFormFactory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimpleFormFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\InMemoryFactory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\InMemoryFactory;
@ -40,6 +41,7 @@ class SecurityBundle extends Bundle
$extension->addSecurityListenerFactory(new HttpDigestFactory()); $extension->addSecurityListenerFactory(new HttpDigestFactory());
$extension->addSecurityListenerFactory(new RememberMeFactory()); $extension->addSecurityListenerFactory(new RememberMeFactory());
$extension->addSecurityListenerFactory(new X509Factory()); $extension->addSecurityListenerFactory(new X509Factory());
$extension->addSecurityListenerFactory(new RemoteUserFactory());
$extension->addSecurityListenerFactory(new SimplePreAuthenticationFactory()); $extension->addSecurityListenerFactory(new SimplePreAuthenticationFactory());
$extension->addSecurityListenerFactory(new SimpleFormFactory()); $extension->addSecurityListenerFactory(new SimpleFormFactory());

View File

@ -79,6 +79,7 @@ abstract class CompleteConfigurationTest extends \PHPUnit_Framework_TestCase
'security.channel_listener', 'security.channel_listener',
'security.logout_listener.secure', 'security.logout_listener.secure',
'security.authentication.listener.x509.secure', 'security.authentication.listener.x509.secure',
'security.authentication.listener.remote_user.secure',
'security.authentication.listener.form.secure', 'security.authentication.listener.form.secure',
'security.authentication.listener.basic.secure', 'security.authentication.listener.basic.secure',
'security.authentication.listener.digest.secure', 'security.authentication.listener.digest.secure',

View File

@ -69,6 +69,7 @@ $container->loadFromExtension('security', array(
'anonymous' => true, 'anonymous' => true,
'switch_user' => true, 'switch_user' => true,
'x509' => true, 'x509' => true,
'remote_user' => true,
'logout' => true, 'logout' => true,
), ),
'host' => array( 'host' => array(

View File

@ -54,6 +54,7 @@
<anonymous /> <anonymous />
<switch-user /> <switch-user />
<x509 /> <x509 />
<remote-user />
<logout /> <logout />
</firewall> </firewall>

View File

@ -52,6 +52,7 @@ security:
anonymous: true anonymous: true
switch_user: true switch_user: true
x509: true x509: true
remote_user: true
logout: true logout: true
host: host:
pattern: /test pattern: /test

View File

@ -0,0 +1,49 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* REMOTE_USER authentication listener.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Maxime Douailin <maxime.douailin@gmail.com>
*/
class RemoteUserAuthenticationListener extends AbstractPreAuthenticatedListener
{
private $userKey;
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, $userKey = 'REMOTE_USER', LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
{
parent::__construct($securityContext, $authenticationManager, $providerKey, $logger, $dispatcher);
$this->userKey = $userKey;
}
/**
* {@inheritdoc}
*/
protected function getPreAuthenticatedData(Request $request)
{
if (!$request->server->has($this->userKey)) {
throw new BadCredentialsException(sprintf('User key was not found: %s', $this->userKey));
}
return array($request->server->get($this->userKey), null);
}
}

View File

@ -0,0 +1,91 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Tests\Firewall;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Firewall\RemoteUserAuthenticationListener;
class RemoteUserAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
{
public function testGetPreAuthenticatedData()
{
$serverVars = array(
'REMOTE_USER' => 'TheUser'
);
$request = new Request(array(), array(), array(), array(), array(), $serverVars);
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
$authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
$listener = new RemoteUserAuthenticationListener(
$context,
$authenticationManager,
'TheProviderKey'
);
$method = new \ReflectionMethod($listener, 'getPreAuthenticatedData');
$method->setAccessible(true);
$result = $method->invokeArgs($listener, array($request));
$this->assertSame($result, array('TheUser', null));
}
/**
* @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException
*/
public function testGetPreAuthenticatedDataNoUser()
{
$request = new Request(array(), array(), array(), array(), array(), array());
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
$authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
$listener = new RemoteUserAuthenticationListener(
$context,
$authenticationManager,
'TheProviderKey'
);
$method = new \ReflectionMethod($listener, 'getPreAuthenticatedData');
$method->setAccessible(true);
$result = $method->invokeArgs($listener, array($request));
}
public function testGetPreAuthenticatedDataWithDifferentKeys()
{
$userCredentials = array('TheUser', null);
$request = new Request(array(), array(), array(), array(), array(), array(
'TheUserKey' => 'TheUser'
));
$context = $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface');
$authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
$listener = new RemoteUserAuthenticationListener(
$context,
$authenticationManager,
'TheProviderKey',
'TheUserKey'
);
$method = new \ReflectionMethod($listener, 'getPreAuthenticatedData');
$method->setAccessible(true);
$result = $method->invokeArgs($listener, array($request));
$this->assertSame($result, $userCredentials);
}
}