PluginProbe
Advanced Access Manager – Access Governance for WordPress / 6.9.26
Advanced Access Manager – Access Governance for WordPress v6.9.26
7.1.4 7.1.2 7.1.3 6.8.4 6.8.5 6.9.0 6.9.1 6.9.10 6.9.11 6.9.12 6.9.13 6.9.14 6.9.15 6.9.16 6.9.17 6.9.18 6.9.19 6.9.2 6.9.20 6.9.21 6.9.22 6.9.23 6.9.24 6.9.25 6.9.26 All 210 releases
advanced-access-manager / application / Service / UserLevelFilter.php
UserLevelFilter.php
324 lines 8.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * ======================================================================
5 * LICENSE: This file is subject to the terms and conditions defined in *
6 * file 'license.txt', which is part of this source code package. *
7 * ======================================================================
8 */
9
10 /**
11 * User Level Filter service
12 *
13 * @since 6.9.9 https://github.com/aamplugin/advanced-access-manager/issues/266
14 * @since 6.7.9 https://github.com/aamplugin/advanced-access-manager/issues/193
15 * @since 6.4.0 https://github.com/aamplugin/advanced-access-manager/issues/71
16 * @since 6.0.0 Initial implementation of the class
17 *
18 * @package AAM
19 * @version 6.9.9
20 */
21 class AAM_Service_UserLevelFilter
22 {
23 use AAM_Core_Contract_ServiceTrait;
24
25 /**
26 * Service alias
27 *
28 * Is used to get service instance if it is enabled
29 *
30 * @version 6.4.0
31 */
32 const SERVICE_ALIAS = 'user-level';
33
34 /**
35 * AAM configuration setting that is associated with the service
36 *
37 * @version 6.0.0
38 */
39 const FEATURE_FLAG = 'core.service.user-level-filter.enabled';
40
41 /**
42 * Constructor
43 *
44 * @return void
45 *
46 * @since 6.7.9 https://github.com/aamplugin/advanced-access-manager/issues/193
47 * @since 6.0.0 Initial implementation of the method
48 *
49 * @access protected
50 * @version 6.7.9
51 */
52 protected function __construct()
53 {
54 if (is_admin()) {
55 // Hook that returns the detailed information about the nature of the
56 // service. This is used to display information about service on the
57 // Settings->Services tab
58 add_filter('aam_service_list_filter', function ($services) {
59 $services[] = array(
60 'title' => __('User Level Filter', AAM_KEY),
61 'description' => __('Enhance the built-in WordPress core user and role management system to ensure that users with lower user levels are restricted from viewing or managing users and roles with higher levels of authority.', AAM_KEY),
62 'setting' => self::FEATURE_FLAG,
63 'defaultEnabled' => false
64 );
65
66 return $services;
67 }, 1);
68 }
69
70 if (AAM_Core_Config::get(self::FEATURE_FLAG, false)) {
71 $this->initializeHooks();
72 }
73 }
74
75 /**
76 * Initialize service hooks
77 *
78 * @return void
79 *
80 * @since 6.9.9 https://github.com/aamplugin/advanced-access-manager/issues/266
81 * @since 6.4.0 https://github.com/aamplugin/advanced-access-manager/issues/71
82 * @since 6.0.0 Initial implementation of the method
83 *
84 * @access protected
85 * @version 6.9.9
86 */
87 protected function initializeHooks()
88 {
89 // User/role filters
90 add_action('init', function() {
91 add_filter('editable_roles', array($this, 'filterRoles'));
92 add_action('pre_get_users', array($this, 'filterUserQuery'), 999);
93 add_filter('views_users', array($this, 'filterViews'));
94 // RESTful user querying
95 add_filter('rest_user_query', array($this, 'prepareUserQueryArgs'));
96 }, 1);
97
98 // Check if user has ability to perform certain task on other users
99 add_filter('map_meta_cap', array($this, 'mapMetaCaps'), 999, 4);
100
101 // Determine if current user is allowed to manage specific user level
102 add_filter(
103 'aam_user_can_manage_level_filter', array($this, 'isUserLevelAllowed'), 10, 2
104 );
105
106 // Determine if user is allowed to be managed by other user based on the
107 // user level
108 add_filter('aam_get_user', function($user) {
109 if (is_a($user, WP_User::class)) {
110 $max_cap = AAM_Core_API::maxLevel($user->allcaps);
111
112 if (!$this->isUserLevelAllowed(true, $max_cap)) {
113 $user = new WP_Error(
114 'unauthorized_user_level',
115 'This user is not allowed for managing'
116 );
117 }
118 }
119
120 return $user;
121 });
122
123 // Service fetch
124 $this->registerService();
125 }
126
127 /**
128 * Determine if current user is allowed to manage provided user level
129 *
130 * @param boolean $allowed
131 * @param int $level
132 *
133 * @return boolean
134 *
135 * @access public
136 * @version 6.0.0
137 */
138 public function isUserLevelAllowed($allowed, $level)
139 {
140 $allow_equal_level = true;
141
142 if (AAM_Core_API::capExists('aam_manage_same_user_level')) {
143 $allow_equal_level = current_user_can('aam_manage_same_user_level');
144 }
145
146 $user_level = AAM::getUser()->getMaxLevel();
147
148 if ($allow_equal_level) {
149 $allowed = $user_level >= $level;
150 } else {
151 $allowed = $user_level > $level;
152 }
153
154 return $allowed;
155 }
156
157 /**
158 * Filter list of allowed roles
159 *
160 * @param array $roles
161 *
162 * @return array
163 *
164 * @access public
165 * @version 6.0.0
166 */
167 public function filterRoles($roles)
168 {
169 static $levels = array(); // to speed-up the execution
170
171 foreach ($roles as $id => $role) {
172 if (!empty($role['capabilities']) && is_array($role['capabilities'])) {
173 if (!isset($levels[$id])) {
174 $levels[$id] = AAM_Core_API::maxLevel($role['capabilities']);
175 }
176
177 if (!$this->isUserLevelAllowed(true, $levels[$id])) {
178 unset($roles[$id]);
179 }
180 }
181 }
182
183 return $roles;
184 }
185
186 /**
187 * Prepare the user query arguments
188 *
189 * @param array $args
190 *
191 * @return array
192 *
193 * @access public
194 * @version 6.0.0
195 */
196 public function prepareUserQueryArgs($args)
197 {
198 $args['role__not_in'] = $this->prepareExcludedRoleList();
199
200 return $args;
201 }
202
203 /**
204 * Filter user query
205 *
206 * Exclude all users that have higher user level
207 *
208 * @param object $query
209 *
210 * @return void
211 *
212 * @access public
213 * @version 6.0.0
214 */
215 public function filterUserQuery($query)
216 {
217 $query->query_vars['role__not_in'] = $this->prepareExcludedRoleList();
218 }
219
220 /**
221 * Prepare the list of roles that are not allowed
222 *
223 * @return array
224 *
225 * @access protected
226 * @version 6.0.0
227 */
228 protected function prepareExcludedRoleList()
229 {
230 $exclude = array();
231 $roles = AAM_Framework_Manager::roles();
232
233 foreach ($roles->role_objects as $id => $role) {
234 $roleMax = AAM_Core_API::maxLevel($role->capabilities);
235
236 if (!$this->isUserLevelAllowed(true, $roleMax)) {
237 $exclude[] = $id;
238 }
239 }
240
241 return $exclude;
242 }
243
244 /**
245 * Filter user list view options
246 *
247 * @param array $views
248 *
249 * @return array
250 *
251 * @access public
252 * @version 6.0.0
253 */
254 public function filterViews($views)
255 {
256 $roles = AAM_Framework_Manager::roles();
257
258 foreach ($roles->role_objects as $id => $role) {
259 $roleMax = AAM_Core_API::maxLevel($role->capabilities);
260 if (isset($views[$id]) && !$this->isUserLevelAllowed(true, $roleMax)) {
261 unset($views[$id]);
262 }
263 }
264
265 return $views;
266 }
267
268 /**
269 * Check user capability
270 *
271 * This is a hack function that add additional layout on top of WordPress
272 * core functionality. Based on the capability passed in the $args array as
273 * "0" element, it performs additional check on user's capability to manage
274 * post, users etc.
275 *
276 * @param array $caps
277 * @param string $cap
278 * @param int $user_id
279 * @param array $args
280 *
281 * @return array
282 *
283 * @access public
284 * @version 6.0.0
285 */
286 public function mapMetaCaps($caps, $cap, $user_id, $args)
287 {
288 $id = (isset($args[0]) ? $args[0] : null);
289
290 if (in_array($cap, array('edit_user', 'delete_user')) && !empty($id)) {
291 $caps = $this->authorizeUserUpdate($caps, $id);
292 }
293
294 return $caps;
295 }
296
297 /**
298 * Check if current user is allowed to manager specified user
299 *
300 * @param array $caps
301 * @param int $userId
302 *
303 * @return array
304 *
305 * @access protected
306 * @version 6.0.0
307 */
308 protected function authorizeUserUpdate($caps, $userId)
309 {
310 $user = AAM::api()->getUser($userId);
311 $userLevel = AAM_Core_API::maxLevel($user->allcaps);
312
313 if (!$this->isUserLevelAllowed(true, $userLevel)) {
314 $caps[] = 'do_not_allow';
315 }
316
317 return $caps;
318 }
319
320 }
321
322 if (defined('AAM_KEY')) {
323 AAM_Service_UserLevelFilter::bootstrap();
324 }