Skip to content

Commit 857a7ac

Browse files
freiondrej-lmcfabpot
authored andcommitted
[SecurityBundle] Allow specifying attributes for RequestMatcher
1 parent 8e07ae6 commit 857a7ac

File tree

4 files changed

+120
-5
lines changed

4 files changed

+120
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ CHANGELOG
55
---
66

77
* The `security.access_control` now accepts a `RequestMatcherInterface` under the `request_matcher` option as scope configuration
8+
* The `security.access_control` now accepts an `attributes` array to match request attributes in the `RequestMatcher`
9+
* The `security.access_control` now accepts a `route` option to match request route in the `RequestMatcher`
810
* Display the inherited roles of the logged-in user in the Web Debug Toolbar
911

1012
6.0

DependencyInjection/MainConfiguration.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ private function addAccessControlSection(ArrayNodeDefinition $rootNode)
147147
->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end()
148148
->prototype('scalar')->end()
149149
->end()
150+
->arrayNode('attributes')
151+
->prototype('scalar')->end()
152+
->end()
153+
->scalarNode('route')->defaultNull()->end()
150154
->arrayNode('methods')
151155
->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end()
152156
->prototype('scalar')->end()

DependencyInjection/SecurityExtension.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,24 +199,34 @@ private function createAuthorization(array $config, ContainerBuilder $container)
199199
{
200200
foreach ($config['access_control'] as $access) {
201201
if (isset($access['request_matcher'])) {
202-
if ($access['path'] || $access['host'] || $access['port'] || $access['ips'] || $access['methods']) {
202+
if ($access['path'] || $access['host'] || $access['port'] || $access['ips'] || $access['methods'] || $access['attributes'] || $access['route']) {
203203
throw new InvalidConfigurationException('The "request_matcher" option should not be specified alongside other options. Consider integrating your constraints inside your RequestMatcher directly.');
204204
}
205205
$matcher = new Reference($access['request_matcher']);
206206
} else {
207+
$attributes = $access['attributes'];
208+
209+
if ($access['route']) {
210+
if (\array_key_exists('_route', $attributes)) {
211+
throw new InvalidConfigurationException('The "route" option should not be specified alongside "attributes._route" option. Use just one of the options.');
212+
}
213+
$attributes['_route'] = $access['route'];
214+
}
215+
207216
$matcher = $this->createRequestMatcher(
208217
$container,
209218
$access['path'],
210219
$access['host'],
211220
$access['port'],
212221
$access['methods'],
213-
$access['ips']
222+
$access['ips'],
223+
$attributes
214224
);
215225
}
216226

217-
$attributes = $access['roles'];
227+
$roles = $access['roles'];
218228
if ($access['allow_if']) {
219-
$attributes[] = $this->createExpression($container, $access['allow_if']);
229+
$roles[] = $this->createExpression($container, $access['allow_if']);
220230
}
221231

222232
$emptyAccess = 0 === \count(array_filter($access));
@@ -226,7 +236,7 @@ private function createAuthorization(array $config, ContainerBuilder $container)
226236
}
227237

228238
$container->getDefinition('security.access_map')
229-
->addMethodCall('add', [$matcher, $attributes, $access['requires_channel']]);
239+
->addMethodCall('add', [$matcher, $roles, $access['requires_channel']]);
230240
}
231241

232242
// allow cache warm-up for expressions

Tests/DependencyInjection/SecurityExtensionTest.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,105 @@ public function provideAdditionalRequestMatcherConstraints()
323323
yield 'Invalid configuration with port' => [['port' => 80]];
324324
yield 'Invalid configuration with methods' => [['methods' => ['POST']]];
325325
yield 'Invalid configuration with ips' => [['ips' => ['0.0.0.0']]];
326+
yield 'Invalid configuration with attributes' => [['attributes' => ['_route' => 'foo_route']]];
327+
yield 'Invalid configuration with route' => [['route' => 'foo_route']];
328+
}
329+
330+
public function testRegisterAccessControlWithSpecifiedAttributes()
331+
{
332+
$container = $this->getRawContainer();
333+
$container->loadFromExtension('security', [
334+
'enable_authenticator_manager' => true,
335+
'providers' => [
336+
'default' => ['id' => 'foo'],
337+
],
338+
'firewalls' => [
339+
'some_firewall' => [
340+
'pattern' => '/.*',
341+
'http_basic' => [],
342+
],
343+
],
344+
'access_control' => [
345+
['attributes' => ['_route' => 'foo_route']],
346+
],
347+
]);
348+
349+
$container->compile();
350+
351+
$accessMap = $container->getDefinition('security.access_map');
352+
$this->assertCount(1, $accessMap->getMethodCalls());
353+
$call = $accessMap->getMethodCalls()[0];
354+
$this->assertSame('add', $call[0]);
355+
$args = $call[1];
356+
$requestMatcherId = (string) $args[0];
357+
358+
$requestMatcherDefinition = $container->getDefinition($requestMatcherId);
359+
$requestMatcherConstructorArguments = $requestMatcherDefinition->getArguments();
360+
$this->assertArrayHasKey(4, $requestMatcherConstructorArguments);
361+
$attributes = $requestMatcherConstructorArguments[4];
362+
$this->assertArrayHasKey('_route', $attributes);
363+
$this->assertSame('foo_route', $attributes['_route']);
364+
}
365+
366+
public function testRegisterAccessControlWithSpecifiedRoute()
367+
{
368+
$container = $this->getRawContainer();
369+
$container->loadFromExtension('security', [
370+
'enable_authenticator_manager' => true,
371+
'providers' => [
372+
'default' => ['id' => 'foo'],
373+
],
374+
'firewalls' => [
375+
'some_firewall' => [
376+
'pattern' => '/.*',
377+
'http_basic' => [],
378+
],
379+
],
380+
'access_control' => [
381+
['route' => 'foo_route'],
382+
],
383+
]);
384+
385+
$container->compile();
386+
387+
$accessMap = $container->getDefinition('security.access_map');
388+
$this->assertCount(1, $accessMap->getMethodCalls());
389+
$call = $accessMap->getMethodCalls()[0];
390+
$this->assertSame('add', $call[0]);
391+
$args = $call[1];
392+
$requestMatcherId = (string) $args[0];
393+
394+
$requestMatcherDefinition = $container->getDefinition($requestMatcherId);
395+
$requestMatcherConstructorArguments = $requestMatcherDefinition->getArguments();
396+
$this->assertArrayHasKey(4, $requestMatcherConstructorArguments);
397+
$attributes = $requestMatcherConstructorArguments[4];
398+
$this->assertArrayHasKey('_route', $attributes);
399+
$this->assertSame('foo_route', $attributes['_route']);
400+
}
401+
402+
public function testRegisterAccessControlWithSpecifiedAttributesThrowsException()
403+
{
404+
$container = $this->getRawContainer();
405+
$container->loadFromExtension('security', [
406+
'enable_authenticator_manager' => true,
407+
'providers' => [
408+
'default' => ['id' => 'foo'],
409+
],
410+
'firewalls' => [
411+
'some_firewall' => [
412+
'pattern' => '/.*',
413+
'http_basic' => [],
414+
],
415+
],
416+
'access_control' => [
417+
['route' => 'anything', 'attributes' => ['_route' => 'foo_route']],
418+
],
419+
]);
420+
421+
$this->expectException(InvalidConfigurationException::class);
422+
$this->expectExceptionMessage('The "route" option should not be specified alongside "attributes._route" option. Use just one of the options.');
423+
424+
$container->compile();
326425
}
327426

328427
public function testRemovesExpressionCacheWarmerDefinitionIfNoExpressions()

0 commit comments

Comments
 (0)