8000 feature #60222 [FrameworkBundle][HttpFoundation] Add Clock support fo… · symfony/symfony@904df78 · GitHub
[go: up one dir, main page]

Skip to content

Commit 904df78

Browse files
committed
feature #60222 [FrameworkBundle][HttpFoundation] Add Clock support for UriSigner (kbond)
This PR was merged into the 7.3 branch. Discussion ---------- [FrameworkBundle][HttpFoundation] Add Clock support for `UriSigner` | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | n/a | License | MIT Optionally adds the ability to inject a `ClockInterface` to `UriSigner` (mostly to help with testing). The framework-bundle wires this up if possible. Commits ------- 4566f3a [HttpFoundation][FrameworkBundle] clock support for `UriSigner`
2 parents ed7dba6 + 4566f3a commit 904df78

File tree

5 files changed

+38
-2
lines changed

5 files changed

+38
-2
lines changed

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

+3
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : []
158158
->set('uri_signer', UriSigner::class)
159159
->args([
160160
new Parameter('kernel.secret'),
161+
'_hash',
162+
'_expiration',
163+
service('clock')->nullOnInvalid(),
161164
])
162165
->lazy()
163166
->alias(UriSigner::class, 'uri_signer')

src/Symfony/Component/HttpFoundation/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* Add `EventStreamResponse` and `ServerEvent` classes to streamline server event streaming
99
* Add support for `valkey:` / `valkeys:` schemes for sessions
1010
* `Request::getPreferredLanguage()` now favors a more preferred language above exactly matching a locale
11+
* Allow `UriSigner` to use a `ClockInterface`
1112

1213
7.2
1314
---

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

+24
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpFoundation\Tests;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Clock\MockClock;
1516
use Symfony\Component\HttpFoundation\Exception\LogicException;
1617
use Symfony\Component\HttpFoundation\Request;
1718
use Symfony\Component\HttpFoundation\UriSigner;
@@ -199,6 +200,29 @@ public function testCheckWithUriExpiration()
199200
$this->assertFalse($signer->check($relativeUriFromNow3));
200201
}
201202

203+
public function testCheckWithUriExpirationWithClock()
204+
{
205+
$clock = new MockClock();
206+
$signer = new UriSigner('foobar', clock: $clock);
207+
208+
$this->assertFalse($signer->check($signer->sign('http://example.com/foo', new \DateTimeImmutable('2000-01-01 00:00:00'))));
209+
$this->assertFalse($signer->check($signer->sign('http://example.com/foo?foo=bar', new \DateTimeImmutable('2000-01-01 00:00:00'))));
210+
$this->assertFalse($signer->check($signer->sign('http://example.com/foo?foo=bar&0=integer', new \DateTimeImmutable('2000-01-01 00:00:00'))));
211+
212+
$this->assertFalse($signer->check($signer->sign('http://example.com/foo', 1577836800))); // 2000-01-01
213+
$this->assertFalse($signer->check($signer->sign('http://example.com/foo?foo=bar', 1577836800))); // 2000-01-01
214+
$this->assertFalse($signer->check($signer->sign('http://example.com/foo?foo=bar&0=integer', 1577836800))); // 2000-01-01
215+
216+
$relativeUriFromNow1 = $signer->sign('http://example.com/foo', new \DateInterval('PT3S'));
217+
$relativeUriFromNow2 = $signer->sign('http://example.com/foo?foo=bar', new \DateInterval('PT3S'));
218+
$relativeUriFromNow3 = $signer->sign('http://example.com/foo?foo=bar&0=integer', new \DateInterval('PT3S'));
219+
$clock->sleep(10);
220+
221+
$this->assertFalse($signer->check($relativeUriFromNow1));
222+
$this->assertFalse($signer->check($relativeUriFromNow2));
223+
$this->assertFalse($signer->check($relativeUriFromNow3));
224+
}
225+
202226
public function testNonUrlSafeBase64()
203227
{
204228
$signer = new UriSigner('foobar');

src/Symfony/Component/HttpFoundation/UriSigner.php

+9-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\HttpFoundation;
1313

14+
use Psr\Clock\ClockInterface;
1415
use Symfony\Component\HttpFoundation\Exception\LogicException;
1516

1617
/**
@@ -26,6 +27,7 @@ public function __construct(
2627
#[\SensitiveParameter] private string $secret,
2728
private string $hashParameter = '_hash',
2829
private string $expirationParameter = '_expiration',
30+
private ?ClockInterface $clock = null,
2931
) {
3032
if (!$secret) {
3133
throw new \InvalidArgumentException('A non-empty secret is required.');
@@ -109,7 +111,7 @@ public function check(string $uri): bool
109111
}
110112

111113
if ($expiration = $params[$this->expirationParameter] ?? false) {
112-
return time() < $expiration;
114+
return $this->now()->getTimestamp() < $expiration;
113115
}
114116

115117
return true;
@@ -153,9 +155,14 @@ private function getExpirationTime(\DateTimeInterface|\DateInterval|int $expirat
153155
}
154156

155157
if ($expiration instanceof \DateInterval) {
156-
return \DateTimeImmutable::createFromFormat('U', time())->add($expiration)->format('U');
158+
return $this->now()->add($expiration)->format('U');
157159
}
158160

159161
return (string) $expiration;
160162
}
163+
164+
private function now(): \DateTimeImmutable
165+
{
166+
return $this->clock?->now() ?? \DateTimeImmutable::createFromFormat('U', time());
167+
}
161168
}

src/Symfony/Component/HttpFoundation/composer.json

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"doctrine/dbal": "^3.6|^4",
2626
"predis/predis": "^1.1|^2.0",
2727
"symfony/cache": "^6.4.12|^7.1.5",
28+
"symfony/clock": "^6.4|^7.0",
2829
"symfony/dependency-injection": "^6.4|^7.0",
2930
"symfony/http-kernel": "^6.4|^7.0",
3031
"symfony/mime": "^6.4|^7.0",

0 commit comments

Comments
 (0)
0