Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitae44f50

Browse files
committed
[Security] Handle placeholders in role hierarchy
1 parentabe5555 commitae44f50

File tree

4 files changed

+103
-14
lines changed

4 files changed

+103
-14
lines changed

‎src/Symfony/Component/Security/Core/CHANGELOG.md

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

7+
* Allow using wildcards as placeholders in`RoleHierarchy` map's keys
78
* Make`PersistentToken` immutable
89
* Deprecate accepting only`DateTime` for`TokenProviderInterface::updateToken()`, use`DateTimeInterface` instead
910

‎src/Symfony/Component/Security/Core/Role/RoleHierarchy.php

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919
class RoleHierarchyimplements RoleHierarchyInterface
2020
{
2121
privatearray$hierarchy;
22+
23+
/**
24+
* Map role placeholders with their regex pattern.
25+
*
26+
* @var array<string,string>
27+
*/
28+
privatearray$rolePlaceholdersPatterns;
29+
2230
/** @var array<string, list<string>> */
2331
protected$map;
2432

@@ -34,19 +42,7 @@ public function __construct(array $hierarchy)
3442

3543
publicfunctiongetReachableRoleNames(array$roles):array
3644
{
37-
$reachableRoles =$roles;
38-
39-
foreach ($rolesas$role) {
40-
if (!isset($this->map[$role])) {
41-
continue;
42-
}
43-
44-
foreach ($this->map[$role]as$r) {
45-
$reachableRoles[] =$r;
46-
}
47-
}
48-
49-
returnarray_values(array_unique($reachableRoles));
45+
return$this->resolveReachableRoleNames($roles);
5046
}
5147

5248
/**
@@ -55,6 +51,8 @@ public function getReachableRoleNames(array $roles): array
5551
protectedfunctionbuildRoleMap()
5652
{
5753
$this->map = [];
54+
$this->rolePlaceholdersPatterns = [];
55+
5856
foreach ($this->hierarchyas$main =>$roles) {
5957
$this->map[$main] =$roles;
6058
$visited = [];
@@ -76,6 +74,49 @@ protected function buildRoleMap()
7674
}
7775

7876
$this->map[$main] =array_unique($this->map[$main]);
77+
78+
if (str_contains($main,'*')) {
79+
$this->rolePlaceholdersPatterns[$main] =sprintf('/%s/',strtr($main, ['*' =>'[^\*]+']));
80+
}
7981
}
8082
}
83+
84+
privatefunctionresolveReachableRoleNames(array$roles,array &$visitedPlaceholders = []):array
85+
{
86+
$reachableRoles =$roles;
87+
88+
foreach ($rolesas$role) {
89+
if (!isset($this->map[$role])) {
90+
continue;
91+
}
92+
93+
foreach ($this->map[$role]as$r) {
94+
$reachableRoles[] =$r;
95+
}
96+
}
97+
98+
$placeholderRoles =array_diff($this->getMatchingPlaceholders($reachableRoles),$visitedPlaceholders);
99+
if (!empty($placeholderRoles)) {
100+
array_push($visitedPlaceholders, ...$placeholderRoles);
101+
$resolvedPlaceholderRoles =$this->resolveReachableRoleNames($placeholderRoles,$visitedPlaceholders);
102+
foreach (array_diff($resolvedPlaceholderRoles,$placeholderRoles)as$r) {
103+
$reachableRoles[] =$r;
104+
}
105+
}
106+
107+
returnarray_values(array_unique($reachableRoles));
108+
}
109+
110+
privatefunctiongetMatchingPlaceholders(array$roles):array
111+
{
112+
$resolved = [];
113+
114+
foreach ($this->rolePlaceholdersPatternsas$placeholder =>$pattern) {
115+
if (!\in_array($placeholder,$resolved) &&\count(preg_grep($pattern,$roles) ??null)) {
116+
$resolved[] =$placeholder;
117+
}
118+
}
119+
120+
return$resolved;
121+
}
81122
}

‎src/Symfony/Component/Security/Core/Tests/Authorization/Voter/RoleHierarchyVoterTest.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ class RoleHierarchyVoterTest extends RoleVoterTest
2222
*/
2323
publicfunctiontestVoteUsingTokenThatReturnsRoleNames($roles,$attributes,$expected)
2424
{
25-
$voter =newRoleHierarchyVoter(newRoleHierarchy(['ROLE_FOO' => ['ROLE_FOOBAR']]));
25+
$voter =newRoleHierarchyVoter(newRoleHierarchy([
26+
'ROLE_FOO' => ['ROLE_FOOBAR'],
27+
'ROLE_FOO_*' => ['ROLE_BAR_A','ROLE_FOO'],
28+
'ROLE_BAR_*' => ['ROLE_BAZ'],
29+
]));
2630

2731
$this->assertSame($expected,$voter->vote($this->getTokenWithRoleNames($roles),null,$attributes));
2832
}
@@ -31,6 +35,9 @@ public static function getVoteTests()
3135
{
3236
returnarray_merge(parent::getVoteTests(), [
3337
[['ROLE_FOO'], ['ROLE_FOOBAR'], VoterInterface::ACCESS_GRANTED],
38+
[['ROLE_FOO_A'], ['ROLE_BAR_A'], VoterInterface::ACCESS_GRANTED],// ROLE_FOO_A => ROLE_FOO_* => ROLE_BAR_A
39+
[['ROLE_FOO_A'], ['ROLE_FOOBAR'], VoterInterface::ACCESS_GRANTED],// ROLE_FOO_A => ROLE_FOO_* => ROLE_FOO => ROLE_FOOBAR
40+
[['ROLE_FOO_A'], ['ROLE_BAZ'], VoterInterface::ACCESS_GRANTED],// ROLE_FOO_A => ROLE_FOO_* => ROLE_BAR_A => ROLE_BAR_* => ROLE_BAZ
3441
]);
3542
}
3643

‎src/Symfony/Component/Security/Core/Tests/Role/RoleHierarchyTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,44 @@ public function testGetReachableRoleNames()
3030
$this->assertEquals(['ROLE_SUPER_ADMIN','ROLE_ADMIN','ROLE_FOO','ROLE_USER'],$role->getReachableRoleNames(['ROLE_SUPER_ADMIN']));
3131
$this->assertEquals(['ROLE_SUPER_ADMIN','ROLE_ADMIN','ROLE_FOO','ROLE_USER'],$role->getReachableRoleNames(['ROLE_SUPER_ADMIN','ROLE_SUPER_ADMIN']));
3232
}
33+
34+
publicfunctiontestGetReachableRoleNamesWithPlaceholders()
35+
{
36+
$role =newRoleHierarchy([
37+
'ROLE_BAZ_*' => ['ROLE_USER'],
38+
'ROLE_FOO_*' => ['ROLE_BAZ_FOO'],
39+
'ROLE_BAR_*' => ['ROLE_BAZ_BAR'],
40+
]);
41+
42+
$this->assertEquals(['ROLE_BAZ_A','ROLE_USER'],$role->getReachableRoleNames(['ROLE_BAZ_A']));
43+
$this->assertEquals(['ROLE_FOO_A','ROLE_BAZ_FOO','ROLE_USER'],$role->getReachableRoleNames(['ROLE_FOO_A']));
44+
45+
// Multiple roles matching the same placeholder
46+
$this->assertEquals(['ROLE_FOO_A','ROLE_FOO_B','ROLE_BAZ_FOO','ROLE_USER'],$role->getReachableRoleNames(['ROLE_FOO_A','ROLE_FOO_B']));
47+
48+
// Multiple roles matching multiple placeholders
49+
$this->assertEquals(['ROLE_FOO_A','ROLE_BAR_A','ROLE_BAZ_FOO','ROLE_BAZ_BAR','ROLE_USER'],$role->getReachableRoleNames(['ROLE_FOO_A','ROLE_BAR_A']));
50+
}
51+
52+
publicfunctiontestGetReachableRoleNamesWithRecursivePlaceholders()
53+
{
54+
$role =newRoleHierarchy([
55+
'ROLE_FOO_*' => ['ROLE_BAR_BAZ'],
56+
'ROLE_BAR_*' => ['ROLE_FOO_BAZ'],
57+
'ROLE_QUX_*' => ['ROLE_QUX_BAZ'],
58+
]);
59+
60+
// ROLE_FOO_* expanded once
61+
$this->assertEquals(['ROLE_FOO_A','ROLE_BAR_BAZ','ROLE_FOO_BAZ'],$role->getReachableRoleNames(['ROLE_FOO_A']));
62+
63+
// ROLE_FOO_* expanded once even with multiple ROLE_FOO_* input roles
64+
$this->assertEquals(['ROLE_FOO_A','ROLE_FOO_B','ROLE_BAR_BAZ','ROLE_FOO_BAZ'],$role->getReachableRoleNames(['ROLE_FOO_A','ROLE_FOO_B']));
65+
66+
// ROLE_BAR_* expanded once with ROLE_FOO_A => ROLE_FOO_* => ROLE_BAR_BAZ => ROLE_BAR_* => ROLE_FOO_BAZ
67+
$this->assertEquals(['ROLE_FOO_A','ROLE_BAR_A','ROLE_BAR_BAZ','ROLE_FOO_BAZ'],$role->getReachableRoleNames(['ROLE_FOO_A','ROLE_BAR_A']));
68+
69+
// Self matching placeholder
70+
$this->assertEquals(['ROLE_QUX_A','ROLE_QUX_BAZ'],$role->getReachableRoleNames(['ROLE_QUX_A']));
71+
$this->assertEquals(['ROLE_QUX_BAZ'],$role->getReachableRoleNames(['ROLE_QUX_BAZ']));
72+
}
3373
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp