Completed
Push — update/exit-after-redirect ( 430c2f )
by
unknown
29:03 queued 19:00
created

Jetpack_Client_Server::get_token()   D

Complexity

Conditions 22
Paths 94

Size

Total Lines 99
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 57
c 0
b 0
f 0
nc 94
nop 1
dl 0
loc 99
rs 4.6625

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Client = Plugin
5
 * Client Server = API Methods the Plugin must respond to
6
 */
7
class Jetpack_Client_Server {
8
9
	/**
10
	 * Authorizations
11
	 */
12
	function client_authorize() {
13
		$data              = stripslashes_deep( $_GET );
14
		$data['auth_type'] = 'client';
15
		$role              = Jetpack::translate_current_user_to_role();
16
		$redirect          = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
17
18
		check_admin_referer( "jetpack-authorize_{$role}_{$redirect}" );
19
20
		$result = $this->authorize( $data );
21
		if ( is_wp_error( $result ) ) {
22
			Jetpack::state( 'error', $result->get_error_code() );
23
		}
24
25
		if ( wp_validate_redirect( $redirect ) ) {
26
			wp_safe_redirect( $redirect );
27
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method client_authorize() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
28
		} else {
29
			wp_safe_redirect( Jetpack::admin_url() );
30
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method client_authorize() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
31
		}
32
33
		/**
34
		 * Fires after the Jetpack client is authorized to communicate with WordPress.com.
35
		 *
36
		 * @since 4.2.0
37
		 *
38
		 * @param int Jetpack Blog ID.
39
		 */
40
		do_action( 'jetpack_client_authorized', Jetpack_Options::get_option( 'id' ) );
0 ignored issues
show
Unused Code introduced by
/** * Fires after the J...ons::get_option('id')); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
41
42
		$this->do_exit();
43
	}
44
45
	function authorize( $data = array() ) {
46
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
0 ignored issues
show
Unused Code introduced by
$redirect is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
47
48
		$jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
49
		// Checking if site has been active/connected previously before recording unique connection
50
		if ( ! $jetpack_unique_connection ) {
51
			// jetpack_unique_connection option has never been set
52
			$jetpack_unique_connection = array(
53
				'connected'     => 0,
54
				'disconnected'  => 0,
55
				'version'       => '3.6.1',
56
			);
57
58
			update_option( 'jetpack_unique_connection', $jetpack_unique_connection );
59
60
			//track unique connection
61
			$jetpack = $this->get_jetpack();;
0 ignored issues
show
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
62
63
			$jetpack->stat( 'connections', 'unique-connection' );
64
			$jetpack->do_stats( 'server_side' );
65
		}
66
67
		// increment number of times connected
68
		$jetpack_unique_connection['connected'] += 1;
69
		Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
70
71
		$role = Jetpack::translate_current_user_to_role();
72
73
		if ( ! $role ) {
74
			return new Jetpack_Error( 'no_role', 'Invalid request.', 400 );
75
		}
76
77
		$cap = Jetpack::translate_role_to_cap( $role );
78
		if ( ! $cap ) {
79
			return new Jetpack_Error( 'no_cap', 'Invalid request.', 400 );
80
		}
81
82
		if ( ! empty( $data['error'] ) ) {
83
			return new Jetpack_Error( $data['error'], 'Error included in the request.', 400 );
84
		}
85
86
		if ( ! isset( $data['state'] ) ) {
87
			return new Jetpack_Error( 'no_state', 'Request must include state.', 400 );
88
		}
89
90
		if ( ! ctype_digit( $data['state'] ) ) {
91
			return new Jetpack_Error( $data['error'], 'State must be an integer.', 400 );
92
		}
93
94
		$current_user_id = get_current_user_id();
95
		if ( $current_user_id != $data['state'] ) {
96
			return new Jetpack_Error( 'wrong_state', 'State does not match current user.', 400 );
97
		}
98
99
		if ( empty( $data['code'] ) ) {
100
			return new Jetpack_Error( 'no_code', 'Request must include an authorization code.', 400 );
101
		}
102
103
		$token = $this->get_token( $data );
104
105
		if ( is_wp_error( $token ) ) {
106
			$code = $token->get_error_code();
107
			if ( empty( $code ) ) {
108
				$code = 'invalid_token';
109
			}
110
			return new Jetpack_Error( $code, $token->get_error_message(), 400 );
111
		}
112
113
		if ( ! $token ) {
114
			return new Jetpack_Error( 'no_token', 'Error generating token.', 400 );
115
		}
116
117
		$is_master_user = ! Jetpack::is_active();
118
119
		Jetpack::update_user_token( $current_user_id, sprintf( '%s.%d', $token, $current_user_id ), $is_master_user );
120
121
		if ( ! $is_master_user ) {
122
			Jetpack::state( 'message', 'linked' );
123
			// Don't activate anything since we are just connecting a user.
124
			return 'linked';
125
		}
126
127
		$redirect_on_activation_error = ( 'client' === $data['auth_type'] ) ? true : false;
128 View Code Duplication
		if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
129
			Jetpack::delete_active_modules();
130
131
			Jetpack::activate_default_modules( 999, 1, $active_modules, $redirect_on_activation_error, false );
0 ignored issues
show
Documentation introduced by
999 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
132
		} else {
133
			Jetpack::activate_default_modules( false, false, array(), $redirect_on_activation_error, false );
134
		}
135
136
		// If redirect_uri is SSO, ensure SSO module is enabled
137
		parse_str( parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
138
		/** This filter is documented in class.jetpack-cli.php */
139
		if ( isset( $redirect_options['action'] ) && 'jetpack-sso' === $redirect_options['action'] && apply_filters( 'jetpack_start_enable_sso', true ) ) {
140
			Jetpack::activate_module( 'sso', false, false );
141
		}
142
143
		// Since this is a fresh connection, be sure to clear out IDC options
144
		Jetpack_IDC::clear_all_idc_options();
145
146
		// Start nonce cleaner
147
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
148
		wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
149
150
		Jetpack::state( 'message', 'authorized' );
151
		return 'authorized';
152
	}
153
154
	public static function deactivate_plugin( $probable_file, $probable_title ) {
155
		include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
156
		if ( is_plugin_active( $probable_file ) ) {
157
			deactivate_plugins( $probable_file );
158
			return 1;
159
		} else {
160
			// If the plugin is not in the usual place, try looking through all active plugins.
161
			$active_plugins = Jetpack::get_active_plugins();
162
			foreach ( $active_plugins as $plugin ) {
163
				$data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
164
				if ( $data['Name'] == $probable_title ) {
165
					deactivate_plugins( $plugin );
166
					return 1;
167
				}
168
			}
169
		}
170
171
		return 0;
172
	}
173
174
	/**
175
	 * @return object|WP_Error
176
	 */
177
	function get_token( $data ) {
178
		$role = Jetpack::translate_current_user_to_role();
179
180
		if ( ! $role ) {
181
			return new Jetpack_Error( 'role', __( 'An administrator for this blog must set up the Jetpack connection.', 'jetpack' ) );
182
		}
183
184
		$client_secret = Jetpack_Data::get_access_token();
185
		if ( ! $client_secret ) {
186
			return new Jetpack_Error( 'client_secret', __( 'You need to register your Jetpack before connecting it.', 'jetpack' ) );
187
		}
188
189
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
190
		$redirect_uri = ( 'calypso' === $data['auth_type'] )
191
			? $data['redirect_uri']
192
			: add_query_arg( array(
193
				'action' => 'authorize',
194
				'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
195
				'redirect' => $redirect ? urlencode( $redirect ) : false,
196
			), menu_page_url( 'jetpack', false ) );
197
198
		$body = array(
199
			'client_id' => Jetpack_Options::get_option( 'id' ),
200
			'client_secret' => $client_secret->secret,
201
			'grant_type' => 'authorization_code',
202
			'code' => $data['code'],
203
			'redirect_uri' => $redirect_uri,
204
		);
205
206
		$args = array(
207
			'method' => 'POST',
208
			'body' => $body,
209
			'headers' => array(
210
				'Accept' => 'application/json',
211
			),
212
		);
213
		$response = Jetpack_Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'token' ) ), $args );
