Completed
Push — move-action-for-authorization-... ( 6cb92a )
by
unknown
20:34 queued 09:27
created

Jetpack_Client_Server::do_exit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
		} else {
28
			wp_safe_redirect( Jetpack::admin_url() );
29
		}
30
31
		/**
32
		 * Fires after the Jetpack client is authorized to communicate with WordPress.com.
33
		 *
34
		 * @since 4.2.0
35
		 *
36
		 * @param int Jetpack Blog ID.
37
		 */
38
		do_action( 'jetpack_client_authorized', Jetpack_Options::get_option( 'id' ) );
39
40
		$this->do_exit();
41
	}
42
43
	function authorize( $data = array() ) {
44
		$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...
45
46
		$jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
47
		// Checking if site has been active/connected previously before recording unique connection
48
		if ( ! $jetpack_unique_connection ) {
49
			// jetpack_unique_connection option has never been set
50
			$jetpack_unique_connection = array(
51
				'connected'     => 0,
52
				'disconnected'  => 0,
53
				'version'       => '3.6.1',
54
			);
55
56
			update_option( 'jetpack_unique_connection', $jetpack_unique_connection );
57
58
			//track unique connection
59
			$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...
60
61
			$jetpack->stat( 'connections', 'unique-connection' );
62
			$jetpack->do_stats( 'server_side' );
63
		}
64
65
		// increment number of times connected
66
		$jetpack_unique_connection['connected'] += 1;
67
		Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
68
69
		$role = Jetpack::translate_current_user_to_role();
70
71
		if ( ! $role ) {
72
			return new Jetpack_Error( 'no_role', 'Invalid request.', 400 );
73
		}
74
75
		$cap = Jetpack::translate_role_to_cap( $role );
76
		if ( ! $cap ) {
77
			return new Jetpack_Error( 'no_cap', 'Invalid request.', 400 );
78
		}
79
80
		if ( ! empty( $data['error'] ) ) {
81
			return new Jetpack_Error( $data['error'], 'Error included in the request.', 400 );
82
		}
83
84
		if ( ! isset( $data['state'] ) ) {
85
			return new Jetpack_Error( 'no_state', 'Request must include state.', 400 );
86
		}
87
88
		if ( ! ctype_digit( $data['state'] ) ) {
89
			return new Jetpack_Error( $data['error'], 'State must be an integer.', 400 );
90
		}
91
92
		$current_user_id = get_current_user_id();
93
		if ( $current_user_id != $data['state'] ) {
94
			return new Jetpack_Error( 'wrong_state', 'State does not match current user.', 400 );
95
		}
96
97
		if ( empty( $data['code'] ) ) {
98
			return new Jetpack_Error( 'no_code', 'Request must include an authorization code.', 400 );
99
		}
100
101
		$token = $this->get_token( $data );
102
103
		if ( is_wp_error( $token ) ) {
104
			$code = $token->get_error_code();
105
			if ( empty( $code ) ) {
106
				$code = 'invalid_token';
107
			}
108
			return new Jetpack_Error( $code, $token->get_error_message(), 400 );
109
		}
110
111
		if ( ! $token ) {
112
			return new Jetpack_Error( 'no_token', 'Error generating token.', 400 );
113
		}
114
115
		$is_master_user = ! Jetpack::is_active();
116
117
		$token_set = Jetpack::update_user_token( $current_user_id, sprintf( '%s.%d', $token, $current_user_id ), $is_master_user );
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $token_set is correct as \Jetpack::update_user_to...r_id), $is_master_user) (which targets Jetpack::update_user_token()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
118
119
		if ( ! $token_set ) {
120
			return new Jetpack_Error( 'token_not_saved', 'Error saving token.', 400 );
121
		}
122
123
		/**
124
		 * Fires after user has successfully received an auth token.
125
		 *
126
		 * @since 3.9.0
127
		 */
128
		do_action( 'jetpack_user_authorized' );
129
130
131
		if ( ! $is_master_user ) {
132
			Jetpack::state( 'message', 'linked' );
133
			// Don't activate anything since we are just connecting a user.
134
			return 'linked';
135
		}
136
137
		$redirect_on_activation_error = ( 'client' === $data['auth_type'] ) ? true : false;
138
		if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
139
			Jetpack::delete_active_modules();
140
141
			Jetpack::activate_default_modules( 999, 1, $active_modules, $redirect_on_activation_error );
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...
142
		} else {
143
			Jetpack::activate_default_modules( false, false, array(), $redirect_on_activation_error );
144
		}
145
146
		// If redirect_uri is SSO, ensure SSO module is enabled
