8000 feature #50392 Move UriSigner from HttpKernel to HttpFoundation packa… · symfony/symfony@f7bfffa · GitHub
[go: up one dir, main page]

Skip to content

Commit f7bfffa

Browse files
committed
feature #50392 Move UriSigner from HttpKernel to HttpFoundation package (alexander-schranz)
This PR was merged into the 6.4 branch. Discussion ---------- Move UriSigner from HttpKernel to HttpFoundation package | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | no | New feature? | yes | Deprecations? | yes | Tickets | Fix #50384 | License | MIT | Doc PR | TODO Move UriSigner from HttpKernel to HttpFoundation package as discussed in #50384. Commits ------- 2caaef8 Move UriSigner from HttpKernel to HttpFoundation package
2 parents b3c91c2 + 2caaef8 commit f7bfffa

19 files changed

+225
-101
lines changed

UPGRADE-6.4.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ HttpKernel
108108
* [BC break] `BundleInterface` no longer extends `ContainerAwareInterface`
109109
* [BC break] Add native return types to `TraceableEventDispatcher` and to `MergeExtensionConfigurationPass`
110110
* Deprecate `Kernel::stripComments()`
111+
* Deprecate `UriSigner`, use `UriSigner` from the HttpFoundation component instead
111112

112113
Messenger
113114
---------

src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
use Symfony\Component\HttpFoundation\Request;
1919
use Symfony\Component\HttpFoundation\RequestStack;
2020
use Symfony\Component\HttpFoundation\Response;
21+
use Symfony\Component\HttpFoundation\UriSigner;
2122
use Symfony\Component\HttpKernel\Fragment\FragmentHandler;
2223
use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface;
2324
use Symfony\Component\HttpKernel\Fragment\FragmentUriGenerator;
24-
use Symfony\Component\HttpKernel\UriSigner;
2525
use Twig\Environment;
2626
use Twig\Loader\ArrayLoader;
2727
use Twig\RuntimeLoader\RuntimeLoaderInterface;

src/Symfony/Bridge/Twig/composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"symfony/form": "^6.3|^7.0",
3232
"symfony/html-sanitizer": "^6.1|^7.0",
3333
"symfony/http-foundation": "^5.4|^6.0|^7.0",
34-
"symfony/http-kernel": "^6.2|^7.0",
34+
"symfony/http-kernel": "^6.4|^7.0",
3535
"symfony/intl": "^5.4|^6.0|^7.0",
3636
"symfony/mime": "^6.2|^7.0",
3737
"symfony/polyfill-intl-icu": "~1.0",
@@ -59,7 +59,7 @@
5959
"symfony/console": "<5.4",
6060
"symfony/form": "<6.3",
6161
"symfony/http-foundation": "<5.4",
62-
"symfony/http-kernel": "<6.2",
62+
"symfony/http-kernel": "<6.4",
6363
"symfony/mime": "<6.2",
6464
"symfony/serializer": "<6.2",
6565
"symfony/translation": "<5.4",

src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use Symfony\Component\HttpFoundation\RequestStack;
3636
use Symfony\Component\HttpFoundation\Response;
3737
use Symfony\Component\HttpFoundation\Session\SessionInterface;
38+
use Symfony\Component\HttpFoundation\UriSigner;
3839
use Symfony\Component\HttpFoundation\UrlHelper;
3940
use Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer;
4041
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate;
@@ -47,7 +48,7 @@
4748
use Symfony\Component\HttpKernel\HttpKernelInterface;
4849
use Symfony\Component\HttpKernel\KernelEvents;
4950
use Symfony\Component\HttpKernel\KernelInterface;
50-
use Symfony\Component\HttpKernel\UriSigner;
51+
use Symfony\Component\HttpKernel\UriSigner as HttpKernelUriSigner;
5152
use Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner;
5253
use Symfony\Component\Runtime\Runner\Symfony\ResponseRunner;
5354
use Symfony\Component\Runtime\SymfonyRuntime;
@@ -157,6 +158,8 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : []
157158
param('kernel.secret'),
158159
])
159160
->alias(UriSigner::class, 'uri_signer')
161+
->alias(HttpKernelUriSigner::class, 'uri_signer')
162+
->deprecate('symfony/framework-bundle', '6.4', 'The "%alias_id%" alias is deprecated, use "'.UriSigner::class.'" instead.')
160163

