Skip to content

[HttpFoundation] deprecate finding deep items in request parameters #16007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Symfony/Component/HttpFoundation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
CHANGELOG
=========

2.8.0
-----

* Finding deep items in `ParameterBag::get()` is deprecated since version 2.8 and
will be removed in 3.0.

2.6.0
-----

Expand Down
20 changes: 13 additions & 7 deletions src/Symfony/Component/HttpFoundation/ParameterBag.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,29 +78,35 @@ public function add(array $parameters = array())
/**
* Returns a parameter by name.
*
* @param string $path The key
* Note: Finding deep items is deprecated since version 2.8, to be removed in 3.0.
*
* @param string $key The key
* @param mixed $default The default value if the parameter key does not exist
* @param bool $deep If true, a path like foo[bar] will find deeper items
*
* @return mixed
*
* @throws \InvalidArgumentException
*/
public function get($path, $default = null, $deep = false)
public function get($key, $default = null, $deep = false)
{
if (!$deep || false === $pos = strpos($path, '[')) {
return array_key_exists($path, $this->parameters) ? $this->parameters[$path] : $default;
if (true === $deep) {
@trigger_error('Using paths to find deeper items in '.__METHOD__.' is deprecated since version 2.8 and will be removed in 3.0. Filter the returned value in your own code instead.', E_USER_DEPRECATED);
}

if (!$deep || false === $pos = strpos($key, '[')) {
return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
}

$root = substr($path, 0, $pos);
$root = substr($key, 0, $pos);
if (!array_key_exists($root, $this->parameters)) {
return $default;
}

$value = $this->parameters[$root];
$currentKey = null;
for ($i = $pos, $c = strlen($path); $i < $c; ++$i) {
$char = $path[$i];
for ($i = $pos, $c = strlen($key); $i < $c; ++$i) {
$char = $key[$i];

if ('[' === $char) {
if (null !== $currentKey) {
Expand Down
6 changes: 6 additions & 0 deletions src/Symfony/Component/HttpFoundation/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,8 @@ public static function getHttpMethodParameterOverride()
* It is better to explicitly get request parameters from the appropriate
* public property instead (query, attributes, request).
*
* Note: Finding deep items is deprecated since version 2.8, to be removed in 3.0.
*
* @param string $key the key
* @param mixed $default the default value
* @param bool $deep is parameter deep in multidimensional array
Expand All @@ -722,6 +724,10 @@ public static function getHttpMethodParameterOverride()
*/
public function get($key, $default = null, $deep = false)
{
if (true === $deep) {
@trigger_error('Using paths to find deeper items in '.__METHOD__.' is deprecated since version 2.8 and will be removed in 3.0. Filter the returned value in your own code instead.', E_USER_DEPRECATED);
}

if ($this !== $result = $this->query->get($key, $this, $deep)) {
return $result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public function testGetDoesNotUseDeepByDefault()
}

/**
* @group legacy
* @dataProvider getInvalidPaths
* @expectedException \InvalidArgumentException
*/
Expand All @@ -106,6 +107,9 @@ public function getInvalidPaths()
);
}

/**
* @group legacy
*/
public function testGetDeep()
{
$bag = new ParameterBag(array('foo' => array('bar' => array('moo' => 'boo'))));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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.
Expand Down Expand Up @@ -82,7 +83,7 @@ public function setOptions(array $options)
*/
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
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.
Expand Down Expand Up @@ -108,7 +109,7 @@ protected function determineTargetUrl(Request $request)
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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.
Expand Down Expand Up @@ -98,7 +99,7 @@ public function handle(GetResponseEvent $event)
}

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))) {
throw new LogoutException('Invalid CSRF token.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
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;

Expand Down Expand Up @@ -101,19 +102,19 @@ protected function requiresAuthentication(Request $request)
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))) {
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']);
}

$request->getSession()->set(Security::LAST_USERNAME, $username);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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;
Expand Down Expand Up @@ -76,19 +77,19 @@ protected function requiresAuthentication(Request $request)
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))) {
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']);
}

$request->getSession()->set(Security::LAST_USERNAME, $username);
Expand Down
96 changes: 96 additions & 0 deletions src/Symfony/Component/Security/Http/ParameterBagUtils.php
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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.
Expand Down Expand Up @@ -319,7 +320,7 @@ protected function isRememberMeRequested(Request $request)
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']));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public function testFailurePathCanBeOverwritten()
public function testFailurePathCanBeOverwrittenWithRequest()
{
$this->request->expects($this->once())
->method('get')->with('_failure_path', null, true)
->method('get')->with('_failure_path', null, false)
->will($this->returnValue('/auth/login'));

$this->httpUtils->expects($this->once())
Expand All @@ -155,12 +155,25 @@ public function testFailurePathCanBeOverwrittenWithRequest()
$handler->onAuthenticationFailure($this->request, $this->exception);
}

public function testFailurePathCanBeOverwrittenWithNestedAttributeInRequest()
{
$this->request->expects($this->once())
->method('get')->with('_failure_path', null, false)
->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', null, false)
->will($this->returnValue('/auth/login'));

$this->httpUtils->expects($this->once())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ public function testTargetPathIsPassedWithRequest()
$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');
Expand Down
3 changes: 2 additions & 1 deletion src/Symfony/Component/Security/Http/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"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/http-kernel": "~2.4|~3.0.0",
"symfony/property-access": "~2.3|~3.0.0"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7|~3.0.0",
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy