401 ) ); } if ( ! current_user_can( 'edit_posts' ) ) { return new WP_Error( 'schema_ai_forbidden', __( 'You do not have permission to access this endpoint.', 'ai-schema-gen' ), array( 'status' => 403 ) ); } return true; } /** * Check whether the current user can edit a specific post. * * @param WP_REST_Request $request The current REST request. * @param int $post_id The WordPress post ID to check. * @return true|WP_Error True if allowed, WP_Error otherwise. */ public function check_edit_permission( $request, $post_id ) { if ( ! is_user_logged_in() ) { return new WP_Error( 'schema_ai_not_logged_in', __( 'You must be logged in to edit this post.', 'ai-schema-gen' ), array( 'status' => 401 ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new WP_Error( 'schema_ai_forbidden', __( 'You do not have permission to edit this post.', 'ai-schema-gen' ), array( 'status' => 403 ) ); } return true; } /** * Verify the X-WP-Nonce header on the request. * * @param WP_REST_Request $request The current REST request. * @return true|WP_Error True if the nonce is valid, WP_Error otherwise. */ public function verify_nonce( $request ) { $nonce = $request->get_header( 'X-WP-Nonce' ); if ( ! $nonce ) { return new WP_Error( 'schema_ai_missing_nonce', __( 'Missing security nonce.', 'ai-schema-gen' ), array( 'status' => 403 ) ); } if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) { return new WP_Error( 'schema_ai_invalid_nonce', __( 'Invalid security nonce.', 'ai-schema-gen' ), array( 'status' => 403 ) ); } return true; } /** * Get the current user's subscription plan. * * @return string Plan identifier: 'free', 'pro', or 'premium'. */ public function get_current_user_plan() { $user_id = get_current_user_id(); if ( ! $user_id ) { return 'free'; } $plan = get_user_meta( $user_id, 'schema_ai_plan', true ); return $plan ? $plan : 'free'; } /** * Check whether the current user is on a paid plan. * * Backend plans are 'free', 'pro', and 'premium' — there is no 'agency' plan. * * @return bool True if the user is on pro or premium. */ public function is_premium() { $plan = $this->get_current_user_plan(); return in_array( $plan, array( 'pro', 'premium' ), true ); } /** * Register authentication hooks. * * Hooks into rest_authentication_errors to add custom authentication * logic for Schema AI endpoints. */ public function register_hooks() { add_filter( 'rest_authentication_errors', array( $this, 'authenticate' ) ); } /** * Custom authentication callback for the REST API. * * Runs on every REST request. If the request targets a ai-schema-gen * endpoint, verifies that the user is authenticated. Passes through * any existing errors from earlier authentication filters. * * @param WP_Error|null|true $errors Existing authentication result. * @return WP_Error|null|true Authentication result. */ public function authenticate( $errors ) { // If a previous authentication filter already failed, honour it. if ( is_wp_error( $errors ) ) { return $errors; } // Only apply to ai-schema-gen endpoints. $rest_route = ''; if ( isset( $GLOBALS['wp']->query_vars['rest_route'] ) ) { $rest_route = $GLOBALS['wp']->query_vars['rest_route']; } if ( empty( $rest_route ) || 0 !== strpos( $rest_route, '/ai-schema-gen/' ) ) { return $errors; } // Ensure the user is logged in for all ai-schema-gen endpoints. if ( ! is_user_logged_in() ) { return new WP_Error( 'schema_ai_not_authenticated', __( 'Authentication is required for AI Schema Gen endpoints.', 'ai-schema-gen' ), array( 'status' => 401 ) ); } return $errors; } }