161164
->set('config_cache_factory', ResourceCheckerConfigCacheFactory::class)
162165
->args([

src/Symfony/Component/HttpFoundation/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

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

1011
6.3
1112
---
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distribut F987 ed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpFoundation\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\UriSigner;
17+
18+
class UriSignerTest extends TestCase
19+
{
20+
public function testSign()
21+
{
22+
$signer = new UriSigner('foobar');
23+
24+
$this->assertStringContainsString('?_hash=', $signer->sign('http://example.com/foo'));
25+
$this->assertStringContainsString('?_hash=', $signer->sign('http://example.com/foo?foo=bar'));
26+
$this->assertStringContainsString('&foo=', $signer->sign('http://example.com/foo?foo=bar'));
27+
}
28+
29+
public function testCheck()
30+
{
31+
$signer = new UriSigner('foobar');
32+
33+
$this->assertFalse($signer->check('http://example.com/foo?_hash=foo'));
34+
$this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo'));
35+
$this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo&bar=foo'));
36+
37+
$this->assertTrue($signer->check($signer->sign('http://example.com/foo')));
38+
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar')));
39+
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&0=integer')));
40+
41+
$this->assertSame($signer->sign('http://example.com/foo?foo=bar&bar=foo'), $signer->sign('http://example.com/foo?bar=foo&foo=bar'));
42+
}
43+
44+
public function testCheckWithDifferentArgSeparator()
45+
{
46+
$this->iniSet('arg_separator.output', '&amp;');
47+
$signer = new UriSigner('foobar');
48+
49+
$this->assertSame(
50+
'http://example.com/foo?_hash=rIOcC%2FF3DoEGo%2FvnESjSp7uU9zA9S%2F%2BOLhxgMexoPUM%3D&baz=bay&foo=bar',
51+
$signer->sign('http://example.com/foo?foo=bar&baz=bay')
52+
);
53+
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay')));
54+
}
55+
56+
public function testCheckWithRequest()
57+
{
58+
$signer = new UriSigner('foobar');
59+
60+
$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo'))));
61+
$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo?foo=bar'))));
62+
$this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo?foo=bar&0=integer'))));
63+
}
64+
65+
public function testCheckWithDifferentParameter()
66+
{
67+
$signer = new UriSigner('foobar', 'qux');
68+
69+
$this->assertSame(
70+
'http://example.com/foo?baz=bay&foo=bar&qux=rIOcC%2FF3DoEGo%2FvnESjSp7uU9zA9S%2F%2BOLhxgMexoPUM%3D',
71+
$signer->sign('http://example.com/foo?foo=bar&baz=bay')
72+
);
73+
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay')));
74+
}
75+
76+
public function testSignerWorksWithFragments()
77+
{
78+
$signer = new UriSigner('foobar');
79+
80+
$this->assertSame(
81+
'http://example.com/foo?_hash=EhpAUyEobiM3QTrKxoLOtQq5IsWyWedoXDPqIjzNj5o%3D&bar=foo&foo=bar#foobar',
82+
$signer->sign('http://example.com/foo?bar=foo&foo=bar#foobar')
83+
);
84+
$this->assertTrue($signer->check($signer->sign('http://example.com/foo?bar=foo&foo=bar#foobar')));
85+
}
86+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpFoundation;
13+
14+
/**
15+
* Signs URIs.
16+
*
17+
* @author Fabien Potencier <fabien@symfony.com>
18+
*/
19+
class UriSigner
20+
{
21+
private string $secret;
22+
private string $parameter;
23+
24+
/**
25+
* @param string $secret A secret
26+
* @param string $parameter Query string parameter to use
27+
*/
28+
public function __construct(#[\SensitiveParameter] string $secret, string $parameter = '_hash')
29+
{
30+
$this->secret = $secret;
31+
$this->parameter = $parameter;
32+
}
33+
34+
/**
35+
* Signs a URI.
36+
*
37+
* The given URI is signed by adding the query string parameter
38+
* which value depends on the URI and the secret.
39+
*/
40+
public function sign(string $uri): string
41+
{
42+
$url = parse_url($uri);
43+
$params = [];
44+
45+
if (isset($url['query'])) {
46+
parse_str($url['query'], $params);
47+
}
48+
49+
$uri = $this->buildUrl($url, $params);
50+
$params[$this->parameter] = $this->computeHash($uri);
51+
52+
return $this->buildUrl($url, $params);
53+
}
54+
55+
/**
56+
* Checks that a URI contains the correct hash.
57+
*/
58+
public function check(string $uri): bool
59+
{
60+
$url = parse_url($uri);
61+
$params = [];
62+
63+
if (isset($url['query'])) {
64+
parse_str($url['query'], $params);
65+
}
66+
67+
if (empty($params[$this->parameter])) {
68+
return false;
69+
}
70+
71+
$hash = $params[$this->parameter];
72+
unset($params[$this->parameter]);
73+
74+
return hash_equals($this->computeHash($this->buildUrl($url, $params)), $hash);
75+
}
76+
77+
public function checkRequest(Request $request): bool
78+
{
79+
$qs = ($qs = $request->server->get('QUERY_STRING')) ? '?'.$qs : '';
80+
81+
// we cannot use $request->getUri() here as we want to work with the original URI (no query string reordering)
82+
return $this->check($request->getSchemeAndHttpHost().$request->getBaseUrl().$request->getPathInfo().$qs);
83+
}
84+
85+
private function computeHash(string $uri): string
86+
{
87+
return base64_encode(hash_hmac('sha256', $uri, $this->secret, true));
88+
}
89+
90+
private function buildUrl(array $url, array $params = []): string
91+
{
92+
ksort($params, \SORT_STRING);
93+
$url['query'] = http_build_query($params, '', '&');
94+
95+
$scheme = isset($url['scheme']) ? $url['scheme'].'://' : '';
96+
$host = $url['host'] ?? '';
97+
$port = isset($url['port']) ? ':'.$url['port'] : '';
98+
$user = $url['user'] ?? '';
99+
$pass = isset($url['pass']) ? ':'.$url['pass'] : '';
100+
$pass = ($user || $pass) ? "$pass@" : '';
101+
$path = $url['path'] ?? '';
102+
$query = $url['query'] ? '?'.$url['query'] : '';
103+
$fragment = isset($url['fragment']) ? '#'.$url['fragment'] : '';
104+
105+
return $scheme.$user.$pass.$host.$port.$path.$query.$fragment;
106+
}
107+
}
108+
109+
if (!class_exists(\Symfony\Component\HttpKernel\UriSigner::class, false)) {
110+
class_alias(UriSigner::class, \Symfony\Component\HttpKernel\UriSigner::class);
111+
}

src/Symfony/Component/HttpKernel/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ CHANGELOG
1313
* Add class `DebugLoggerConfigurator`
1414
* Deprecate `Kernel::stripComments()`
1515
* Support the `!` character at the beginning of a string as a negation operator in the url filter of the profiler
16+
* Deprecate `UriSigner`, use `UriSigner` from the HttpFoundation component instead
1617

1718
6.3
1819
---

src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313

1414
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1515
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\UriSigner;
1617
use Symfony\Component\HttpKernel\Event\RequestEvent;
1718
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
1819
use Symfony\Component\HttpKernel\KernelEvents;
19-
use Symfony\Component\HttpKernel\UriSigner;
2020

2121
/**
2222
* Handles content fragments represented by special URIs.

src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313

1414
use Symfony\Component\HttpFoundation\Request;
1515
use Symfony\Component\HttpFoundation\Response;
16+
use Symfony\Component\HttpFoundation\UriSigner;
1617
use Symfony\Component\HttpKernel\Controller\ControllerReference;
1718
use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface;
18-
use Symfony\Component\HttpKernel\UriSigner;
1919

2020
/**
2121
* Implements Surrogate rendering strategy.

src/Symfony/Component/HttpKernel/Fragment/FragmentUriGenerator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313

1414
use Symfony\Component\HttpFoundation\Request;
1515
use Symfony\Component\HttpFoundation\RequestStack;
16+
use Symfony\Component\HttpFoundation\UriSigner;
1617
use Symfony\Component\HttpKernel\Controller\ControllerReference;
17-
use Symfony\Component\HttpKernel\UriSigner;
1818

1919
/**
2020
* Generates a fragment URI.

src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313

1414
use Symfony\Component\HttpFoundation\Request;
1515
use Symfony\Component\HttpFoundation\Response;
16+
use Symfony\Component\HttpFoundation\UriSigner;
1617
use Symfony\Component\HttpKernel\Controller\ControllerReference;
17-
use Symfony\Component\HttpKernel\UriSigner;
1818
use Twig\Environment;
1919

2020
/**

src/Symfony/Component/HttpKernel/Tests/EventListener/FragmentListenerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\UriSigner;
1617
use Symfony\Component\HttpKernel\Event\RequestEvent;
1718
use Symfony\Component\HttpKernel\EventListener\FragmentListener;
1819
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
1920
use Symfony\Component\HttpKernel\HttpKernelInterface;
20-
use Symfony\Component\HttpKernel\UriSigner;
2121

2222
class FragmentListenerTest extends TestCase
2323
{

src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\UriSigner;
1617
use Symfony\Component\HttpKernel\Controller\ControllerReference;
1718
use Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer;
1819
use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer;
1920
use Symfony\Component\HttpKernel\HttpCache\Esi;
20-
use Symfony\Component\HttpKernel\UriSigner;
2121

2222
class EsiFragmentRendererTest extends TestCase
2323
{

src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\UriSigner;
1617
use Symfony\Component\HttpKernel\Controller\ControllerReference;
1718
use Symfony\Component\HttpKernel\Fragment\HIncludeFragmentRenderer;
18-
use Symfony\Component\HttpKernel\UriSigner;
1919
use Twig\Environment;
2020
use Twig\Loader\ArrayLoader;
2121

src/Symfony/Component/HttpKernel/Tests/Fragment/SsiFragmentRendererTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\UriSigner;
1617
use Symfony\Component\HttpKernel\Controller\ControllerReference;
1718
use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer;
1819
use Symfony\Component\HttpKernel\Fragment\SsiFragmentRenderer;
1920
use Symfony\Component\HttpKernel\HttpCache\Ssi;
20-
use Symfony\Component\HttpKernel\UriSigner;
2121

2222
class SsiFragmentRendererTest extends TestCase
2323
{

src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
use Symfony\Component\HttpFoundation\Request;
1616
use Symfony\Component\HttpKernel\UriSigner;
1717

18+
/**
19+
* @group legacy
20+
*/
1821
class UriSignerTest extends TestCase
1922
{
2023
public function testSign()

0 commit comments

Comments
 (0)
0