214
215
		if ( is_wp_error( $response ) ) {
216
			return new Jetpack_Error( 'token_http_request_failed', $response->get_error_message() );
217
		}
218
219
		$code = wp_remote_retrieve_response_code( $response );
220
		$entity = wp_remote_retrieve_body( $response );
221
222
		if ( $entity ) {
223
			$json = json_decode( $entity );
224
		} else {
225
			$json = false;
226
		}
227
228
		if ( 200 != $code || ! empty( $json->error ) ) {
229
			if ( empty( $json->error ) ) {
230
				return new Jetpack_Error( 'unknown', '', $code );
231
			}
232
233
			$error_description = isset( $json->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $json->error_description ) : '';
234
235
			return new Jetpack_Error( (string) $json->error, $error_description, $code );
236
		}
237
238
		if ( empty( $json->access_token ) || ! is_scalar( $json->access_token ) ) {
239
			return new Jetpack_Error( 'access_token', '', $code );
240
		}
241
242
		if ( empty( $json->token_type ) || 'X_JETPACK' != strtoupper( $json->token_type ) ) {
243
			return new Jetpack_Error( 'token_type', '', $code );
244
		}
245
246
		if ( empty( $json->scope ) ) {
247
			return new Jetpack_Error( 'scope', 'No Scope', $code );
248
		}
249
250
		@list( $role, $hmac ) = explode( ':', $json->scope );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
251
		if ( empty( $role ) || empty( $hmac ) ) {
252
			return new Jetpack_Error( 'scope', 'Malformed Scope', $code );
253
		}
254
255
		if ( Jetpack::sign_role( $role ) !== $json->scope ) {
256
			return new Jetpack_Error( 'scope', 'Invalid Scope', $code );
257
		}
258
259
		if ( ! $cap = Jetpack::translate_role_to_cap( $role ) ) {
260
			return new Jetpack_Error( 'scope', 'No Cap', $code );
261
		}
262
263
		if ( ! current_user_can( $cap ) ) {
264
			return new Jetpack_Error( 'scope', 'current_user_cannot', $code );
265
		}
266
267
		/**
268
		 * Fires after user has successfully received an auth token.
269
		 *
270
		 * @since 3.9.0
271
		 */
272
		do_action( 'jetpack_user_authorized' );
273
274
		return (string) $json->access_token;
275
	}
276
277
	public function get_jetpack() {
278
		return Jetpack::init();
279
	}
280
281
	public function do_exit() {
282
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method do_exit() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
283
	}
284
}
285