Completed
Push — add/protect-module-sync-packag... ( ad899a...19deca )
by
unknown
133:23 queued 125:08
created

_inc/class.jetpack-provision.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 //phpcs:ignore
2
3
use Automattic\Jetpack\Connection\Client;
4
use Automattic\Jetpack\Sync\Actions;
5
6
class Jetpack_Provision { //phpcs:ignore
7
8
	/**
9
	 * Responsible for checking pre-conditions, registering site, and returning an array of details
10
	 * that can be used to provision a plan for the site.
11
	 *
12
	 * @param array $named_args The array of arguments.
13
	 *
14
	 * @return WP_Error|array
15
	 */
16
	public static function register_and_build_request_body( $named_args ) {
17
		$url_args = array(
18
			'home_url' => 'WP_HOME',
19
			'site_url' => 'WP_SITEURL',
20
		);
21
22
		foreach ( $url_args as $url_arg => $constant_name ) {
23
			// Anonymous functions were introduced in 5.3.0. So, if we're running on
24
			// >= 5.3.0, use an anonymous function to set the home/siteurl value%s.
25
			//
26
			// Otherwise, fallback to setting the home/siteurl value via the WP_HOME and
27
			// WP_SITEURL constants if the constant hasn't already been defined.
28
			if ( isset( $named_args[ $url_arg ] ) ) {
29
				if ( version_compare( phpversion(), '5.3.0', '>=' ) ) {
30
					add_filter( $url_arg, function() use ( $url_arg, $named_args ) { // phpcs:ignore PHPCompatibility.FunctionDeclarations.NewClosure.Found
31
						return $named_args[ $url_arg ];
32
					}, 11 );
33
				} elseif ( ! defined( $constant_name ) ) {
34
					define( $constant_name, $named_args[ $url_arg ] );
35
				}
36
			}
37
		}
38
39
		// If Jetpack is currently connected, and is not in Safe Mode already, kick off a sync of the current
40
		// functions/callables so that we can test if this site is in IDC.
41
		if ( Jetpack::is_active() && ! Jetpack::validate_sync_error_idc_option() && Actions::sync_allowed() ) {
42
			Actions::do_full_sync( array( 'functions' => true ) );
43
			Actions::$sender->do_full_sync();
0 ignored issues
show
The property sender cannot be accessed from this context as it is declared private in class Automattic\Jetpack\Sync\Actions.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
44
		}
45
46
		if ( Jetpack::validate_sync_error_idc_option() ) {
47
			return new WP_Error(
48
				'site_in_safe_mode',
49
				__( 'Can not provision a plan while in safe mode. See: https://jetpack.com/support/safe-mode/', 'jetpack' )
50
			);
51
		}
52
53
		$blog_id    = Jetpack_Options::get_option( 'id' );
54
		$blog_token = Jetpack_Data::get_access_token();
55
56
		if ( ! $blog_id || ! $blog_token || ( isset( $named_args['force_register'] ) && intval( $named_args['force_register'] ) ) ) {
57
			// This code mostly copied from Jetpack::admin_page_load.
58
			Jetpack::maybe_set_version_option();
59
			$registered = Jetpack::try_registration();
60
			if ( is_wp_error( $registered ) ) {
61
				return $registered;
62
			} elseif ( ! $registered ) {
63
				return new WP_Error( 'registration_error', __( 'There was an unspecified error registering the site', 'jetpack' ) );
64
			}
65
66
			$blog_id    = Jetpack_Options::get_option( 'id' );
67
			$blog_token = Jetpack_Data::get_access_token();
68
		}
69
70
		// If the user isn't specified, but we have a current master user, then set that to current user.
71
		$master_user_id = Jetpack_Options::get_option( 'master_user' );
72
		if ( ! get_current_user_id() && $master_user_id ) {
73
			wp_set_current_user( $master_user_id );
74
		}
75
76
		$site_icon = get_site_icon_url();
77
78
		$auto_enable_sso = ( ! Jetpack::is_active() || Jetpack::is_module_active( 'sso' ) );
79
80
		/** This filter is documented in class.jetpack-cli.php */
81 View Code Duplication
		if ( apply_filters( 'jetpack_start_enable_sso', $auto_enable_sso ) ) {
82
			$redirect_uri = add_query_arg(
83
				array(
84
					'action'      => 'jetpack-sso',
85
					'redirect_to' => rawurlencode( admin_url() ),
86
				),
87
				wp_login_url() // TODO: come back to Jetpack dashboard?
88
			);
89
		} else {
90
			$redirect_uri = admin_url();
91
		}
92
93
		$request_body = array(
94
			'jp_version'   => JETPACK__VERSION,
95
			'redirect_uri' => $redirect_uri,
96
		);
97
98
		if ( $site_icon ) {
99
			$request_body['site_icon'] = $site_icon;
100
		}
101
102
		if ( get_current_user_id() ) {
103
			$user = wp_get_current_user();
104
105
			// Role.
106
			$role        = Jetpack::translate_current_user_to_role();
107
			$signed_role = Jetpack::sign_role( $role );
108
109
			$secrets = Jetpack::init()->generate_secrets( 'authorize' );
110
111
			// Jetpack auth stuff.
112
			$request_body['scope']  = $signed_role;
113
			$request_body['secret'] = $secrets['secret_1'];
114
115
			// User stuff.
116
			$request_body['user_id']    = $user->ID;
117
			$request_body['user_email'] = $user->user_email;
118
			$request_body['user_login'] = $user->user_login;
119
		}
120
121
		// Optional additional params.
122 View Code Duplication
		if ( isset( $named_args['wpcom_user_id'] ) && ! empty( $named_args['wpcom_user_id'] ) ) {
123
			$request_body['wpcom_user_id'] = $named_args['wpcom_user_id'];
124
		}
125
126
		// Override email of selected user.
127 View Code Duplication
		if ( isset( $named_args['wpcom_user_email'] ) && ! empty( $named_args['wpcom_user_email'] ) ) {
128
			$request_body['user_email'] = $named_args['wpcom_user_email'];
129
		}
130
131 View Code Duplication
		if ( isset( $named_args['plan'] ) && ! empty( $named_args['plan'] ) ) {
132
			$request_body['plan'] = $named_args['plan'];
133
		}
134
135 View Code Duplication
		if ( isset( $named_args['onboarding'] ) && ! empty( $named_args['onboarding'] ) ) {
136
			$request_body['onboarding'] = intval( $named_args['onboarding'] );
137
		}
138
139 View Code Duplication
		if ( isset( $named_args['force_connect'] ) && ! empty( $named_args['force_connect'] ) ) {
140
			$request_body['force_connect'] = intval( $named_args['force_connect'] );
141
		}
142
143
		if ( isset( $request_body['onboarding'] ) && (bool) $request_body['onboarding'] ) {
144
			Jetpack::create_onboarding_token();
145
		}
146
147
		return $request_body;
148
	}
149
150
	/**
151
	 * Given an access token and an array of arguments, will provision a plan for this site.
152
	 *
153
	 * @param string $access_token The access token from the partner.
154
	 * @param array  $named_args   The arguments used for registering the site and then provisioning a plan.
155
	 *
156
	 * @return WP_Error|array
157
	 */
158
	public static function partner_provision( $access_token, $named_args ) {
159
		// First, verify the token.
160
		$verify_response = self::verify_token( $access_token );
161
162
		if ( is_wp_error( $verify_response ) ) {
163
			return $verify_response;
164
		}
165
166
		$request_body = self::register_and_build_request_body( $named_args );
167
		if ( is_wp_error( $request_body ) ) {
168
			return $request_body;
169
		}
170
171
		$request = array(
172
			'headers' => array(
173
				'Authorization' => "Bearer $access_token",
174
				'Host'          => 'public-api.wordpress.com',
175
			),
176
			'timeout' => 60,
177
			'method'  => 'POST',
178
			'body'    => wp_json_encode( $request_body ),
179
		);
180
181
		$blog_id = Jetpack_Options::get_option( 'id' );
182
		$url     = esc_url_raw( sprintf(
183
			'https://%s/rest/v1.3/jpphp/%d/partner-provision',
184
			self::get_api_host(),
185
			$blog_id
186
		) );
187 View Code Duplication
		if ( ! empty( $named_args['partner_tracking_id'] ) ) {
188
			$url = esc_url_raw( add_query_arg( 'partner_tracking_id', $named_args['partner_tracking_id'], $url ) );
189
		}
190
191
		// Add calypso env if set.
192
		$calypso_env = Jetpack::get_calypso_env();
193
		if ( ! empty( $calypso_env ) ) {
194
			$url = add_query_arg( array( 'calypso_env' => $calypso_env ), $url );
195
		}
196
197
		$result = Client::_wp_remote_request( $url, $request );
198
199
		if ( is_wp_error( $result ) ) {
200
			return $result;
201
		}
202
203
		$response_code = wp_remote_retrieve_response_code( $result );
204
		$body_json     = json_decode( wp_remote_retrieve_body( $result ) );
205
206 View Code Duplication
		if ( 200 !== $response_code ) {
207
			if ( isset( $body_json->error ) ) {
208
				return new WP_Error( $body_json->error, $body_json->message );
209
			} else {
210
				return new WP_Error(
211
					'server_error',
212
					/* translators: %s is an HTTP status code retured from an API request. Ex. – 400 */
213
					sprintf( __( 'Request failed with code %s', 'jetpack' ), $response_code )
214
				);
215
			}
216
		}
217
218
		if ( isset( $body_json->access_token ) && is_user_logged_in() ) {
219
			// Check if this matches the existing token before replacing.
220
			$existing_token = Jetpack_Data::get_access_token( get_current_user_id() );
221
			if ( empty( $existing_token ) || $existing_token->secret !== $body_json->access_token ) {
222
				self::authorize_user( get_current_user_id(), $body_json->access_token );
223
			}
224
		}
225
226
		return $body_json;
227
	}
228
229
	private static function authorize_user( $user_id, $access_token ) {
230
		// authorize user and enable SSO
231
		Jetpack::update_user_token( $user_id, sprintf( '%s.%d', $access_token, $user_id ), true );
232
233
		/**
234
		 * Auto-enable SSO module for new Jetpack Start connections
235
		 *
236
		 * @since 5.0.0
237
		 *
238
		 * @param bool $enable_sso Whether to enable the SSO module. Default to true.
239
		 */
240
		$other_modules = apply_filters( 'jetpack_start_enable_sso', true )
241
			? array( 'sso' )
242
			: array();
243
244
		if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
245
			Jetpack::delete_active_modules();
246
			Jetpack::activate_default_modules( 999, 1, array_merge( $active_modules, $other_modules ), false );
247
		} else {
248
			Jetpack::activate_default_modules( false, false, $other_modules, false );
249
		}
250
	}
