Created
July 5, 2012 17:17
-
-
Save adrienbrault/3055001 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* This file is part of the Symfony package. | |
* | |
* (c) Fabien Potencier <[email protected]> | |
* | |
* 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\Http\Session\SessionAuthenticationStrategyInterface; | |
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; | |
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; | |
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; | |
use Symfony\Component\Security\Core\SecurityContextInterface; | |
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; | |
use Symfony\Component\Security\Core\Exception\AuthenticationException; | |
use Symfony\Component\Security\Core\Exception\SessionUnavailableException; | |
use Symfony\Component\HttpKernel\Log\LoggerInterface; | |
use Symfony\Component\HttpKernel\HttpKernelInterface; | |
use Symfony\Component\HttpKernel\Event\GetResponseEvent; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; | |
use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; | |
use Symfony\Component\Security\Http\SecurityEvents; | |
use Symfony\Component\Security\Http\HttpUtils; | |
/** | |
* 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 UserInterface implementation from a database, and | |
* perform the specific account checks using the UserChecker | |
* | |
* 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 <[email protected]> | |
* @author Johannes M. Schmitt <[email protected]> | |
*/ | |
abstract class AbstractAuthenticationListener implements ListenerInterface | |
{ | |
protected $options; | |
protected $logger; | |
protected $authenticationManager; | |
protected $providerKey; | |
protected $httpUtils; | |
private $securityContext; | |
private $sessionStrategy; | |
private $dispatcher; | |
private $successHandler; | |
private $failureHandler; | |
private $rememberMeServices; | |
/** | |
* 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 array $options An array of options for the processing of a | |
* successful, or failed authentication attempt | |
* @param AuthenticationSuccessHandlerInterface $successHandler | |
* @param AuthenticationFailureHandlerInterface $failureHandler | |
* @param LoggerInterface $logger A LoggerInterface instance | |
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance | |
*/ | |
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = 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', | |
'always_use_default_target_path' => false, | |
'default_target_path' => '/', | |
'target_path_parameter' => '_target_path', | |
'use_referer' => false, | |
'failure_path' => null, | |
'failure_forward' => false, | |
'require_previous_session' => true, | |
), $options); | |
$this->logger = $logger; | |
$this->dispatcher = $dispatcher; | |
$this->httpUtils = $httpUtils; | |
} | |
/** | |
* Sets the RememberMeServices implementation to use | |
* | |
* @param RememberMeServicesInterface $rememberMeServices | |
*/ | |
public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices) | |
{ | |
$this->rememberMeServices = $rememberMeServices; | |
} | |
/** | |
* Handles form based authentication. | |
* | |
* @param GetResponseEvent $event A GetResponseEvent instance | |
*/ | |
public final function handle(GetResponseEvent $event) | |
{ | |
$request = $event->getRequest(); | |
if (!$this->requiresAuthentication($request)) { | |
return; | |
} | |
if (!$request->hasSession()) { | |
throw new \RuntimeException('This authentication method requires a session.'); | |
} | |
try { | |
if ($this->options['require_previous_session'] && !$request->hasPreviousSession()) { | |
throw new SessionUnavailableException('Your session has timed-out, or you have disabled cookies.'); | |
} | |
if (null === $returnValue = $this->attemptAuthentication($request)) { | |
return; | |
} | |
if ($returnValue instanceof TokenInterface) { | |
$this->sessionStrategy->onAuthentication($request, $returnValue); | |
$response = $this->onSuccess($event, $request, $returnValue); | |
} elseif ($returnValue instanceof Response) { | |
$response = $returnValue; | |
} else { | |
throw new \RuntimeException('attemptAuthentication() must either return a Response, an implementation of TokenInterface, or null.'); | |
} | |
} catch (AuthenticationException $e) { | |
$response = $this->onFailure($event, $request, $e); | |
} | |
$event->setResponse($response); | |
} | |
/** | |
* 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->httpUtils->checkRequestPath($request, $this->options['check_path']); | |
} | |
/** | |
* Performs authentication. | |
* | |
* @param Request $request A Request instance | |
* | |
* @return TokenInterface The authenticated token, or null if full authentication is not possible | |
* | |
* @throws AuthenticationException if the authentication fails | |
*/ | |
abstract protected function attemptAuthentication(Request $request); | |
private function onFailure(GetResponseEvent $event, Request $request, AuthenticationException $failed) | |
{ | |
if (null !== $this->logger) { | |
$this->logger->info(sprintf('Authentication request failed: %s', $failed->getMessage())); | |
} | |
$this->securityContext->setToken(null); | |
if (null !== $this->failureHandler) { | |
return $this->failureHandler->onAuthenticationFailure($request, $failed); | |
} | |
if (null === $this->options['failure_path']) { | |
$this->options['failure_path'] = $this->options['login_path']; | |
} | |
if ($this->options['failure_forward']) { | |
if (null !== $this->logger) { | |
$this->logger->debug(sprintf('Forwarding to %s', $this->options['failure_path'])); | |
} | |
$subRequest = $this->httpUtils->createRequest($request, $this->options['failure_path']); | |
$subRequest->attributes->set(SecurityContextInterface::AUTHENTICATION_ERROR, $failed); | |
return $event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST); | |
} | |
if (null !== $this->logger) { | |
$this->logger->debug(sprintf('Redirecting to %s', $this->options['failure_path'])); | |
} | |
$request->getSession()->set(SecurityContextInterface::AUTHENTICATION_ERROR, $failed); | |
return $this->httpUtils->createRedirectResponse($request, $this->options['failure_path']); | |
} | |
private function onSuccess(GetResponseEvent $event, Request $request, TokenInterface $token) | |
{ | |
if (null !== $this->logger) { | |
$this->logger->info(sprintf('User "%s" has been authenticated successfully', $token->getUsername())); | |
} | |
$this->securityContext->setToken($token); | |
$session = $request->getSession(); | |
$session->remove(SecurityContextInterface::AUTHENTICATION_ERROR); | |
$session->remove(SecurityContextInterface::LAST_USERNAME); | |
if (null !== $this->dispatcher) { | |
$loginEvent = new InteractiveLoginEvent($request, $token); | |
$this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent); | |
} | |
if (null !== $this->successHandler) { | |
$response = $this->successHandler->onAuthenticationSuccess($request, $token); | |
} else { | |
$response = $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request)); | |
} | |
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 | |
*/ | |
private function determineTargetUrl(Request $request) | |
{ | |
if ($this->options['always_use_default_target_path']) { | |
return $this->options['default_target_path']; | |
} | |
if ($targetUrl = $request->get($this->options['target_path_parameter'], null, true)) { | |
return $targetUrl; | |
} | |
$session = $request->getSession(); | |
if ($targetUrl = $session->get('_security.target_path')) { | |
$session->remove('_security.target_path'); | |
return $targetUrl; | |
} | |
if ($this->options['use_referer'] && $targetUrl = $request->headers->get('Referer')) { | |
return $targetUrl; | |
} | |
return $this->options['default_target_path']; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* This file is part of the Symfony package. | |
* | |
* (c) Fabien Potencier <[email protected]> | |
* | |
* 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; | |
/** | |
* AbstractFactory is the base class for all classes inheriting from | |
* AbstractAuthenticationListener | |
* | |
* @author Lukas Kahwe Smith <[email protected]> | |
* @author Johannes M. Schmitt <[email protected]> | |
*/ | |
abstract class AbstractFactory implements SecurityFactoryInterface | |
{ | |
protected $options = array( | |
'check_path' => '/login_check', | |
'login_path' => '/login', | |
'use_forward' => false, | |
'always_use_default_target_path' => false, | |
'default_target_path' => '/', | |
'target_path_parameter' => '_target_path', | |
'use_referer' => false, | |
'failure_path' => null, | |
'failure_forward' => false, | |
'require_previous_session' => true, | |
); | |
public function create(ContainerBuilder $container, $id, $config, $userProviderId, $defaultEntryPointId) | |
{ | |
// authentication provider | |
$authProviderId = $this->createAuthProvider($container, $id, $config, $userProviderId); | |
// authentication listener | |
$listenerId = $this->createListener($container, $id, $config, $userProviderId); | |
// add remember-me aware tag if requested | |
if ($this->isRememberMeAware($config)) { | |
$container | |
->getDefinition($listenerId) | |
->addTag('security.remember_me_aware', array('id' => $id, 'provider' => $userProviderId)) | |
; | |
} | |
// create entry point if applicable (optional) | |
$entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPointId); | |
return array($authProviderId, $listenerId, $entryPointId); | |
} | |
public function addConfiguration(NodeDefinition $node) | |
{ | |
$builder = $node->children(); | |
$builder | |
->scalarNode('provider')->end() | |
->booleanNode('remember_me')->defaultTrue()->end() | |
->scalarNode('success_handler')->end() | |
->scalarNode('failure_handler')->end() | |
; | |
foreach ($this->options as $name => $default) { | |
if (is_bool($default)) { | |
$builder->booleanNode($name)->defaultValue($default); | |
} else { | |
$builder->scalarNode($name)->defaultValue($default); | |
} | |
} | |
} | |
public final function addOption($name, $default = null) | |
{ | |
$this->options[$name] = $default; | |
} | |
/** | |
* Subclasses must return the id of a service which implements the | |
* AuthenticationProviderInterface. | |
* | |
* @param ContainerBuilder $container | |
* @param string $id The unique id of the firewall | |
* @param array $config The options array for this listener | |
* @param string $userProviderId The id of the user provider | |
* | |
* @return string never null, the id of the authentication provider | |
*/ | |
abstract protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId); | |
/** | |
* Subclasses must return the id of the abstract listener template. | |
* | |
* Listener definitions should inherit from the AbstractAuthenticationListener | |
* like this: | |
* | |
* <service id="my.listener.id" | |
* class="My\Concrete\Classname" | |
* parent="security.authentication.listener.abstract" | |
* abstract="true" /> | |
* | |
* In the above case, this method would return "my.listener.id". | |
* | |
* @return string | |
*/ | |
abstract protected function getListenerId(); | |
/** | |
* Subclasses may create an entry point of their as they see fit. The | |
* default implementation does not change the default entry point. | |
* | |
* @param ContainerBuilder $container | |
* @param string $id | |
* @param array $config | |
* @param string $defaultEntryPointId | |
* | |
* @return string the entry point id | |
*/ | |
protected function createEntryPoint($container, $id, $config, $defaultEntryPointId) | |
{ | |
return $defaultEntryPointId; | |
} | |
/** | |
* Subclasses may disable remember-me features for the listener, by | |
* always returning false from this method. | |
* | |
* @param array $config | |
* | |
* @return Boolean Whether a possibly configured RememberMeServices should be set for this listener | |
*/ | |
protected function isRememberMeAware($config) | |
{ | |
return $config['remember_me']; | |
} | |
protected function createListener($container, $id, $config, $userProvider) | |
{ | |
$listenerId = $this->getListenerId(); | |
$listener = new DefinitionDecorator($listenerId); | |
$listener->replaceArgument(4, $id); | |
$listener->replaceArgument(5, array_intersect_key($config, $this->options)); | |
// success handler | |
if (isset($config['success_handler'])) { | |
$listener->replaceArgument(6, new Reference($config['success_handler'])); | |
} | |
// failure handler | |
if (isset($config['failure_handler'])) { | |
$listener->replaceArgument(7, new Reference($config['failure_handler'])); | |
} | |
$listenerId .= '.'.$id; | |
$container->setDefinition($listenerId, $listener); | |
return $listenerId; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
jms_security_extra: | |
secure_all_services: false | |
expressions: true | |
security: | |
encoders: | |
Symfony\Component\Security\Core\User\User: plaintext | |
role_hierarchy: | |
ROLE_ADMIN: ROLE_USER | |
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] | |
providers: | |
in_memory: | |
memory: | |
users: | |
user: { password: userpass, roles: [ 'ROLE_USER' ] } | |
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] } | |
firewalls: | |
dev: | |
pattern: ^/(_(profiler|wdt)|css|images|js)/ | |
security: false | |
login: | |
pattern: ^/demo/secured/login$ | |
security: false | |
secured_area: | |
pattern: ^/demo/secured/ | |
form_login: | |
check_path: /demo/secured/login_check | |
login_path: /demo/secured/login | |
require_previous_session: false | |
logout: | |
path: /demo/secured/logout | |
target: /demo/ | |
#anonymous: ~ | |
#http_basic: | |
# realm: "Secured Demo Area" | |
access_control: | |
#- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https } | |
#- { path: ^/_internal/secure, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment