summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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.php220
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;
+ }
+}