/
home
/
assocoweys
/
marina
/
wp-content
/
plugins
/
advanced-access-manager
/
application
/
Framework
/
Resource
/
Upload File
HOME
<?php /** * ====================================================================== * LICENSE: This file is subject to the terms and conditions defined in * * file 'license.txt', which is part of this source code package. * * ====================================================================== */ /** * Base trait to represents AAM resource concept * * AAM Resource is a website resource that you manage access to for users, roles or * visitors. For example, it can be any website post, page, term, backend menu etc. * * On another hand, AAM Resource is a “container” with specific settings for any user, * role or visitor. For example login, logout redirect, default category or access * denied redirect rules. * * @method AAM_Framework_Service_Urls urls(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_ApiRoutes api_routes(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Jwts jwts(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_LoginRedirect login_redirect(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_LogoutRedirect logout_redirect(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_NotFoundRedirect not_found_redirect(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_BackendMenu backend_menu(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_AdminToolbar admin_toolbar(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Metaboxes metaboxes(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Widgets widgets(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_AccessDeniedRedirect access_denied_redirect(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Roles roles(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Users users(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Posts posts(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Terms terms(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_PostTypes post_types(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Taxonomies taxonomies(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Capabilities capabilities(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Capabilities caps(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Settings settings(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Policies policies(mixed $access_level = null, array $settings = []) * @method AAM_Framework_Service_Hooks hooks(mixed $access_level = null, array $settings = []) * * @property AAM_Framework_Utility_Cache $cache * @property AAM_Framework_Utility_ObjectCache $object_cache * @property AAM_Framework_Utility_Capabilities $caps * @property AAM_Framework_Utility_Capabilities $capabilities * @property AAM_Framework_Utility_Config $config * @property AAM_Framework_Utility_Misc $misc * @property AAM_Framework_Utility_Redirect $redirect * @property AAM_Framework_Utility_Roles $roles * @property AAM_Framework_Utility_Users $users * @property AAM_Framework_Utility_Db $db * @property AAM_Framework_Utility_AccessLevels $access_levels * @property AAM_Framework_Utility_Jwt $jwt * @property AAM_Framework_Utility_Policy $policy * @property AAM_Framework_Utility_Content $content * @property AAM_Framework_Utility_Rest $rest * * @package AAM * @version 7.1.0 */ trait AAM_Framework_Resource_BaseTrait { /** * Reference to the access level * * @var AAM_Framework_AccessLevel_Interface * @access private * * @version 7.0.0 */ private $_access_level = null; /** * Collection of extended methods * * @var array * @access private * * @version 7.0.0 */ private $_extended_methods = []; /** * Resource permissions * * Array of final permissions. The final permissions are those that have been * properly inherited and merged. * * @var array * @access private * * @version 7.0.0 */ private $_permissions = []; /** * Array of flags to indicate which resource already triggered inheritance * mechanism * * @var array * @access private * * @version 7.0.0 */ private $_inheritance_completed = []; /** * Explicit permissions (not inherited from parent access level) * * When resource is initialized, it already contains the final set of the permissions, * inherited from the parent access levels. This property contains permissions * that are explicitly defined for current resource. * * @var array * @access private * * @version 7.0.0 */ private $_explicit_permissions = []; /** * Constructor * * Initialize the resource container * * @param AAM_Framework_AccessLevel_Interface $access_level * * @return void * @access public * * @version 7.0.0 */ public function __construct($access_level) { $this->_access_level = $access_level; // Extend access level with more methods $closures = apply_filters( 'aam_framework_resource_methods_filter', $this->_extended_methods, $this ); if (is_array($closures)) { foreach($closures as $name => $closure) { $closures[$name] = $closure->bindTo($this, $this); } $this->_extended_methods = $closures; } // Initialize permissions $this->_init_permissions(); } /** * Get access level this resource is tight to * * @return AAM_Framework_AccessLevel_Interface * @access public * * @version 7.0.0 */ public function get_access_level() { return $this->_access_level; } /** * Proxy methods to WordPress core instance * * @param string $name * @param array $args * * @return mixed * @access public * * @since 7.0.0 */ public function __call($name, $args) { $response = null; if (array_key_exists($name, $this->_extended_methods)) { $response = call_user_func_array( $this->_extended_methods[$name], $args ); } elseif (AAM_Framework_Manager::_()->has_service($name)) { $response = AAM_Framework_Manager::_()->{$name}( $this->get_access_level(), ...$args ); } else { throw new BadMethodCallException(sprintf( 'Method %s does not exist in %s resource', esc_js($name), static::class )); } return $response; } /** * Property overload * * @param string $name * * @return string * @access public * * @version 7.0.0 */ public function __get($name) { $result = null; if ($name === 'type') { $result = $this->type; } elseif (AAM_Framework_Manager::_()->has_utility($name)) { $result = AAM_Framework_Manager::_()->{$name}; } else { _doing_it_wrong( static::class . '::' . $name, 'Property does not exist', AAM_VERSION ); } return $result; } /** * @inheritDoc */ public function is_customized($resource_identifier = null) { if (!empty($resource_identifier)) { $id = $this->_get_resource_id($resource_identifier); $result = !empty($this->_explicit_permissions[$id]); } else { $result = !empty($this->_explicit_permissions); } return $result; } /** * @inheritDoc */ public function reset($resource_identifier = null) { if (!empty($resource_identifier)) { $id = $this->_get_resource_id($resource_identifier); if (array_key_exists($id, $this->_explicit_permissions)) { unset($this->_explicit_permissions[$id]); } if (array_key_exists($id, $this->_permissions)) { unset($this->_permissions[$id]); } if (isset($this->_inheritance_completed[$id])) { unset($this->_inheritance_completed[$id]); } $result = $this->settings()->set_setting( $this->_get_settings_ns(), $this->_explicit_permissions ); } else { $this->_explicit_permissions = []; $this->_permissions = []; $this->_inheritance_completed = []; $result = $this->settings()->delete_setting($this->_get_settings_ns()); } return $result; } /** * @inheritDoc */ public function get_permissions($resource_identifier = null) { if (empty($resource_identifier)) { $result = $this->_permissions; } else { $result = $this->_get_permissions($resource_identifier); } // Remove empty permission containers return array_filter( $result, function($perms) { return !empty($perms); } ); } /** * @inheritDoc */ public function set_permissions(array $permissions, $resource_identifier = null) { if (!empty($resource_identifier)) { $id = $this->_get_resource_id($resource_identifier); } // First, settings the explicit permissions if (empty($id)) { $this->_explicit_permissions = $permissions; $this->_permissions = array_replace( $this->_permissions, $permissions ); } else { $this->_explicit_permissions[$id] = $permissions; $this->_permissions[$id] = array_replace( !empty($this->_permissions[$id]) ? $this->_permissions[$id]: [], $permissions ); } // Store changes in DB $result = $this->settings()->set_setting( $this->_get_settings_ns(), $this->_explicit_permissions ); return $result; } /** * @inheritDoc */ public function set_permission( $resource_identifier, $permission_key, $permission, ...$args ) { $id = $this->_get_resource_id($resource_identifier); // Prepare the permission that will be merged with others $sanitized = apply_filters( 'aam_resource_set_permission_filter', $this->_sanitize_permission($permission), $args ); // Update explicit permissions if (!array_key_exists($id, $this->_explicit_permissions)) { $this->_explicit_permissions[$id] = []; } $this->_explicit_permissions[$id] = array_replace( $this->_explicit_permissions[$id], [ $permission_key => $sanitized ] ); // Store changes in DB $result = $this->settings()->set_setting( $this->_get_settings_ns(), $this->_explicit_permissions ); // Also sync it with final set of permissions if (!array_key_exists($id, $this->_permissions)) { $this->_permissions[$id] = []; } $this->_permissions[$id] = array_replace( $this->_permissions[$id], [ $permission_key => $sanitized ] ); return $result; } /** * @inheritDoc */ public function get_permission($resource_identifier, $permission_key) { $result = null; $permissions = $this->_get_permissions($resource_identifier); if (!empty($permissions[$permission_key])) { $result = $permissions[$permission_key]; } return is_array($result) ? $result : null; } /** * @inheritDoc */ public function remove_permission( $resource_identifier, $permission_key ) { $result = true; $id = $this->_get_resource_id($resource_identifier); // If permission is part of explicit, delete it from their and store changes if (!empty($this->_explicit_permissions[$id][$permission_key])) { unset($this->_explicit_permissions[$id][$permission_key]); // Allow to re-init the permissions if (isset($this->_inheritance_completed[$id])) { unset($this->_inheritance_completed[$id]); } // Store changes in DB $result = $this->settings()->set_setting( $this->_get_settings_ns(), $this->_explicit_permissions ); } if (!empty($this->_permissions[$id][$permission_key])) { unset($this->_permissions[$id][$permission_key]); } return $result; } /** * Initialize basic set of permissions * * @return void * @access private * * @version 7.0.0 */ private function _init_permissions() { // Read explicitly defined settings from DB $permissions = $this->settings()->get_setting($this->_get_settings_ns(), []); if (!is_array($permissions)) { // Deal with corrupted data $permissions = []; } // Store explicit permissions separately from final set of permissions $this->_explicit_permissions = $permissions; // These are the base permissions that are subject to override $this->_permissions = $permissions; // JSON Access Policy is deeply embedded in the framework, thus take it into // consideration during resource initialization if ($this->_should_apply_policies()) { $policy_permissions = $this->_apply_policy(); foreach ($policy_permissions as $resource_id => $permissions) { if (array_key_exists($resource_id, $this->_permissions)) { $this->_permissions[$resource_id] = array_replace( $permissions, $this->_permissions[$resource_id] ); } else { $this->_permissions[$resource_id] = $permissions; } } } // Pre-load all explicitly defined permissions $inherited_permissions = $this->_trigger_inheritance(); foreach ($inherited_permissions as $resource_id => $permissions) { if (array_key_exists($resource_id, $this->_permissions)) { $this->_permissions[$resource_id] = array_replace( $permissions, $this->_permissions[$resource_id] ); } else { $this->_permissions[$resource_id] = $permissions; } } $this->_post_init_hook(); } /** * Allow to implement a custom post initialization * * @return void * @access private * * @version 7.0.11 */ private function _post_init_hook() { } /** * Determine if we should tap into JSON policies * * This method performs two crucial checks: * 1. Verifies that the JSON Access Policies service is enabled; * 2. Confirms that only the lowest possible access level is currently used * * The second step is extremely important to ensure that we retain proper level * flexibility. For instance if policy X is attached to the Editor role, we * should be able to detach it from any individual user with Editor role. Without * this trade-off users will inherit initialized resourced from Editor role and * there will be no chance to suppress them on user level as we have no awareness * that these resources where initialized through policies. * * @return bool * @access private * * @version 7.0.0 */ private function _should_apply_policies() { $is_enabled = $this->config->get('service.policies.enabled', true); $is_lowest_level = in_array($this->get_access_level()->type, [ AAM_Framework_Type_AccessLevel::USER, AAM_Framework_Type_AccessLevel::VISITOR ], true); return $is_enabled && $is_lowest_level; } /** * Get settings namespace * * @return string * @access private * * @version 7.0.0 */ private function _get_settings_ns() { return $this->type; } /** * Get final set of permissions for given resource * * @param mixed $resource_identifier * * @return array * @access private * * @version 7.0.0 */ private function _get_permissions($resource_identifier) { // This is done for performance reasons so we do not have to normalize // resource twice $resource_id = $this->_get_resource_id($resource_identifier); if (empty($this->_inheritance_completed[$resource_id])) { // Get the base set of permissions if (array_key_exists($resource_id, $this->_permissions)) { $result = $this->_permissions[$resource_id]; } else { $result = []; } // Trigger inheritance mechanism $this->_permissions[$resource_id] = array_replace( $this->_trigger_inheritance($resource_identifier), $result ); // Making sure we do not trigger inheritance again $this->_inheritance_completed[$resource_id] = true; } return $this->_permissions[$resource_id]; } /** * Trigger inheritance mechanism * * @param mixed $resource_identifier * * @return array * @access private * * @version 7.0.0 */ private function _trigger_inheritance($resource_identifier = null) { // Allow other implementations to influence set of permissions $result = apply_filters( 'aam_resource_get_permissions_filter', [], $resource_identifier, $this ); // Trigger inheritance mechanism return array_replace( $this->_inherit_from_parent($resource_identifier), $result ); } /** * Inherit settings from parent access level (if any) * * @param mixed $resource_identifier [Optional] * * @return array * @access private * * @version 7.1.0 */ private function _inherit_from_parent($resource_identifier = null) { $parent = $this->get_access_level()->get_parent(); $result = []; if (is_a($parent, AAM_Framework_AccessLevel_Interface::class)) { // Merge access settings if multi access levels config is enabled $multi_support = $this->config->get('core.settings.multi_access_levels'); if ($multi_support && $parent->has_siblings()) { $siblings = $parent->get_siblings(); } else { $siblings = []; } // Getting resource from the parent access level $result = $parent->get_resource( $this->type )->get_permissions($resource_identifier); foreach ($siblings as $sibling) { $sib_perms = $sibling->get_resource( $this->type )->get_permissions($resource_identifier); if (empty($resource_identifier)) { // Aggregated merge $resource_ids = array_unique(array_merge( array_keys($sib_perms), array_keys($result) )); foreach($resource_ids as $id) { // Pulling the exact permissions inherited from the vertical // tree of permissions. This is done to take into consideration // complex combination of permissions when it comes to multi // TODO: Find a better way to identify accurate permissions // for a post to avoid memory allocation issues with large // volume of defined access controls // access level support // $sib_perms[$id] = $sibling->get_resource( // $this->type // )->get_permissions($this->_get_resource_identifier($id)); $result[$id] = $this->misc->merge_permissions( isset($sib_perms[$id]) ? $sib_perms[$id] : [], isset($result[$id]) ? $result[$id] : [], $this->type ); } } else { $result =$this->misc->merge_permissions( $sib_perms, $result, $this->type ); } } } return $result; } /** * Sanitize array of permissions * * @param array $permissions * * @return array * @access private * * @version 7.0.0 */ private function _sanitize_permissions(array $permissions) { $response = []; foreach($permissions as $key => $permission) { $response[$key] = $this->_sanitize_permission($key, $permission); } return $response; } /** * Sanitize permission * * Take given permission and convert to a standardized permission model * * @param mixed $permission * * @return array * @access private * * @version 7.0.0 */ private function _sanitize_permission($permission) { if (is_string($permission)) { // Word like "allow" or "deny" $result = [ 'effect' => strtolower($permission) ]; } elseif (is_bool($permission)) { // Boolean "true" or "false" $result = [ 'effect' => $permission ? 'deny' : 'allow' ]; } elseif (is_numeric($permission)) { // Numeric "1" or "0" $result = [ 'effect' => intval($permission) > 0 ? 'deny' : 'allow' ]; } elseif (is_array($permission)) { // Raw permission data $result = array_merge([ 'effect' => 'deny' ], $permission); } else { $result = [ 'effect' => 'deny' ]; } return $result; } /** * Convert resource identifier into internal ID * * The internal ID represents unique resource identify AAM Framework users to * distinguish between collection of resources * * @param mixed $identifier * * @return mixed * @access private * * @version 7.0.0 */ private function _get_resource_id($identifier) { return (string) $identifier; } /** * Reverse method to _get_resource_id * * @param string $id * * @return mixed * @access private * * @version 7.0.11 */ private function _get_resource_identifier($id) { return (string) $id; } /** * Apply permissions extracted from policies * * @return array * @access private * * @version 7.0.0 */ private function _apply_policy() { return apply_filters('aam_apply_policy_filter', [], $this); } }