true, 'core.settings.xmlrpc' => true, 'core.settings.restful' => true ]; /** * Constructor * * @return void * * @access protected * @version 6.0.0 */ protected function __construct() { add_filter('aam_get_config_filter', function($result, $key) { if (is_null($result) && array_key_exists($key, self::DEFAULT_CONFIG)) { $result = self::DEFAULT_CONFIG[$key]; } return $result; }, 10, 2); $enabled = AAM_Framework_Manager::configs()->get_config(self::FEATURE_FLAG); if (is_admin()) { // Hook that initialize the AAM UI part of the service if ($enabled) { add_action('aam_init_ui_action', function () { AAM_Backend_Feature_Main_Route::register(); }); } // Hook that returns the detailed information about the nature of the // service. This is used to display information about service on the // Settings->Services tab add_filter('aam_service_list_filter', function ($services) { $services[] = array( 'title' => __('API Routes', AAM_KEY), 'description' => __('Manage access to any individual RESTful endpoint for any role, user or unauthenticated application request. The service works great with JWT service that authenticate requests with JWT Bearer token.', AAM_KEY), 'setting' => self::FEATURE_FLAG ); return $services; }, 45); } if ($enabled) { $this->initializeHooks(); } } /** * Initialize API Route hooks * * @return void * * @since 6.9.41 https://github.com/aamplugin/advanced-access-manager/issues/420 * @since 6.9.10 https://github.com/aamplugin/advanced-access-manager/issues/274 * @since 6.7.0 https://github.com/aamplugin/advanced-access-manager/issues/153 * @since 6.4.0 https://github.com/aamplugin/advanced-access-manager/issues/71 * https://github.com/aamplugin/advanced-access-manager/issues/76 * @since 6.0.0 Initial implementation of the method * * @access protected * @version 6.9.41 */ protected function initializeHooks() { if (is_admin()) { add_filter('aam_settings_list_filter', function ($settings, $type) { if ($type === 'core') { $service = AAM_Framework_Manager::configs(); $settings = array_merge($settings, array( 'core.settings.xmlrpc' => array( 'title' => __('XML-RPC WordPress API', AAM_KEY), 'description' => sprintf(AAM_Backend_View_Helper::preparePhrase('Remote procedure call (RPC) interface is used to manage WordPress website content and features. For more information check %sXML-RPC Support%s article.', 'b'), '', ''), 'value' => $service->get_config('core.settings.xmlrpc') ), 'core.settings.restful' => array( 'title' => __('RESTful WordPress API', AAM_KEY), 'description' => sprintf(AAM_Backend_View_Helper::preparePhrase('[Note!] If disabled, the AAM UI may not function as expected. The RESTful interface is used to manage WordPress website content and features. For detail, refer to %sREST API handbook%s.', 'b'), '', ''), 'value' => $service->get_config('core.settings.restful') ) )); } return $settings; }, 10, 2); } // Register RESTful API endpoints AAM_Restful_ApiRouteService::bootstrap(); // Disable XML-RPC if needed add_filter('xmlrpc_enabled', function($enabled) { if (AAM_Framework_Manager::configs()->get_config( 'core.settings.xmlrpc') === false ) { $enabled = false; } return $enabled; }); // Disable RESTful API if needed if (!current_user_can('aam_manager')) { add_filter( 'rest_authentication_errors', function ($response) { if (!is_wp_error($response) && !AAM_Framework_Manager::configs()->get_config( 'core.settings.restful' )) { $response = new WP_Error( 'rest_access_disabled', __('RESTful API is disabled', AAM_KEY), array('status' => 403) ); } return $response; }, PHP_INT_MAX ); } // Register API manager is applicable add_filter('rest_pre_dispatch', array($this, 'authorizeRequest'), PHP_INT_MAX, 3); // Policy generation hook add_filter( 'aam_generated_policy_filter', array($this, 'generatePolicy'), 10, 4 ); // Service fetch $this->registerService(); } /** * Generate API Route policy statements * * @param array $policy * @param string $resource_type * @param array $options * @param AAM_Core_Policy_Generator $generator * * @return array * * @access public * @version 6.4.0 */ public function generatePolicy($policy, $resource_type, $options, $generator) { if ($resource_type === AAM_Core_Object_Route::OBJECT_TYPE) { if (!empty($options)) { $normalized = array(); foreach($options as $id => $effect) { $normalized[str_replace('|', ':', $id)] = !empty($effect); } $policy['Statement'] = array_merge( $policy['Statement'], $generator->generateBasicStatements($normalized, 'Route') ); } } return $policy; } /** * Authorize REST request * * Based on the matched route, check if it is disabled for current user * * @param WP_Error|null $response * @param WP_REST_Server $server * @param WP_REST_Request $request * * @return WP_Error|null * * @since 6.9.17 https://github.com/aamplugin/advanced-access-manager/issues/324 * @since 6.7.2 https://github.com/aamplugin/advanced-access-manager/issues/163 * @since 6.0.0 Initial implementation of the method * * @access public * @version 6.9.17 */ public function authorizeRequest($response, $server, $request) { if (!is_wp_error($response)) { $user = AAM::getUser(); $object = $user->getObject('route'); $matched = $request->get_route(); $method = $request->get_method(); foreach (array_keys($server->get_routes()) as $route) { if ($route === $matched || preg_match('#^' . $route . '$#i', $matched) ) { if ($object->isRestricted('restful', $route, $method)) { $response = new WP_Error( 'rest_access_denied', __('Access Denied', AAM_KEY), array('status' => 401) ); break; } } } } return $response; } } if (defined('AAM_KEY')) { AAM_Service_Route::bootstrap(); }