8000 Move UriSigner from HttpKernel to HttpFoundation package by alexander-schranz · Pull Request #50392 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

Move UriSigner from HttpKernel to HttpFoundation package #50392

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
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
1 change: 1 addition & 0 deletions UPGRADE-6.4.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ HttpKernel
* [BC break] `BundleInterface` no longer extends `ContainerAwareInterface`
* [BC break] Add native return types to `TraceableEventDispatcher` and to `MergeExtensionConfigurationPass`
* Deprecate `Kernel::stripComments()`
* Deprecate `UriSigner`, use `UriSigner` from the HttpFoundation component instead

Messenger
---------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\HttpKernel\Fragment\FragmentHandler;
use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface;
use Symfony\Component\HttpKernel\Fragment\FragmentUriGenerator;
use Symfony\Component\HttpKernel\UriSigner;
use Twig\Environment;
use Twig\Loader\ArrayLoader;
use Twig\RuntimeLoader\RuntimeLoaderInterface;
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Bridge/Twig/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"symfony/form": "^6.3|^7.0",
"symfony/html-sanitizer": "^6.1|^7.0",
"symfony/http-foundation": "^5.4|^6.0|^7.0",
"symfony/http-kernel": "^6.2|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/intl": "^5.4|^6.0|^7.0",
"symfony/mime": "^6.2|^7.0",
"symfony/polyfill-intl-icu": "~1.0",
Expand Down Expand Up @@ -59,7 +59,7 @@
"symfony/console": "<5.4",
"symfony/form": "<6.3",
"symfony/http-foundation": "<5.4",
"symfony/http-kernel": "<6.2",
"symfony/http-kernel": "<6.4",
"symfony/mime": "<6.2",
"symfony/serializer": "<6.2",
"symfony/translation": "<5.4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\HttpFoundation\UrlHelper;
use Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate;
Expand All @@ -47,7 +48,7 @@
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpKernel\UriSigner;
use Symfony\Component\HttpKernel\UriSigner as HttpKernelUriSigner;
use Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner;
use Symfony\Component\Runtime\Runner\Symfony\ResponseRunner;
use Symfony\Component\Runtime\SymfonyRuntime;
Expand Down Expand Up @@ -157,6 +158,8 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : []
param('kernel.secret'),
])
->alias(UriSigner::class, 'uri_signer')
->alias(HttpKernelUriSigner::class, 'uri_signer')
->deprecate('symfony/framework-bundle', '6.4', 'The "%alias_id%" alias is deprecated, use "'.UriSigner::class.'" instead.')

