Skip to content

Commit 43c46e8

Browse files
committed
[Security] Add request query parameters as a source for #[IsCsrfTokenValid]
1 parent 07ee6ee commit 43c46e8

File tree

3 files changed

+53
-28
lines changed

3 files changed

+53
-28
lines changed

src/Symfony/Component/Security/Http/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* Deprecate `AbstractListener::__invoke`
1010
* Add `$methods` argument to `#[IsGranted]` to restrict validation to specific HTTP methods
1111
* Allow subclassing `#[IsGranted]`
12+
* Add request query parameters as a source for `#[IsCsrfTokenValid]`
1213

1314
7.3
1415
---

src/Symfony/Component/Security/Http/EventListener/IsCsrfTokenValidAttributeListener.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event): vo
5050
continue;
5151
}
5252

53-
if (!$this->csrfTokenManager->isTokenValid(new CsrfToken($id, $request->getPayload()->getString($attribute->tokenKey)))) {
53+
$tokenValue = $request->getPayload()->get($attribute->tokenKey) ?? $request->query->get($attribute->tokenKey);
54+
if (!$this->csrfTokenManager->isTokenValid(new CsrfToken($id, (string) $tokenValue))) {
5455
throw new InvalidCsrfTokenException('Invalid CSRF token.');
5556
}
5657
}

src/Symfony/Component/Security/Http/Tests/EventListener/IsCsrfTokenValidAttributeListenerTest.php

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Security\Http\Tests\EventListener;
1313

14+
use PHPUnit\Framework\Attributes\DataProvider;
1415
use PHPUnit\Framework\TestCase;
1516
use Symfony\Component\ExpressionLanguage\Expression;
1617
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
@@ -26,10 +27,9 @@
2627

