Skip to content

Commit

Permalink
merged branch Seldaek/simplesecurity (PR #6069)
Browse files Browse the repository at this point in the history
This PR was merged into the master branch.

Discussion
----------

[Security] Add simpler customization options

The goal of this is to provide a simpler extension point for people that don't have the time to dive into the whole security factory + authentication provider + user provider + authentication listener + token mess. As it stands, it gives you a way to just create one class that is handling all the security stuff in one (by implementing SimpleFormAuthenticatorInterface and UserProviderInterface) + one or more token classes.

I would like feedback on whether people think this makes sense or not before continuing and doing a SimpleHttpAuthenticatorInterface for non-form based stuff.

Just FYI that's how it would look in security.yml:

```yaml
security:
    providers:
        simple:
            id: simple_authenticator
    firewalls:
        foo:
            pattern: ^/
            simple_form:
                provider: simple
                authenticator: simple_authenticator
```

/cc @atrauzzi (who posted a long rant on the ML about how hard this all is, and I can't agree more - I hope it's the right account on github?)

Commits
-------

74cfc84 marked some classes as being experimental in 2.3
471e5bc [Security] allowed simple pre-auth to be optional if another auth mechanism already authenticated the user
01c913b moved the simple HTTP authenticator to a pre-auth one
887d9b8 fixed wrong Logger interface
65335ea [Security] Renamed simple_token to simple_http, added support for failure and success handler to both simple firewalls
f7a11a1 [Security] Add simple_token auth method
1fe2ed6 [Security] Add SimpleForm authentication
  • Loading branch information
fabpot committed Jun 13, 2013
2 parents 6f77195 + 74cfc84 commit d938834
Show file tree
Hide file tree
Showing 12 changed files with 685 additions and 2 deletions.
Expand Up @@ -174,7 +174,7 @@ protected function createAuthenticationSuccessHandler($container, $id, $config)
return $config['success_handler'];
}

$successHandlerId = 'security.authentication.success_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
$successHandlerId = $this->getSuccessHandlerId($id);

$successHandler = $container->setDefinition($successHandlerId, new DefinitionDecorator('security.authentication.success_handler'));
$successHandler->replaceArgument(1, array_intersect_key($config, $this->defaultSuccessHandlerOptions));
Expand All @@ -189,11 +189,21 @@ protected function createAuthenticationFailureHandler($container, $id, $config)
return $config['failure_handler'];
}

$id = 'security.authentication.failure_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
$id = $this->getFailureHandlerId($id);

$failureHandler = $container->setDefinition($id, new DefinitionDecorator('security.authentication.failure_handler'));
$failureHandler->replaceArgument(2, array_intersect_key($config, $this->defaultFailureHandlerOptions));

return $id;
}

protected function getSuccessHandlerId($id)
{
return 'security.authentication.success_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
}

protected function getFailureHandlerId($id)
{
return 'security.authentication.failure_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
}
}
@@ -0,0 +1,99 @@
<?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;

/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @experimental This feature is experimental in 2.3 and might change in future versions
*/
class SimpleFormFactory extends FormLoginFactory
{
public function __construct()
{
parent::__construct();

$this->addOption('authenticator', null);
}

public function getKey()
{
return 'simple-form';
}

public function addConfiguration(NodeDefinition $node)
{
parent::addConfiguration($node);

$node->children()
->scalarNode('authenticator')->cannotBeEmpty()->end()
->end();
}

protected function getListenerId()
{
return 'security.authentication.listener.simple_form';
}

protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId)
{
$provider = 'security.authentication.provider.simple_form.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.simple'))
->replaceArgument(0, new Reference($config['authenticator']))
->replaceArgument(1, new Reference($userProviderId))
->replaceArgument(2, $id)
;

return $provider;
}

