Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
93.55% |
58 / 62 |
|
83.33% |
5 / 6 |
CRAP | |
0.00% |
0 / 1 |
GroupPermissionsLookup | |
93.55% |
58 / 62 |
|
83.33% |
5 / 6 |
25.17 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
groupHasPermission | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
10 | |||
getGrantedPermissions | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
getRevokedPermissions | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
getGroupPermissions | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
7 | |||
getGroupsWithPermission | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | namespace MediaWiki\Permissions; |
22 | |
23 | use MediaWiki\Config\ServiceOptions; |
24 | use MediaWiki\MainConfigNames; |
25 | |
26 | /** |
27 | * A service class for looking up permissions bestowed to groups, groups bestowed with |
28 | * permissions, and permissions bestowed by membership in a combination of groups, solely |
29 | * according to site configuration for group permissions and inheritence thereof. |
30 | * |
31 | * This class does *not* account for implicit rights (which are not associated with groups). |
32 | * Callers might want to use {@see PermissionManager} if this is an issue. |
33 | * |
34 | * This class does *not* infer membership in one group (e.g. '*') from membership in another |
35 | * (e.g. 'user'). Callers must account for this when using {@see self::getGroupPermissions()}. |
36 | * |
37 | * @since 1.36 |
38 | * @package MediaWiki\Permissions |
39 | */ |
40 | class GroupPermissionsLookup { |
41 | |
42 | /** |
43 | * @internal |
44 | * @var string[] |
45 | */ |
46 | public const CONSTRUCTOR_OPTIONS = [ |
47 | MainConfigNames::GroupInheritsPermissions, |
48 | MainConfigNames::GroupPermissions, |
49 | MainConfigNames::RevokePermissions, |
50 | ]; |
51 | |
52 | /** @var array[] */ |
53 | private $groupPermissions; |
54 | |
55 | /** @var array[] */ |
56 | private $revokePermissions; |
57 | |
58 | /** @var string[] */ |
59 | private $groupInheritance; |
60 | |
61 | /** |
62 | * @param ServiceOptions $options |
63 | */ |
64 | public function __construct( ServiceOptions $options ) { |
65 | $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); |
66 | $this->groupPermissions = $options->get( MainConfigNames::GroupPermissions ); |
67 | $this->revokePermissions = $options->get( MainConfigNames::RevokePermissions ); |
68 | $this->groupInheritance = $options->get( MainConfigNames::GroupInheritsPermissions ); |
69 | } |
70 | |
71 | /** |
72 | * Check, if the given group has the given permission |
73 | * |
74 | * If you're wanting to check whether all users have a permission, |
75 | * use PermissionManager::isEveryoneAllowed() instead. |
76 | * That properly checks if it's revoked from anyone. |
77 | * |
78 | * @param string $group Group to check |
79 | * @param string $permission Role to check |
80 | * |
81 | * @return bool |
82 | */ |
83 | public function groupHasPermission( string $group, string $permission ): bool { |
84 | $inheritsFrom = $this->groupInheritance[$group] ?? false; |
85 | $has = isset( $this->groupPermissions[$group][$permission] ) && |
86 | $this->groupPermissions[$group][$permission]; |
87 | // If the group doesn't have the permission and inherits from somewhere, |
88 | // check that group too |
89 | if ( !$has && $inheritsFrom !== false ) { |
90 | $has = isset( $this->groupPermissions[$inheritsFrom][$permission] ) && |
91 | $this->groupPermissions[$inheritsFrom][$permission]; |
92 | } |
93 | if ( !$has ) { |
94 | // If they don't have the permission, exit early |
95 | return false; |
96 | } |
97 | |
98 | // Check if the permission has been revoked |
99 | $revoked = isset( $this->revokePermissions[$group][$permission] ) && |
100 | $this->revokePermissions[$group][$permission]; |
101 | if ( !$revoked && $inheritsFrom !== false ) { |
102 | $revoked = isset( $this->revokePermissions[$inheritsFrom][$permission] ) && |
103 | $this->revokePermissions[$inheritsFrom][$permission]; |
104 | } |
105 | |
106 | return !$revoked; |
107 | } |
108 | |
109 | /** |
110 | * Get a list of permissions granted to this group. This |
111 | * must *NOT* be used for permissions checking as it |
112 | * does not check whether a permission has been revoked |
113 | * from this group. |
114 | * |
115 | * @param string $group Group to get permissions of |
116 | * @return string[] |
117 | * @since 1.38 |
118 | */ |
119 | public function getGrantedPermissions( string $group ): array { |
120 | $rights = array_keys( array_filter( $this->groupPermissions[$group] ?? [] ) ); |
121 | $inheritsFrom = $this->groupInheritance[$group] ?? false; |
122 | if ( $inheritsFrom !== false ) { |
123 | $rights = array_merge( |
124 | $rights, |
125 | // array_filter removes empty items |
126 | array_keys( array_filter( $this->groupPermissions[$inheritsFrom] ?? [] ) ) |
127 | ); |
128 | } |
129 | |
130 | return array_unique( $rights ); |
131 | } |
132 | |
133 | /** |
134 | * Get a list of permissions revoked from this group |
135 | * |
136 | * @param string $group Group to get revoked permissions of |
137 | * @return string[] |
138 | * @since 1.38 |
139 | */ |
140 | public function getRevokedPermissions( string $group ): array { |
141 | $rights = array_keys( array_filter( $this->revokePermissions[$group] ?? [] ) ); |
142 | $inheritsFrom = $this->groupInheritance[$group] ?? false; |
143 | if ( $inheritsFrom !== false ) { |
144 | $rights = array_merge( |
145 | $rights, |
146 | // array_filter removes empty items |
147 | array_keys( array_filter( $this->revokePermissions[$inheritsFrom] ?? [] ) ) |
148 | ); |
149 | } |
150 | |
151 | return array_unique( $rights ); |
152 | } |
153 | |
154 | /** |
155 | * Get the permissions associated with membership in a combination of groups |
156 | * |
157 | * Group-based revocation of a permission negates all group-based assignments of that |
158 | * permission. |
159 | * |
160 | * @param string[] $groups internal group names |
161 | * @return string[] permission key names for given groups combined |
162 | */ |
163 | public function getGroupPermissions( array $groups ): array { |
164 | $rights = []; |
165 | $checkGroups = []; |
166 | |
167 | // Add inherited groups to the list of groups to check |
168 | foreach ( $groups as $group ) { |
169 | $checkGroups[] = $group; |
170 | if ( isset( $this->groupInheritance[$group] ) ) { |
171 | $checkGroups[] = $this->groupInheritance[$group]; |
172 | } |
173 | } |
174 | |
175 | // grant every granted permission first |
176 | foreach ( $checkGroups as $group ) { |
177 | if ( isset( $this->groupPermissions[$group] ) ) { |
178 | $rights = array_merge( |
179 | $rights, |
180 | // array_filter removes empty items |
181 | array_keys( array_filter( $this->groupPermissions[$group] ) ) |
182 | ); |
183 | } |
184 | } |
185 | // now revoke the revoked permissions |
186 | foreach ( $checkGroups as $group ) { |
187 | if ( isset( $this->revokePermissions[$group] ) ) { |
188 | $rights = array_diff( |
189 | $rights, |
190 | array_keys( array_filter( $this->revokePermissions[$group] ) ) |
191 | ); |
192 | } |
193 | } |
194 | return array_unique( $rights ); |
195 | } |
196 | |
197 | /** |
198 | * Get all the groups who have a given permission |
199 | * |
200 | * @param string $permission |
201 | * @return string[] internal group names with the given permission |
202 | */ |
203 | public function getGroupsWithPermission( string $permission ): array { |
204 | $allowedGroups = []; |
205 | $groups = array_unique( array_merge( |
206 | array_keys( $this->groupPermissions ), |
207 | array_keys( $this->groupInheritance ) |
208 | ) ); |
209 | foreach ( $groups as $group ) { |
210 | if ( $this->groupHasPermission( $group, $permission ) ) { |
211 | $allowedGroups[] = $group; |
212 | } |
213 | } |
214 | return $allowedGroups; |
215 | } |
216 | } |