Completed
Push — move-jetpack-client-to-connect... ( 762fb0 )
by
unknown
08:24
created

class.jetpack-client-server.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
use Automattic\Jetpack\Tracking;
4
5
/**
6
 * Client = Plugin
7
 * Client Server = API Methods the Plugin must respond to
8
 */
9
class Jetpack_Client_Server {
10
11
	/**
12
	 * Authorizations
13
	 */
14
	function client_authorize() {
15
		$data              = stripslashes_deep( $_GET );
16
		$data['auth_type'] = 'client';
17
		$role              = Jetpack::translate_current_user_to_role();
18
		$redirect          = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
19
20
		check_admin_referer( "jetpack-authorize_{$role}_{$redirect}" );
21
22
		$tracking = new Tracking();
23
		$result = $this->authorize( $data );
24
		if ( is_wp_error( $result ) ) {
25
			Jetpack::state( 'error', $result->get_error_code() );
26
27
			$tracking->record_user_event( 'jpc_client_authorize_fail', array(
28
				'error_code' => $result->get_error_code(),
29
				'error_message' => $result->get_error_message()
30
			) );
31
		} else {
32
			/**
33
			 * Fires after the Jetpack client is authorized to communicate with WordPress.com.
34
			 *
35
			 * @since 4.2.0
36
			 *
37
			 * @param int Jetpack Blog ID.
38
			 */
39
			do_action( 'jetpack_client_authorized', Jetpack_Options::get_option( 'id' ) );
40
		}
41
42
		if ( wp_validate_redirect( $redirect ) ) {
43
			// Exit happens below in $this->do_exit()
44
			wp_safe_redirect( $redirect );
45
		} else {
46
			// Exit happens below in $this->do_exit()
47
			wp_safe_redirect( Jetpack::admin_url() );
48
		}
49
50
		$tracking->record_user_event( 'jpc_client_authorize_success' );
51
52
		$this->do_exit();
53
	}
54
55
	function authorize( $data = array() ) {
56
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
57
58
		$jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
59
		// Checking if site has been active/connected previously before recording unique connection
60
		if ( ! $jetpack_unique_connection ) {
61
			// jetpack_unique_connection option has never been set
62
			$jetpack_unique_connection = array(
63
				'connected'     => 0,
64
				'disconnected'  => 0,
65
				'version'       => '3.6.1',
66
			);
67
68
			update_option( 'jetpack_unique_connection', $jetpack_unique_connection );
69
70
			//track unique connection
71
			$jetpack = $this->get_jetpack();
72
73
			$jetpack->stat( 'connections', 'unique-connection' );
74
			$jetpack->do_stats( 'server_side' );
75
		}
76
77
		// increment number of times connected
78
		$jetpack_unique_connection['connected'] += 1;
79
		Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
80
81
		$role = Jetpack::translate_current_user_to_role();
82
83
		if ( ! $role ) {
84
			return new Jetpack_Error( 'no_role', 'Invalid request.', 400 );
85
		}
86
87
		$cap = Jetpack::translate_role_to_cap( $role );
88
		if ( ! $cap ) {
89
			return new Jetpack_Error( 'no_cap', 'Invalid request.', 400 );
90
		}
91
92
		if ( ! empty( $data['error'] ) ) {
93
			return new Jetpack_Error( $data['error'], 'Error included in the request.', 400 );
94
		}
95
96
		if ( ! isset( $data['state'] ) ) {
97
			return new Jetpack_Error( 'no_state', 'Request must include state.', 400 );
98
		}
99
100
		if ( ! ctype_digit( $data['state'] ) ) {
101
			return new Jetpack_Error( $data['error'], 'State must be an integer.', 400 );
102
		}
103
104
		$current_user_id = get_current_user_id();
105
		if ( $current_user_id != $data['state'] ) {
106
			return new Jetpack_Error( 'wrong_state', 'State does not match current user.', 400 );
107
		}
108
109
		if ( empty( $data['code'] ) ) {
110
			return new Jetpack_Error( 'no_code', 'Request must include an authorization code.', 400 );
111
		}
112
113
		$token = $this->get_token( $data );
114
115 View Code Duplication
		if ( is_wp_error( $token ) ) {
116
			$code = $token->get_error_code();
117
			if ( empty( $code ) ) {
118
				$code = 'invalid_token';
119
			}
120
			return new Jetpack_Error( $code, $token->get_error_message(), 400 );
121
		}
122
123
		if ( ! $token ) {
124
			return new Jetpack_Error( 'no_token', 'Error generating token.', 400 );
125
		}
126
127
		$is_master_user = ! Jetpack::is_active();
128
129
		Jetpack::update_user_token( $current_user_id, sprintf( '%s.%d', $token, $current_user_id ), $is_master_user );
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
		// If this site has been through the Jetpack Onboarding flow, delete the onboarding token
138
		Jetpack::invalidate_onboarding_token();
139
140
		// If redirect_uri is SSO, ensure SSO module is enabled
141
		parse_str( parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
142
143
		/** This filter is documented in class.jetpack-cli.php */
144
		$jetpack_start_enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
145
146
		$activate_sso = (
147
			isset( $redirect_options['action'] ) &&
148
			'jetpack-sso' === $redirect_options['action'] &&
149
			$jetpack_start_enable_sso
150
		);
151
152
		$do_redirect_on_error = ( 'client' === $data['auth_type'] );
153
154
		Jetpack::handle_post_authorization_actions( $activate_sso, $do_redirect_on_error );
155
156
		return 'authorized';
157
	}
158
159
	public static function deactivate_plugin( $probable_file, $probable_title ) {
160
		include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
161
		if ( is_plugin_active( $probable_file ) ) {
162
			deactivate_plugins( $probable_file );
163
			return 1;
164
		} else {
165
			// If the plugin is not in the usual place, try looking through all active plugins.
166
			$active_plugins = Jetpack::get_active_plugins();
167
			foreach ( $active_plugins as $plugin ) {
168
				$data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
169
				if ( $data['Name'] == $probable_title ) {
170
					deactivate_plugins( $plugin );
171
					return 1;
172
				}
173
			}
174
		}
175
176
		return 0;
177
	}
178
179
	/**
180
	 * @return object|WP_Error
181
	 */
182
	function get_token( $data ) {
183
		$role = Jetpack::translate_current_user_to_role();
184
185
		if ( ! $role ) {
186
			return new Jetpack_Error( 'role', __( 'An administrator for this blog must set up the Jetpack connection.', 'jetpack' ) );
187
		}
188
189
		$client_secret = Jetpack_Data::get_access_token();
190
		if ( ! $client_secret ) {
191
			return new Jetpack_Error( 'client_secret', __( 'You need to register your Jetpack before connecting it.', 'jetpack' ) );
192
		}
193
194
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
195
		$redirect_uri = ( 'calypso' === $data['auth_type'] )
196
			? $data['redirect_uri']
197
			: add_query_arg( array(
198
				'action' => 'authorize',
199
				'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
200
				'redirect' => $redirect ? urlencode( $redirect ) : false,
201
			), menu_page_url( 'jetpack', false ) );
202
203
		// inject identity for analytics
204
		$tracks = new Automattic\Jetpack\Tracking();
205
		$tracks_identity = $tracks->tracks_get_identity( get_current_user_id() );
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
			'_ui' => $tracks_identity['_ui'],
214
			'_ut' => $tracks_identity['_ut'],
215
		);
216
217
		$args = array(
218
			'method' => 'POST',
219
			'body' => $body,
220
			'headers' => array(
221
				'Accept' => 'application/json',
222
			),
223
		);
224
		$response = Jetpack_Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'token' ) ), $args );