251
252
	private static function verify_token( $access_token ) {
253
		$request = array(
254
			'headers' => array(
255
				'Authorization' => "Bearer " . $access_token,
256
				'Host'          => 'public-api.wordpress.com',
257
			),
258
			'timeout' => 10,
259
			'method'  => 'POST',
260
			'body'    => ''
261
		);
262
263
		$url = sprintf( 'https://%s/rest/v1.3/jpphp/partner-keys/verify', self::get_api_host() );
264
		$result = Client::_wp_remote_request( $url, $request );
265
266
		if ( is_wp_error( $result ) ) {
267
			return $result;
268
		}
269
270
		$response_code = wp_remote_retrieve_response_code( $result );
271
		$body_json     = json_decode( wp_remote_retrieve_body( $result ) );
272
273 View Code Duplication
		if( 200 !== $response_code ) {
274
			if ( isset( $body_json->error ) ) {
275
				return new WP_Error( $body_json->error, $body_json->message );
276
			} else {
277
				return new WP_Error( 'server_error', sprintf( __( 'Request failed with code %s', 'jetpack' ), $response_code ) );
278
			}
279
		}
280
281
		return true;
282
	}
283
284
	private static function get_api_host() {
285
		$env_api_host = getenv( 'JETPACK_START_API_HOST', true ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.getenv_local_onlyFound
286
		return $env_api_host ? $env_api_host : JETPACK__WPCOM_JSON_API_HOST;
287
	}
288
}
289