_register_route('/api-routes', array( 'methods' => WP_REST_Server::READABLE, 'callback' => array($this, 'get_items') ), self::PERMISSIONS); // Get a route $this->_register_route('/api-route/(?P[A-Za-z0-9\/\+=]+)', array( 'methods' => WP_REST_Server::READABLE, 'callback' => array($this, 'get_item'), 'args' => array( 'id' => array( 'description' => 'Based64 encoded API route + method', 'type' => 'string', 'required' => true, 'validate_callback' => function ($value) { return $this->_validate_base64($value); } ) ) ), self::PERMISSIONS); // Update a route permission $this->_register_route('/api-route/(?P[A-Za-z0-9\/\+=]+)', array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array($this, 'update_item_permissions'), 'args' => array( 'id' => array( 'description' => 'Based64 encoded API route + method', 'type' => 'string', 'required' => true, 'validate_callback' => function ($value) { return $this->_validate_base64($value); } ), 'effect' => array( 'description' => 'Either route is restricted or not', 'type' => 'string', 'default' => 'deny', 'enum' => [ 'allow', 'deny' ] ) ) ), self::PERMISSIONS); // Delete a route permission $this->_register_route('/api-route/(?P[A-Za-z0-9\/\+=]+)', array( 'methods' => WP_REST_Server::DELETABLE, 'callback' => array($this, 'reset_item_permissions'), 'args' => array( 'id' => array( 'description' => 'Based64 encoded API route + method', 'type' => 'string', 'required' => true, 'validate_callback' => function ($value) { return $this->_validate_base64($value); } ) ) ), self::PERMISSIONS); // Reset all routes' permissions $this->_register_route('/api-routes', array( 'methods' => WP_REST_Server::DELETABLE, 'callback' => array($this, 'reset_permissions') ), self::PERMISSIONS); }); } /** * Get list of all API routes with permissions * * @param WP_REST_Request $request * * @return WP_REST_Response * @access public * * @version 7.0.0 */ public function get_items(WP_REST_Request $request) { try { $result = $this->_get_route_list($this->_get_service($request)); $resource = $this->_determine_access_level($request)->get_resource( AAM_Framework_Type_Resource::API_ROUTE ); foreach ($result as &$route) { $route['is_customized'] = $resource->is_customized( strtolower($route['method'] . ' ' . $route['endpoint']) ); } unset($route); } catch (Exception $e) { $result = $this->_prepare_error_response($e); } return rest_ensure_response($result); } /** * Get a route * * @param WP_REST_Request $request * * @return WP_REST_Response * @access public * * @version 7.0.0 */ public function get_item(WP_REST_Request $request) { try { $result = $this->_find_route_by_id( $this->_get_service($request), $request->get_param('id') ); if (empty($result)) { throw new OutOfRangeException('Route does not exist'); } } catch (Exception $e) { $result = $this->_prepare_error_response($e); } return rest_ensure_response($result); } /** * Update a route permissions * * @param WP_REST_Request $request * * @return WP_REST_Response * @access public * * @version 7.0.0 */ public function update_item_permissions(WP_REST_Request $request) { try { $service = $this->_get_service($request); $id = $request->get_param('id'); $effect = $request->get_param('effect'); if ($effect === 'allow') { $service->allow(base64_decode($id)); } else { $service->deny(base64_decode($id)); } $result = $this->_find_route_by_id($service, $id); } catch (Exception $e) { $result = $this->_prepare_error_response($e); } return rest_ensure_response($result); } /** * Delete a route permissions * * @param WP_REST_Request $request * * @return WP_REST_Response * @access public * * @version 7.0.0 */ public function reset_item_permissions(WP_REST_Request $request) { try { $result = [ 'success' => $this->_get_service($request)->reset( base64_decode($request->get_param('id')) ) ]; } catch (Exception $e) { $result = $this->_prepare_error_response($e); } return rest_ensure_response($result); } /** * Reset all routes' permissions * * @param WP_REST_Request $request * * @return WP_REST_Response * @access public * * @version 7.0.0 */ public function reset_permissions(WP_REST_Request $request) { try { $result = [ 'success' => $this->_get_service($request)->reset() ]; } catch (Exception $e) { $result = $this->_prepare_error_response($e); } return rest_ensure_response($result); } /** * Get complete list of routes * * @param AAM_Framework_Service_ApiRoutes $service * * @return array * @access private * * @version 7.0.0 */ private function _get_route_list($service) { $result = []; // Iterating over the list of all registered API routes and compile the // list foreach (rest_get_server()->get_routes() as $endpoint => $handlers) { $methods = []; foreach ($handlers as $handler) { $methods = array_merge( $methods, array_keys($handler['methods']) ); } foreach (array_unique($methods) as $method) { array_push($result, [ 'endpoint' => strtolower($endpoint), 'method' => strtoupper($method), 'is_restricted' => $service->is_denied($method . ' ' . $endpoint), 'id' => base64_encode(strtolower( "{$method} {$endpoint}" )) ]); } } return $result; } /** * Find API route by ID * * @param AAM_Framework_Service_ApiRoute $service * @param string $id * * @return array|null * @access private * * @version 7.0.0 */ private function _find_route_by_id($service, $id) { $routes = $this->_get_route_list($service); $found = array_filter($routes, function($r) use ($id) { return $r['id'] === $id; }); return !empty($found) ? array_shift($found) : null; } /** * Get JWT framework service * * @param WP_REST_Request $request * * @return AAM_Framework_Service_ApiRoutes * @access private * * @version 7.0.0 */ private function _get_service($request) { return AAM::api()->api_routes( $this->_determine_access_level($request), [ 'error_handling' => 'exception' ] ); } /** * Validate that the string is valid base64 encoded * * @param string $value * * @return boolean|WP_Error * @access private * * @version 7.0.0 */ private function _validate_base64($value) { $response = true; if (!AAM::api()->misc->is_base64_encoded($value)) { $response = new WP_Error( 'rest_invalid_param', 'Invalid ID', [ 'status' => 400 ] ); } return $response; } }