Api\\Integrations' ], 'access' => 'aioseo_tools_settings' ], 'crawl-cleanup' => [ 'callback' => [ 'CrawlCleanup', 'fetchLogs', 'AIOSEO\\Plugin\\Common\\QueryArgs' ], 'access' => [ 'aioseo_search_appearance_settings' ] ], 'crawl-cleanup/block' => [ 'callback' => [ 'CrawlCleanup', 'blockArg', 'AIOSEO\\Plugin\\Common\\QueryArgs' ], 'access' => [ 'aioseo_search_appearance_settings' ] ], 'crawl-cleanup/delete-blocked' => [ 'callback' => [ 'CrawlCleanup', 'deleteBlocked', 'AIOSEO\\Plugin\\Common\\QueryArgs' ], 'access' => [ 'aioseo_search_appearance_settings' ] ], 'crawl-cleanup/delete-unblocked' => [ 'callback' => [ 'CrawlCleanup', 'deleteLog', 'AIOSEO\\Plugin\\Common\\QueryArgs' ], 'access' => [ 'aioseo_search_appearance_settings' ] ], ], 'DELETE' => [ 'backup' => [ 'callback' => [ 'Tools', 'deleteBackup' ], 'access' => 'aioseo_tools_settings' ], 'search-statistics/auth' => [ 'callback' => [ 'SearchStatistics', 'deleteAuth' ], 'access' => [ 'aioseo_search_statistics_settings', 'aioseo_general_settings' ] ] ] // phpcs:enable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound ]; /** * Class contructor. * * @since 4.0.0 */ public function __construct() { add_filter( 'rest_allowed_cors_headers', [ $this, 'allowedHeaders' ] ); add_action( 'rest_api_init', [ $this, 'registerRoutes' ] ); } /** * Get all the routes to register. * * @since 4.0.0 * * @return array An array of routes. */ protected function getRoutes() { return $this->routes; } /** * Registers the API routes. * * @since 4.0.0 * * @return void */ public function registerRoutes() { $class = new \ReflectionClass( get_called_class() ); foreach ( $this->getRoutes() as $method => $data ) { foreach ( $data as $route => $options ) { register_rest_route( $this->namespace, $route, [ 'methods' => $method, 'permission_callback' => empty( $options['permissions'] ) ? [ $this, 'validRequest' ] : [ $this, $options['permissions'] ], 'callback' => is_array( $options['callback'] ) ? [ ( ! empty( $options['callback'][2] ) ? $options['callback'][2] . '\\' . $options['callback'][0] : ( class_exists( $class->getNamespaceName() . '\\' . $options['callback'][0] ) ? $class->getNamespaceName() . '\\' . $options['callback'][0] : __NAMESPACE__ . '\\' . $options['callback'][0] ) ), $options['callback'][1] ] : [ $this, $options['callback'] ] ] ); } } } /** * Sets headers that are allowed for our API routes. * * @since 4.0.0 * * @return void */ public function allowHeaders() { // TODO: Remove this entire function after a while. It's only here to ensure compatibility with people that are still using Image SEO 1.0.3 or lower. header( 'Access-Control-Allow-Headers: X-WP-Nonce' ); } /** * Sets headers that are allowed for our API routes. * * @since 4.1.1 * * @param array $allowHeaders The allowed request headers. * @return array $allowHeaders The allowed request headers. */ public function allowedHeaders( $allowHeaders ) { if ( ! array_search( 'X-WP-Nonce', $allowHeaders, true ) ) { $allowHeaders[] = 'X-WP-Nonce'; } return $allowHeaders; } /** * Determine if logged in or has the proper permissions. * * @since 4.0.0 * * @param \WP_REST_Request $request The REST Request. * @return bool True if validated, false if not. */ public function validRequest( $request ) { return is_user_logged_in() && $this->validateAccess( $request ); } /** * Validates access from the routes array. * * @since 4.0.0 * * @param \WP_REST_Request $request The REST Request. * @return bool True if validated, false if not. */ public function validateAccess( $request ) { $routeData = $this->getRouteData( $request ); if ( empty( $routeData ) || empty( $routeData['access'] ) ) { return false; } // Admins always have access. if ( aioseo()->access->isAdmin() ) { return true; } switch ( $routeData['access'] ) { case 'everyone': // Any user is able to access the route. return true; default: return aioseo()->access->hasCapability( $routeData['access'] ); } } /** * Returns the data for the route that is being accessed. * * @since 4.1.6 * * @param \WP_REST_Request $request The REST Request. * @return array The route data. */ protected function getRouteData( $request ) { // NOTE: Since WordPress uses case-insensitive patterns to match routes, // we are forcing everything to lowercase to ensure we have the proper route. // This prevents users with lower privileges from accessing routes they shouldn't. $route = aioseo()->helpers->toLowercase( $request->get_route() ); $route = untrailingslashit( str_replace( '/' . $this->namespace . '/', '', $route ) ); $routeData = isset( $this->getRoutes()[ $request->get_method() ][ $route ] ) ? $this->getRoutes()[ $request->get_method() ][ $route ] : []; // No direct route name, let's try the regexes. if ( empty( $routeData ) ) { foreach ( $this->getRoutes()[ $request->get_method() ] as $routeRegex => $routeInfo ) { $routeRegex = str_replace( '@', '\@', $routeRegex ); if ( preg_match( "@{$routeRegex}@", $route ) ) { $routeData = $routeInfo; break; } } } return $routeData; } }