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 / AccessPolicy.php
AccessPolicy.php
1,118 lines 38.6 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 * Access Policy service
12 *
13 * @since 6.9.26 https://github.com/aamplugin/advanced-access-manager/issues/360
14 * @since 6.9.25 https://github.com/aamplugin/advanced-access-manager/issues/354
15 * @since 6.9.17 https://github.com/aamplugin/advanced-access-manager/issues/323
16 * @since 6.9.13 https://github.com/aamplugin/advanced-access-manager/issues/294
17 * https://github.com/aamplugin/advanced-access-manager/issues/299
18 * @since 6.9.12 https://github.com/aamplugin/advanced-access-manager/issues/285
19 * @since 6.9.4 https://github.com/aamplugin/advanced-access-manager/issues/238
20 * @since 6.9.1 https://github.com/aamplugin/advanced-access-manager/issues/225
21 * @since 6.8.3 https://github.com/aamplugin/advanced-access-manager/issues/207
22 * @since 6.4.0 https://github.com/aamplugin/advanced-access-manager/issues/71
23 * Added new hook `aam_post_read_action_conversion_filter`
24 * @since 6.3.1 Fixed incompatibility with plugins that use WP_User::get_role_caps
25 * method. This method re-index all user capabilities based on assigned
26 * roles and that flushes capabilities attached with Access Policy
27 * @since 6.3.0 Removed dependency on PHP core `list` function
28 * @since 6.2.0 Bug fixing and enhancements for the multi-site support
29 * @since 6.1.0 Changed the way access policy manager is obtained
30 * @since 6.0.0 Initial implementation of the class
31 *
32 * @package AAM
33 * @version 6.9.26
34 */
35 class AAM_Service_AccessPolicy
36 {
37 use AAM_Core_Contract_ServiceTrait,
38 AAM_Core_Contract_RequestTrait;
39
40 /**
41 * Service alias
42 *
43 * Is used to get service instance if it is enabled
44 *
45 * @version 6.4.0
46 */
47 const SERVICE_ALIAS = 'access-policy';
48
49 /**
50 * AAM configuration setting that is associated with the feature
51 *
52 * @version 6.0.0
53 */
54 const FEATURE_FLAG = 'core.service.access-policy.enabled';
55
56 /**
57 * Access policy CPT
58 *
59 * @version 6.0.0
60 */
61 const POLICY_CPT = 'aam_policy';
62
63 /**
64 * Constructor
65 *
66 * @return void
67 *
68 * @access protected
69 * @version 6.0.0
70 */
71 protected function __construct()
72 {
73 if (is_admin()) {
74 // Hook that initialize the AAM UI part of the service
75 if (AAM_Core_Config::get(self::FEATURE_FLAG, true)) {
76 add_action('aam_init_ui_action', function () {
77 AAM_Backend_Feature_Main_Policy::register();
78 }, 40);
79
80 //register custom access control metabox
81 add_action('add_meta_boxes', array($this, 'registerMetaboxes'));
82
83 //access policy save
84 add_filter('wp_insert_post_data', array($this, 'managePolicyContent'));
85 }
86
87 // Hook that returns the detailed information about the nature of the
88 // service. This is used to display information about service on the
89 // Settings->Services tab
90 add_filter('aam_service_list_filter', function ($services) {
91 $services[] = array(
92 'title' => __('Access Policies', AAM_KEY),
93 'description' => __('Manage access to the website with well documented JSON access policies for any user, role or visitors. Keep the paper-trail of all the access changes with policy revisions.', AAM_KEY),
94 'setting' => self::FEATURE_FLAG
95 );
96
97 return $services;
98 }, 40);
99 }
100
101 if (AAM_Core_Config::get(self::FEATURE_FLAG, true)) {
102 $this->initializeHooks();
103 }
104 }
105
106 /**
107 * Register UI metaboxes for the Access Policy edit screen
108 *
109 * @global WP_Post $post
110 *
111 * @return void
112 *
113 * @access public
114 * @version 6.0.0
115 */
116 public function registerMetaboxes()
117 {
118 global $post;
119
120 if (is_a($post, 'WP_Post') && ($post->post_type === self::POLICY_CPT)) {
121 add_meta_box(
122 self::POLICY_CPT,
123 __('Access Policy Document', AAM_KEY),
124 function() {
125 echo AAM_Backend_View::getInstance()->renderPolicyMetabox();
126 },
127 null,
128 'normal',
129 'high'
130 );
131
132 add_meta_box(
133 'aam-policy-assignee',
134 __('Access Policy Assignee', AAM_KEY),
135 function() {
136 echo AAM_Backend_View::getInstance()->renderPolicyPrincipalMetabox();
137 },
138 null,
139 'side'
140 );
141 }
142 }
143
144 /**
145 * Hook into policy submission and filter its content
146 *
147 * @param array $data
148 *
149 * @return array
150 *
151 * @since 6.9.13 https://github.com/aamplugin/advanced-access-manager/issues/294
152 * @since 6.3.0 https://github.com/aamplugin/advanced-access-manager/issues/27
153 * @since 6.0.0 Initial implementation of the method
154 *
155 * @access public
156 * @version 6.9.13
157 */
158 public function managePolicyContent($data)
159 {
160 if (isset($data['post_type']) && ($data['post_type'] === self::POLICY_CPT)) {
161 $content = $this->getFromPost('aam-policy');
162
163 if (empty($content)) {
164 if (empty($data['post_content'])) {
165 $content = AAM_Backend_Feature_Main_Policy::getDefaultPolicy();
166 } else {
167 $content = $data['post_content'];
168 }
169 }
170
171 // Removing any slashes
172 $content = htmlspecialchars_decode(stripslashes($content));
173
174 // Reformat the policy content
175 $json = json_decode($content);
176
177 if (!empty($json)) {
178 $content = wp_json_encode($json, JSON_PRETTY_PRINT);
179 }
180
181 if (!empty($content)) { // Edit form was submitted
182 $content = addslashes($content);
183 }
184
185 $data['post_content'] = $content;
186 }
187
188 return $data;
189 }
190
191 /**
192 * Initialize Access Policy hooks
193 *
194 * @return void
195 *
196 * @since 6.9.25 https://github.com/aamplugin/advanced-access-manager/issues/354
197 * @since 6.9.17 https://github.com/aamplugin/advanced-access-manager/issues/323
198 * @since 6.9.12 https://github.com/aamplugin/advanced-access-manager/issues/286
199 * @since 6.9.4 https://github.com/aamplugin/advanced-access-manager/issues/238
200 * @since 6.9.1 https://github.com/aamplugin/advanced-access-manager/issues/225
201 * @since 6.8.3 https://github.com/aamplugin/advanced-access-manager/issues/207
202 * @since 6.4.0 https://github.com/aamplugin/advanced-access-manager/issues/71
203 * https://github.com/aamplugin/advanced-access-manager/issues/62
204 * https://github.com/aamplugin/advanced-access-manager/issues/63
205 * @since 6.2.1 Access support for custom-fields
206 * @since 6.2.0 Added new hook into Multi-site service through
207 * `aam_allowed_site_filter`
208 * @since 6.1.1 Refactored the way access policy is applied to object
209 * @since 6.0.0 Initial implementation of the method
210 *
211 * @access protected
212 * @version 6.9.25
213 */
214 protected function initializeHooks()
215 {
216 // Register Access Policy CPT
217 add_action('init', function () {
218 register_post_type('aam_policy', array(
219 'label' => __('Access Policy', AAM_KEY),
220 'labels' => array(
221 'name' => __('Access Policies', AAM_KEY),
222 'edit_item' => __('Edit Policy', AAM_KEY),
223 'singular_name' => __('Policy', AAM_KEY),
224 'add_new_item' => __('Add New Policy', AAM_KEY),
225 'new_item' => __('New Policy', AAM_KEY)
226 ),
227 'description' => __('Access and security policy', AAM_KEY),
228 'public' => false,
229 'show_ui' => true,
230 'show_in_menu' => false,
231 'exclude_from_search' => true,
232 'publicly_queryable' => false,
233 'hierarchical' => false,
234 'supports' => array(
235 'title', 'excerpt', 'revisions', 'custom-fields'
236 ),
237 'delete_with_user' => false,
238 'capabilities' => array(
239 'edit_post' => 'aam_edit_policy',
240 'read_post' => 'aam_read_policy',
241 'delete_post' => 'aam_delete_policy',
242 'delete_posts' => 'aam_delete_policies',
243 'edit_posts' => 'aam_edit_policies',
244 'edit_others_posts' => 'aam_edit_others_policies',
245 'publish_posts' => 'aam_publish_policies',
246 )
247 ));
248 });
249
250 // Can register this only after user object is initialized
251 add_action('init', function() {
252 AAM_Service_AccessPolicy_HookController::bootstrap();
253 }, -10);
254
255 // Hook into AAM core objects initialization
256 add_filter('aam_menu_object_option_filter', array($this, 'applyAccessPolicyToObject'), 10, 2);
257 add_filter('aam_metabox_object_option_filter', array($this, 'applyAccessPolicyToObject'), 10, 2);
258 add_filter('aam_toolbar_object_option_filter', array($this, 'applyAccessPolicyToObject'), 10, 2);
259 add_filter('aam_post_object_option_filter', array($this, 'applyAccessPolicyToObject'), 10, 2);
260 add_action('aam_visibility_object_init_action', function(AAM_Core_Object_Visibility $object) {
261 $subject = $object->getSubject();
262
263 if ($subject::UID === AAM_Core_Subject_User::UID) {
264 $this->initializeVisibility($object);
265 }
266 });
267 add_filter('aam_uri_object_option_filter', array($this, 'applyAccessPolicyToObject'), 10, 2);
268 add_filter('aam_route_object_option_filter', array($this, 'applyAccessPolicyToObject'), 10, 2);
269
270 // Hooks to support all available Redirects
271 add_filter('aam_redirect_object_option_filter', array($this, 'applyAccessPolicyToObject'), 10, 2);
272 add_filter('aam_login_redirect_object_option_filter', array($this, 'applyAccessPolicyToObject'), 10, 2);
273 add_filter('aam_logout_redirect_object_option_filter', array($this, 'applyAccessPolicyToObject'), 10, 2);
274 add_filter('aam_404_redirect_object_option_filter', array($this, 'applyAccessPolicyToObject'), 10, 2);
275
276 // Allow third-party to hook into Post resource conversion
277 add_filter('aam_post_resource_filter', array($this, 'convertPostStatement'), 10, 4);
278
279 // Manage access to the Capabilities
280 add_filter('aam_cap_can_filter', array($this, 'isCapabilityAllowed'), 10, 3);
281 add_action('aam_initialize_user_action', array($this, 'initializeUser'));
282
283 // Manage access to the Plugin list and individual plugins
284 add_filter('aam_allowed_plugin_action_filter', array($this, 'isPluginActionAllowed'), 10, 3);
285 add_filter('all_plugins', array($this, 'filterPlugins'));
286
287 // Multisite support
288 add_filter('aam_allowed_site_filter', function() {
289 $manager = AAM::api()->getAccessPolicyManager();
290
291 return $manager->isAllowed('SITE:' . get_current_blog_id()) !== false;
292 });
293
294 // Enrich the RESTful API
295 add_filter('aam_role_rest_field_filter', array($this, 'enrich_role_rest_output'), 1, 3);
296
297 add_action('aam_valid_jwt_token_detected_action', function($token, $claims) {
298 update_user_meta($claims->userId, 'aam_auth_token', $token);
299 }, 10, 2);
300
301 // Service fetch
302 $this->registerService();
303 }
304
305 /**
306 * Apply access policy statements to passed object
307 *
308 * @param array $options
309 * @param AAM_Core_Object $object
310 *
311 * @return array
312 *
313 * @since 6.4.0 Enhanced with redirects support
314 * @since 6.2.0 Fixed bug when access policy was not applied to visitors
315 * @since 6.1.1 Optimized policy implementation
316 * @since 6.0.0 Initial implementation of the method
317 *
318 * @access public
319 * @version 6.4.0
320 */
321 public function applyAccessPolicyToObject($options, AAM_Core_Object $object)
322 {
323 $subject = $object->getSubject();
324 $lowest_level = array(
325 AAM_Core_Subject_User::UID, AAM_Core_Subject_Visitor::UID
326 );
327
328 if (in_array($subject::UID, $lowest_level, true)) {
329 switch($object::OBJECT_TYPE) {
330 case AAM_Core_Object_Menu::OBJECT_TYPE:
331 $options = $this->initializeMenu($options, $object);
332 break;
333
334 case AAM_Core_Object_Toolbar::OBJECT_TYPE:
335 $options = $this->initializeToolbar($options, $object);
336 break;
337
338 case AAM_Core_Object_Metabox::OBJECT_TYPE:
339 $options = $this->initializeMetabox($options, $object);
340 break;
341
342 case AAM_Core_Object_Post::OBJECT_TYPE:
343 $options = $this->initializePost($options, $object);
344 break;
345
346 case AAM_Core_Object_Uri::OBJECT_TYPE:
347 $options = $this->initializeUri($options, $object);
348 break;
349
350 case AAM_Core_Object_Route::OBJECT_TYPE:
351 $options = $this->initializeRoute($options, $object);
352 break;
353
354 case AAM_Core_Object_Redirect::OBJECT_TYPE:
355 $options = $this->initializeAccessDeniedRedirect($options);
356 break;
357
358 case AAM_Core_Object_LoginRedirect::OBJECT_TYPE:
359 $options = $this->initializeRedirect($options, $subject, 'login');
360 break;
361
362 case AAM_Core_Object_LogoutRedirect::OBJECT_TYPE:
363 $options = $this->initializeRedirect($options, $subject, 'logout');
364 break;
365
366 case AAM_Core_Object_NotFoundRedirect::OBJECT_TYPE:
367 $options = $this->initializeRedirect($options, $subject, '404');
368 break;
369
370 default:
371 break;
372 }
373 }
374
375 return $options;
376 }
377
378 /**
379 * Initialize Admin Menu Object options
380 *
381 * @param array $option
382 * @param AAM_Core_Object_Menu $object
383 *
384 * @return array
385 *
386 * @since 6.1.1 Method becomes protected
387 * @since 6.1.0 Changed the way access policy manage is obtained
388 * @since 6.0.0 Initial implementation of the method
389 *
390 * @access protected
391 * @see https://aamportal.com/reference/json-access-policy/resource-action/backendmenu
392 * @version 6.1.1
393 */
394 protected function initializeMenu($option)
395 {
396 $manager = AAM::api()->getAccessPolicyManager();
397 $found = $manager->getResources(AAM_Core_Policy_Resource::MENU);
398 $parsed = array();
399
400 foreach ($found as $key => $stm) {
401 $parsed[$key] = ($stm['Effect'] === 'deny' ? true : false);
402 }
403
404 return array_replace($option, $parsed); // First-class citizen
405 }
406
407 /**
408 * Initialize Toolbar Object options
409 *
410 * @param array $option
411 *
412 * @return array
413 *
414 * @since 6.1.1 Method becomes protected
415 * @since 6.1.0 Changed the way access policy manage is obtained
416 * @since 6.0.0 Initial implementation of the method
417 *
418 * @access protected
419 * @see https://aamportal.com/reference/json-access-policy/resource-action/toolbar
420 * @version 6.1.1
421 */
422 protected function initializeToolbar($option)
423 {
424 $manager = AAM::api()->getAccessPolicyManager();
425 $found = $manager->getResources(AAM_Core_Policy_Resource::TOOLBAR);
426 $parsed = array();
427
428 foreach ($found as $key => $stm) {
429 $parsed[$key] = ($stm['Effect'] === 'deny' ? true : false);
430 }
431
432 return array_replace($option, $parsed); // First-class citizen
433 }
434
435 /**
436 * Initialize Metabox Object options
437 *
438 * @param array $option
439 *
440 * @return array
441 *
442 * @since 6.1.1 Method becomes protected
443 * @since 6.1.0 Changed the way access policy manage is obtained
444 * @since 6.0.0 Initial implementation of the method
445 *
446 * @access public
447 * @see https://aamportal.com/reference/json-access-policy/resource-action/metabox
448 * @version 6.1.1
449 */
450 protected function initializeMetabox($option)
451 {
452 $manager = AAM::api()->getAccessPolicyManager();
453 $found = $manager->getResources(array(
454 AAM_Core_Policy_Resource::METABOX, AAM_Core_Policy_Resource::WIDGET
455 ));
456
457 $parsed = array();
458
459 foreach ($found as $key => $stm) {
460 $parsed[$key] = ($stm['Effect'] === 'deny' ? true : false);
461 }
462
463 return array_replace($option, $parsed); // First-class citizen
464 }
465
466 /**
467 * Initialize Post Object options
468 *
469 * @param array $option
470 * @param AAM_Core_Object_Post $object
471 *
472 * @return array
473 *
474 * @since 6.1.1 Method becomes protected
475 * @since 6.1.0 Changed the way access policy manage is obtained
476 * @since 6.0.0 Initial implementation of the method
477 *
478 * @access protected
479 * @see https://aamportal.com/reference/json-access-policy/resource-action/post
480 * @version 6.1.1
481 */
482 protected function initializePost($option, AAM_Core_Object_Post $object)
483 {
484 $manager = AAM::api()->getAccessPolicyManager();
485 $found = $manager->getResources(sprintf(
486 '%s:%s:(%d|%s)',
487 AAM_Core_Policy_Resource::POST,
488 $object->post_type,
489 $object->ID,
490 $object->post_name
491 ));
492
493 $parsed = array();
494
495 foreach($found as $action => $stmt) {
496 $parsed = $this->convertPostStatement($parsed, $action, $stmt);
497 }
498
499 return array_replace_recursive($option, $parsed); // First-class citizen
500 }
501
502 /**
503 * Initialize post visibility options
504 *
505 * @param AAM_Core_Object_Visibility $visibility
506 *
507 * @return void
508 *
509 * @since 6.1.1 Method becomes protected
510 * @since 6.1.0 Changed the way access policy manage is obtained
511 * @since 6.0.0 Initial implementation of the method
512 *
513 * @access protected
514 * @version 6.1.1
515 */
516 protected function initializeVisibility(AAM_Core_Object_Visibility $visibility)
517 {
518 $manager = AAM::api()->getAccessPolicyManager();
519 $found = $manager->getResources(AAM_Core_Policy_Resource::POST);
520
521 foreach($found as $resource => $stm) {
522 $chunks = explode(':', $resource);
523 $effect = (strtolower($stm['Effect']) === 'allow' ? false : true);
524
525 // Allow other plugins to determine what access options should be
526 // considered during visibility check. For example Complete Package uses
527 // HIDDEN TO OTHERS options
528 $map = apply_filters('aam_policy_post_visibility_map_filter', array(
529 'list' => 'hidden'
530 ));
531
532 // Take in consideration only visibility properties
533 if (array_key_exists($chunks[2], $map)) {
534 if (is_numeric($chunks[1])) {
535 $id = intval($chunks[1]);
536 } else {
537 $post = get_page_by_path($chunks[1], OBJECT, $chunks[0]);
538 $id = (is_a($post, 'WP_Post') ? $post->ID : null);
539 }
540
541 // Making sure that we have at least numeric post ID
542 if (!empty($id)) {
543 $visibility->pushOptions('post', "{$id}|{$chunks[0]}", array(
544 $map[$chunks[2]] => $effect
545 ));
546 }
547 }
548 }
549 }
550
551 /**
552 * Initialize URI Object options
553 *
554 * @param array $option
555 *
556 * @return array
557 *
558 * @since 6.9.26 https://github.com/aamplugin/advanced-access-manager/issues/360
559 * @since 6.1.1 Method becomes protected
560 * @since 6.1.0 Changed the way access policy manage is obtained
561 * @since 6.0.0 Initial implementation of the method
562 *
563 * @access protected
564 * @see https://aamportal.com/reference/json-access-policy/resource-action/uri
565 *
566 * @version 6.9.26
567 */
568 protected function initializeUri($option)
569 {
570 $manager = AAM::api()->getAccessPolicyManager();
571 $found = $manager->getResources(AAM_Core_Policy_Resource::URI);
572 $parsed = array();
573
574 foreach($found as $uri => $stm) {
575 $uri = rtrim($uri, '/'); // No need to honor the trailing forward slash
576 $effect = (strtolower($stm['Effect']) === 'allow' ? false : true);
577
578 if ($effect === false) {
579 $parsed[$uri] = array(
580 'type' => 'allow'
581 );
582 } elseif(isset($stm['Metadata']['Redirect'])) {
583 $props = $this->_processRedirectParams($stm['Metadata']['Redirect']);
584
585 if (!empty($props)) {
586 $type = $props['type'];
587
588 // TODO: Post redirect stores the redirect values in a different
589 // format. Normalize it to be the same way as any other redirect
590 $option[$uri] = array(
591 'type' => $type,
592 'action' => isset($props[$type]) ? $props[$type] : null
593 );
594
595 // No need to store the HTTP status code
596 if (!is_null($props['code'])) {
597 $option[$uri]['code'] = $props['code'];
598 }
599 }
600 } else {
601 $option[$uri] = array(
602 'type' => 'default',
603 'action' => null
604 );
605 }
606 }
607
608 return array_merge($option, $parsed); //First-class citizen
609 }
610
611 /**
612 * Initialize Route Object options
613 *
614 * @param array $option
615 *
616 * @return array
617 *
618 * @since 6.1.1 Method becomes protected
619 * @since 6.1.0 Changed the way access policy manage is obtained
620 * @since 6.0.0 Initial implementation of the method
621 *
622 * @access protected
623 * @see https://aamportal.com/reference/json-access-policy/resource-action/route
624 * @version 6.1.1
625 */
626 protected function initializeRoute($option)
627 {
628 $manager = AAM::api()->getAccessPolicyManager();
629 $found = $manager->getResources(AAM_Core_Policy_Resource::ROUTE);
630 $parsed = array();
631
632 foreach($found as $route => $stm) {
633 $effect = (strtolower($stm['Effect']) === 'allow' ? false : true);
634 $parsed[strtolower(str_replace(':', '|', $route))] = $effect;
635 }
636
637 return array_merge($option, $parsed); //First-class citizen
638 }
639
640 /**
641 * Initialize Access Denied Redirect rules
642 *
643 * @param array $option
644 *
645 * @return array
646 *
647 * @since 6.9.26 https://github.com/aamplugin/advanced-access-manager/issues/360
648 * @since 6.4.0 Initial implementation of the method
649 *
650 * @access protected
651 * @version 6.9.26
652 */
653 protected function initializeAccessDeniedRedirect($option)
654 {
655 $manager = AAM::api()->getAccessPolicyManager();
656 $parsed = array();
657
658 // Fetching both frontend & backend access denied redirect params
659 $params = $manager->getParams('redirect:on:access-denied:(.*)');
660
661 foreach($params as $key => $param) {
662 $parts = explode(':', $key);
663 $area = array_pop($parts);
664 $props = $this->_processRedirectParams(
665 $param['Value'],
666 AAM_Framework_Service_AccessDeniedRedirect::HTTP_DEFAULT_STATUS_CODES
667 );
668
669 // Convert the identified properties to the legacy AAM key/value pair
670 $type = $props['type'];
671 $parsed["{$area}.redirect.type"] = $type;
672
673 if (!is_null($props['code'])) {
674 $parsed["{$area}.redirect.{$type}.code"] = $props['code'];
675 }
676
677 // The default type does not have any additional configurations, so
678 // make sure that we take this into account
679 if (isset($props[$type])) {
680 $parsed["{$area}.redirect.{$type}"] = $props[$type];
681 }
682 }
683
684 return array_merge($option, $parsed); //First-class citizen
685 }
686
687 /**
688 * Initialize the Redirect rules
689 *
690 * @param array $option
691 * @param AAM_Core_Subject $subject
692 * @param string $redirect_type
693 *
694 * @return array
695 *
696 * @since 6.9.26 https://github.com/aamplugin/advanced-access-manager/issues/360
697 * @since 6.9.13 https://github.com/aamplugin/advanced-access-manager/issues/299
698 * @since 6.9.12 Initial implementation of the method
699 *
700 * @access protected
701 * @version 6.9.26
702 */
703 protected function initializeRedirect($option, $subject, $redirect_type)
704 {
705 $manager = AAM::api()->getAccessPolicyManager($subject);
706 $properties = $this->_processRedirectParams(
707 $manager->getParam("redirect:on:{$redirect_type}")
708 );
709
710 // Convert the identified properties to the legacy AAM key/value pair
711 $parsed = array();
712
713 foreach($properties as $key => $value) {
714 if (!is_null($value)) {
715 $parsed["{$redirect_type}.redirect.{$key}"] = $value;
716 }
717 }
718
719 return array_merge($option, $parsed); //First-class citizen
720 }
721
722 /**
723 * Convert policy redirect definition to AAM settings
724 *
725 * @param array $param
726 * @param array $default_status_codes
727 *
728 * @return array
729 *
730 * @access private
731 * @version 6.9.26
732 */
733 private function _processRedirectParams($param, $default_status_codes = array())
734 {
735 $response = array();
736
737 if (!empty($param)) {
738 $type = isset($param['Type']) ? $param['Type'] : 'default';
739 $status_code = isset($default_status_codes[$type]) ? $default_status_codes[$type] : null;
740
741 if (in_array($type, array('page', 'page_redirect'))) {
742 // Adding the redirect type
743 $response['type'] = 'page';
744
745 if (isset($param['PageId'])) {
746 $response['page'] = intval($param['PageId']);
747 } elseif (isset($param['Id'])) { // legacy param
748 $response['page'] = intval($param['Id']);
749 } elseif (isset($param['Slug'])) {
750 $page = get_page_by_path($param['Slug'], OBJECT);
751 $response['page'] = (is_a($page, 'WP_Post') ? $page->ID : 0);
752 } elseif (isset($param['PageSlug'])) {
753 $page = get_page_by_path($param['PageSlug'], OBJECT);
754 $response['page'] = (is_a($page, 'WP_Post') ? $page->ID : 0);
755 }
756 } elseif (in_array($type, array('url', 'url_redirect'))) {
757 // Adding the redirect type
758 $response['type'] = 'url';
759
760 if (isset($param['Url'])) {
761 $response['url'] = $param['Url'];
762 } elseif (isset($param['URL'])) { // legacy
763 $response['url'] = $param['URL'];
764 }
765 } elseif (in_array($type, array('callback', 'trigger_callback'))) {
766 $response['type'] = 'callback';
767 $response['callback'] = $param['Callback'];
768 } elseif (in_array($type, array('message', 'custom_message'))) {
769 $response['type'] = 'message';
770 $response['message'] = $param['Message'];
771 $default_status_code = 401;
772 } elseif (in_array($type, array('login', 'login_redirect'), true)) {
773 $response['type'] = 'login';
774 } else {
775 $response['type'] = 'default';
776 }
777
778 if (isset($param['Code'])) {
779 $response['code'] = intval($param['Code']);
780 } else {
781 $response['code'] = $status_code;
782 }
783 }
784
785 return $response;
786 }
787
788 /**
789 * Check if specified action is allowed upon capability
790 *
791 * @param boolean $allowed
792 * @param string $cap
793 * @param string $action
794 *
795 * @return boolean
796 *
797 * @since 6.1.1 Fixed bug with access policy inheritance
798 * @since 6.1.0 Changed the way access policy manage is obtained
799 * @since 6.0.0 Initial implementation of the method
800 *
801 * @access public
802 * @link https://aamportal.com/reference/json-access-policy/resource-action/capability
803 * @version 6.1.1
804 */
805 public function isCapabilityAllowed($allowed, $cap, $action)
806 {
807 $manager = AAM::api()->getAccessPolicyManager();
808 $result = $manager->isAllowed("Capability:{$cap}:AAM:{$action}");
809
810 return ($result === null ? $allowed : $result);
811 }
812
813 /**
814 * Initialize user with policy capabilities and roles
815 *
816 * @param AAM_Core_Subject_User $subject
817 *
818 * @return void
819 *
820 * @since 6.3.1 Fixed bug https://github.com/aamplugin/advanced-access-manager/issues/45
821 * @since 6.1.0 Changed the way access policy manage is obtained
822 * @since 6.0.0 Initial implementation of the method
823 *
824 * @access public
825 * @link https://aamportal.com/reference/json-access-policy/resource-action/capability
826 * @link https://aamportal.com/reference/json-access-policy/resource-action/role
827 *
828 * @version 6.3.1
829 */
830 public function initializeUser(AAM_Core_Subject_User $subject)
831 {
832 $manager = AAM::api()->getAccessPolicyManager($subject);
833 $wp_user = $subject->getPrincipal();
834
835 // Update user's list of roles if policy states so
836 $roles = $manager->getResources(AAM_Core_Policy_Resource::ROLE);
837
838 if (count($roles)) {
839 foreach($roles as $id => $statement) {
840 $effect = strtolower($statement['Effect']);
841 $exists = array_key_exists($id, $wp_user->caps);
842
843 if ($effect === 'allow') { // Add new
844 $wp_user->caps[$id] = true;
845 } elseif (($effect === 'deny') && $exists) { // Remove
846 unset($wp_user->caps[$id]);
847 }
848 }
849
850 // Re-index all user capabilities based on new set of roles
851 $wp_user->get_role_caps();
852
853 // Add siblings to the User subject
854 $user_roles = array_values($wp_user->roles);
855
856 if (count($user_roles) > 1) {
857 $subject->getParent()->setSiblings(array_map(function($id) {
858 return AAM::api()->getRole($id);
859 }, array_slice($user_roles, 1)));
860 }
861 }
862
863 // Get all the capabilities that mentioned in the policies explicitly
864 $caps = array_filter(
865 $manager->getResources(AAM_Core_Policy_Resource::CAPABILITY),
866 function($stm, $res) {
867 return (strpos($res, ':') === false); // Exclude any :AAM: resources
868 },
869 ARRAY_FILTER_USE_BOTH
870 );
871
872 foreach($caps as $cap => $statement) {
873 $effect = (strtolower($statement['Effect']) === 'allow' ? true : false);
874
875 $wp_user->allcaps[$cap] = $effect;
876
877 // Also update user's specific cap if exists
878 $wp_user->caps[$cap] = $effect;
879 }
880
881 // Finally update user level
882 $wp_user->user_level = array_reduce(
883 array_keys($wp_user->allcaps), array($wp_user, 'level_reduction'), 0
884 );
885 }
886
887 /**
888 * Convert Post resource statement
889 *
890 * @param array $output
891 * @param string $action
892 * @param array $stmt
893 * @param string $ns
894 *
895 * @return array
896 *
897 * @access public
898 * @version 6.0.0
899 */
900 public function convertPostStatement($output, $action, $stmt, $ns = '')
901 {
902 switch($action) {
903 case 'edit':
904 case 'delete':
905 case 'publish':
906 case 'comment':
907 $this->convertedPostSimpleAction($output, $ns . $action, $stmt);
908 break;
909
910 case 'list':
911 $this->convertedPostSimpleAction($output, $ns . 'hidden', $stmt);
912 break;
913
914 case 'read':
915 $this->convertedPostReadAction($output, $stmt, $ns);
916 break;
917
918 default:
919 $output = apply_filters(
920 'aam_convert_post_action_filter', $output, $action, $stmt, $ns
921 );
922 break;
923 }
924
925 return $output;
926 }
927
928 /**
929 * Covert simple post action to post object property
930 *
931 * @param array &$options
932 * @param string $action
933 * @param array $statement
934 *
935 * @return void
936 *
937 * @access protected
938 * @version 6.0.0
939 */
940 protected function convertedPostSimpleAction(&$options, $action, $statement)
941 {
942 $options[$action] = strtolower($statement['Effect']) !== 'allow';
943 }
944
945 /**
946 * Convert Post Read action based on metadata
947 *
948 * @param array &$options
949 * @param array $statement
950 * @param string $ns
951 *
952 * @return void
953 *
954 * @since 6.9.26 https://github.com/aamplugin/advanced-access-manager/issues/360
955 * @since 6.4.0 Added `aam_post_read_action_conversion_filter` to support
956 * https://github.com/aamplugin/advanced-access-manager/issues/68
957 * @since 6.0.0 Initial implementation of the method
958 *
959 * @access protected
960 * @version 6.9.26
961 */
962 protected function convertedPostReadAction(&$options, $statement, $ns = '')
963 {
964 $effect = strtolower($statement['Effect']) !== 'allow';
965
966 if (array_key_exists('Metadata', $statement)) {
967 $metadata = $statement['Metadata'];
968
969 // Password Protected options
970 if(array_key_exists('Password', $metadata)) {
971 $options[$ns . 'protected'] = array(
972 'enabled' => $effect,
973 'password' => $metadata['Password']['Value']
974 );
975 }
976
977 // Teaser message is defined
978 if(array_key_exists('Teaser', $metadata)) {
979 $options[$ns . 'teaser'] = array(
980 'enabled' => $effect,
981 'message' => $metadata['Teaser']['Value']
982 );
983 }
984
985 // Redirect options
986 if(array_key_exists('Redirect', $metadata)) {
987 $redirect = array();
988 $props = $this->_processRedirectParams($metadata['Redirect'], 307);
989
990 // TODO: Post redirect stores the redirect values in a different
991 // format. Normalize it to be the same way as any other redirect
992 if (!empty($props)) {
993 $type = $props['type'];
994 $redirect['type'] = $type;
995 $redirect['destination'] = isset($props[$type]) ? $props[$type] : null;
996 $redirect['enabled'] = $effect;
997
998 if (!is_null($props['code'])) {
999 $redirect['httpCode'] = $props['code'];
1000 }
1001
1002 // Set the converted access controls
1003 $options[$ns . 'redirected'] = $redirect;
1004 }
1005 }
1006
1007 // Limited option
1008 if(array_key_exists('Limited', $metadata)) {
1009 $options[$ns . 'limited'] = array(
1010 'enabled' => $effect,
1011 'threshold' => $metadata['Limited']['Threshold']
1012 );
1013 }
1014
1015 $options = apply_filters(
1016 'aam_post_read_action_conversion_filter', $options, $statement, $ns
1017 );
1018 } else { // Simply restrict access to read a post
1019 $options[$ns . 'restricted'] = $effect;
1020 }
1021 }
1022
1023 /**
1024 * Check if specific action is allowed upon all plugins or specified plugin
1025 *
1026 * @param boolean|null $allowed
1027 * @param string $action
1028 * @param string $slug
1029 *
1030 * @return boolean
1031 *
1032 * @since 6.1.0 Changed the way access policy manage is obtained
1033 * @since 6.0.0 Initial implementation of the method
1034 *
1035 * @access public
1036 * @link https://aamportal.com/reference/json-access-policy/resource-action/plugin
1037 * @version 6.1.0
1038 */
1039 public function isPluginActionAllowed($allowed, $action, $slug = null)
1040 {
1041 $manager = AAM::api()->getAccessPolicyManager();
1042
1043 if ($slug === null) {
1044 $id = AAM_Core_Policy_Resource::PLUGIN . ":WP:{$action}";
1045 } else {
1046 $id = AAM_Core_Policy_Resource::PLUGIN . ":{$slug}:WP:{$action}";
1047 }
1048
1049 return $manager->isAllowed($id);
1050 }
1051
1052 /**
1053 * Filter out all the plugins that are not allowed to be listed
1054 *
1055 * @param array $plugins
1056 *
1057 * @return array
1058 *
1059 * @since 6.3.0 Fixed potential bug https://github.com/aamplugin/advanced-access-manager/issues/38
1060 * @since 6.1.0 Changed the way access policy manage is obtained
1061 * @since 6.0.0 Initial implementation of the method
1062 *
1063 * @access public
1064 * @version 6.3.0
1065 */
1066 public function filterPlugins($plugins)
1067 {
1068 $manager = AAM::api()->getAccessPolicyManager();
1069 $filtered = array();
1070
1071 foreach($plugins as $id => $plugin) {
1072 $parts = explode('/', $id);
1073 $resource = AAM_Core_Policy_Resource::PLUGIN . ":{$parts[0]}:WP:list";
1074
1075 if ($manager->isAllowed($resource) !== false) {
1076 $filtered[$id] = $plugin;
1077 }
1078 }
1079
1080 return $filtered;
1081 }
1082
1083 /**
1084 * Get the list of attached policies to role
1085 *
1086 * @param null $output
1087 * @param AAM_Framework_Proxy_Role $id
1088 * @param string $field
1089 *
1090 * @return array
1091 *
1092 * @access public
1093 * @version 6.9.6
1094 */
1095 public function enrich_role_rest_output($output, $role, $field)
1096 {
1097 if ($field === 'applied_policy_ids') {
1098 $object = AAM::api()->getRole($role->slug)->getObject(
1099 AAM_Core_Object_Policy::OBJECT_TYPE
1100 );
1101
1102 $output = array();
1103
1104 foreach($object->getOption() as $id => $effect) {
1105 if (!empty($effect)) {
1106 array_push($output, $id);
1107 }
1108 }
1109 }
1110
1111 return $output;
1112 }
1113
1114 }
1115
1116 if (defined('AAM_KEY')) {
1117 AAM_Service_AccessPolicy::bootstrap();
1118 }