diff options
Diffstat (limited to 'Http')
27 files changed, 395 insertions, 98 deletions
diff --git a/Http/Authentication/DefaultAuthenticationFailureHandler.php b/Http/Authentication/DefaultAuthenticationFailureHandler.php index 830c00a..ea5c356 100644 --- a/Http/Authentication/DefaultAuthenticationFailureHandler.php +++ b/Http/Authentication/DefaultAuthenticationFailureHandler.php @@ -17,6 +17,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\HttpUtils; +use Symfony\Component\Security\Http\ParameterBagUtils; /** * Class with the default authentication failure handling logic. @@ -82,7 +83,7 @@ class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandle */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { - if ($failureUrl = $request->get($this->options['failure_path_parameter'], null, true)) { + if ($failureUrl = ParameterBagUtils::getRequestParameterValue($request, $this->options['failure_path_parameter'])) { $this->options['failure_path'] = $failureUrl; } diff --git a/Http/Authentication/DefaultAuthenticationSuccessHandler.php b/Http/Authentication/DefaultAuthenticationSuccessHandler.php index b6a7df5..bfc0c8b 100644 --- a/Http/Authentication/DefaultAuthenticationSuccessHandler.php +++ b/Http/Authentication/DefaultAuthenticationSuccessHandler.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Http\Authentication; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Http\HttpUtils; +use Symfony\Component\Security\Http\ParameterBagUtils; /** * Class with the default authentication success handling logic. @@ -108,7 +109,7 @@ class DefaultAuthenticationSuccessHandler implements AuthenticationSuccessHandle return $this->options['default_target_path']; } - if ($targetUrl = $request->get($this->options['target_path_parameter'], null, true)) { + if ($targetUrl = ParameterBagUtils::getRequestParameterValue($request, $this->options['target_path_parameter'])) { return $targetUrl; } diff --git a/Http/Authentication/SimpleFormAuthenticatorInterface.php b/Http/Authentication/SimpleFormAuthenticatorInterface.php new file mode 100644 index 0000000..112688c --- /dev/null +++ b/Http/Authentication/SimpleFormAuthenticatorInterface.php @@ -0,0 +1,21 @@ +<?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\Authentication; + +use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface as BaseSimpleFormAuthenticatorInterface; + +/** + * @author Jordi Boggiano <j.boggiano@seld.be> + */ +interface SimpleFormAuthenticatorInterface extends BaseSimpleFormAuthenticatorInterface +{ +} diff --git a/Http/Authentication/SimplePreAuthenticatorInterface.php b/Http/Authentication/SimplePreAuthenticatorInterface.php new file mode 100644 index 0000000..afa8049 --- /dev/null +++ b/Http/Authentication/SimplePreAuthenticatorInterface.php @@ -0,0 +1,21 @@ +<?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\Authentication; + +use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface as BaseSimplePreAuthenticatorInterface; + +/** + * @author Jordi Boggiano <j.boggiano@seld.be> + */ +interface SimplePreAuthenticatorInterface extends BaseSimplePreAuthenticatorInterface +{ +} diff --git a/Http/EntryPoint/AuthenticationEntryPointInterface.php b/Http/EntryPoint/AuthenticationEntryPointInterface.php index c8e43e5..9bade0c 100644 --- a/Http/EntryPoint/AuthenticationEntryPointInterface.php +++ b/Http/EntryPoint/AuthenticationEntryPointInterface.php @@ -16,8 +16,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; /** - * AuthenticationEntryPointInterface is the interface used to start the - * authentication scheme. + * Implement this interface for any classes that will be called to "start" + * the authentication process (see method for more details). * * @author Fabien Potencier <fabien@symfony.com> */ diff --git a/Http/EntryPoint/DigestAuthenticationEntryPoint.php b/Http/EntryPoint/DigestAuthenticationEntryPoint.php index 89f80ad..cdb98eb 100644 --- a/Http/EntryPoint/DigestAuthenticationEntryPoint.php +++ b/Http/EntryPoint/DigestAuthenticationEntryPoint.php @@ -24,15 +24,15 @@ use Psr\Log\LoggerInterface; */ class DigestAuthenticationEntryPoint implements AuthenticationEntryPointInterface { - private $key; + private $secret; private $realmName; private $nonceValiditySeconds; private $logger; - public function __construct($realmName, $key, $nonceValiditySeconds = 300, LoggerInterface $logger = null) + public function __construct($realmName, $secret, $nonceValiditySeconds = 300, LoggerInterface $logger = null) { $this->realmName = $realmName; - $this->key = $key; + $this->secret = $secret; $this->nonceValiditySeconds = $nonceValiditySeconds; $this->logger = $logger; } @@ -43,7 +43,7 @@ class DigestAuthenticationEntryPoint implements AuthenticationEntryPointInterfac public function start(Request $request, AuthenticationException $authException = null) { $expiryTime = microtime(true) + $this->nonceValiditySeconds * 1000; - $signatureValue = md5($expiryTime.':'.$this->key); + $signatureValue = md5($expiryTime.':'.$this->secret); $nonceValue = $expiryTime.':'.$signatureValue; $nonceValueBase64 = base64_encode($nonceValue); @@ -65,11 +65,21 @@ class DigestAuthenticationEntryPoint implements AuthenticationEntryPointInterfac } /** - * @return string + * @deprecated Since version 2.8, to be removed in 3.0. Use getSecret() instead. */ public function getKey() { - return $this->key; + @trigger_error(__method__.'() is deprecated since version 2.8 and will be removed in 3.0. Use getSecret() instead.', E_USER_DEPRECATED); + + return $this->getSecret(); + } + + /** + * @return string + */ + public function getSecret() + { + return $this->secret; } /** diff --git a/Http/Firewall/AnonymousAuthenticationListener.php b/Http/Firewall/AnonymousAuthenticationListener.php index f7feee8..0d60673 100644 --- a/Http/Firewall/AnonymousAuthenticationListener.php +++ b/Http/Firewall/AnonymousAuthenticationListener.php @@ -27,14 +27,14 @@ use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; class AnonymousAuthenticationListener implements ListenerInterface { private $tokenStorage; - private $key; + private $secret; private $authenticationManager; private $logger; - public function __construct(TokenStorageInterface $tokenStorage, $key, LoggerInterface $logger = null, AuthenticationManagerInterface $authenticationManager = null) + public function __construct(TokenStorageInterface $tokenStorage, $secret, LoggerInterface $logger = null, AuthenticationManagerInterface $authenticationManager = null) { $this->tokenStorage = $tokenStorage; - $this->key = $key; + $this->secret = $secret; $this->authenticationManager = $authenticationManager; $this->logger = $logger; } @@ -51,7 +51,7 @@ class AnonymousAuthenticationListener implements ListenerInterface } try { - $token = new AnonymousToken($this->key, 'anon.', array()); + $token = new AnonymousToken($this->secret, 'anon.', array()); if (null !== $this->authenticationManager) { $token = $this->authenticationManager->authenticate($token); } diff --git a/Http/Firewall/DigestAuthenticationListener.php b/Http/Firewall/DigestAuthenticationListener.php index 702cf33..71bdf6c 100644 --- a/Http/Firewall/DigestAuthenticationListener.php +++ b/Http/Firewall/DigestAuthenticationListener.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Core\User\UserProviderInterface; -use Symfony\Component\Security\Core\Util\StringUtils; use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -79,7 +78,7 @@ class DigestAuthenticationListener implements ListenerInterface } try { - $digestAuth->validateAndDecode($this->authenticationEntryPoint->getKey(), $this->authenticationEntryPoint->getRealmName()); + $digestAuth->validateAndDecode($this->authenticationEntryPoint->getSecret(), $this->authenticationEntryPoint->getRealmName()); } catch (BadCredentialsException $e) { $this->fail($event, $request, $e); @@ -100,7 +99,7 @@ class DigestAuthenticationListener implements ListenerInterface return; } - if (!StringUtils::equals($serverDigestMd5, $digestAuth->getResponse())) { + if (!hash_equals($serverDigestMd5, $digestAuth->getResponse())) { if (null !== $this->logger) { $this->logger->debug('Unexpected response from the DigestAuth received; is the header returning a clear text passwords?', array('expected' => $serverDigestMd5, 'received' => $digestAuth->getResponse())); } diff --git a/Http/Firewall/LogoutListener.php b/Http/Firewall/LogoutListener.php index 96f5685..e19d39c 100644 --- a/Http/Firewall/LogoutListener.php +++ b/Http/Firewall/LogoutListener.php @@ -24,6 +24,7 @@ use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface; use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface; +use Symfony\Component\Security\Http\ParameterBagUtils; /** * LogoutListener logout users. @@ -56,11 +57,21 @@ class LogoutListener implements ListenerInterface throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.'); } + if (isset($options['intention'])) { + if (isset($options['csrf_token_id'])) { + throw new \InvalidArgumentException(sprintf('You should only define an option for one of "intention" or "csrf_token_id" for the "%s". Use the "csrf_token_id" as it replaces "intention".', __CLASS__)); + } + + @trigger_error('The "intention" option for the '.__CLASS__.' is deprecated since version 2.8 and will be removed in 3.0. Use the "csrf_token_id" option instead.', E_USER_DEPRECATED); + + $options['csrf_token_id'] = $options['intention']; + } + $this->tokenStorage = $tokenStorage; $this->httpUtils = $httpUtils; $this->options = array_merge(array( 'csrf_parameter' => '_csrf_token', - 'intention' => 'logout', + 'csrf_token_id' => 'logout', 'logout_path' => '/logout', ), $options); $this->successHandler = $successHandler; @@ -98,9 +109,9 @@ class LogoutListener implements ListenerInterface } if (null !== $this->csrfTokenManager) { - $csrfToken = $request->get($this->options['csrf_parameter'], null, true); + $csrfToken = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']); - if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) { + if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $csrfToken))) { throw new LogoutException('Invalid CSRF token.'); } } diff --git a/Http/Firewall/SimpleFormAuthenticationListener.php b/Http/Firewall/SimpleFormAuthenticationListener.php index 8123e0e..331d018 100644 --- a/Http/Firewall/SimpleFormAuthenticationListener.php +++ b/Http/Firewall/SimpleFormAuthenticationListener.php @@ -27,6 +27,7 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\HttpUtils; +use Symfony\Component\Security\Http\ParameterBagUtils; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Psr\Log\LoggerInterface; @@ -70,6 +71,16 @@ class SimpleFormAuthenticationListener extends AbstractAuthenticationListener throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.'); } + if (isset($options['intention'])) { + if (isset($options['csrf_token_id'])) { + throw new \InvalidArgumentException(sprintf('You should only define an option for one of "intention" or "csrf_token_id" for the "%s". Use the "csrf_token_id" as it replaces "intention".', __CLASS__)); + } + + @trigger_error('The "intention" option for the '.__CLASS__.' is deprecated since version 2.8 and will be removed in 3.0. Use the "csrf_token_id" option instead.', E_USER_DEPRECATED); + + $options['csrf_token_id'] = $options['intention']; + } + $this->simpleAuthenticator = $simpleAuthenticator; $this->csrfTokenManager = $csrfTokenManager; @@ -77,7 +88,7 @@ class SimpleFormAuthenticationListener extends AbstractAuthenticationListener 'username_parameter' => '_username', 'password_parameter' => '_password', 'csrf_parameter' => '_csrf_token', - 'intention' => 'authenticate', + 'csrf_token_id' => 'authenticate', 'post_only' => true, ), $options); @@ -102,19 +113,19 @@ class SimpleFormAuthenticationListener extends AbstractAuthenticationListener protected function attemptAuthentication(Request $request) { if (null !== $this->csrfTokenManager) { - $csrfToken = $request->get($this->options['csrf_parameter'], null, true); + $csrfToken = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']); - if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) { + if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $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); + $username = trim(ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter'])); + $password = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']); } else { - $username = trim($request->get($this->options['username_parameter'], null, true)); - $password = $request->get($this->options['password_parameter'], null, true); + $username = trim(ParameterBagUtils::getRequestParameterValue($request, $this->options['username_parameter'])); + $password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']); } if (strlen($username) > Security::MAX_USERNAME_LENGTH) { diff --git a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index ba4329b..866d0c3 100644 --- a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -19,6 +19,7 @@ use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; +use Symfony\Component\Security\Http\ParameterBagUtils; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; @@ -48,11 +49,21 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.'); } + if (isset($options['intention'])) { + if (isset($options['csrf_token_id'])) { + throw new \InvalidArgumentException(sprintf('You should only define an option for one of "intention" or "csrf_token_id" for the "%s". Use the "csrf_token_id" as it replaces "intention".', __CLASS__)); + } + + @trigger_error('The "intention" option for the '.__CLASS__.' is deprecated since version 2.8 and will be removed in 3.0. Use the "csrf_token_id" option instead.', E_USER_DEPRECATED); + + $options['csrf_token_id'] = $options['intention']; + } + parent::__construct($tokenStorage, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array( 'username_parameter' => '_username', 'password_parameter' => '_password', 'csrf_parameter' => '_csrf_token', - 'intention' => 'authenticate', + 'csrf_token_id' => 'authenticate', 'post_only' => true, ), $options), $logger, $dispatcher); @@ -77,19 +88,19 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL protected function attemptAuthentication(Request $request) { if (null !== $this->csrfTokenManager) { - $csrfToken = $request->get($this->options['csrf_parameter'], null, true); + $csrfToken = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']); - if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) { + if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $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); + $username = trim(ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter'])); + $password = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']); } else { - $username = trim($request->get($this->options['username_parameter'], null, true)); - $password = $request->get($this->options['password_parameter'], null, true); + $username = trim(ParameterBagUtils::getRequestParameterValue($request, $this->options['username_parameter'])); + $password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']); } if (strlen($username) > Security::MAX_USERNAME_LENGTH) { diff --git a/Http/Logout/LogoutUrlGenerator.php b/Http/Logout/LogoutUrlGenerator.php index 4ad63cc..761e56a 100644 --- a/Http/Logout/LogoutUrlGenerator.php +++ b/Http/Logout/LogoutUrlGenerator.php @@ -86,7 +86,7 @@ class LogoutUrlGenerator * Generates the logout URL for the firewall. * * @param string|null $key The firewall key or null to use the current firewall key - * @param bool|string $referenceType The type of reference (one of the constants in UrlGeneratorInterface) + * @param int $referenceType The type of reference (one of the constants in UrlGeneratorInterface) * * @return string The logout URL * diff --git a/Http/ParameterBagUtils.php b/Http/ParameterBagUtils.php new file mode 100644 index 0000000..eed5421 --- /dev/null +++ b/Http/ParameterBagUtils.php @@ -0,0 +1,96 @@ +<?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; + +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\PropertyAccess\Exception\AccessException; +use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; +use Symfony\Component\PropertyAccess\PropertyAccess; + +/** + * @internal + */ +final class ParameterBagUtils +{ + private static $propertyAccessor; + + /** + * Returns a "parameter" value. + * + * Paths like foo[bar] will be evaluated to find deeper items in nested data structures. + * + * @param ParameterBag $parameters The parameter bag + * @param string $path The key + * + * @return mixed + * + * @throws InvalidArgumentException when the given path is malformed + */ + public static function getParameterBagValue(ParameterBag $parameters, $path) + { + if (false === $pos = strpos($path, '[')) { + return $parameters->get($path); + } + + $root = substr($path, 0, $pos); + + if (null === $value = $parameters->get($root)) { + return; + } + + if (null === self::$propertyAccessor) { + self::$propertyAccessor = PropertyAccess::createPropertyAccessor(); + } + + try { + return self::$propertyAccessor->getValue($value, substr($path, $pos)); + } catch (AccessException $e) { + return; + } + } + + /** + * Returns a request "parameter" value. + * + * Paths like foo[bar] will be evaluated to find deeper items in nested data structures. + * + * @param Request $request The request + * @param string $path The key + * + * @return mixed + * + * @throws InvalidArgumentException when the given path is malformed + */ + public static function getRequestParameterValue(Request $request, $path) + { + if (false === $pos = strpos($path, '[')) { + return $request->get($path); + } + + $root = substr($path, 0, $pos); + + if (null === $value = $request->get($root)) { + return; + } + + if (null === self::$propertyAccessor) { + self::$propertyAccessor = PropertyAccess::createPropertyAccessor(); + } + + try { + return self::$propertyAccessor->getValue($value, substr($path, $pos)); + } catch (AccessException $e) { + return; + } + } +} diff --git a/Http/RememberMe/AbstractRememberMeServices.php b/Http/RememberMe/AbstractRememberMeServices.php index cd8640d..8627bc8 100644 --- a/Http/RememberMe/AbstractRememberMeServices.php +++ b/Http/RememberMe/AbstractRememberMeServices.php @@ -23,6 +23,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Cookie; use Psr\Log\LoggerInterface; +use Symfony\Component\Security\Http\ParameterBagUtils; /** * Base class implementing the RememberMeServicesInterface. @@ -39,24 +40,24 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface 'httponly' => true, ); private $providerKey; - private $key; + private $secret; private $userProviders; /** * Constructor. * * @param array $userProviders - * @param string $key + * @param string $secret * @param string $providerKey * @param array $options * @param LoggerInterface $logger * * @throws \InvalidArgumentException */ - public function __construct(array $userProviders, $key, $providerKey, array $options = array(), LoggerInterface $logger = null) + public function __construct(array $userProviders, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null) { - if (empty($key)) { - throw new \InvalidArgumentException('$key must not be empty.'); + if (empty($secret)) { + throw new \InvalidArgumentException('$secret must not be empty.'); } if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); @@ -66,7 +67,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface } $this->userProviders = $userProviders; - $this->key = $key; + $this->secret = $secret; $this->providerKey = $providerKey; $this->options = array_merge($this->options, $options); $this->logger = $logger; @@ -84,11 +85,21 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface } /** - * @return string + * @deprecated Since version 2.8, to be removed in 3.0. Use getSecret() instead. */ public function getKey() { - return $this->key; + @trigger_error(__method__.'() is deprecated since version 2.8 and will be removed in 3.0. Use getSecret() instead.', E_USER_DEPRECATED); + + return $this->getSecret(); + } + + /** + * @return string + */ + public function getSecret() + { + return $this->secret; } /** @@ -125,7 +136,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface $this->logger->info('Remember-me cookie accepted.'); } - return new RememberMeToken($user, $this->providerKey, $this->key); + return new RememberMeToken($user, $this->providerKey, $this->secret); } catch (CookieTheftException $e) { $this->cancelCookie($request); @@ -312,7 +323,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface return true; } - $parameter = $request->get($this->options['remember_me_parameter'], null, true); + $parameter = ParameterBagUtils::getRequestParameterValue($request, $this->options['remember_me_parameter']); if (null === $parameter && null !== $this->logger) { $this->logger->debug('Did not send remember-me cookie.', array('parameter' => $this->options['remember_me_parameter'])); diff --git a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index cbbbb23..807a4a7 100644 --- a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -21,7 +21,6 @@ use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Util\SecureRandomInterface; use Psr\Log\LoggerInterface; -use Symfony\Component\Security\Core\Util\StringUtils; /** * Concrete implementation of the RememberMeServicesInterface which needs @@ -33,23 +32,26 @@ use Symfony\Component\Security\Core\Util\StringUtils; class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices { private $tokenProvider; - private $secureRandom; /** * Constructor. * + * Note: The $secureRandom parameter is deprecated since version 2.8 and will be removed in 3.0. + * * @param array $userProviders - * @param string $key + * @param string $secret * @param string $providerKey * @param array $options * @param LoggerInterface $logger * @param SecureRandomInterface $secureRandom */ - public function __construct(array $userProviders, $key, $providerKey, array $options, LoggerInterface $logger = null, SecureRandomInterface $secureRandom) + public function __construct(array $userProviders, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null, SecureRandomInterface $secureRandom = null) { - parent::__construct($userProviders, $key, $providerKey, $options, $logger); + if (null !== $secureRandom) { + @trigger_error('The $secureRandom parameter in '.__METHOD__.' is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } - $this->secureRandom = $secureRandom; + parent::__construct($userProviders, $secret, $providerKey, $options, $logger); } /** @@ -91,7 +93,7 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices list($series, $tokenValue) = $cookieParts; $persistentToken = $this->tokenProvider->loadTokenBySeries($series); - if (!StringUtils::equals($persistentToken->getTokenValue(), $tokenValue)) { + if (!hash_equals($persistentToken->getTokenValue(), $tokenValue)) { throw new CookieTheftException('This token was already used. The account is possibly compromised.'); } @@ -99,7 +101,7 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices throw new AuthenticationException('The cookie has expired.'); } - $tokenValue = base64_encode($this->secureRandom->nextBytes(64)); + $tokenValue = base64_encode(random_bytes(64)); $this->tokenProvider->updateToken($series, $tokenValue, new \DateTime()); $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie( @@ -121,8 +123,8 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices */ protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token) { - $series = base64_encode($this->secureRandom->nextBytes(64)); - $tokenValue = base64_encode($this->secureRandom->nextBytes(64)); + $series = base64_encode(random_bytes(64)); + $tokenValue = base64_encode(random_bytes(64)); $this->tokenProvider->createNewToken( new PersistentToken( diff --git a/Http/RememberMe/TokenBasedRememberMeServices.php b/Http/RememberMe/TokenBasedRememberMeServices.php index d68ada5..a443702 100644 --- a/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/Http/RememberMe/TokenBasedRememberMeServices.php @@ -17,7 +17,6 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\Util\StringUtils; /** * Concrete implementation of the RememberMeServicesInterface providing @@ -54,7 +53,7 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_class($user))); } - if (true !== StringUtils::equals($this->generateCookieHash($class, $username, $expires, $user->getPassword()), $hash)) { + if (true !== hash_equals($this->generateCookieHash($class, $username, $expires, $user->getPassword()), $hash)) { throw new AuthenticationException('The cookie\'s hash is invalid.'); } @@ -121,6 +120,6 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices */ protected function generateCookieHash($class, $username, $expires, $password) { - return hash_hmac('sha256', $class.$username.$expires.$password, $this->getKey()); + return hash_hmac('sha256', $class.$username.$expires.$password, $this->getSecret()); } } diff --git a/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php b/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php index 252b124..c97ee69 100644 --- a/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php +++ b/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php @@ -18,17 +18,12 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; class DefaultAuthenticationFailureHandlerTest extends \PHPUnit_Framework_TestCase { - private $httpKernel = null; - - private $httpUtils = null; - - private $logger = null; - - private $request = null; - - private $session = null; - - private $exception = null; + private $httpKernel; + private $httpUtils; + private $logger; + private $request; + private $session; + private $exception; protected function setUp() { @@ -146,7 +141,7 @@ class DefaultAuthenticationFailureHandlerTest extends \PHPUnit_Framework_TestCas public function testFailurePathCanBeOverwrittenWithRequest() { $this->request->expects($this->once()) - ->method('get')->with('_failure_path', null, true) + ->method('get')->with('_failure_path') ->will($this->returnValue('/auth/login')); $this->httpUtils->expects($this->once()) @@ -156,12 +151,25 @@ class DefaultAuthenticationFailureHandlerTest extends \PHPUnit_Framework_TestCas $handler->onAuthenticationFailure($this->request, $this->exception); } + public function testFailurePathCanBeOverwrittenWithNestedAttributeInRequest() + { + $this->request->expects($this->once()) + ->method('get')->with('_failure_path') + ->will($this->returnValue(array('value' => '/auth/login'))); + + $this->httpUtils->expects($this->once()) + ->method('createRedirectResponse')->with($this->request, '/auth/login'); + + $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, array('failure_path_parameter' => '_failure_path[value]'), $this->logger); + $handler->onAuthenticationFailure($this->request, $this->exception); + } + public function testFailurePathParameterCanBeOverwritten() { $options = array('failure_path_parameter' => '_my_failure_path'); $this->request->expects($this->once()) - ->method('get')->with('_my_failure_path', null, true) + ->method('get')->with('_my_failure_path') ->will($this->returnValue('/auth/login')); $this->httpUtils->expects($this->once()) diff --git a/Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php b/Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php index ae9f02b..5372993 100644 --- a/Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php +++ b/Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php @@ -69,6 +69,20 @@ class DefaultAuthenticationSuccessHandlerTest extends \PHPUnit_Framework_TestCas $this->assertSame($response, $result); } + public function testTargetPathIsPassedAsNestedParameterWithRequest() + { + $this->request->expects($this->once()) + ->method('get')->with('_target_path') + ->will($this->returnValue(array('value' => '/dashboard'))); + + $response = $this->expectRedirectResponse('/dashboard'); + + $handler = new DefaultAuthenticationSuccessHandler($this->httpUtils, array('target_path_parameter' => '_target_path[value]')); + $result = $handler->onAuthenticationSuccess($this->request, $this->token); + + $this->assertSame($response, $result); + } + public function testTargetPathParameterIsCustomised() { $options = array('target_path_parameter' => '_my_target_path'); diff --git a/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php b/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php index 181e340..4082986 100644 --- a/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php +++ b/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php @@ -23,7 +23,7 @@ class DigestAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase $authenticationException = new AuthenticationException('TheAuthenticationExceptionMessage'); - $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey'); + $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheSecret'); $response = $entryPoint->start($request, $authenticationException); $this->assertEquals(401, $response->getStatusCode()); @@ -34,7 +34,7 @@ class DigestAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase { $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); - $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey'); + $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheSecret'); $response = $entryPoint->start($request); $this->assertEquals(401, $response->getStatusCode()); @@ -47,7 +47,7 @@ class DigestAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase $nonceExpiredException = new NonceExpiredException('TheNonceExpiredExceptionMessage'); - $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey'); + $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheSecret'); $response = $entryPoint->start($request, $nonceExpiredException); $this->assertEquals(401, $response->getStatusCode()); diff --git a/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php b/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php index 3450c1e..d99b562 100644 --- a/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php +++ b/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php @@ -35,7 +35,7 @@ class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase ->method('authenticate') ; - $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', null, $authenticationManager); + $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheSecret', null, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } @@ -48,14 +48,14 @@ class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue(null)) ; - $anonymousToken = new AnonymousToken('TheKey', 'anon.', array()); + $anonymousToken = new AnonymousToken('TheSecret', 'anon.', array()); $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); $authenticationManager ->expects($this->once()) ->method('authenticate') ->with($this->callback(function ($token) { - return 'TheKey' === $token->getKey(); + return 'TheSecret' === $token->getSecret(); })) ->will($this->returnValue($anonymousToken)) ; @@ -66,7 +66,7 @@ class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase ->with($anonymousToken) ; - $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', null, $authenticationManager); + $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheSecret', null, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } @@ -81,7 +81,7 @@ class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); - $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', $logger, $authenticationManager); + $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheSecret', $logger, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } } diff --git a/Http/Tests/Firewall/DigestAuthenticationListenerTest.php b/Http/Tests/Firewall/DigestAuthenticationListenerTest.php new file mode 100644 index 0000000..80b2dc4 --- /dev/null +++ b/Http/Tests/Firewall/DigestAuthenticationListenerTest.php @@ -0,0 +1,79 @@ +<?php + +namespace Symfony\Component\Security\Http\Tests\Firewall; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; +use Symfony\Component\Security\Http\Firewall\DigestAuthenticationListener; + +class DigestAuthenticationListenerTest extends \PHPUnit_Framework_TestCase +{ + public function testHandleWithValidDigest() + { + $time = microtime(true) + 1000; + $secret = 'ThisIsASecret'; + $nonce = base64_encode($time.':'.md5($time.':'.$secret)); + $username = 'user'; + $password = 'password'; + $realm = 'Welcome, robot!'; + $cnonce = 'MDIwODkz'; + $nc = '00000001'; + $qop = 'auth'; + $uri = '/path/info?p1=5&p2=5'; + + $serverDigest = $this->calculateServerDigest($username, $realm, $password, $nc, $nonce, $cnonce, $qop, 'GET', $uri); + + $digestData = + 'username="'.$username.'", realm="'.$realm.'", nonce="'.$nonce.'", '. + 'uri="'.$uri.'", cnonce="'.$cnonce.'", nc='.$nc.', qop="'.$qop.'", '. + 'response="'.$serverDigest.'"' + ; + + $request = new Request(array(), array(), array(), array(), array(), array('PHP_AUTH_DIGEST' => $digestData)); + + $entryPoint = new DigestAuthenticationEntryPoint($realm, $secret); + + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user->method('getPassword')->willReturn($password); + + $providerKey = 'TheProviderKey'; + + $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $tokenStorage + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + $tokenStorage + ->expects($this->once()) + ->method('setToken') + ->with($this->equalTo(new UsernamePasswordToken($user, $password, $providerKey))) + ; + + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $userProvider->method('loadUserByUsername')->willReturn($user); + + $listener = new DigestAuthenticationListener($tokenStorage, $userProvider, $providerKey, $entryPoint); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } + + private function calculateServerDigest($username, $realm, $password, $nc, $nonce, $cnonce, $qop, $method, $uri) + { + $response = md5( + md5($username.':'.$realm.':'.$password).':'.$nonce.':'.$nc.':'.$cnonce.':'.$qop.':'.md5($method.':'.$uri) + ); + + return sprintf('username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=%s, qop="%s", response="%s"', + $username, $realm, $nonce, $uri, $cnonce, $nc, $qop, $response + ); + } +} diff --git a/Http/Tests/Firewall/LogoutListenerTest.php b/Http/Tests/Firewall/LogoutListenerTest.php index 15c996e..367c810 100644 --- a/Http/Tests/Firewall/LogoutListenerTest.php +++ b/Http/Tests/Firewall/LogoutListenerTest.php @@ -213,7 +213,7 @@ class LogoutListenerTest extends \PHPUnit_Framework_TestCase $successHandler ?: $this->getSuccessHandler(), $options = array( 'csrf_parameter' => '_csrf_token', - 'intention' => 'logout', + 'csrf_token_id' => 'logout', 'logout_path' => '/logout', 'target_url' => '/', ), diff --git a/Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php b/Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php index 0a1286c..adf91b1 100644 --- a/Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php +++ b/Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php @@ -42,7 +42,7 @@ class SimplePreAuthenticationListenerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue($this->token)) ; - $simpleAuthenticator = $this->getMock('Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface'); + $simpleAuthenticator = $this->getMock('Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface'); $simpleAuthenticator ->expects($this->once()) ->method('createToken') @@ -79,7 +79,7 @@ class SimplePreAuthenticationListenerTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo(null)) ; - $simpleAuthenticator = $this->getMock('Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface'); + $simpleAuthenticator = $this->getMock('Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface'); $simpleAuthenticator ->expects($this->once()) ->method('createToken') diff --git a/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php b/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php index ddfaaeb..7495398 100644 --- a/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php +++ b/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php @@ -25,10 +25,10 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foo', $service->getRememberMeParameter()); } - public function testGetKey() + public function testGetSecret() { $service = $this->getService(); - $this->assertEquals('fookey', $service->getKey()); + $this->assertEquals('foosecret', $service->getSecret()); } public function testAutoLoginReturnsNullWhenNoCookie() @@ -78,7 +78,7 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase $returnedToken = $service->autoLogin($request); $this->assertSame($user, $returnedToken->getUser()); - $this->assertSame('fookey', $returnedToken->getKey()); + $this->assertSame('foosecret', $returnedToken->getSecret()); $this->assertSame('fookey', $returnedToken->getProviderKey()); } @@ -284,7 +284,7 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase } return $this->getMockForAbstractClass('Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices', array( - array($userProvider), 'fookey', 'fookey', $options, $logger, + array($userProvider), 'foosecret', 'fookey', $options, $logger, )); } diff --git a/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php index f43963e..30cf4a2 100644 --- a/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php +++ b/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php @@ -20,7 +20,6 @@ use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices; use Symfony\Component\Security\Core\Exception\TokenNotFoundException; use Symfony\Component\Security\Core\Exception\CookieTheftException; -use Symfony\Component\Security\Core\Util\SecureRandom; class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase { @@ -183,7 +182,7 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken); $this->assertSame($user, $returnedToken->getUser()); - $this->assertEquals('fookey', $returnedToken->getKey()); + $this->assertEquals('foosecret', $returnedToken->getSecret()); $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)); } @@ -322,7 +321,7 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test $userProvider = $this->getProvider(); } - return new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger, new SecureRandom(sys_get_temp_dir().'/_sf2.seed')); + return new PersistentTokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger); } protected function getProvider() diff --git a/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php b/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php index e3b58e9..ee8a99e 100644 --- a/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php +++ b/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php @@ -140,7 +140,7 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken); $this->assertSame($user, $returnedToken->getUser()); - $this->assertEquals('fookey', $returnedToken->getKey()); + $this->assertEquals('foosecret', $returnedToken->getSecret()); } public function provideUsernamesForAutoLogin() @@ -265,7 +265,7 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase $userProvider = $this->getProvider(); } - $service = new TokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger); + $service = new TokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger); return $service; } diff --git a/Http/composer.json b/Http/composer.json index 1b36428..24708ac 100644 --- a/Http/composer.json +++ b/Http/composer.json @@ -17,14 +17,17 @@ ], "require": { "php": ">=5.3.9", - "symfony/security-core": "~2.6", - "symfony/event-dispatcher": "~2.1", - "symfony/http-foundation": "~2.4", - "symfony/http-kernel": "~2.4" + "symfony/security-core": "~2.8", + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/http-foundation": "~2.4|~3.0.0", + "symfony/http-kernel": "~2.4|~3.0.0", + "symfony/polyfill-php56": "~1.0", + "symfony/polyfill-php70": "~1.0", + "symfony/property-access": "~2.3|~3.0.0" }, "require-dev": { - "symfony/routing": "~2.2", - "symfony/security-csrf": "~2.4", + "symfony/routing": "~2.2|~3.0.0", + "symfony/security-csrf": "~2.4|~3.0.0", "psr/log": "~1.0" }, "suggest": { @@ -40,7 +43,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } |