diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php index 27f08b0a1f..10032c6aa1 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php @@ -174,7 +174,7 @@ abstract class AbstractFactory implements SecurityFactoryInterface 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)); @@ -189,11 +189,21 @@ abstract class AbstractFactory implements SecurityFactoryInterface 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()); + } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimpleFormFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimpleFormFactory.php new file mode 100644 index 0000000000..f13fd5e08d --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimpleFormFactory.php @@ -0,0 +1,99 @@ + + * + * 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 + * + * @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; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimplePreAuthenticationFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimplePreAuthenticationFactory.php new file mode 100644 index 0000000000..689316e26f --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimplePreAuthenticationFactory.php @@ -0,0 +1,64 @@ + + * + * 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 + * + * @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); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml index 6148337520..2bd379931a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml @@ -12,6 +12,10 @@ Symfony\Component\Security\Http\EntryPoint\FormAuthenticationEntryPoint Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener + Symfony\Component\Security\Http\Firewall\SimpleFormAuthenticationListener + + Symfony\Component\Security\Http\Firewall\SimplePreAuthenticationListener + Symfony\Component\Security\Http\Firewall\BasicAuthenticationListener Symfony\Component\Security\Http\EntryPoint\BasicAuthenticationEntryPoint @@ -35,12 +39,14 @@ Symfony\Component\Security\Http\Firewall\ContextListener Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider + Symfony\Component\Security\Core\Authentication\Provider\SimpleAuthenticationProvider Symfony\Component\Security\Core\Authentication\Provider\PreAuthenticatedAuthenticationProvider Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler + Symfony\Component\Security\Http\Authentication\SimpleAuthenticationHandler @@ -133,6 +139,29 @@ abstract="true"> + + + + + + + + + + + + + + + + + + + + @@ -170,6 +199,12 @@ %security.authentication.hide_user_not_found% + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php b/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php index 7d810fde38..5de4136586 100644 --- a/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php +++ b/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php @@ -19,6 +19,8 @@ use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasic 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; /** @@ -38,6 +40,8 @@ class SecurityBundle extends Bundle $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()); diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php new file mode 100644 index 0000000000..50c9a08a7a --- /dev/null +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php @@ -0,0 +1,58 @@ + + * + * 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 + * + * @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); + } +} diff --git a/src/Symfony/Component/Security/Core/Authentication/SimpleAuthenticatorInterface.php b/src/Symfony/Component/Security/Core/Authentication/SimpleAuthenticatorInterface.php new file mode 100644 index 0000000000..e8ad7b8fe7 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Authentication/SimpleAuthenticatorInterface.php @@ -0,0 +1,27 @@ + + * + * 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 + * + * @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); +} diff --git a/src/Symfony/Component/Security/Core/Authentication/SimpleFormAuthenticatorInterface.php b/src/Symfony/Component/Security/Core/Authentication/SimpleFormAuthenticatorInterface.php new file mode 100644 index 0000000000..bfc3ec240e --- /dev/null +++ b/src/Symfony/Component/Security/Core/Authentication/SimpleFormAuthenticatorInterface.php @@ -0,0 +1,24 @@ + + * + * 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 + * + * @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); +} diff --git a/src/Symfony/Component/Security/Core/Authentication/SimplePreAuthenticatorInterface.php b/src/Symfony/Component/Security/Core/Authentication/SimplePreAuthenticatorInterface.php new file mode 100644 index 0000000000..8fa7037d39 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Authentication/SimplePreAuthenticatorInterface.php @@ -0,0 +1,24 @@ + + * + * 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 + * + * @experimental This feature is experimental in 2.3 and might change in future versions + */ +interface SimplePreAuthenticatorInterface extends SimpleAuthenticatorInterface +{ + public function createToken(Request $request, $providerKey); +} diff --git a/src/Symfony/Component/Security/Http/Authentication/SimpleAuthenticationHandler.php b/src/Symfony/Component/Security/Http/Authentication/SimpleAuthenticationHandler.php new file mode 100644 index 0000000000..88be8e4b1f --- /dev/null +++ b/src/Symfony/Component/Security/Http/Authentication/SimpleAuthenticationHandler.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\Authentication; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Psr\Log\LoggerInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface; + +/** + * Class to proxy authentication success/failure handlers + * + * Events are sent to the SimpleAuthenticatorInterface if it implements + * the right interface, otherwise (or if it fails to return a Response) + * the default handlers are triggered. + * + * @author Jordi Boggiano + * + * @experimental This feature is experimental in 2.3 and might change in future versions + */ +class SimpleAuthenticationHandler implements AuthenticationFailureHandlerInterface, AuthenticationSuccessHandlerInterface +{ + protected $successHandler; + protected $failureHandler; + protected $simpleAuthenticator; + + /** + * Constructor. + * + * @param SimpleAuthenticatorInterface $authenticator SimpleAuthenticatorInterface instance + * @param AuthenticationSuccessHandlerInterface $successHandler Default success handler + * @param AuthenticationFailureHandlerInterface $failureHandler Default failure handler + * @param LoggerInterface $logger Optional logger + */ + public function __construct(SimpleAuthenticatorInterface $authenticator, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, LoggerInterface $logger = null) + { + $this->simpleAuthenticator = $authenticator; + $this->successHandler = $successHandler; + $this->failureHandler = $failureHandler; + $this->logger = $logger; + } + + /** + * {@inheritDoc} + */ + public function onAuthenticationSuccess(Request $request, TokenInterface $token) + { + if ($this->simpleAuthenticator instanceof AuthenticationSuccessHandlerInterface) { + if ($this->logger) { + $this->logger->debug(sprintf('Using the %s object as authentication success handler', get_class($this->simpleAuthenticator))); + } + + $response = $this->simpleAuthenticator->onAuthenticationSuccess($request, $token); + if ($response instanceof Response) { + return $response; + } + + if (null !== $response) { + throw new \UnexpectedValueException(sprintf('The %s::onAuthenticationSuccess method must return null to use the default success handler, or a Response object', get_class($this->simpleAuthenticator))); + } + } + + if ($this->logger) { + $this->logger->debug('Fallback to the default authentication success handler'); + } + + return $this->successHandler->onAuthenticationSuccess($request, $token); + } + + /** + * {@inheritDoc} + */ + public function onAuthenticationFailure(Request $request, AuthenticationException $exception) + { + if ($this->simpleAuthenticator instanceof AuthenticationFailureHandlerInterface) { + if ($this->logger) { + $this->logger->debug(sprintf('Using the %s object as authentication failure handler', get_class($this->simpleAuthenticator))); + } + + $response = $this->simpleAuthenticator->onAuthenticationFailure($request, $exception); + if ($response instanceof Response) { + return $response; + } + + if (null !== $response) { + throw new \UnexpectedValueException(sprintf('The %s::onAuthenticationFailure method must return null to use the default failure handler, or a Response object', get_class($this->simpleAuthenticator))); + } + } + + if ($this->logger) { + $this->logger->debug('Fallback to the default authentication failure handler'); + } + + return $this->failureHandler->onAuthenticationFailure($request, $exception); + } +} diff --git a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php new file mode 100644 index 0000000000..8325bb1920 --- /dev/null +++ b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php @@ -0,0 +1,118 @@ + + * + * 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\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; +use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; +use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\Exception\SessionUnavailableException; +use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; +use Symfony\Component\Security\Http\HttpUtils; +use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; +use Symfony\Component\Security\Http\SecurityEvents; +use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; +use Psr\Log\LoggerInterface; + +/** + * @author Jordi Boggiano + * + * @experimental This feature is experimental in 2.3 and might change in future versions + */ +class SimpleFormAuthenticationListener extends AbstractAuthenticationListener +{ + private $simpleAuthenticator; + private $csrfProvider; + + /** + * Constructor. + * + * @param SecurityContextInterface $securityContext A SecurityContext instance + * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance + * @param SessionAuthenticationStrategyInterface $sessionStrategy + * @param HttpUtils $httpUtils An HttpUtilsInterface instance + * @param string $providerKey + * @param AuthenticationSuccessHandlerInterface $successHandler + * @param AuthenticationFailureHandlerInterface $failureHandler + * @param array $options An array of options for the processing of a + * successful, or failed authentication attempt + * @param LoggerInterface $logger A LoggerInterface instance + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * @param SimpleFormAuthenticatorInterface $simpleAuthenticator A SimpleFormAuthenticatorInterface instance + * @param CsrfProviderInterface $csrfProvider A CsrfProviderInterface instance + */ + public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null, SimpleFormAuthenticatorInterface $simpleAuthenticator = null) + { + if (!$simpleAuthenticator) { + throw new \InvalidArgumentException('Missing simple authenticator'); + } + + $this->simpleAuthenticator = $simpleAuthenticator; + $this->csrfProvider = $csrfProvider; + + $options = array_merge(array( + 'username_parameter' => '_username', + 'password_parameter' => '_password', + 'csrf_parameter' => '_csrf_token', + 'intention' => 'authenticate', + 'post_only' => true, + ), $options); + parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, $options, $logger, $dispatcher); + } + + /** + * {@inheritdoc} + */ + protected function requiresAuthentication(Request $request) + { + if ($this->options['post_only'] && !$request->isMethod('POST')) { + return false; + } + + return parent::requiresAuthentication($request); + } + + /** + * {@inheritdoc} + */ + protected function attemptAuthentication(Request $request) + { + if (null !== $this->csrfProvider) { + $csrfToken = $request->get($this->options['csrf_parameter'], null, true); + + if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) { + throw new InvalidCsrfTokenException('Invalid CSRF token.'); + } + } + + if ($this->options['post_only']) { + $username = trim($request->request->get($this->options['username_parameter'], null, true)); + $password = $request->request->get($this->options['password_parameter'], null, true); + } else { + $username = trim($request->get($this->options['username_parameter'], null, true)); + $password = $request->get($this->options['password_parameter'], null, true); + } + + $request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username); + + $token = $this->simpleAuthenticator->createToken($request, $username, $password, $this->providerKey); + + return $this->authenticationManager->authenticate($token); + } +} diff --git a/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php new file mode 100644 index 0000000000..637ed396a0 --- /dev/null +++ b/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php @@ -0,0 +1,113 @@ + + * + * 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 Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; +use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; + +/** + * SimplePreAuthenticationListener implements simple proxying to an authenticator. + * + * @author Jordi Boggiano + * + * @experimental This feature is experimental in 2.3 and might change in future versions + */ +class SimplePreAuthenticationListener implements ListenerInterface +{ + private $securityContext; + private $authenticationManager; + private $providerKey; + private $simpleAuthenticator; + private $logger; + + /** + * Constructor. + * + * @param SecurityContextInterface $securityContext A SecurityContext instance + * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance + * @param string $providerKey + * @param SimplePreAuthenticatorInterface $simpleAuthenticator A SimplePreAuthenticatorInterface instance + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, SimplePreAuthenticatorInterface $simpleAuthenticator, LoggerInterface $logger = null) + { + if (empty($providerKey)) { + throw new \InvalidArgumentException('$providerKey must not be empty.'); + } + + $this->securityContext = $securityContext; + $this->authenticationManager = $authenticationManager; + $this->providerKey = $providerKey; + $this->simpleAuthenticator = $simpleAuthenticator; + $this->logger = $logger; + } + + /** + * Handles basic authentication. + * + * @param GetResponseEvent $event A GetResponseEvent instance + */ + public function handle(GetResponseEvent $event) + { + $request = $event->getRequest(); + + if (null !== $this->logger) { + $this->logger->info(sprintf('Attempting simple pre-authorization %s', $this->providerKey)); + } + + if (null !== $this->context->getToken() && !$this->context->getToken() instanceof AnonymousToken) { + return; + } + + try { + $token = $this->simpleAuthenticator->createToken($request, $this->providerKey); + $token = $this->authenticationManager->authenticate($token); + $this->securityContext->setToken($token); + } catch (AuthenticationException $e) { + $this->securityContext->setToken(null); + + if (null !== $this->logger) { + $this->logger->info(sprintf('Authentication request failed: %s', $e->getMessage())); + } + + if ($this->simpleAuthenticator instanceof AuthenticationFailureHandlerInterface) { + $response = $this->simpleAuthenticator->onAuthenticationFailure($request, $e); + if ($response instanceof Response) { + $event->setResponse($response); + } elseif (null !== $response) { + throw new \UnexpectedValueException(sprintf('The %s::onAuthenticationFailure method must return null or a Response object', get_class($this->simpleAuthenticator))); + } + } + + return; + } + + if ($this->simpleAuthenticator instanceof AuthenticationSuccessHandlerInterface) { + $response = $this->simpleAuthenticator->onAuthenticationSuccess($request, $token); + if ($response instanceof Response) { + $event->setResponse($response); + } elseif (null !== $response) { + throw new \UnexpectedValueException(sprintf('The %s::onAuthenticationSuccess method must return null or a Response object', get_class($this->simpleAuthenticator))); + } + } + } +}