PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-connection / src / class-rest-authentication.php

class-rest-authentication.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at jetpack_vendor/automattic/jetpack-connection/src/class-rest-authentication.php

239 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The Jetpack Connection Rest Authentication file.
4 *
5 * @package automattic/jetpack-connection
6 */
7
8 namespace Automattic\Jetpack\Connection;
9
10 use WP_Error;
11
12 /**
13 * The Jetpack Connection Rest Authentication class.
14 */
15 class Rest_Authentication {
16
17 /**
18 * The rest authentication status.
19 *
20 * @since 1.17.0
21 * @var boolean
22 */
23 private $rest_authentication_status = null;
24
25 /**
26 * The rest authentication type.
27 * Can be either 'user' or 'blog' depending on whether the request
28 * is signed with a user or a blog token.
29 *
30 * @since 1.29.0
31 * @var string
32 */
33 private $rest_authentication_type = null;
34
35 /**
36 * The Manager object.
37 *
38 * @since 1.17.0
39 * @var Object
40 */
41 private $connection_manager = null;
42
43 /**
44 * Holds the singleton instance of this class
45 *
46 * @since 1.17.0
47 * @var Object
48 */
49 private static $instance = false;
50
51 /**
52 * Flag used to avoid determine_current_user filter to enter an infinite loop
53 *
54 * @since 1.26.0
55 * @var boolean
56 */
57 private $doing_determine_current_user_filter = false;
58
59 /**
60 * The constructor.
61 */
62 private function __construct() {
63 $this->connection_manager = new Manager();
64 }
65
66 /**
67 * Controls the single instance of this class.
68 *
69 * @static
70 */
71 public static function init() {
72 if ( ! self::$instance ) {
73 self::$instance = new self();
74
75 add_filter( 'determine_current_user', array( self::$instance, 'wp_rest_authenticate' ) );
76 add_filter( 'rest_authentication_errors', array( self::$instance, 'wp_rest_authentication_errors' ) );
77 }
78
79 return self::$instance;
80 }
81
82 /**
83 * Authenticates requests from Jetpack server to WP REST API endpoints.
84 * Uses the existing XMLRPC request signing implementation.
85 *
86 * @param int|bool $user User ID if one has been determined, false otherwise.
87 *
88 * @return int|null The user id or null if the request was authenticated via blog token, or not authenticated at all.
89 */
90 public function wp_rest_authenticate( $user ) {
91 if ( $this->doing_determine_current_user_filter ) {
92 return $user;
93 }
94
95 $this->doing_determine_current_user_filter = true;
96
97 try {
98 if ( ! empty( $user ) ) {
99 // Another authentication method is in effect.
100 return $user;
101 }
102
103 add_filter(
104 'jetpack_constant_default_value',
105 __NAMESPACE__ . '\Utils::jetpack_api_constant_filter',
106 10,
107 2
108 );
109
110 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
111 if ( ! isset( $_GET['_for'] ) || 'jetpack' !== $_GET['_for'] ) {
112 // Nothing to do for this authentication method.
113 return null;
114 }
115
116 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
117 if ( ! isset( $_GET['token'] ) && ! isset( $_GET['signature'] ) ) {
118 // Nothing to do for this authentication method.
119 return null;
120 }
121
122 // These `rest_invalid_request` errors occur before `verify_xml_rpc_signature()`,
123 // so the request has not been authenticated as WP.com. Do not report them to
124 // Error_Handler: they are malformed unauthenticated requests, not connection errors.
125 if ( ! isset( $_SERVER['REQUEST_METHOD'] ) ) {
126 $this->rest_authentication_status = new WP_Error(
127 'rest_invalid_request',
128 __( 'The request method is missing.', 'jetpack-connection' ),
129 array( 'status' => 400 )
130 );
131 return null;
132 }
133
134 // Only support specific request parameters that have been tested and
135 // are known to work with signature verification. A different method
136 // can be passed to the WP REST API via the '?_method=' parameter if
137 // needed.
138 if ( 'GET' !== $_SERVER['REQUEST_METHOD'] && 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
139 $this->rest_authentication_status = new WP_Error(
140 'rest_invalid_request',
141 __( 'This request method is not supported.', 'jetpack-connection' ),
142 array( 'status' => 400 )
143 );
144 return null;
145 }
146 if ( 'POST' !== $_SERVER['REQUEST_METHOD'] && ! empty( file_get_contents( 'php://input' ) ) ) {
147 $this->rest_authentication_status = new WP_Error(
148 'rest_invalid_request',
149 __( 'This request method does not support body parameters.', 'jetpack-connection' ),
150 array( 'status' => 400 )
151 );
152 return null;
153 }
154
155 $verified = $this->connection_manager->verify_xml_rpc_signature();
156
157 if (
158 $verified &&
159 isset( $verified['type'] ) &&
160 'blog' === $verified['type']
161 ) {
162 // Site-level authentication successful.
163 $this->rest_authentication_status = true;
164 $this->rest_authentication_type = 'blog';
165 return null;
166 }
167
168 if (
169 $verified &&
170 isset( $verified['type'] ) &&
171 'user' === $verified['type'] &&
172 ! empty( $verified['user_id'] )
173 ) {
174 // User-level authentication successful.
175 $this->rest_authentication_status = true;
176 $this->rest_authentication_type = 'user';
177 return $verified['user_id'];
178 }
179
180 // Something else went wrong. Probably a signature error.
181 $this->rest_authentication_status = new WP_Error(
182 'rest_invalid_signature',
183 __( 'The request is not signed correctly.', 'jetpack-connection' ),
184 array( 'status' => 400 )
185 );
186 return null;
187 } finally {
188 $this->doing_determine_current_user_filter = false;
189 }
190 }
191
192 /**
193 * Report authentication status to the WP REST API.
194 *
195 * @param WP_Error|mixed $value Error from another authentication handler, null if we should handle it, or another value if not.
196 * @return WP_Error|boolean|null {@see WP_JSON_Server::check_authentication}
197 */
198 public function wp_rest_authentication_errors( $value ) {
199 if ( null !== $value ) {
200 return $value;
201 }
202 return $this->rest_authentication_status;
203 }
204
205 /**
206 * Resets the saved authentication state in between testing requests.
207 */
208 public function reset_saved_auth_state() {
209 $this->rest_authentication_status = null;
210 $this->connection_manager->reset_saved_auth_state();
211 }
212
213 /**
214 * Whether the request was signed with a blog token.
215 *
216 * @since 1.29.0
217 *
218 * @return bool True if the request was signed with a valid blog token, false otherwise.
219 */
220 public static function is_signed_with_blog_token() {
221 $instance = self::init();
222
223 return true === $instance->rest_authentication_status && 'blog' === $instance->rest_authentication_type;
224 }
225
226 /**
227 * Whether the request was signed with a user token.
228 *
229 * @since 6.7.0
230 *
231 * @return bool True if the request was signed with a valid user token, false otherwise.
232 */
233 public static function is_signed_with_user_token() {
234 $instance = self::init();
235
236 return true === $instance->rest_authentication_status && 'user' === $instance->rest_authentication_type;
237 }
238 }
239