225
226
		if ( is_wp_error( $response ) ) {
227
			return new Jetpack_Error( 'token_http_request_failed', $response->get_error_message() );
0 ignored issues
show
The method get_error_message() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
228
		}
229
230
		$code = wp_remote_retrieve_response_code( $response );
231
		$entity = wp_remote_retrieve_body( $response );
232
233
		if ( $entity ) {
234
			$json = json_decode( $entity );
235
		} else {
236
			$json = false;
237
		}
238
239
		if ( 200 != $code || ! empty( $json->error ) ) {
240
			if ( empty( $json->error ) ) {
241
				return new Jetpack_Error( 'unknown', '', $code );
242
			}
243
244
			$error_description = isset( $json->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $json->error_description ) : '';
245
246
			return new Jetpack_Error( (string) $json->error, $error_description, $code );
247
		}
248
249
		if ( empty( $json->access_token ) || ! is_scalar( $json->access_token ) ) {
250
			return new Jetpack_Error( 'access_token', '', $code );
251
		}
252
253
		if ( empty( $json->token_type ) || 'X_JETPACK' != strtoupper( $json->token_type ) ) {
254
			return new Jetpack_Error( 'token_type', '', $code );
255
		}
256
257
		if ( empty( $json->scope ) ) {
258
			return new Jetpack_Error( 'scope', 'No Scope', $code );
259
		}
260
261
		@list( $role, $hmac ) = explode( ':', $json->scope );
262
		if ( empty( $role ) || empty( $hmac ) ) {
263
			return new Jetpack_Error( 'scope', 'Malformed Scope', $code );
264
		}
265
266
		if ( Jetpack::sign_role( $role ) !== $json->scope ) {
267
			return new Jetpack_Error( 'scope', 'Invalid Scope', $code );
268
		}
269
270
		if ( ! $cap = Jetpack::translate_role_to_cap( $role ) ) {
271
			return new Jetpack_Error( 'scope', 'No Cap', $code );
272
		}
273
274
		if ( ! current_user_can( $cap ) ) {
275
			return new Jetpack_Error( 'scope', 'current_user_cannot', $code );
276
		}
277
278
		/**
279
		 * Fires after user has successfully received an auth token.
280
		 *
281
		 * @since 3.9.0
282
		 */
283
		do_action( 'jetpack_user_authorized' );
284
285
		return (string) $json->access_token;
286
	}
287
288
	public function get_jetpack() {
289
		return Jetpack::init();
290
	}
291
292
	public function do_exit() {
293
		exit;
294
	}
295
}
296