Changeset 9941
- Timestamp:
- 05/29/2020 03:14:48 AM (5 years ago)
- Location:
- sites/trunk/common/includes/wporg-sso
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/common/includes/wporg-sso/class-wporg-sso.php
r7695 r9941 2 2 if ( ! class_exists( 'WPOrg_SSO' ) ) { 3 3 /** 4 * Single Sign-On (SSO) handling for WordPress/bbPress instances under *.wordpress.org.4 * Single Sign-On (SSO) handling for WordPress/bbPress instances on wordpress.org. 5 5 * 6 6 * @author stephdau … … 10 10 11 11 const SUPPORT_EMAIL = 'forum-password-resets@wordpress.org'; 12 13 const VALID_HOSTS = [ 14 'wordpress.org', 15 'bbpress.org', 16 'buddypress.org', 17 'wordcamp.org' 18 ]; 12 19 13 20 public $sso_host_url; … … 76 83 } 77 84 85 if ( ! preg_match( '!wordpress\.org$!', $this->host ) ) { 86 $login_url = add_query_arg( 'from', $this->host, $login_url ); 87 } 88 78 89 return $login_url; 79 90 … … 117 128 // We didn't get a redirect_to, but we got a referrer, use that if a valid target. 118 129 $redirect_to_referrer = $_SERVER['HTTP_REFERER']; 119 if ( $this->_is_valid_targeted_domain( $redirect_to_referrer ) ) {130 if ( $this->_is_valid_targeted_domain( $redirect_to_referrer ) && self::SSO_HOST != parse_url( $redirect_to_referrer, PHP_URL_HOST ) ) { 120 131 $redirect_to = $redirect_to_referrer; 121 132 } 122 } else {133 } elseif ( self::SSO_HOST !== $this->host ) { 123 134 // Otherwise, attempt to guess the parent dir of where they came from and validate that. 124 135 $redirect_to_source_parent = preg_replace( '/\/[^\/]+\.php\??.*$/', '/', "https://{$this->host}{$_SERVER['REQUEST_URI']}" ); … … 132 143 133 144 /** 134 * Tests if the passed host/domain, or URL, is part of the WordPress.org domain.145 * Tests if the passed host/domain, or URL, is part of the WordPress.org network. 135 146 * 136 * @param unknown $ stringA domain, hostname, or URL147 * @param unknown $host A domain, hostname, or URL 137 148 * @return boolean True is ok, false if not 138 149 */ 139 protected function _is_valid_targeted_domain( $ string) {140 if ( empty( $ string ) || ! is_string( $string) ) {141 $string = '';150 protected function _is_valid_targeted_domain( $host ) { 151 if ( empty( $host ) || ! is_string( $host ) || ! strstr( $host, '.' ) ) { 152 return false; 142 153 } 143 154 144 if ( strstr( $string , '/' ) ) { 145 $url = parse_url( $string ); 146 $host = ( ! empty( $url['host'] ) ) ? $url['host'] : ''; 147 } else { 148 $host = $string; 155 if ( strstr( $host, '/' ) ) { 156 $host = parse_url( $host, PHP_URL_HOST ); 149 157 } 150 158 151 if ( ! empty( $host ) && strstr( $host , '.') ) {152 return ( preg_match( '/^(.+\.)?wordpress\.org$/', $host ) ) ? true : false;159 if ( in_array( $host, self::VALID_HOSTS, true ) ) { 160 return true; 153 161 } 154 162 155 return false; 163 // If not a top-level domain, shrink it down and try again. 164 $top_level_host = implode( '.', array_slice( explode( '.', $host ), -2 ) ); 165 166 return in_array( $top_level_host, self::VALID_HOSTS, true ); 156 167 } 157 168 -
sites/trunk/common/includes/wporg-sso/wp-plugin.php
r9833 r9941 67 67 add_filter( 'register_url', [ $this, 'add_locale' ], 21 ); 68 68 add_filter( 'lostpassword_url', [ $this, 'add_locale' ], 21 ); 69 } else { 70 add_filter( 'login_redirect', [ $this, 'maybe_add_remote_login_bounce_to_post_login_url' ], 10, 3 ); 69 71 } 70 72 } … … 186 188 if ( preg_match( '!/wp-login\.php$!', $this->script ) ) { 187 189 // Don't redirect the 'confirmaction' wp-login handlers to login.wordpress.org. 188 if ( isset( $_GET['action'] 190 if ( isset( $_GET['action'] ) && empty( $_POST ) && 'confirmaction' == $_GET['action'] ) { 189 191 return; 192 } 193 194 // Allow logout on non-dotorg hosts. 195 if ( isset( $_GET['action'] ) && empty( $_POST ) && 'logout' == $_GET['action'] ) { 196 if ( ! preg_match( '!wordpress\.org$!', $_SERVER['HTTP_HOST'] ) ) { 197 return; 198 } 199 } 200 201 // Remote SSO login? 202 if ( isset( $_GET['action'] ) && 'remote-login' === $_GET['action'] && ! empty( $_GET['sso_token'] ) ) { 203 $this->_maybe_perform_remote_login(); 190 204 } 191 205 … … 200 214 // Pay extra attention to the post-process redirect_to 201 215 $redirect_to_sso_login = add_query_arg( 'redirect_to', urlencode( $redirect_req ), $redirect_to_sso_login ); 216 if ( ! preg_match( '!wordpress\.org$!', $this->host ) ) { 217 $redirect_to_sso_login = add_query_arg( 'from', $this->host, $redirect_to_sso_login ); 218 } 202 219 203 220 // And actually redirect to the SSO login … … 244 261 // Else let the theme render, or redirect if logged in 245 262 if ( is_user_logged_in() ) { 246 $this->_redirect_to_ profile();263 $this->_redirect_to_source_or_profile(); 247 264 } else { 248 265 if ( empty( $_GET['screen'] ) ) { … … 259 276 } else if ( is_user_logged_in() ) { 260 277 // Otherwise, redirect to the their profile. 261 $this->_redirect_to_ profile();278 $this->_redirect_to_source_or_profile(); 262 279 } 263 280 } elseif ( ( is_admin() && is_super_admin() ) || 0 === strpos( $_SERVER['REQUEST_URI'], '/wp-json' ) || 0 === strpos( $_SERVER['REQUEST_URI'], '/xmlrpc.php' ) ) { … … 265 282 } elseif ( is_user_logged_in() ) { 266 283 // Logged in catch all, before last fallback 267 $this->_redirect_to_ profile();284 $this->_redirect_to_source_or_profile(); 268 285 } else { 269 286 // Otherwise, redirect to the login screen. … … 365 382 366 383 /** 367 * Redirects the user to her/his (support) profile.368 */ 369 protected function _redirect_to_ profile() {370 if ( ! is_user_logged_in() ) {371 return; 372 }373 374 if ( ! empty( $_GET['redirect_to']) ) {375 $this->_safe_redirect( $this->_get_safer_redirect_to());384 * Redirects the user back to where they came from (or w.org profile) 385 */ 386 protected function _redirect_to_source_or_profile() { 387 $redirect = $this->_get_safer_redirect_to(); 388 389 if ( $redirect ) { 390 $this->_safe_redirect( $this->_maybe_add_remote_login_bounce( $redirect ) ); 391 } elseif ( is_user_logged_in() ) { 392 $this->_safe_redirect( 'https://profiles.wordpress.org/' . wp_get_current_user()->user_nicename . '/' ); 376 393 } else { 377 $this->_safe_redirect( 'https://profiles.wordpress.org/' . wp_get_current_user()->user_nicename . '/' ); 378 } 394 $this->_safe_redirect( 'https://wordpress.org/' ); 395 } 396 } 397 398 /** 399 * Logs in a user on the current domain on a remote-login action. 400 */ 401 protected function _maybe_perform_remote_login() { 402 $remote_token = wp_unslash( $_GET['sso_token'] ); 403 if ( ! is_string( $remote_token ) || 3 !== substr_count( $remote_token, '|' ) ) { 404 wp_die( 'Invalid token.' ); 405 } 406 407 list( $user_id, $sso_hash, $valid_until, $remember_me ) = explode( '|', $remote_token, 4 ); 408 409 $remote_expiration_valid = ( 410 // +/- 5s on a 5s timeout. 411 $valid_until >= ( time() - 5 ) && 412 $valid_until <= ( time() + 10 ) 413 ); 414 415 $valid_remote_hash = false; 416 $user = get_user_by( 'id', $user_id ); 417 if ( $user ) { 418 $valid_remote_hash = hash_equals( 419 $this->_generate_remote_login_hash( $user, $valid_until, $remember_me ), 420 $sso_hash 421 ); 422 } 423 424 if ( $remote_expiration_valid && $valid_remote_hash ) { 425 wp_set_current_user( (int) $user_id ); 426 wp_set_auth_cookie( (int) $user_id, (bool) $remember_me ); 427 428 if ( isset( $_GET['redirect_to'] ) ) { 429 $this->_safe_redirect( wp_unslash( $_GET['redirect_to'] ) ); 430 } else { 431 $this->_safe_redirect( home_url( '/' ) ); 432 } 433 exit; 434 } 435 436 return false; 437 } 438 439 public function maybe_add_remote_login_bounce_to_post_login_url( $redirect, $requested, $user ) { 440 return $this->_maybe_add_remote_login_bounce( $redirect, $user ); 441 } 442 443 protected function _maybe_add_remote_login_bounce( $redirect, $user = false ) { 444 if ( ! $user ) { 445 $user = wp_get_current_user(); 446 } 447 448 // If it's on a different _supported_ host, bounce through the remote-login. 449 $redirect_host = parse_url( $redirect, PHP_URL_HOST ); 450 451 if ( $user && $this->_is_valid_targeted_domain( $redirect_host ) && ! preg_match( '!wordpress.org$!i', $redirect_host ) ) { 452 453 // Fetch auth cookie parts to find out if the user has selected 'remember me'. 454 $auth_cookie_parts = wp_parse_auth_cookie( '', 'secure_auth' ); 455 456 $valid_until = time() + 5; // Super short timeout. 457 $remember_me = ! empty( $_POST['rememberme'] ) || ( $auth_cookie_parts && $auth_cookie_parts['expiration'] >= ( time() + ( 2 * DAY_IN_SECONDS ) ) ); 458 459 $hash = $this->_generate_remote_login_hash( $user, $valid_until, $remember_me ); 460 $sso_token = $user->ID . '|' . $hash . '|' . $valid_until . '|' . $remember_me; 461 462 $redirect = add_query_arg( 463 array( 464 'action' => 'remote-login', 465 'sso_token' => urlencode( $sso_token ), 466 'redirect_to' => urlencode( $redirect ), 467 ), 468 'https://' . $redirect_host . '/wp-login.php' // Assume that wp-login exists and is accessible 469 ); 470 } 471 472 return $redirect; 473 } 474 475 /** 476 * Generate a hash for remote-login for non-wordpress.org domains 477 */ 478 protected function _generate_remote_login_hash( $user, $valid_until, $remember_me = false ) { 479 // re-use the same frag that Auth cookies use to invalidate sessions. 480 $pass_frag = substr( $user->user_pass, 8, 4 ); 481 $key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $valid_until ); 482 $hash = hash_hmac( 'sha256', $user->user_login . '|' . $valid_until . '|' . (int) $remember_me, $key ); 483 484 return $hash; 379 485 } 380 486 }
Note: See TracChangeset
for help on using the changeset viewer.