get_header( 'authorization' ); if ( $auth && 0 === stripos( $auth, 'bearer ' ) ) { $given = trim( substr( $auth, 7 ) ); } if ( '' === $given ) { $given = trim( (string) $request->get_header( 'x_spindle_key' ) ); } if ( '' === $given || strlen( $given ) > 128 ) { return ''; } $opt = SPFAQ_API_Options::options(); $hash = self::hash_key( $given ); if ( $opt['write_hash'] && hash_equals( $opt['write_hash'], $hash ) ) { return 'write'; } if ( $opt['read_hash'] && hash_equals( $opt['read_hash'], $hash ) ) { return 'read'; } return ''; } /** * Permission callback for read endpoints. * * @param WP_REST_Request $request Request. * @return true|WP_Error */ public static function can_read( $request ) { $opt = SPFAQ_API_Options::options(); if ( empty( $opt['enabled'] ) ) { return new WP_Error( 'spfaq_api_disabled', __( 'The Spindle FAQ knowledge API is switched off.', 'spindle-faq' ), array( 'status' => 403 ) ); } if ( ! empty( $opt['public_read'] ) ) { return self::throttle( 'r', 120 ); } if ( 'read' === self::key_scope( $request ) || current_user_can( 'manage_options' ) ) { return true; } return new WP_Error( 'spfaq_api_forbidden', __( 'A valid read key is required.', 'spindle-faq' ), array( 'status' => rest_authorization_required_code() ) ); } /** * Permission callback for write endpoints. Only the write key (or an * administrator) is accepted. A read key never grants writes. * * @param WP_REST_Request $request Request. * @return true|WP_Error */ public static function can_write( $request ) { $opt = SPFAQ_API_Options::options(); if ( empty( $opt['enabled'] ) ) { return new WP_Error( 'spfaq_api_disabled', __( 'The Spindle FAQ knowledge API is switched off.', 'spindle-faq' ), array( 'status' => 403 ) ); } if ( 'write' === self::key_scope( $request ) || current_user_can( 'manage_options' ) ) { return self::throttle( 'w', 60 ); } return new WP_Error( 'spfaq_api_forbidden', __( 'A valid write key is required.', 'spindle-faq' ), array( 'status' => rest_authorization_required_code() ) ); } /** * Per-client request limit (requests per minute). * * @param string $kind Bucket. * @param int $max Maximum requests per minute. * @return true|WP_Error */ private static function throttle( $kind, $max ) { $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown'; $key = 'spfaq_api_' . $kind . '_' . md5( $ip . wp_salt( 'auth' ) ); $n = (int) get_transient( $key ); if ( $n >= $max ) { return new WP_Error( 'spfaq_rate_limited', __( 'Too many requests. Please slow down.', 'spindle-faq' ), array( 'status' => 429 ) ); } set_transient( $key, $n + 1, MINUTE_IN_SECONDS ); return true; } }