protected function createListener($container, $id, $config, $userProvider)
{
$listenerId = parent::createListener($container, $id, $config, $userProvider);
$listener = $container->getDefinition($listenerId);

if (!isset($config['csrf_provider'])) {
$listener->addArgument(null);
}

$simpleAuthHandlerId = 'security.authentication.simple_success_failure_handler.'.$id;
$simpleAuthHandler = $container->setDefinition($simpleAuthHandlerId, new DefinitionDecorator('security.authentication.simple_success_failure_handler'));
$simpleAuthHandler->replaceArgument(0, new Reference($config['authenticator']));
$simpleAuthHandler->replaceArgument(1, new Reference($this->getSuccessHandlerId($id)));
$simpleAuthHandler->replaceArgument(2, new Reference($this->getFailureHandlerId($id)));

$listener->replaceArgument(5, new Reference($simpleAuthHandlerId));
$listener->replaceArgument(6, new Reference($simpleAuthHandlerId));
$listener->addArgument(new Reference($config['authenticator']));

return $listenerId;
}

protected function createEntryPoint($container, $id, $config, $defaultEntryPoint)
{
$entryPointId = 'security.authentication.form_entry_point.'.$id;
$container
->setDefinition($entryPointId, new DefinitionDecorator('security.authentication.form_entry_point'))
->addArgument(new Reference('security.http_utils'))
->addArgument($config['login_path'])
->addArgument($config['use_forward'])
;

return $entryPointId;
}
}
@@ -0,0 +1,64 @@
<?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;

/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @experimental This feature is experimental in 2.3 and might change in future versions
*/
class SimplePreAuthenticationFactory implements SecurityFactoryInterface
{
public function getPosition()
{
return 'pre_auth';
}

public function getKey()
{
return 'simple-preauth';
}

public function addConfiguration(NodeDefinition $node)
{
$node
->children()
->scalarNode('provider')->end()
->scalarNode('authenticator')->cannotBeEmpty()->end()
->end()
;
}

public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$provider = 'security.authentication.provider.simple_preauth.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.simple'))
->replaceArgument(0, new Reference($config['authenticator']))
->replaceArgument(1, new Reference($userProvider))
->replaceArgument(2, $id)
;

// listener
$listenerId = 'security.authentication.listener.simple_preauth.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.simple_preauth'));
$listener->replaceArgument(2, $id);
$listener->replaceArgument(3, new Reference($config['authenticator']));

return array($provider, $listenerId, null);
}
}
Expand Up @@ -12,6 +12,10 @@
<parameter key="security.authentication.form_entry_point.class">Symfony\Component\Security\Http\EntryPoint\FormAuthenticationEntryPoint</parameter>
<parameter key="security.authentication.listener.form.class">Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener</parameter>

<parameter key="security.authentication.listener.simple_form.class">Symfony\Component\Security\Http\Firewall\SimpleFormAuthenticationListener</parameter>

<parameter key="security.authentication.listener.simple_preauth.class">Symfony\Component\Security\Http\Firewall\SimplePreAuthenticationListener</parameter>

<parameter key="security.authentication.listener.basic.class">Symfony\Component\Security\Http\Firewall\BasicAuthenticationListener</parameter>
<parameter key="security.authentication.basic_entry_point.class">Symfony\Component\Security\Http\EntryPoint\BasicAuthenticationEntryPoint</parameter>

Expand All @@ -35,12 +39,14 @@
<parameter key="security.context_listener.class">Symfony\Component\Security\Http\Firewall\ContextListener</parameter>

<parameter key="security.authentication.provider.dao.class">Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider</parameter>
<parameter key="security.authentication.provider.simple.class">Symfony\Component\Security\Core\Authentication\Provider\SimpleAuthenticationProvider</parameter>
<parameter key="security.authentication.provider.pre_authenticated.class">Symfony\Component\Security\Core\Authentication\Provider\PreAuthenticatedAuthenticationProvider</parameter>

<parameter key="security.authentication.provider.anonymous.class">Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider</parameter>

<parameter key="security.authentication.success_handler.class">Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler</parameter>
<parameter key="security.authentication.failure_handler.class">Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler</parameter>
<parameter key="security.authentication.simple_success_failure_handler.class">Symfony\Component\Security\Http\Authentication\SimpleAuthenticationHandler</parameter>
</parameters>

<services>
Expand Down Expand Up @@ -133,6 +139,29 @@
abstract="true">
</service>

