Completed
Push — kraftbj-patch-1 ( 3ba27f...085235 )
by
unknown
09:39
created

Jetpack_SSO   D

Complexity

Total Complexity 121

Size/Duplication

Total Lines 954
Duplicated Lines 4.19 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 121
c 4
b 0
f 0
lcom 2
cbo 3
dl 40
loc 954
rs 4.4444

44 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 33 3
A get_instance() 0 7 2
A module_configure_button() 0 6 1
A module_configuration_load() 0 4 1
A module_configuration_head() 0 1 1
A module_configuration_screen() 0 9 1
A maybe_logout_user() 0 10 2
A xmlrpc_methods() 0 4 1
A xmlrpc_user_disconnect() 0 18 2
A login_enqueue_scripts() 0 10 2
A admin_enqueue_scripts() 0 9 3
A register_settings() 0 64 1
A render_require_two_step() 0 9 2
A validate_jetpack_sso_require_two_step() 0 3 2
A render_match_by_email() 0 8 2
A validate_jetpack_sso_match_by_email() 0 3 2
A render_remove_login_form_checkbox() 0 10 2
A validate_settings_remove_login_form_checkbox() 0 3 2
A remove_lost_password_text() 0 6 2
A wants_to_login() 0 15 4
A bypass_login_forward_wpcom() 0 12 1
C login_init() 0 56 10
B maybe_save_cookie_redirect() 0 21 6
A should_hide_login_form() 0 12 1
A login_form() 0 8 2
A delete_connection_for_user() 16 16 3
A request_initial_nonce() 13 13 2
F handle_login() 11 183 26
A profile_page_url() 0 3 1
A match_by_email() 0 15 3
A new_user_override() 0 14 2
A allowed_redirect_hosts() 0 8 2
A button() 0 16 2
A build_sso_url() 0 16 3
A get_user_by_wpcom_id() 0 10 2
A error_msg_enable_two_step() 0 7 1
A error_msg_email_already_exists() 0 7 1
A msg_login_by_jetpack() 0 18 1
A error_msg_login_method_not_allowed() 0 6 1
A cant_find_user() 0 10 2
A admin_init() 0 15 4
A is_user_connected() 0 3 1
A get_user_data() 0 3 1
B edit_profile_fields() 0 43 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_SSO often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_SSO, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Module Name: Single Sign On
5
 * Module Description: Secure user authentication.
6
 * Jumpstart Description: Lets you log in to all your Jetpack-enabled sites with one click using your WordPress.com account.
7
 * Sort Order: 30
8
 * Recommendation Order: 5
9
 * First Introduced: 2.6
10
 * Requires Connection: Yes
11
 * Auto Activate: No
12
 * Module Tags: Developers
13
 * Feature: Jumpstart, Performance-Security
14
 * Additional Search Queries: sso, single sign on, login, log in
15
 */
