diff options
Diffstat (limited to 'plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-authentication.php')
-rw-r--r-- | plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-authentication.php | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-authentication.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-authentication.php new file mode 100644 index 00000000..c5a89f53 --- /dev/null +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-authentication.php @@ -0,0 +1,220 @@ +<?php +/** + * The Jetpack Connection Rest Authentication file. + * + * @package automattic/jetpack-connection + */ + +namespace Automattic\Jetpack\Connection; + +/** + * The Jetpack Connection Rest Authentication class. + */ +class Rest_Authentication { + + /** + * The rest authentication status. + * + * @since 1.17.0 + * @var boolean + */ + private $rest_authentication_status = null; + + /** + * The rest authentication type. + * Can be either 'user' or 'blog' depending on whether the request + * is signed with a user or a blog token. + * + * @since 1.29.0 + * @var string + */ + private $rest_authentication_type = null; + + /** + * The Manager object. + * + * @since 1.17.0 + * @var Object + */ + private $connection_manager = null; + + /** + * Holds the singleton instance of this class + * + * @since 1.17.0 + * @var Object + */ + private static $instance = false; + + /** + * Flag used to avoid determine_current_user filter to enter an infinite loop + * + * @since 1.26.0 + * @var boolean + */ + private $doing_determine_current_user_filter = false; + + /** + * The constructor. + */ + private function __construct() { + $this->connection_manager = new Manager(); + } + + /** + * Controls the single instance of this class. + * + * @static + */ + public static function init() { + if ( ! self::$instance ) { + self::$instance = new self(); + + add_filter( 'determine_current_user', array( self::$instance, 'wp_rest_authenticate' ) ); + add_filter( 'rest_authentication_errors', array( self::$instance, 'wp_rest_authentication_errors' ) ); + } + + return self::$instance; + } + + /** + * Authenticates requests from Jetpack server to WP REST API endpoints. + * Uses the existing XMLRPC request signing implementation. + * + * @param int|bool $user User ID if one has been determined, false otherwise. + * + * @return int|null The user id or null if the request was authenticated via blog token, or not authenticated at all. + */ + public function wp_rest_authenticate( $user ) { + if ( $this->doing_determine_current_user_filter ) { + return $user; + } + + $this->doing_determine_current_user_filter = true; + + try { + if ( ! empty( $user ) ) { + // Another authentication method is in effect. + return $user; + } + + add_filter( + 'jetpack_constant_default_value', + __NAMESPACE__ . '\Utils::jetpack_api_constant_filter', + 10, + 2 + ); + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( ! isset( $_GET['_for'] ) || 'jetpack' !== $_GET['_for'] ) { + // Nothing to do for this authentication method. + return null; + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( ! isset( $_GET['token'] ) && ! isset( $_GET['signature'] ) ) { + // Nothing to do for this authentication method. + return null; + } + + if ( ! isset( $_SERVER['REQUEST_METHOD'] ) ) { + $this->rest_authentication_status = new \WP_Error( + 'rest_invalid_request', + __( 'The request method is missing.', 'jetpack-connection' ), + array( 'status' => 400 ) + ); + return null; + } + + // Only support specific request parameters that have been tested and + // are known to work with signature verification. A different method + // can be passed to the WP REST API via the '?_method=' parameter if + // needed. + if ( 'GET' !== $_SERVER['REQUEST_METHOD'] && 'POST' !== $_SERVER['REQUEST_METHOD'] ) { + $this->rest_authentication_status = new \WP_Error( + 'rest_invalid_request', + __( 'This request method is not supported.', 'jetpack-connection' ), + array( 'status' => 400 ) + ); + return null; + } + if ( 'POST' !== $_SERVER['REQUEST_METHOD'] && ! empty( file_get_contents( 'php://input' ) ) ) { + $this->rest_authentication_status = new \WP_Error( + 'rest_invalid_request', + __( 'This request method does not support body parameters.', 'jetpack-connection' ), + array( 'status' => 400 ) + ); + return null; + } + + $verified = $this->connection_manager->verify_xml_rpc_signature(); + + if ( + $verified && + isset( $verified['type'] ) && + 'blog' === $verified['type'] + ) { + // Site-level authentication successful. + $this->rest_authentication_status = true; + $this->rest_authentication_type = 'blog'; + return null; + } + + if ( + $verified && + isset( $verified['type'] ) && + 'user' === $verified['type'] && + ! empty( $verified['user_id'] ) + ) { + // User-level authentication successful. + $this->rest_authentication_status = true; + $this->rest_authentication_type = 'user'; + return $verified['user_id']; + } + + // Something else went wrong. Probably a signature error. + $this->rest_authentication_status = new \WP_Error( + 'rest_invalid_signature', + __( 'The request is not signed correctly.', 'jetpack-connection' ), + array( 'status' => 400 ) + ); + return null; + } finally { + $this->doing_determine_current_user_filter = false; + } + } + + /** + * Report authentication status to the WP REST API. + * + * @param WP_Error|mixed $value Error from another authentication handler, null if we should handle it, or another value if not. + * @return WP_Error|boolean|null {@see WP_JSON_Server::check_authentication} + */ + public function wp_rest_authentication_errors( $value ) { + if ( null !== $value ) { + return $value; + } + return $this->rest_authentication_status; + } + + /** + * Resets the saved authentication state in between testing requests. + */ + public function reset_saved_auth_state() { + $this->rest_authentication_status = null; + $this->connection_manager->reset_saved_auth_state(); + } + + /** + * Whether the request was signed with a blog token. + * + * @since 1.29.0 + * + * @return bool True if the request was signed with a valid blog token, false otherwise. + */ + public static function is_signed_with_blog_token() { + $instance = self::init(); + + return true === $instance->rest_authentication_status && 'blog' === $instance->rest_authentication_type; + } +} |