->set('config_cache_factory', ResourceCheckerConfigCacheFactory::class)
->args([
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/HttpFoundation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ CHANGELOG

* Make `HeaderBag::getDate()`, `Response::getDate()`, `getExpires()` and `getLastModified()` return a `DateTimeImmutable`
* Support root-level `Generator` in `StreamedJsonResponse`
* Add `UriSigner` from the HttpKernel component

6.3
---
Expand Down
86 changes: 86 additions & 0 deletions src/Symfony/Component/HttpFoundation/Tests/UriSignerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?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\HttpFoundation\Tests;

use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\UriSigner;

class UriSignerTest extends TestCase
{
public function testSign()
{
$signer = new UriSigner('foobar');

$this->assertStringContainsString('?_hash=', $signer->sign('http://example.com/foo'));
$this->assertStringContainsString('?_hash=', $signer->sign('http://example.com/foo?foo=bar'));
$this->assertStringContainsString('&foo=', $signer->sign('http://example.com/foo?foo=bar'));
}

public function testCheck()
{
$signer = new UriSigner('foobar');

$this->assertFalse($signer->check('http://example.com/foo?_hash=foo'));
$this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo'));
$this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo&bar=foo'));

$this->assertTrue($signer->check($signer->sign('http://example.com/foo')));
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar')));
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&0=integer')));

$this->assertSame($signer->sign('http://example.com/foo?foo=bar&bar=foo'), $signer->sign('http://example.com/foo?bar=foo&foo=bar'));
}

public function testCheckWithDifferentArgSeparator()
{
$this->iniSet('arg_separator.output', '&amp;');
$signer = new UriSigner('foobar');

$this->assertSame(
'http://example.com/foo?_hash=rIOcC%2FF3DoEGo%2FvnESjSp7uU9zA9S%2F%2BOLhxgMexoPUM%3D&baz=bay&foo=bar',
$signer->sign('http://example.com/foo?foo=bar&baz=bay')
);
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay')));
}

public function testCheckWithRequest()
F438 {
$signer = new UriSigner('foobar');

$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo'))));
$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo?foo=bar'))));
$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo?foo=bar&0=integer'))));
}

public function testCheckWithDifferentParameter()
{
$signer = new UriSigner('foobar', 'qux');

$this->assertSame(
'http://example.com/foo?baz=bay&foo=bar&qux=rIOcC%2FF3DoEGo%2FvnESjSp7uU9zA9S%2F%2BOLhxgMexoPUM%3D',
$signer->sign('http://example.com/foo?foo=bar&baz=bay')
);
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay')));
}

public function testSignerWorksWithFragments()
{
$signer = new UriSigner('foobar');

$this->assertSame(
'http://example.com/foo?_hash=EhpAUyEobiM3QTrKxoLOtQq5IsWyWedoXDPqIjzNj5o%3D&bar=foo&foo=bar#foobar',
$signer->sign('http://example.com/foo?bar=foo&foo=bar#foobar')
);
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?bar=foo&foo=bar#foobar')));
}
}
111 changes: 111 additions & 0 deletions src/Symfony/Component/HttpFoundation/UriSigner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?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\HttpFoundation;

/**
* Signs URIs.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class UriSigner
{
private string $secret;
private string $parameter;

/**
* @param string $secret A secret
* @param string $parameter Query string parameter to use
*/
public function __construct(#[\SensitiveParameter] string $secret, string $parameter = '_hash')
{
$this->secret = $secret;
$this->parameter = $parameter;
}

/**
* Signs a URI.
*
* The given URI is signed by adding the query string parameter
* which value depends on the URI and the secret.
*/
public function sign(string $uri): string
{
$url = parse_url($uri);
$params = [];

if (isset($url['query'])) {
parse_str($url['query'], $params);
}

$uri = $this->buildUrl($url, $params);
$params[$this->parameter] = $this->computeHash($uri);

return $this->buildUrl($url, $params);
}

/**
* Checks that a URI contains the correct hash.
*/
public function check(string $uri): bool
{
$url = parse_url($uri);
$params = [];

if (isset($url['query'])) {
parse_str($url['query'], $params);
}

if (empty($params[$this->parameter])) {
return false;
}

$hash = $params[$this->parameter];
unset($params[$this->parameter]);

return hash_equals($this->computeHash($this->buildUrl($url, $params)), $hash);
}

public function checkRequest(Request $request): bool
{
$qs = ($qs = $request->server->get('QUERY_STRING')) ? '?'.$qs : '';

// we cannot use $request->getUri() here as we want to work with the original URI (no query string reordering)
return $this->check($request->getSchemeAndHttpHost().$request->getBaseUrl().$request->getPathInfo().$qs);
}

private function computeHash(string $uri): string
{
return base64_encode(hash_hmac('sha256', $uri, $this->secret, true));
}

private function buildUrl(array $url, array $params = []): string
{
ksort($params, \SORT_STRING);
$url['query'] = http_build_query($params, '', '&');

$scheme = isset($url['scheme']) ? $url['scheme'].'://' : '';
$host = $url['host'] ?? '';
$port = isset($url['port']) ? ':'.$url['port'] : '';
$user = $url['user'] ?? '';
$pass = isset($url['pass']) ? ':'.$url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = $url['path'] ?? '';
$query = $url['query'] ? '?'.$url['query'] : '';
$fragment = isset($url['fragment']) ? '#'.$url['fragment'] : '';

return $scheme.$user.$pass.$host.$port.$path.$query.$fragment;
}
}

if (!class_exists(\Symfony\Component\HttpKernel\UriSigner::class, false)) {
class_alias(UriSigner::class, \Symfony\Component\HttpKernel\UriSigner::class);
}
1 change: 1 addition & 0 deletions src/Symfony/Component/HttpKernel/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ CHANGELOG
* Add class `DebugLoggerConfigurator`
* Deprecate `Kernel::stripComments()`
* Support the `!` character at the beginning of a string as a negation operator in the url filter of the profiler
* Deprecate `UriSigner`, use `UriSigner` from the HttpFoundation component instead

6.3
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\UriSigner;

/**
* Handles content fragments represented by special URIs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface;
use Symfony\Component\HttpKernel\UriSigner;

/**
* Implements Surrogate rendering strategy.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpKernel\UriSigner;

/**
* Generates a fragment URI.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpKernel\UriSigner;
use Twig\Environment;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\EventListener\FragmentListener;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\UriSigner;

class FragmentListenerTest extends TestCase
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer;
use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer;
use Symfony\Component\HttpKernel\HttpCache\Esi;
use Symfony\Component\HttpKernel\UriSigner;

class EsiFragmentRendererTest extends TestCase
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpKernel\Fragment\HIncludeFragmentRenderer;
use Symfony\Component\HttpKernel\UriSigner;
use Twig\Environment;
use Twig\Loader\ArrayLoader;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer;
use Symfony\Component\HttpKernel\Fragment\SsiFragmentRenderer;
use Symfony\Component\HttpKernel\HttpCache\Ssi;
use Symfony\Component\HttpKernel\UriSigner;

class SsiFragmentRendererTest extends TestCase
{
Expand Down
3 changes: 3 additions & 0 deletions src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\UriSigner;

/**
* @group legacy
*/
class UriSignerTest extends TestCase
{
public function testSign()
Expand Down
Loading
0