Completed
Push — update/enable-connected-plugin... ( 2e5284 )
by
unknown
168:38 queued 158:16
created

REST_Connector   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 337
Duplicated Lines 10.39 %

Coupling/Cohesion

Components 2
Dependencies 4

Importance

Changes 0
Metric Value
dl 35
loc 337
rs 9.84
c 0
b 0
f 0
wmc 32
lcom 2
cbo 4

11 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 66 2
A verify_registration() 0 5 1
A remote_authorize() 0 10 2
A connection_status() 0 28 5
A get_connection_plugins() 0 16 2
A activate_plugins_permission_check() 0 7 2
A connection_plugins_permission_check() 0 12 3
A is_request_signed_by_jetpack_debugger() 35 35 5
A jetpack_reconnect_permission_check() 0 7 2
A get_user_permissions_error_msg() 0 3 1
B connection_reconnect() 0 36 7

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * Sets up the Connection REST API endpoints.
4
 *
5
 * @package automattic/jetpack-connection
6
 */
7
8
namespace Automattic\Jetpack\Connection;
9
10
use Automattic\Jetpack\Status;
11
use Jetpack_XMLRPC_Server;
12
use WP_Error;
13
use WP_REST_Request;
14
use WP_REST_Response;
15
use WP_REST_Server;
16
17
/**
18
 * Registers the REST routes for Connections.
19
 */
