Completed
Push — add/user-authentication ( 34f7c8...802602 )
by Marin
06:45
created

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

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

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

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