16
17
class Jetpack_SSO {
18
	static $instance = null;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $instance.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
19
20
	private function __construct() {
21
22
		self::$instance = $this;
23
24
		add_action( 'admin_init',  array( $this, 'admin_init' ) );
25
		add_action( 'admin_init',  array( $this, 'register_settings' ) );
26
		add_action( 'login_init',  array( $this, 'login_init' ) );
27
		add_action( 'delete_user', array( $this, 'delete_connection_for_user' ) );
28
		add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
29
		add_action( 'init', array( $this, 'maybe_logout_user' ), 5 );
30
		add_action( 'jetpack_modules_loaded', array( $this, 'module_configure_button' ) );
31
		add_action( 'login_enqueue_scripts', array( $this, 'login_enqueue_scripts' ) );
32
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
33
34
		// Adding this action so that on login_init, the action won't be sanitized out of the $action global.
35
		add_action( 'login_form_jetpack-sso', '__return_true' );
36
37
		if (
38
			$this->should_hide_login_form() &&
39
			/**
40
			 * Filter the display of the disclaimer message appearing when default WordPress login form is disabled.
41
			 *
42
			 * @module sso
43
			 *
44
			 * @since 2.8.0
45
			 *
46
			 * @param bool true Should the disclaimer be displayed. Default to true.
47
			 */
48
			apply_filters( 'jetpack_sso_display_disclaimer', true )
49
		) {
50
			add_action( 'login_message', array( $this, 'msg_login_by_jetpack' ) );
51
		}
52
	}
53
54
	/**
55
	 * Returns the single instance of the Jetpack_SSO object
56
	 *
57
	 * @since 2.8
58
	 * @return Jetpack_SSO
59
	 **/
60
	public static function get_instance() {
61
		if ( ! is_null( self::$instance ) ) {
62
			return self::$instance;
63
		}
64
65
		return self::$instance = new Jetpack_SSO;
66
	}
67
68
	/**
69
	 * Add configure button and functionality to the module card on the Jetpack screen
70
	 **/
71
	public static function module_configure_button() {
72
		Jetpack::enable_module_configurable( __FILE__ );
73
		Jetpack::module_configuration_load( __FILE__, array( __CLASS__, 'module_configuration_load' ) );
74
		Jetpack::module_configuration_head( __FILE__, array( __CLASS__, 'module_configuration_head' ) );
75
		Jetpack::module_configuration_screen( __FILE__, array( __CLASS__, 'module_configuration_screen' ) );
76
	}
77
78
	public static function module_configuration_load() {
79
		// wp_safe_redirect( admin_url( 'options-general.php#configure-sso' ) );
80
		// exit;
81
	}
82
83
	public static function module_configuration_head() {}
84
85
	public static function module_configuration_screen() {
86
		?>
87
		<form method="post" action="options.php">
88
			<?php settings_fields( 'jetpack-sso' ); ?>
89
			<?php do_settings_sections( 'jetpack-sso' ); ?>
90
			<?php submit_button(); ?>
91
		</form>
92
		<?php
93
	}
94
95
	/**
96
	 * If jetpack_force_logout == 1 in current user meta the user will be forced
97
	 * to logout and reauthenticate with the site.
98
	 **/
99
	public function maybe_logout_user() {
100
		global $current_user;
101
102
		if ( 1 == $current_user->jetpack_force_logout ) {
103
			delete_user_meta( $current_user->ID, 'jetpack_force_logout' );
104
			self::delete_connection_for_user( $current_user->ID );
105
			wp_logout();
106
			wp_safe_redirect( wp_login_url() );
107
		}
108
	}
109
110
111
	/**
112
	 * Adds additional methods the WordPress xmlrpc API for handling SSO specific features
113
	 *
114
	 * @param array $methods
115
	 * @return array
116
	 **/
117
	public function xmlrpc_methods( $methods ) {
118
		$methods['jetpack.userDisconnect'] = array( $this, 'xmlrpc_user_disconnect' );
119
		return $methods;
120
	}
121
122
	/**
123
	 * Marks a user's profile for disconnect from WordPress.com and forces a logout
124
	 * the next time the user visits the site.
125
	 **/
126
	public function xmlrpc_user_disconnect( $user_id ) {
127
		$user_query = new WP_User_Query(
128
			array(
129
				'meta_key' => 'wpcom_user_id',
130
				'meta_value' => $user_id,
131
			)
132
		);
133
		$user = $user_query->get_results();
134
		$user = $user[0];
135
136
		if ( $user instanceof WP_User ) {
0 ignored issues
show
Bug introduced by
The class WP_User does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
137
			$user = wp_set_current_user( $user->ID );
138
			update_user_meta( $user->ID, 'jetpack_force_logout', '1' );
139
			self::delete_connection_for_user( $user->ID );
140
			return true;
141
		}
142
		return false;
143
	}
144
145
	/**
146
	 * Enqueues scripts and styles necessary for SSO login.
147
	 */
148
	public function login_enqueue_scripts() {
149
		global $action;
150
151
		if ( ! in_array( $action, array( 'jetpack-sso', 'login' ) ) ) {
152
			return;
153
		}
154
155
		wp_enqueue_style( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login.css', JETPACK__PLUGIN_FILE ), array( 'login', 'genericons' ), JETPACK__VERSION );
156
		wp_enqueue_script( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login.js', JETPACK__PLUGIN_FILE ), array( 'jquery' ), JETPACK__VERSION );
157
	}
158
159
	/**
160
	 * Enqueue styles neceessary for Jetpack SSO on users' profiles
161
	 */
162
	public function admin_enqueue_scripts() {
163
		$screen = get_current_screen();
164
165
		if ( empty( $screen ) || ! in_array( $screen->base, array( 'edit-user', 'profile' ) ) ) {
166
			return;
167
		}
168
169
		wp_enqueue_style( 'jetpack-sso-profile', plugins_url( 'modules/sso/jetpack-sso-profile.css', JETPACK__PLUGIN_FILE ), array( 'genericons' ), JETPACK__VERSION );
170
	}
171
172
	/**
173
	 * Adds settings fields to Settings > General > Single Sign On that allows users to
174
	 * turn off the login form on wp-login.php
175
	 *
176
	 * @since 2.7
177
	 **/
178
	public function register_settings() {
179
180
		add_settings_section(
181
			'jetpack_sso_settings',
182
			__( 'Single Sign On' , 'jetpack' ),
183
			'__return_false',
184
			'jetpack-sso'
185
		);
186
187
		/*
188
		 * Settings > General > Single Sign On
189
		 * Checkbox for Remove default login form
190
		 */
191
		 /* Hide in 2.9
192
		register_setting(
193
			'general',
194
			'jetpack_sso_remove_login_form',
195
			array( $this, 'validate_settings_remove_login_form_checkbox' )
196
		);
197
198
		add_settings_field(
199
			'jetpack_sso_remove_login_form',
200
			__( 'Remove default login form?' , 'jetpack' ),
201
			array( $this, 'render_remove_login_form_checkbox' ),
202
			'general',
203
			'jetpack_sso_settings'
204
		);
205
		*/
206
207
		/*
208
		 * Settings > General > Single Sign On
209
		 * Require two step authentication
210
		 */
211
		register_setting(
212
			'jetpack-sso',
213
			'jetpack_sso_require_two_step',
214
			array( $this, 'validate_jetpack_sso_require_two_step' )
215
		);
216
217
		add_settings_field(
218
			'jetpack_sso_require_two_step',
219
			'', // __( 'Require Two-Step Authentication' , 'jetpack' ),
220
			array( $this, 'render_require_two_step' ),
221
			'jetpack-sso',
222
			'jetpack_sso_settings'
223
		);
224
225
		/*
226
		 * Settings > General > Single Sign On
227
		 */
228
		register_setting(
229
			'jetpack-sso',
230
			'jetpack_sso_match_by_email',
231
			array( $this, 'validate_jetpack_sso_match_by_email' )
232
		);
233
234
		add_settings_field(
235
			'jetpack_sso_match_by_email',
236
			'', // __( 'Match by Email' , 'jetpack' ),
237
			array( $this, 'render_match_by_email' ),
238
			'jetpack-sso',
239
			'jetpack_sso_settings'
240
		);
241
	}
242
243
	/**
244
	 * Builds the display for the checkbox allowing user to require two step
245
	 * auth be enabled on WordPress.com accounts before login. Displays in Settings > General
246
	 *
247
	 * @since 2.7
248
	 **/
249
	public function render_require_two_step() {
250
		/** This filter is documented in modules/sso.php */
251
		$require_two_step = ( 1 == apply_filters( 'jetpack_sso_require_two_step', get_option( 'jetpack_sso_require_two_step' ) ) );
252
		$disabled = $require_two_step ? ' disabled="disabled"' : '';
253
		echo '<label>';
254
		echo '<input type="checkbox" name="jetpack_sso_require_two_step" ' . checked( $require_two_step, true, false ) . "$disabled>";
255
		esc_html_e( 'Require Two-Step Authentication' , 'jetpack' );
256
		echo '</label>';
257
	}
258
259
	/**
260
	 * Validate the require  two step checkbox in Settings > General
261
	 *
262
	 * @since 2.7
263
	 * @return boolean
264
	 **/
265
	public function validate_jetpack_sso_require_two_step( $input ) {
266
		return ( ! empty( $input ) ) ? 1 : 0;
267
	}
268
269
	/**
270
	 * Builds the display for the checkbox allowing the user to allow matching logins by email
271
	 * Displays in Settings > General
272
	 *
273
	 * @since 2.9
274
	 **/
275
	public function render_match_by_email() {
276
		$match_by_email = 1 == $this->match_by_email();
277
		$disabled = $match_by_email ? ' disabled="disabled"' : '';
278
		echo '<label>';
279
		echo '<input type="checkbox" name="jetpack_sso_match_by_email"' . checked( $match_by_email, true, false ) . "$disabled>";
280
		esc_html_e( 'Match by Email', 'jetpack' );
281
		echo '</label>';
282
	}
283
284
	/**
285
	 * Validate the match by email check in Settings > General
286
	 *
287
	 * @since 2.9
288
	 * @return boolean
289
	 **/
290
	public function validate_jetpack_sso_match_by_email( $input ) {
291
		return ( ! empty( $input ) ) ? 1 : 0;
292
	}
293
294
	/**
295
	 * Builds the display for the checkbox allowing users to remove the default
296
	 * WordPress login form from wp-login.php. Displays in Settings > General
297
	 *
298
	 * @since 2.7
299
	 **/
300
	public function render_remove_login_form_checkbox() {
301
		if ( $this->is_user_connected( get_current_user_id() ) ) {
302
			echo '<a name="configure-sso"></a>';
303
			echo '<input type="checkbox" name="jetpack_sso_remove_login_form[remove_login_form]" ' . checked( 1 == get_option( 'jetpack_sso_remove_login_form' ), true, false ) . '>';
304
			echo '<p class="description">Removes default login form and disallows login via POST</p>';
305
		} else {
306
			echo 'Your account must be connected to WordPress.com before disabling the login form.';
307
			echo '<br/>' . $this->button();
308
		}
309
	}
310
311
	/**
312
	 * Validate settings input from Settings > General
313
	 *
314
	 * @since 2.7
315
	 * @return boolean
316
	 **/
317
	public function validate_settings_remove_login_form_checkbox( $input ) {
318
		return ( isset( $input['remove_login_form'] ) )? 1: 0;
319
	}
320
321
	/**
322
	 * Removes 'Lost your password?' text from the login form if user
323
	 * does not want to show the login form
324
	 *
325
	 * @since 2.7
326
	 * @return string
327
	 **/
328
	public function remove_lost_password_text( $text ) {
329
		if ( 'Lost your password?' == $text ) {
330
			$text = '';
331
		}
332
		return $text;
333
	}
334
335
	/**
336
	 * Checks to determine if the user wants to login on wp-login
337
	 *
338
	 * This function mostly exists to cover the exceptions to login
339
	 * that may exist as other parameters to $_GET[action] as $_GET[action]
340
	 * does not have to exist. By default WordPress assumes login if an action
341
	 * is not set, however this may not be true, as in the case of logout
342
	 * where $_GET[loggedout] is instead set
343
	 *
344
	 * @return boolean
345
	 **/
346
	private function wants_to_login() {
347
		$wants_to_login = false;
348
349
		// Cover default WordPress behavior
350
		$action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : 'login';
351
352
		// And now the exceptions
353
		$action = isset( $_GET['loggedout'] ) ? 'loggedout' : $action;
354
355
		if ( 'login' == $action ) {
356
			$wants_to_login = true;
357
		}
358
359
		return $wants_to_login;
360
	}
361
362
	private function bypass_login_forward_wpcom() {
363
		/**
364
		 * Redirect the site's log in form to WordPress.com's log in form.
365
		 *
366
		 * @module sso
367
		 *
368
		 * @since 3.1.0
369
		 *
370
		 * @param bool false Should the site's log in form be automatically forwarded to WordPress.com's log in form.
371
		 */
372
		return apply_filters( 'jetpack_sso_bypass_login_forward_wpcom', false );
373
	}
374
375
	function login_init() {
376
		global $action;
377
378
		/**
379
		 * If the user is attempting to logout AND the auto-forward to WordPress.com
380
		 * login is set then we need to ensure we do not auto-forward the user and get
381
		 * them stuck in an infinite logout loop.
382
		 */
383
		if ( isset( $_GET['loggedout'] ) && $this->bypass_login_forward_wpcom() ) {
384
			add_filter( 'jetpack_remove_login_form', '__return_true' );
385
			add_filter( 'gettext', array( $this, 'remove_lost_password_text' ) );
386
		}
387
388
		/**
389
		 * Check to see if the site admin wants to automagically forward the user
390
		 * to the WordPress.com login page AND  that the request to wp-login.php
391
		 * is not something other than login (Like logout!)
392
		 */
393
		if (
394
			$this->wants_to_login()
395
			&& $this->bypass_login_forward_wpcom()
396
		) {
397
			add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
398
			$this->maybe_save_cookie_redirect();
399
			wp_safe_redirect( $this->build_sso_url() );
400
		}
401
402
		if ( 'login' === $action ) {
403
			add_action( 'login_footer', array( $this, 'login_form' ) );
404
405
			/*
406
			if ( get_option( 'jetpack_sso_remove_login_form' ) ) {
407
				// Check to see if the user is attempting to login via the default login form.
408
				// If so we need to deny it and forward elsewhere.
409
				if( isset( $_REQUEST['wp-submit'] ) && 'Log In' == $_REQUEST['wp-submit'] ) {
410
					wp_die( 'Login not permitted by this method. ');
411
				}
412
				add_filter( 'gettext', array( $this, 'remove_lost_password_text' ) );
413
			}
414
			*/
415
		} elseif ( 'jetpack-sso' === $action ) {
416
			if ( isset( $_GET['result'], $_GET['user_id'], $_GET['sso_nonce'] ) && 'success' == $_GET['result'] ) {
417
				$this->handle_login();
418
				add_action( 'login_footer', array( $this, 'login_form' ) );
419
			} else {
420
				if ( Jetpack::check_identity_crisis() ) {
421
					wp_die( __( "Error: This site's Jetpack connection is currently experiencing problems.", 'jetpack' ) );
422
				} else {
423
					$this->maybe_save_cookie_redirect();
424
					// Is it wiser to just use wp_redirect than do this runaround to wp_safe_redirect?
425
					add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
426
					wp_safe_redirect( $this->build_sso_url() );
427
				}
428
			}
429
		}
430
	}
431
432
	/**
433
	 * Conditionally save the redirect_to url as a cookie.
434
	 */
435
	public static function maybe_save_cookie_redirect() {
436
		if ( headers_sent() ) {
437
			return new WP_Error( 'headers_sent', __( 'Cannot deal with cookie redirects, as headers are already sent.', 'jetpack' ) );
438
		}
439
440
		if ( ! empty( $_GET['redirect_to'] ) ) {
441
			// If we have something to redirect to
442
			$url = esc_url_raw( $_GET['redirect_to'] );
443
			setcookie( 'jetpack_sso_redirect_to', $url, time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
444
445
		} elseif ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
446
			// Otherwise, if it's already set, purge it.
447
			setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
448
		}
449
450
		if ( ! empty( $_GET['rememberme'] ) ) {
451
			setcookie( 'jetpack_sso_remember_me', '1', time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
452
		} elseif ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
453
			setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
454
		}
455
	}
456
457
	/**
458
	 * Determine if the login form should be hidden or not
459
	 *
460
	 * Method is private only because it is only used in this class so far.
461
	 * Feel free to change it later
462
	 *
463
	 * @return bool
464
	 **/
465
	private function should_hide_login_form() {
466
		/**
467
		 * Remove the default log in form, only leave the WordPress.com log in button.
468
		 *
469
		 * @module sso
470
		 *
471
		 * @since 3.1.0
472
		 *
473
		 * @param bool get_option( 'jetpack_sso_remove_login_form', false ) Should the default log in form be removed. Default to false.
474
		 */
475
		return apply_filters( 'jetpack_remove_login_form', get_option( 'jetpack_sso_remove_login_form', false ) );
476
	}
477
478
	function login_form() {
479
		$classes = '';
480
481
		if ( $this->should_hide_login_form() ) {
482
			$classes .= ' forced-sso';
483
		}
484
		echo '<div id="jetpack-sso-wrap" class="jetpack-sso-wrap' . $classes . '">' . $this->button() . '</div>';
485
	}
486
487 View Code Duplication
	static function delete_connection_for_user( $user_id ) {
488
		if ( ! $wpcom_user_id = get_user_meta( $user_id, 'wpcom_user_id', true ) ) {
489
			return;
490
		}
491
		Jetpack::load_xml_rpc_client();
492
		$xml = new Jetpack_IXR_Client( array(
493
			'user_id' => $user_id,
494
		) );
495
		$xml->query( 'jetpack.sso.removeUser', $wpcom_user_id );
496
497
		if ( $xml->isError() ) {
498
			return false;
499
		}
500
501
		return $xml->getResponse();
502
	}
503
504 View Code Duplication
	static function request_initial_nonce() {
505
		Jetpack::load_xml_rpc_client();
506
		$xml = new Jetpack_IXR_Client( array(
507
			'user_id' => get_current_user_id(),
508
		) );
509
		$xml->query( 'jetpack.sso.requestNonce' );
510
511
		if ( $xml->isError() ) {
512
			wp_die( sprintf( '%s: %s', $xml->getErrorCode(), $xml->getErrorMessage() ) );
513
		}
514
515
		return $xml->getResponse();
516
	}
517
518
	/**
519
	 * The function that actually handles the login!
520
	 */
521
	function handle_login() {
522
		$wpcom_nonce   = sanitize_key( $_GET['sso_nonce'] );
523
		$wpcom_user_id = (int) $_GET['user_id'];
524
		$result        = sanitize_key( $_GET['result'] );
0 ignored issues
show
Unused Code introduced by
$result 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...
525
526
		Jetpack::load_xml_rpc_client();
527
		$xml = new Jetpack_IXR_Client( array(
528
			'user_id' => get_current_user_id(),
529
		) );
530
		$xml->query( 'jetpack.sso.validateResult', $wpcom_nonce, $wpcom_user_id );
531
532
		if ( $xml->isError() ) {
533
			wp_die( sprintf( '%s: %s', $xml->getErrorCode(), $xml->getErrorMessage() ) );
534
		}
535
536
		$user_data = $xml->getResponse();
537
538
		if ( empty( $user_data ) ) {
539
			wp_die( __( 'Error, invalid response data.', 'jetpack' ) );
540
		}
541
542
		$user_data = (object) $user_data;
543
		$user = null;
544
545
		/**
546
		 * Fires before Jetpack's SSO modifies the log in form.
547
		 *
548
		 * @module sso
549
		 *
550
		 * @since 2.6.0
551
		 *
552
		 * @param object $user_data User login information.
553
		 */
554
		do_action( 'jetpack_sso_pre_handle_login', $user_data );
555
556
		/**
557
		 * Is it required to have 2-step authentication enabled on WordPress.com to use SSO?
558
		 *
559
		 * @module sso
560
		 *
561
		 * @since 2.8.0
562
		 *
563
		 * @param bool get_option( 'jetpack_sso_require_two_step' ) Does SSO require 2-step authentication?
564
		 */
565
		$require_two_step = apply_filters( 'jetpack_sso_require_two_step', get_option( 'jetpack_sso_require_two_step' ) );
566
		if ( $require_two_step && 0 == (int) $user_data->two_step_enabled ) {
567
			$this->user_data = $user_data;
0 ignored issues
show
Bug introduced by
The property user_data does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
568
			/** This filter is documented in core/src/wp-includes/pluggable.php */
569
			do_action( 'wp_login_failed', $user_data->login );
570
			add_action( 'login_message', array( $this, 'error_msg_enable_two_step' ) );
571
			return;
572
		}
573
574
		if ( isset( $_GET['state'] ) && ( 0 < strpos( $_GET['state'], '|' ) ) ) {
575
			list( $state, $nonce ) = explode( '|', $_GET['state'] );
576
577
			if ( wp_verify_nonce( $nonce, $state ) ) {
578
				if ( 'sso-link-user' == $state ) {
579
					$user = wp_get_current_user();
580
					update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
581
					add_filter( 'login_redirect', array( __CLASS__, 'profile_page_url' ) );
582
				}
583
			} else {
584
				wp_nonce_ays();
585
			}
586
		}
587
588
		if ( empty( $user ) ) {
589
			$user = $this->get_user_by_wpcom_id( $user_data->ID );
590
		}
591
592
		// If we don't have one by wpcom_user_id, try by the email?
593
		if ( empty( $user ) && self::match_by_email() ) {
594
			$user = get_user_by( 'email', $user_data->email );
595
			if ( $user ) {
596
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
597
			}
598
		}
599
600
		// If we've still got nothing, create the user.
601
		if ( empty( $user ) && ( get_option( 'users_can_register' ) || self::new_user_override() ) ) {
602
			// If not matching by email we still need to verify the email does not exist
603
			// or this blows up
604
			/**
605
			 * If match_by_email is true, we know the email doesn't exist, as it would have
606
			 * been found in the first pass.  If get_user_by( 'email' ) doesn't find the
607
			 * user, then we know that email is unused, so it's safe to add.
608
			 */
609
			if ( self::match_by_email() || ! get_user_by( 'email', $user_data->email ) ) {
610
				$username = $user_data->login;
611
612
				if ( username_exists( $username ) ) {
613
					$username = $user_data->login . '_' . $user_data->ID;
614
				}
615
616
				$tries = 0;
617
				while ( username_exists( $username ) ) {
618
					$username = $user_data->login . '_' . $user_data->ID . '_' . mt_rand();
619
					if ( $tries++ >= 5 ) {
620
						wp_die( __( "Error: Couldn't create suitable username.", 'jetpack' ) );
621
					}
622
				}
623
624
				$password = wp_generate_password( 20 );
625
				$user_id  = wp_create_user( $username, $password, $user_data->email );
626
				$user     = get_userdata( $user_id );
627
628
				$user->display_name = $user_data->display_name;
629
				$user->first_name   = $user_data->first_name;
630
				$user->last_name    = $user_data->last_name;
631
				$user->url          = $user_data->url;
632
				$user->description  = $user_data->description;
633
				wp_update_user( $user );
634
635
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
636
			} else {
637
				$this->user_data = $user_data;
638
				// do_action( 'wp_login_failed', $user_data->login );
639
				add_action( 'login_message', array( $this, 'error_msg_email_already_exists' ) );
640
				return;
641
			}
642
		}
643
644
		/**
645
		 * Fires after we got login information from WordPress.com.
646
		 *
647
		 * @module sso
648
		 *
649
		 * @since 2.6.0
650
		 *
651
		 * @param array $user WordPress.com User information.
652
		 * @param object $user_data User Login information.
653
		 */
654
		do_action( 'jetpack_sso_handle_login', $user, $user_data );
655
656
		if ( $user ) {
657
			// Cache the user's details, so we can present it back to them on their user screen.
658
			update_user_meta( $user->ID, 'wpcom_user_data', $user_data );
659
660
			$remember = false;
661 View Code Duplication
			if ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
662
				$remember = true;
663
				// And then purge it
664
				setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
665
			}
666
			/**
667
			 * Filter the remember me value.
668
			 *
669
			 * @module sso
670
			 *
671
			 * @since 2.8.0
672
			 *
673
			 * @param bool $remember Is the remember me option checked?
674
			 */
675
			$remember = apply_filters( 'jetpack_remember_login', $remember );
676
			wp_set_auth_cookie( $user->ID, $remember );
677
678
			/** This filter is documented in core/src/wp-includes/user.php */
679
			do_action( 'wp_login', $user->user_login, $user );
680
681
			$_request_redirect_to = isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '';
682
			$redirect_to = user_can( $user, 'edit_posts' ) ? admin_url() : self::profile_page_url();
683
684
			// If we have a saved redirect to request in a cookie
685 View Code Duplication
			if ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
686
				// Set that as the requested redirect to
687
				$redirect_to = $_request_redirect_to = esc_url_raw( $_COOKIE['jetpack_sso_redirect_to'] );
688
				// And then purge it
689
				setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
690
			}
691
692
			wp_safe_redirect(
693
				/** This filter is documented in core/src/wp-login.php */
694
				apply_filters( 'login_redirect', $redirect_to, $_request_redirect_to, $user )
695
			);
696
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method handle_login() 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...
697
		}
698
699
		$this->user_data = $user_data;
700
		/** This filter is documented in core/src/wp-includes/pluggable.php */
701
		do_action( 'wp_login_failed', $user_data->login );
702
		add_action( 'login_message', array( $this, 'cant_find_user' ) );
703
	}
704
705
	static function profile_page_url() {
706
		return admin_url( 'profile.php' );
707
	}
708
709
	static function match_by_email() {
710
		$match_by_email = ( 1 == get_option( 'jetpack_sso_match_by_email', true ) ) ? true: false;
711
		$match_by_email = defined( 'WPCC_MATCH_BY_EMAIL' ) ? WPCC_MATCH_BY_EMAIL : $match_by_email;
712
713
		/**
714
		 * Link the local account to an account on WordPress.com using the same email address.
715
		 *
716
		 * @module sso
717
		 *
718
		 * @since 2.6.0
719
		 *
720
		 * @param bool $match_by_email Should we link the local account to an account on WordPress.com using the same email address. Default to false.
721
		 */
722
		return apply_filters( 'jetpack_sso_match_by_email', $match_by_email );
723
	}
724
725
	static function new_user_override() {
726
		$new_user_override = defined( 'WPCC_NEW_USER_OVERRIDE' ) ? WPCC_NEW_USER_OVERRIDE : false;
727
728
		/**
729
		 * Allow users to register on your site with a WordPress.com account, even though you disallow normal registrations.
730
		 *
731
		 * @module sso
732
		 *
733
		 * @since 2.6.0
734
		 *
735
		 * @param bool $new_user_override Allow users to register on your site with a WordPress.com account. Default to false.
736
		 */
737
		return apply_filters( 'jetpack_sso_new_user_override', $new_user_override );
738
	}
739
740
	function allowed_redirect_hosts( $hosts ) {
741
		if ( empty( $hosts ) ) {
742
			$hosts = array();
743
		}
744
		$hosts[] = 'wordpress.com';
745
746
		return array_unique( $hosts );
747
	}
748
749
	function button( $args = array() ) {
750
		$defaults = array(
751
			'action'  => 'jetpack-sso',
752
		);
753
754
		$args = wp_parse_args( $args, $defaults );
755
756
		if ( ! empty( $_GET['redirect_to'] ) ) {
757
			$args['redirect_to'] = esc_url_raw( $_GET['redirect_to'] );
758
		}
759
760
		$url  = add_query_arg( $args, wp_login_url() );
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
761
762
		$button = sprintf( '<a href="%1$s" class="jetpack-sso button">%2$s</a>', esc_url( $url ), esc_html__( 'Log in with WordPress.com', 'jetpack' ) );
763
		return $button;
764
	}
765
766
	function build_sso_url( $args = array() ) {
767
		$defaults = array(
768
			'action'    => 'jetpack-sso',
769
			'site_id'   => Jetpack_Options::get_option( 'id' ),
770
			'sso_nonce' => self::request_initial_nonce(),
771
		);
772
773
		if ( isset( $_GET['state'] ) && check_admin_referer( $_GET['state'] ) ) {
774
			$defaults['state'] = rawurlencode( $_GET['state'] . '|' . $_GET['_wpnonce'] );
775
		}
776
777
		$args = wp_parse_args( $args, $defaults );
778
		$url  = add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
779
780
		return $url;
781
	}
782
783
	/**
784
	 * Determines local user associated with a given WordPress.com user ID.
785
	 *
786
	 * @since 2.6.0
787
	 *
788
	 * @param int $wpcom_user_id User ID from WordPress.com
789
	 * @return object Local user object if found, null if not.
790
	 */
791
	static function get_user_by_wpcom_id( $wpcom_user_id ) {
792
		$user_query = new WP_User_Query( array(
793
			'meta_key'   => 'wpcom_user_id',
794
			'meta_value' => intval( $wpcom_user_id ),
795
			'number'     => 1,
796
		) );
797
798
		$users = $user_query->get_results();
799
		return $users ? array_shift( $users ) : null;
800
	}
801
802
	/**
803
	 * Error message displayed on the login form when two step is required and
804
	 * the user's account on WordPress.com does not have two step enabled.
805
	 *
806
	 * @since 2.7
807
	 * @param string $message
808
	 * @return string
809
	 **/
810
	public function error_msg_enable_two_step( $message ) {
811
		$err = __( sprintf( 'This site requires two step authentication be enabled for your user account on WordPress.com. Please visit the <a href="%1$s" target="_blank"> Security Settings</a> page to enable two step', 'https://wordpress.com/me/security/two-step' ) , 'jetpack' );
812
813
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
814
815
		return $message;
816
	}
817
818
	/**
819
	 * Error message displayed when the user tries to SSO, but match by email
820
	 * is off and they already have an account with their email address on
821
	 * this site.
822
	 *
823
	 * @param string $message
824
	 * @return string
825
	 */
826
	public function error_msg_email_already_exists( $message ) {
827
		$err = __( sprintf( 'You already have an account on this site. Please visit your <a href="%1$s">profile page</a> page to link your account to WordPress.com!', admin_url( 'profile.php' ) ) , 'jetpack' );
828
829
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
830
831
		return $message;
832
	}
833
834
	/**
835
	 * Message displayed when the site admin has disabled the default WordPress
836
	 * login form in Settings > General > Single Sign On
837
	 *
838
	 * @since 2.7
839
	 * @param string $message
840
	 * @return string
841
	 **/
842
	public function msg_login_by_jetpack( $message ) {
843
844
		$msg = __( sprintf( 'Jetpack authenticates through WordPress.com — to log in, enter your WordPress.com username and password, or <a href="%1$s" target="_blank">visit WordPress.com</a> to create a free account now.', 'http://wordpress.com/signup' ) , 'jetpack' );
845
846
		/**
847
		 * Filter the message displayed when the default WordPress login form is disabled.
848
		 *
849
		 * @module sso
850
		 *
851
		 * @since 2.8.0
852
		 *
853
		 * @param string $msg Disclaimer when default WordPress login form is disabled.
854
		 */
855
		$msg = apply_filters( 'jetpack_sso_disclaimer_message', $msg );
856
857
		$message .= sprintf( '<p class="message">%s</p>', $msg );
858
		return $message;
859
	}
860
861
	/**
862
	 * Error message displayed on the login form when the user attempts
863
	 * to post to the login form and it is disabled.
864
	 *
865
	 * @since 2.8
866
	 * @param string $message
867
	 * @param string
868
	 **/
869
	public function error_msg_login_method_not_allowed( $message ) {
870
		$err = __( 'Login method not allowed' , 'jetpack' );
871
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
872
873
		return $message;
874
	}
875
	function cant_find_user( $message ) {
876
		if ( self::match_by_email() ) {
877
			$err_format = __( 'We couldn\'t find an account with the email <strong><code>%1$s</code></strong> to log you in with.  If you already have an account on <strong>%2$s</strong>, please make sure that <strong><code>%1$s</code></strong> is configured as the email address, or that you have connected to WordPress.com on your profile page.', 'jetpack' );
878
		} else {
879
			$err_format = __( 'We couldn\'t find any account on <strong>%2$s</strong> that is linked to your WordPress.com account to log you in with.  If you already have an account on <strong>%2$s</strong>, please make sure that you have connected to WordPress.com on your profile page.', 'jetpack' );
880
		}
881
		$err = sprintf( $err_format, $this->user_data->email, get_bloginfo( 'name' ) );
882
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
883
		return $message;
884
	}
885
886
	/**
887
	 * Deal with user connections...
888
	 */
889
	function admin_init() {
890
		add_action( 'show_user_profile', array( $this, 'edit_profile_fields' ) ); // For their own profile
891
		add_action( 'edit_user_profile', array( $this, 'edit_profile_fields' ) ); // For folks editing others profiles
892
893
		if ( isset( $_GET['jetpack_sso'] ) && 'purge' == $_GET['jetpack_sso'] && check_admin_referer( 'jetpack_sso_purge' ) ) {
894
			$user = wp_get_current_user();
895
			// Remove the connection on the wpcom end.
896
			self::delete_connection_for_user( $user->ID );
897
			// Clear it locally.
898
			delete_user_meta( $user->ID, 'wpcom_user_id' );
899
			delete_user_meta( $user->ID, 'wpcom_user_data' );
900
			// Forward back to the profile page.
901
			wp_safe_redirect( remove_query_arg( array( 'jetpack_sso', '_wpnonce' ) ) );
902
		}
903
	}
904
905
	/**
906
	 * Determines if a local user is connected to WordPress.com
907
	 *
908
	 * @since 2.8
909
	 * @param integer $user_id - Local user id
910
	 * @return boolean
911
	 **/
912
	public function is_user_connected( $user_id ) {
913
		return $this->get_user_data( $user_id );
914
	}
915
916
	/**
917
	 * Retrieves a user's WordPress.com data
918
	 *
919
	 * @since 2.8
920
	 * @param integer $user_id - Local user id
921
	 * @return mixed null or stdClass
922
	 **/
923
	public function get_user_data( $user_id ) {
924
		return get_user_meta( $user_id, 'wpcom_user_data', true );
925
	}
926
927
	function edit_profile_fields( $user ) {
928
		?>
929
930
		<h3 id="single-sign-on"><?php _e( 'Single Sign On', 'jetpack' ); ?></h3>
931
		<p><?php _e( 'Connecting with Single Sign On enables you to log in via your WordPress.com account.', 'jetpack' ); ?></p>
932
933
		<?php if ( $this->is_user_connected( $user->ID ) ) : /* If the user is currently connected... */ ?>
934
			<?php $user_data = $this->get_user_data( $user->ID ); ?>
935
			<table class="form-table jetpack-sso-form-table">
936
				<tbody>
937
					<tr>
938
						<td>
939
							<div class="profile-card">
940
								<?php echo get_avatar( $user_data->email ); ?>
941
								<p class="connected"><strong><?php _e( 'Connected', 'jetpack' ); ?></strong></p>
942
								<p><?php echo esc_html( $user_data->login ); ?></p>
943
								<span class="two_step">
944
									<?php
945
										if ( $user_data->two_step_enabled ) {
946
											?> <p class="enabled"><a href="https://wordpress.com/me/security/two-step" target="_blank"><?php _e( 'Two-Step Authentication Enabled', 'jetpack' ); ?></a></p> <?php
947
										} else {
948
											?> <p class="disabled"><a href="https://wordpress.com/me/security/two-step" target="_blank"><?php _e( 'Two-Step Authentication Disabled', 'jetpack' ); ?></a></p> <?php
949
										}
950
									?>
951
								</span>
952
953
							</div>
954
							<p><a class="button button-secondary" href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'jetpack_sso', 'purge' ), 'jetpack_sso_purge' ) ); ?>"><?php _e( 'Unlink This Account', 'jetpack' ); ?></a></p>
955
						</td>
956
					</tr>
957
				</tbody>
958
			</table>
959
		<?php elseif ( get_current_user_id() == $user->ID && Jetpack::is_user_connected( $user->ID ) ) : ?>
960
961
			<?php echo $this->button( 'state=sso-link-user&_wpnonce=' . wp_create_nonce( 'sso-link-user' ) ); // update ?>
0 ignored issues
show
Documentation introduced by
'state=sso-link-user&_wp..._nonce('sso-link-user') is of type string, but the function expects a array.

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...
962
963
		<?php else : ?>
964
965
			<p><?php esc_html_e( wptexturize( __( "If you don't have a WordPress.com account yet, you can sign up for free in just a few seconds.", 'jetpack' ) ) ); ?></p>
966
			<a href="<?php echo Jetpack::init()->build_connect_url( false, get_edit_profile_url( get_current_user_id() ) . '#single-sign-on' ); ?>" class="button button-connector" id="wpcom-connect"><?php esc_html_e( 'Link account with WordPress.com', 'jetpack' ); ?></a>
967
968
		<?php endif;
969
	}
970
}
971
972
Jetpack_SSO::get_instance();
973