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

File tree

19 files changed

+225
-101
lines changed

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 distributed 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.

0 commit comments

Comments
 (0)
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