20
class REST_Connector {
21
	/**
22
	 * The Connection Manager.
23
	 *
24
	 * @var Manager
25
	 */
26
	private $connection;
27
28
	/**
29
	 * This property stores the localized "Insufficient Permissions" error message.
30
	 *
31
	 * @var string Generic error message when user is not allowed to perform an action.
32
	 */
33
	private static $user_permissions_error_msg;
34
35
	const JETPACK__DEBUGGER_PUBLIC_KEY = "\r\n" . '-----BEGIN PUBLIC KEY-----' . "\r\n"
36
	. 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm+uLLVoxGCY71LS6KFc6' . "\r\n"
37
	. '1UnF6QGBAsi5XF8ty9kR3/voqfOkpW+gRerM2Kyjy6DPCOmzhZj7BFGtxSV2ZoMX' . "\r\n"
38
	. '9ZwWxzXhl/Q/6k8jg8BoY1QL6L2K76icXJu80b+RDIqvOfJruaAeBg1Q9NyeYqLY' . "\r\n"
39
	. 'lEVzN2vIwcFYl+MrP/g6Bc2co7Jcbli+tpNIxg4Z+Hnhbs7OJ3STQLmEryLpAxQO' . "\r\n"
40
	. 'q8cbhQkMx+FyQhxzSwtXYI/ClCUmTnzcKk7SgGvEjoKGAmngILiVuEJ4bm7Q1yok' . "\r\n"
41
	. 'xl9+wcfW6JAituNhml9dlHCWnn9D3+j8pxStHihKy2gVMwiFRjLEeD8K/7JVGkb/' . "\r\n"
42
	. 'EwIDAQAB' . "\r\n"
43
	. '-----END PUBLIC KEY-----' . "\r\n";
44
45
	/**
46
	 * Constructor.
47
	 *
48
	 * @param Manager $connection The Connection Manager.
49
	 */
50
	public function __construct( Manager $connection ) {
51
		$this->connection = $connection;
52
53
		self::$user_permissions_error_msg = esc_html__(
54
			'You do not have the correct user permissions to perform this action.
55
			Please contact your site admin if you think this is a mistake.',
56
			'jetpack'
57
		);
58
59
		if ( ! $this->connection->is_active() ) {
60
			// Register a site.
61
			register_rest_route(
62
				'jetpack/v4',
63
				'/verify_registration',
64
				array(
65
					'methods'             => WP_REST_Server::EDITABLE,
66
					'callback'            => array( $this, 'verify_registration' ),
67
					'permission_callback' => '__return_true',
68
				)
69
			);
70
		}
71
72
		// Authorize a remote user.
73
		register_rest_route(
74
			'jetpack/v4',
75
			'/remote_authorize',
76
			array(
77
				'methods'             => WP_REST_Server::EDITABLE,
78
				'callback'            => __CLASS__ . '::remote_authorize',
79
				'permission_callback' => '__return_true',
80
			)
81
		);
82
83
		// Get current connection status of Jetpack.
84
		register_rest_route(
85
			'jetpack/v4',
86
			'/connection',
87
			array(
88
				'methods'             => WP_REST_Server::READABLE,
89
				'callback'            => __CLASS__ . '::connection_status',
90
				'permission_callback' => '__return_true',
91
			)
92
		);
93
94
		// Get list of plugins that use the Jetpack connection.
95
		register_rest_route(
96
			'jetpack/v4',
97
			'/connection/plugins',
98
			array(
99
				'methods'             => WP_REST_Server::READABLE,
100
				'callback'            => array( $this, 'get_connection_plugins' ),
101
				'permission_callback' => __CLASS__ . '::connection_plugins_permission_check',
102
			)
103
		);
104
105
		// Full or partial reconnect in case of connection issues.
106
		register_rest_route(
107
			'jetpack/v4',
108
			'/connection/reconnect',
109
			array(
110
				'methods'             => WP_REST_Server::EDITABLE,
111
				'callback'            => array( $this, 'connection_reconnect' ),
112
				'permission_callback' => __CLASS__ . '::jetpack_reconnect_permission_check',
113
			)
114
		);
115
	}
116
117
	/**
118
	 * Handles verification that a site is registered.
119
	 *
120
	 * @since 5.4.0
121
	 *
122
	 * @param WP_REST_Request $request The request sent to the WP REST API.
123
	 *
124
	 * @return string|WP_Error
125
	 */
126
	public function verify_registration( WP_REST_Request $request ) {
127
		$registration_data = array( $request['secret_1'], $request['state'] );
128
129
		return $this->connection->handle_registration( $registration_data );
130
	}
131
132
	/**
133
	 * Handles verification that a site is registered
134
	 *
135
	 * @since 5.4.0
136
	 *
137
	 * @param WP_REST_Request $request The request sent to the WP REST API.
138
	 *
139
	 * @return array|wp-error
0 ignored issues
show
Documentation introduced by
The doc-type array|wp-error could not be parsed: Unknown type name "wp-error" at position 6. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
140
	 */
141
	public static function remote_authorize( $request ) {
142
		$xmlrpc_server = new Jetpack_XMLRPC_Server();
143
		$result        = $xmlrpc_server->remote_authorize( $request );
144
145
		if ( is_a( $result, 'IXR_Error' ) ) {
146
			$result = new WP_Error( $result->code, $result->message );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with $result->code.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
147
		}
148
149
		return $result;
150
	}
151
152
	/**
153
	 * Get connection status for this Jetpack site.
154
	 *
155
	 * @since 4.3.0
156
	 *
157
	 * @param bool $rest_response Should we return a rest response or a simple array. Default to rest response.
158
	 *
159
	 * @return WP_REST_Response|array Connection information.
160
	 */
161
	public static function connection_status( $rest_response = true ) {
162
		$status     = new Status();
163
		$connection = new Manager();
164
165
		$connection_status = array(
166
			'isActive'          => $connection->is_active(),
167
			'isStaging'         => $status->is_staging_site(),
168
			'isRegistered'      => $connection->is_connected(),
169
			'hasConnectedOwner' => $connection->has_connected_owner(),
170
			'offlineMode'       => array(
171
				'isActive'        => $status->is_offline_mode(),
172
				'constant'        => defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG,
173
				'url'             => $status->is_local_site(),
174
				/** This filter is documented in packages/status/src/class-status.php */
175
				'filter'          => ( apply_filters( 'jetpack_development_mode', false ) || apply_filters( 'jetpack_offline_mode', false ) ), // jetpack_development_mode is deprecated.
176
				'wpLocalConstant' => defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV,
177
			),
178
			'isPublic'          => '1' == get_option( 'blog_public' ), // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
179
		);
180
181
		if ( $rest_response ) {
182
			return rest_ensure_response(
183
				$connection_status
184
			);
185
		} else {
186
			return $connection_status;
187
		}
188
	}
189
190
	/**
191
	 * Get plugins connected to the Jetpack.
192
	 *
193
	 * @since 8.6.0
194
	 *
195
	 * @return WP_REST_Response|WP_Error Response or error object, depending on the request result.
196
	 */
197
	public function get_connection_plugins() {
198
		$plugins = $this->connection->get_connected_plugins();
199
200
		if ( is_wp_error( $plugins ) ) {
201
			return $plugins;
202
		}
203
204
		array_walk(
205
			$plugins,
206
			function ( &$data, $slug ) {
207
				$data['slug'] = $slug;
208
			}
209
		);
210
211
		return rest_ensure_response( array_values( $plugins ) );
212
	}
213
214
	/**
215
	 * Verify that user can view Jetpack admin page and can activate plugins.
216
	 *
217
	 * @since 8.8.0
218
	 *
219
	 * @return bool|WP_Error Whether user has the capability 'activate_plugins'.
220
	 */
221
	public static function activate_plugins_permission_check() {
222
		if ( current_user_can( 'activate_plugins' ) ) {
223
			return true;
224
		}
225
226
		return new WP_Error( 'invalid_user_permission_activate_plugins', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_user_permission_activate_plugins'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
227
	}
228
229
	/**
230
	 * Permission check for the connection_plugins endpoint
231
	 *
232
	 * @return bool|WP_Error
233
	 */
234
	public static function connection_plugins_permission_check() {
235
		if ( true === static::activate_plugins_permission_check() ) {
236
			return true;
237
		}
238
239
		if ( true === static::is_request_signed_by_jetpack_debugger() ) {
240
			return true;
241
		}
242
243
		return new WP_Error( 'invalid_user_permission_activate_plugins', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_user_permission_activate_plugins'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
244
245
	}
246
247
	/**
248
	 * Verifies if the request was signed with the Jetpack Debugger key
249
	 *
250
	 * @return bool
251
	 */
252 View Code Duplication
	public static function is_request_signed_by_jetpack_debugger() {
253
		 // phpcs:disable WordPress.Security.NonceVerification.Recommended
254
		if ( ! isset( $_GET['signature'], $_GET['timestamp'], $_GET['url'] ) ) {
255
			return false;
256
		}
257
		$signature = base64_decode( $_GET['signature'] ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
258
259
		$signature_data = wp_json_encode(
260
			array(
261
				'rest_route' => $_GET['rest_route'],
262
				'timestamp'  => (int) $_GET['timestamp'],
263
				'url'        => wp_unslash( $_GET['url'] ),
264
			)
265
		);
266
267
		if (
268
			! function_exists( 'openssl_verify' )
269
			|| 1 !== openssl_verify(
270
				$signature_data,
271
				$signature,
272
				static::JETPACK__DEBUGGER_PUBLIC_KEY
273
			)
274
		) {
275
			return false;
276
		}
277
278
		// signature timestamp must be within 5min of current time.
279
		if ( abs( time() - (int) $_GET['timestamp'] ) > 300 ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
280
			return false;
281
		}
282
283
		// phpcs:enable WordPress.Security.NonceVerification.Recommended
284
285
		return true;
286
	}
287
288
	/**
289
	 * Verify that user is allowed to disconnect Jetpack.
290
	 *
291
	 * @since 8.8.0
292
	 *
293
	 * @return bool|WP_Error Whether user has the capability 'jetpack_disconnect'.
294
	 */
295
	public static function jetpack_reconnect_permission_check() {
296
		if ( current_user_can( 'jetpack_reconnect' ) ) {
297
			return true;
298
		}
299
300
		return new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_user_permission_jetpack_disconnect'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
301
	}
302
303
	/**
304
	 * Returns generic error message when user is not allowed to perform an action.
305
	 *
306
	 * @return string The error message.
307
	 */
308
	public static function get_user_permissions_error_msg() {
309
		return self::$user_permissions_error_msg;
310
	}
311
312
	/**
313
	 * The endpoint tried to partially or fully reconnect the website to WP.com.
314
	 *
315
	 * @since 8.8.0
316
	 *
317
	 * @return \WP_REST_Response|WP_Error
318
	 */
319
	public function connection_reconnect() {
320
		$response = array();
321
322
		$next = null;
323
324
		$result = $this->connection->restore();
325
326
		if ( is_wp_error( $result ) ) {
327
			$response = $result;
328
		} elseif ( is_string( $result ) ) {
329
			$next = $result;
330
		} else {
331
			$next = true === $result ? 'completed' : 'failed';
332
		}
333
334
		switch ( $next ) {
335
			case 'authorize':
336
				$response['status']       = 'in_progress';
337
				$response['authorizeUrl'] = $this->connection->get_authorization_url();
338
				break;
339
			case 'completed':
340
				$response['status'] = 'completed';
341
				/**
342
				 * Action fired when reconnection has completed successfully.
343
				 *
344
				 * @since 9.0.0
345
				 */
346
				do_action( 'jetpack_reconnection_completed' );
347
				break;
348
			case 'failed':
349
				$response = new WP_Error( 'Reconnect failed' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'Reconnect failed'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
350
				break;
351
		}
352
353
		return rest_ensure_response( $response );
354
	}
355
356
}
357