147
		parse_str( parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
148
		if ( isset( $redirect_options['action'] ) && 'jetpack-sso' === $redirect_options['action'] ) {
149
			Jetpack::activate_module( 'sso', false, false );
150
		}
151
152
		// Since this is a fresh connection, be sure to clear out IDC options
153
		Jetpack_IDC::clear_all_idc_options();
154
155
		// Start nonce cleaner
156
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
157
		wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
158
159
		Jetpack::state( 'message', 'authorized' );
160
		return 'authorized';
161
	}
162
163
	public static function deactivate_plugin( $probable_file, $probable_title ) {
164
		include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
165
		if ( is_plugin_active( $probable_file ) ) {
166
			deactivate_plugins( $probable_file );
167
			return 1;
168
		} else {
169
			// If the plugin is not in the usual place, try looking through all active plugins.
170
			$active_plugins = Jetpack::get_active_plugins();
171
			foreach ( $active_plugins as $plugin ) {
172
				$data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
173
				if ( $data['Name'] == $probable_title ) {
174
					deactivate_plugins( $plugin );
175
					return 1;
176
				}
177
			}
178
		}
179
180
		return 0;
181
	}
182
183
	/**
184
	 * @return object|WP_Error
185
	 */
186
	function get_token( $data ) {
187
		$role = Jetpack::translate_current_user_to_role();
188
189
		if ( ! $role ) {
190
			return new Jetpack_Error( 'role', __( 'An administrator for this blog must set up the Jetpack connection.', 'jetpack' ) );
191
		}
192
193
		$client_secret = Jetpack_Data::get_access_token();
194
		if ( ! $client_secret ) {
195
			return new Jetpack_Error( 'client_secret', __( 'You need to register your Jetpack before connecting it.', 'jetpack' ) );
196
		}
197
198
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
199
		$redirect_uri = ( 'calypso' === $data['auth_type'] )
200
			? $data['redirect_uri']
201
			: add_query_arg( array(
202
				'action' => 'authorize',
203
				'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
204
				'redirect' => $redirect ? urlencode( $redirect ) : false,
205
			), menu_page_url( 'jetpack', false ) );
206
207
		$body = array(
208
			'client_id' => Jetpack_Options::get_option( 'id' ),
209
			'client_secret' => $client_secret->secret,
210
			'grant_type' => 'authorization_code',
211
			'code' => $data['code'],
212
			'redirect_uri' => $redirect_uri,
213
		);
214
215
		$args = array(
216
			'method' => 'POST',
217
			'body' => $body,
218
			'headers' => array(
219
				'Accept' => 'application/json',
220
			),
221
		);
222
		$response = Jetpack_Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'token' ) ), $args );
223
224
		if ( is_wp_error( $response ) ) {
225
			return new Jetpack_Error( 'token_http_request_failed', $response->get_error_message() );
226
		}
227
228
		$code = wp_remote_retrieve_response_code( $response );
229
		$entity = wp_remote_retrieve_body( $response );
230
231
		if ( $entity ) {
232
			$json = json_decode( $entity );
233
		} else {
234
			$json = false;
235
		}
236
237
		if ( 200 != $code || ! empty( $json->error ) ) {
238
			if ( empty( $json->error ) ) {
239
				return new Jetpack_Error( 'unknown', '', $code );
240
			}
241
242
			$error_description = isset( $json->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $json->error_description ) : '';
243
244
			return new Jetpack_Error( (string) $json->error, $error_description, $code );
245
		}
246
247
		if ( empty( $json->access_token ) || ! is_scalar( $json->access_token ) ) {
248
			return new Jetpack_Error( 'access_token', '', $code );
249
		}
250
251
		if ( empty( $json->token_type ) || 'X_JETPACK' != strtoupper( $json->token_type ) ) {
252
			return new Jetpack_Error( 'token_type', '', $code );
253
		}
254
255
		if ( empty( $json->scope ) ) {
256
			return new Jetpack_Error( 'scope', 'No Scope', $code );
257
		}
258
259
		@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...
260
		if ( empty( $role ) || empty( $hmac ) ) {
261
			return new Jetpack_Error( 'scope', 'Malformed Scope', $code );
262
		}
263
264
		if ( Jetpack::sign_role( $role ) !== $json->scope ) {
265
			return new Jetpack_Error( 'scope', 'Invalid Scope', $code );
266
		}
267
268
		if ( ! $cap = Jetpack::translate_role_to_cap( $role ) ) {
269
			return new Jetpack_Error( 'scope', 'No Cap', $code );
270
		}
271
272
		if ( ! current_user_can( $cap ) ) {
273
			return new Jetpack_Error( 'scope', 'current_user_cannot', $code );
274
		}
275
276
		return (string) $json->access_token;
277
	}
278
279
	public function get_jetpack() {
280
		return Jetpack::init();
281
	}
282
283
	public function do_exit() {
284
		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...
285
	}
286
}
287