2728
class IsCsrfTokenValidAttributeListenerTest extends TestCase
2829
{
29-
public function testIsCsrfTokenValidCalledCorrectlyOnInvokableClass()
30+
#[DataProvider('requestProvider')]
31+
public function testIsCsrfTokenValidCalledCorrectlyOnInvokableClass(Request $request)
3032
{
31-
$request = new Request(request: ['_token' => 'bar']);
32-
3333
$csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
3434
$csrfTokenManager->expects($this->once())
3535
->method('isTokenValid')
@@ -66,10 +66,9 @@ public function testNothingHappensWithNoConfig()
6666
$listener->onKernelControllerArguments($event);
6767
}
6868

69-
public function testIsCsrfTokenValidCalledCorrectly()
69+
#[DataProvider('requestProvider')]
70+
public function testIsCsrfTokenValidCalledCorrectly(Request $request)
7071
{
71-
$request = new Request(request: ['_token' => 'bar']);
72-
7372
$csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
7473
$csrfTokenManager->expects($this->once())
7574
->method('isTokenValid')
@@ -110,10 +109,9 @@ public function testIsCsrfTokenValidCalledCorrectlyInPayload()
110109
$listener->onKernelControllerArguments($event);
111110
}
112111

113-
public function testIsCsrfTokenValidCalledCorrectlyWithCustomExpressionId()
112+
#[DataProvider('requestWithIdProvider')]
113+
public function testIsCsrfTokenValidCalledCorrectlyWithCustomExpressionId(Request $request)
114114
{
115-
$request = new Request(query: ['id' => '123'], request: ['_token' => 'bar']);
116-
117115
$csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
118116
$csrfTokenManager->expects($this->once())
119117
->method('isTokenValid')
@@ -141,10 +139,9 @@ public function testIsCsrfTokenValidCalledCorrectlyWithCustomExpressionId()
141139
$listener->onKernelControllerArguments($event);
142140
}
143141

144-
public function testIsCsrfTokenValidCalledCorrectlyWithCustomTokenKey()
142+
#[DataProvider('requestWithCustomProvider')]
143+
public function testIsCsrfTokenValidCalledCorrectlyWithCustomTokenKey(Request $request)
145144
{
146-
$request = new Request(request: ['my_token_key' => 'bar']);
147-
148145
$csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
149146
$csrfTokenManager->expects($this->once())
150147
->method('isTokenValid')
@@ -163,10 +160,9 @@ public function testIsCsrfTokenValidCalledCorrectlyWithCustomTokenKey()
163160
$listener->onKernelControllerArguments($event);
164161
}
165162

166-
public function testIsCsrfTokenValidCalledCorrectlyWithInvalidTokenKey()
163+
#[DataProvider('requestProvider')]
164+
public function testIsCsrfTokenValidCalledCorrectlyWithInvalidTokenKey(Request $request)
167165
{
168-
$request = new Request(request: ['_token' => 'bar']);
169-
170166
$csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
171167
$csrfTokenManager->expects($this->once())
172168
->method('isTokenValid')
@@ -207,9 +203,9 @@ public function testExceptionWhenInvalidToken()
207203
$listener->onKernelControllerArguments($event);
208204
}
209205

210-
public function testIsCsrfTokenValidCalledCorrectlyWithDeleteMethod()
206+
#[DataProvider('requestProvider')]
207+
public function testIsCsrfTokenValidCalledCorrectlyWithDeleteMethod(Request $request)
211208
{
212-
$request = new Request(request: ['_token' => 'bar']);
213209
$request->setMethod('DELETE');
214210

215211
$csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
@@ -230,9 +226,9 @@ public function testIsCsrfTokenValidCalledCorrectlyWithDeleteMethod()
230226
$listener->onKernelControllerArguments($event);
231227
}
232228

233-
public function testIsCsrfTokenValidIgnoredWithNonMatchingMethod()
229+
#[DataProvider('requestProvider')]
230+
public function testIsCsrfTokenValidIgnoredWithNonMatchingMethod(Request $request)
234231
{
235-
$request = new Request(request: ['_token' => 'bar']);
236232
$request->setMethod('POST');
237233

238234
$csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
@@ -252,9 +248,9 @@ public function testIsCsrfTokenValidIgnoredWithNonMatchingMethod()
252248
$listener->onKernelControllerArguments($event);
253249
}
254250

255-
public function testIsCsrfTokenValidCalledCorrectlyWithGetOrPostMethodWithGetMethod()
251+
#[DataProvider('requestProvider')]
252+
public function testIsCsrfTokenValidCalledCorrectlyWithGetOrPostMethodWithGetMethod(Request $request)
256253
{
257-
$request = new Request(request: ['_token' => 'bar']);
258254
$request->setMethod('GET');
259255

260256
$csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
@@ -275,9 +271,9 @@ public function testIsCsrfTokenValidCalledCorrectlyWithGetOrPostMethodWithGetMet
275271
$listener->onKernelControllerArguments($event);
276272
}
277273

278-
public function testIsCsrfTokenValidNoIgnoredWithGetOrPostMethodWithPutMethod()
274+
#[DataProvider('requestProvider')]
275+
public function testIsCsrfTokenValidNoIgnoredWithGetOrPostMethodWithPutMethod(Request $request)
279276
{
280-
$request = new Request(request: ['_token' => 'bar']);
281277
$request->setMethod('PUT');
282278

283279
$csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
@@ -297,11 +293,11 @@ public function testIsCsrfTokenValidNoIgnoredWithGetOrPostMethodWithPutMethod()
297293
$listener->onKernelControllerArguments($event);
298294
}
299295

300-
public function testIsCsrfTokenValidCalledCorrectlyWithInvalidTokenKeyAndPostMethod()
296+
#[DataProvider('requestProvider')]
297+
public function testIsCsrfTokenValidCalledCorrectlyWithInvalidTokenKeyAndPostMethod(Request $request)
301298
{
302299
$this->expectException(InvalidCsrfTokenException::class);
303300

304-
$request = new Request(request: ['_token' => 'bar']);
305301
$request->setMethod('POST');
306302

307303
$csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
@@ -322,9 +318,9 @@ public function testIsCsrfTokenValidCalledCorrectlyWithInvalidTokenKeyAndPostMet
322318
$listener->onKernelControllerArguments($event);
323319
}
324320

325-
public function testIsCsrfTokenValidIgnoredWithInvalidTokenKeyAndUnavailableMethod()
321+
#[DataProvider('requestProvider')]
322+
public function testIsCsrfTokenValidIgnoredWithInvalidTokenKeyAndUnavailableMethod(Request $request)
326323
{
327-
$request = new Request(request: ['_token' => 'bar']);
328324
$request->setMethod('PUT');
329325

330326
$csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
@@ -343,4 +339,31 @@ public function testIsCsrfTokenValidIgnoredWithInvalidTokenKeyAndUnavailableMeth
343339
$listener = new IsCsrfTokenValidAttributeListener($csrfTokenManager);
344340
$listener->onKernelControllerArguments($event);
345341
}
342+
343+
public static function requestProvider(): array
344+
{
345+
return [
346+
'request_only' => [new Request(request: ['_token' => 'bar'])],
347+
'query_only' => [new Request(query: ['_token' => 'bar'])],
348+
'query_and_request' => [new Request(query: ['_token' => 'ignored'], request: ['_token' => 'bar'])],
349+
];
350+
}
351+
352+
public static function requestWithIdProvider(): array
353+
{
354+
return [
355+
'request_only' => [new Request(query: ['id' => '123'], request: ['_token' => 'bar'])],
356+
'query_only' => [new Request(query: ['id' => '123', '_token' => 'bar'])],
357+
'query_and_request' => [new Request(query: ['id' => '123', '_token' => 'ignored'], request: ['_token' => 'bar'])],
358+
];
359+
}
360+
361+
public static function requestWithCustomProvider(): array
362+
{
363+
return [
364+
'request_only' => [new Request(request: ['my_token_key' => 'bar'])],
365+
'query_only' => [new Request(query: ['my_token_key' => 'bar'])],
366+
'query_and_request' => [new Request(query: ['my_token_key' => 'ignored'], request: ['my_token_key' => 'bar'])],
367+
];
368+
}
346369
}

0 commit comments

Comments
 (0)