<service id="security.authentication.listener.simple_form"
class="%security.authentication.listener.simple_form.class%"
parent="security.authentication.listener.abstract"
abstract="true">
</service>

<service id="security.authentication.simple_success_failure_handler" class="%security.authentication.simple_success_failure_handler.class%" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument /> <!-- Authenticator -->
<argument type="service" id="security.authentication.success_handler" />
<argument type="service" id="security.authentication.failure_handler" />
<argument type="service" id="logger" on-invalid="null" />
</service>

<service id="security.authentication.listener.simple_preauth" class="%security.authentication.listener.simple_preauth.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 /> <!-- Authenticator -->
<argument type="service" id="logger" on-invalid="null" />
</service>

<service id="security.authentication.listener.x509" class="%security.authentication.listener.x509.class%" public="false" abstract="true">
<tag name="monolog.logger" channel="security" />
<argument type="service" id="security.context" />
Expand Down Expand Up @@ -170,6 +199,12 @@
<argument>%security.authentication.hide_user_not_found%</argument>
</service>

<service id="security.authentication.provider.simple" class="%security.authentication.provider.simple.class%" abstract="true" public="false">
<argument /> <!-- Simple Authenticator -->
<argument /> <!-- User Provider -->
<argument /> <!-- Provider-shared Key -->
</service>

<service id="security.authentication.provider.pre_authenticated" class="%security.authentication.provider.pre_authenticated.class%" abstract="true" public="false">
<argument /> <!-- User Provider -->
<argument type="service" id="security.user_checker" />
Expand Down
4 changes: 4 additions & 0 deletions src/Symfony/Bundle/SecurityBundle/SecurityBundle.php
Expand Up @@ -19,6 +19,8 @@
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpDigestFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\X509Factory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimplePreAuthenticationFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimpleFormFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\InMemoryFactory;

/**
Expand All @@ -38,6 +40,8 @@ public function build(ContainerBuilder $container)
$extension->addSecurityListenerFactory(new HttpDigestFactory());
$extension->addSecurityListenerFactory(new RememberMeFactory());
$extension->addSecurityListenerFactory(new X509Factory());
$extension->addSecurityListenerFactory(new SimplePreAuthenticationFactory());
$extension->addSecurityListenerFactory(new SimpleFormFactory());

$extension->addUserProviderFactory(new InMemoryFactory());
$container->addCompilerPass(new AddSecurityVotersPass());
Expand Down
@@ -0,0 +1,58 @@
<?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\Core\Authentication\Provider;

use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;

/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @experimental This feature is experimental in 2.3 and might change in future versions
*/
class SimpleAuthenticationProvider implements AuthenticationProviderInterface
{
private $simpleAuthenticator;
private $userProvider;
private $providerKey;

public function __construct(SimpleAuthenticatorInterface $simpleAuthenticator, UserProviderInterface $userProvider, $providerKey)
{
$this->simpleAuthenticator = $simpleAuthenticator;
$this->userProvider = $userProvider;
$this->providerKey = $providerKey;
}

public function authenticate(TokenInterface $token)
{
$authToken = $this->simpleAuthenticator->authenticateToken($token, $this->userProvider, $this->providerKey);

if ($authToken instanceof TokenInterface) {
return $authToken;
}

throw new AuthenticationException('Simple authenticator failed to return an authenticated token.');
}

public function supports(TokenInterface $token)
{
return $this->simpleAuthenticator->supportsToken($token, $this->providerKey);
}
}
@@ -0,0 +1,27 @@
<?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\Core\Authentication;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;

/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @experimental This feature is experimental in 2.3 and might change in future versions
*/
interface SimpleAuthenticatorInterface
{
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey);

public function supportsToken(TokenInterface $token, $providerKey);
}
@@ -0,0 +1,24 @@
<?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\Core\Authentication;

use Symfony\Component\HttpFoundation\Request;

/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @experimental This feature is experimental in 2.3 and might change in future versions
*/
interface SimpleFormAuthenticatorInterface extends SimpleAuthenticatorInterface
{
public function createToken(Request $request, $username, $password, $providerKey);
}

0 comments on commit d938834

Please sign in to comment.