PluginProbe
Authorizer / 3.6.0
Authorizer v3.6.0
3.15.3 3.15.2 3.15.1 3.15.0 3.14.3 3.14.4 3.14.2 3.14.1 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.9.0 2.9.1 2.9.10 2.9.11 2.9.12 2.9.13 2.9.2 2.9.3 2.9.6 All 126 releases
← All changes | src/authorizer/class-authorization.php +116 -54 2.9.63.6.0 View file →
@@ -14,9 +14,9 @@
14 14
15 15 /**
16 16 * Implements the authorization (roles and permissions) features of the plugin.
17 17 */
18 -class Authorization extends Static_Instance {
18 +class Authorization extends Singleton {
19 19
20 20 /**
21 21 * This function will fail with a wp_die() message to the user if they
22 22 * don't have access.
@@ -23,9 +23,10 @@
23 23 *
24 24 * @param WP_User $user User to check.
25 25 * @param array $user_emails Array of user's plaintext emails (in case current user doesn't have a WP account).
26 26 * @param array $user_data Array of keys for email, username, first_name, last_name,
27 - * authenticated_by, google_attributes, cas_attributes, ldap_attributes.
27 + * authenticated_by, google_attributes, cas_attributes, ldap_attributes,
28 + * oauth2_attributes.
28 29 * @return WP_Error|WP_User
29 30 * WP_Error if there was an error on user creation / adding user to blog.
30 31 * WP_Error / wp_die() if user does not have access.
31 32 * WP_User if user has access.
@@ -45,8 +46,40 @@
45 46 $auth_settings_access_users_approved_multi
46 47 )
47 48 );
48 49
50 + // Detect whether this user's first and last name should be updated below
51 + // (if the external CAS/LDAP service provides a different value, the option
52 + // is set to update it, and it's empty if the option to only set it if empty
53 + // is enabled).
54 + $should_update_first_name =
55 + $user && ! empty( $user_data['first_name'] ) && $user_data['first_name'] !== $user->first_name &&
56 + (
57 + (
58 + ! empty( $user_data['authenticated_by'] ) && 'cas' === $user_data['authenticated_by'] &&
59 + ! empty( $auth_settings['cas_attr_update_on_login'] ) &&
60 + ( '1' === $auth_settings['cas_attr_update_on_login'] || ( 'update-if-empty' === $auth_settings['cas_attr_update_on_login'] && empty( $user->first_name ) ) )
61 + ) || (
62 + ! empty( $user_data['authenticated_by'] ) && 'ldap' === $user_data['authenticated_by'] &&
63 + ! empty( $auth_settings['ldap_attr_update_on_login'] ) &&
64 + ( '1' === $auth_settings['ldap_attr_update_on_login'] || ( 'update-if-empty' === $auth_settings['ldap_attr_update_on_login'] && empty( $user->first_name ) ) )
65 + )
66 + );
67 +
68 + $should_update_last_name =
69 + $user && ! empty( $user_data['last_name'] ) && $user_data['last_name'] !== $user->last_name &&
70 + (
71 + (
72 + ! empty( $user_data['authenticated_by'] ) && 'cas' === $user_data['authenticated_by'] &&
73 + ! empty( $auth_settings['cas_attr_update_on_login'] ) &&
74 + ( '1' === $auth_settings['cas_attr_update_on_login'] || ( 'update-if-empty' === $auth_settings['cas_attr_update_on_login'] && empty( $user->last_name ) ) )
75 + ) || (
76 + ! empty( $user_data['authenticated_by'] ) && 'ldap' === $user_data['authenticated_by'] &&
77 + ! empty( $auth_settings['ldap_attr_update_on_login'] ) &&
78 + ( '1' === $auth_settings['ldap_attr_update_on_login'] || ( 'update-if-empty' === $auth_settings['ldap_attr_update_on_login'] && empty( $user->last_name ) ) )
79 + )
80 + );
81 +
49 82 /**
50 83 * Filter whether to block the currently logging in user based on any of
51 84 * their user attributes.
52 85 *
@@ -70,9 +103,9 @@
70 103 array_push(
71 104 $auth_settings_access_users_blocked,
72 105 array(
73 106 'email' => Helper::lowercase( $user_email ),
74 - 'date_added' => date( 'M Y' ),
107 + 'date_added' => wp_date( 'M Y' ),
75 108 )
76 109 );
77 110 update_option( 'auth_settings_access_users_blocked', $auth_settings_access_users_blocked );
78 111 }
@@ -103,8 +136,22 @@
103 136 return new \WP_Error( 'invalid_login', __( 'Invalid login attempted.', 'authorizer' ) );
104 137 }
105 138 }
106 139
140 + // If this externally-authenticated user is an existing administrator (admin
141 + // in single site mode, or super admin in network mode), and isn't blocked,
142 + // let them in. Update their first/last name if needed (CAS/LDAP).
143 + if ( $user && is_super_admin( $user->ID ) ) {
144 + if ( $should_update_first_name ) {
145 + update_user_meta( $user->ID, 'first_name', $user_data['first_name'] );
146 + }
147 + if ( $should_update_last_name ) {
148 + update_user_meta( $user->ID, 'last_name', $user_data['last_name'] );
149 + }
150 +
151 + return $user;
152 + }
153 +
107 154 // Get the default role for this user (or their current role, if they
108 155 // already have an account).
109 156 $default_role = $user && is_array( $user->roles ) && count( $user->roles ) > 0 ? $user->roles[0] : $auth_settings['access_default_role'];
110 157 /**
@@ -134,15 +181,8 @@
134 181 reset( $user_emails );
135 182 foreach ( $user_emails as $user_email ) {
136 183 $is_newly_approved_user = false;
137 184
138 - // If this externally authenticated user is an existing administrator
139 - // (administrator in single site mode, or super admin in network mode),
140 - // and is not in the blocked list, let them in.
141 - if ( $user && is_super_admin( $user->ID ) ) {
142 - return $user;
143 - }
144 -
145 185 // If this externally authenticated user isn't in the approved list
146 186 // and login access is set to "All authenticated users," or if they were
147 187 // automatically approved in the "authorizer_approve_login" filter
148 188 // above, then add them to the approved list (they'll get an account
@@ -168,9 +208,9 @@
168 208 // Add this user to the approved list.
169 209 $approved_user = array(
170 210 'email' => Helper::lowercase( $user_email ),
171 211 'role' => $approved_role,
172 - 'date_added' => date( 'Y-m-d H:i:s' ),
212 + 'date_added' => wp_date( 'Y-m-d H:i:s' ),
173 213 );
174 214 array_push( $auth_settings_access_users_approved, $approved_user );
175 215 array_push( $auth_settings_access_users_approved_single, $approved_user );
176 216 update_option( 'auth_settings_access_users_approved', $auth_settings_access_users_approved_single );
@@ -210,9 +250,9 @@
210 250 'user_pass' => wp_generate_password(), // random password.
211 251 'first_name' => array_key_exists( 'first_name', $user_data ) ? $user_data['first_name'] : '',
212 252 'last_name' => array_key_exists( 'last_name', $user_data ) ? $user_data['last_name'] : '',
213 253 'user_email' => Helper::lowercase( $user_info['email'] ),
214 - 'user_registered' => date( 'Y-m-d H:i:s' ),
254 + 'user_registered' => wp_date( 'Y-m-d H:i:s' ),
215 255 'role' => $user_info['role'],
216 256 )
217 257 );
218 258
@@ -320,43 +360,22 @@
320 360 }
321 361 }
322 362 }
323 363 } else {
324 - // Update first/last names of WordPress user from external
325 - // service if that option is set.
326 - if ( ( array_key_exists( 'authenticated_by', $user_data ) && 'cas' === $user_data['authenticated_by'] && array_key_exists( 'cas_attr_update_on_login', $auth_settings ) && 1 === intval( $auth_settings['cas_attr_update_on_login'] ) ) || ( array_key_exists( 'authenticated_by', $user_data ) && 'ldap' === $user_data['authenticated_by'] && array_key_exists( 'ldap_attr_update_on_login', $auth_settings ) && 1 === intval( $auth_settings['ldap_attr_update_on_login'] ) ) ) {
327 - if ( array_key_exists( 'first_name', $user_data ) && 0 < strlen( $user_data['first_name'] ) ) {
328 - wp_update_user(
329 - array(
330 - 'ID' => $user->ID,
331 - 'first_name' => $user_data['first_name'],
332 - )
333 - );
334 - }
335 - if ( array_key_exists( 'last_name', $user_data ) && strlen( $user_data['last_name'] ) > 0 ) {
336 - wp_update_user(
337 - array(
338 - 'ID' => $user->ID,
339 - 'last_name' => $user_data['last_name'],
340 - )
341 - );
342 - }
364 + // Update first/last name from CAS/LDAP if needed.
365 + if ( $should_update_first_name ) {
366 + update_user_meta( $user->ID, 'first_name', $user_data['first_name'] );
343 367 }
368 + if ( $should_update_last_name ) {
369 + update_user_meta( $user->ID, 'last_name', $user_data['last_name'] );
370 + }
344 371
345 372 // Update this user's role if it was modified in the
346 373 // authorizer_custom_role filter.
347 374 if ( $default_role !== $approved_role ) {
348 - // Update user's role in WordPress.
375 + // Update user's role in WordPress. Note: User's role will be changed
376 + // in the approved list via hook `set_user_role` in WP_User::set_role().
349 377 $user->set_role( $approved_role );
350 -
351 - // Update user's role in this site's approved list and save.
352 - foreach ( $auth_settings_access_users_approved_single as $key => $existing_user ) {
353 - if ( 0 === strcasecmp( $user->user_email, $existing_user['email'] ) ) {
354 - $auth_settings_access_users_approved_single[ $key ]['role'] = $approved_role;
355 - break;
356 - }
357 - }
358 - update_option( 'auth_settings_access_users_approved', $auth_settings_access_users_approved_single );
359 378 }
360 379 }
361 380
362 381 // If this is multisite, add new user to current blog.
@@ -420,8 +439,15 @@
420 439 }
421 440 }
422 441 }
423 442
443 + // Fetch the external service this user authenticated with, and append
444 + // it to the logout URL below (so we can fire custom logout routines in
445 + // custom_logout() based on their external service. This is necessary
446 + // because a pending user does not have a WP_User, and thus no
447 + // "authenticated_by" usermeta that is normally used to do this.
448 + $external_param = isset( $user_data['authenticated_by'] ) ? '&external=' . $user_data['authenticated_by'] : '';
449 +
424 450 // Notify user about pending status and return without authenticating them.
425 451 // phpcs:ignore WordPress.Security.NonceVerification
426 452 $redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : home_url();
427 453 $page_title = get_bloginfo( 'name' ) . ' - Access Pending';
@@ -428,9 +454,9 @@
428 454 $error_message =
429 455 apply_filters( 'the_content', $auth_settings['access_pending_redirect_to_message'] ) .
430 456 '<hr />' .
431 457 '<p style="text-align: center;">' .
432 - '<a class="button" href="' . wp_logout_url( $redirect_to ) . '">' .
458 + '<a class="button" href="' . wp_logout_url( $redirect_to ) . $external_param . '">' .
433 459 __( 'Back', 'authorizer' ) .
434 460 '</a></p>';
435 461 update_option( 'auth_settings_advanced_login_error', $error_message );
436 462 wp_die( wp_kses( $error_message, Helper::$allowed_html ), esc_html( $page_title ) );
@@ -447,9 +473,9 @@
447 473 * Restrict access to WordPress site based on settings (everyone, logged_in_users).
448 474 *
449 475 * Action: parse_request
450 476 *
451 - * @param array $wp WordPress object.
477 + * @param WP $wp WordPress object.
452 478 * @return WP|void WP object when passing through to WordPress authentication, or void.
453 479 */
454 480 public function restrict_access( $wp ) {
455 481 // Grab plugin settings.
@@ -468,15 +494,11 @@
468 494 // Allow access if option is set to 'everyone'.
469 495 ( 'everyone' === $auth_settings['access_who_can_view'] ) ||
470 496 // Allow access to approved external users and logged in users if option is set to 'logged_in_users'.
471 497 ( 'logged_in_users' === $auth_settings['access_who_can_view'] && Helper::is_user_logged_in_and_blog_user() && $this->is_email_in_list( $current_user->user_email, 'approved' ) ) ||
472 - // Allow access for requests to /wp-json/oauth1 so oauth clients can authenticate to use the REST API.
473 - ( property_exists( $wp, 'matched_query' ) && stripos( $wp->matched_query, 'rest_oauth1=' ) === 0 ) ||
474 - // Allow access for non-GET requests to /wp-json/*, since REST API authentication already covers them.
475 - ( property_exists( $wp, 'matched_query' ) && 0 === stripos( $wp->matched_query, 'rest_route=' ) && isset( $_SERVER['REQUEST_METHOD'] ) && 'GET' !== $_SERVER['REQUEST_METHOD'] ) ||
476 - // Allow access for GET requests to /wp-json/ (root), since REST API discovery calls rely on this.
477 - ( property_exists( $wp, 'matched_query' ) && 'rest_route=/' === $wp->matched_query )
478 - // Note that GET requests to a rest endpoint will be restricted by authorizer. In that case, error messages will be returned as JSON.
498 + // Allow REST API requests (access is determined later in the rest_authentication_errors hook).
499 + // See: https://github.com/WordPress/WordPress/blob/8e41746cb11271d063608a63e3f6091a8685e677/wp-includes/rest-api.php#L131-L133.
500 + ( ! empty( $GLOBALS['wp']->query_vars['rest_route'] ) )
479 501 );
480 502
481 503 /**
482 504 * Developers can use the `authorizer_has_access` filter to override
@@ -505,9 +527,9 @@
505 527 return $wp;
506 528 }
507 529
508 530 // Allow HEAD requests to the root (usually discovery from a REST client).
509 - if ( 'HEAD' === $_SERVER['REQUEST_METHOD'] && empty( $wp->request ) && empty( $wp->matched_query ) ) {
531 + if ( ! empty( $_SERVER['REQUEST_METHOD'] ) && 'HEAD' === $_SERVER['REQUEST_METHOD'] && empty( $wp->request ) && empty( $wp->matched_query ) ) {
510 532 return $wp;
511 533 }
512 534
513 535 /* We've determined that the current user doesn't have access, so we deal with them now. */
@@ -568,9 +590,10 @@
568 590
569 591 // Check to see if the requested category is public. If so, show it.
570 592 $current_category_name = property_exists( $wp, 'query_vars' ) && array_key_exists( 'category_name', $wp->query_vars ) && strlen( $wp->query_vars['category_name'] ) > 0 ? $wp->query_vars['category_name'] : '';
571 593 if ( $current_category_name ) {
572 - $current_category_name = end( explode( '/', $current_category_name ) );
594 + $current_category_name_pieces = explode( '/', $current_category_name );
595 + $current_category_name = end( $current_category_name_pieces );
573 596 if ( in_array( 'cat_' . $current_category_name, $auth_settings['access_public_pages'], true ) ) {
574 597 if ( 'no_warning' === $auth_settings['access_public_warning'] ) {
575 598 update_option( 'auth_settings_advanced_public_notice', false );
576 599 } else {
@@ -618,8 +641,47 @@
618 641 }
619 642
620 643
621 644 /**
645 + * Prevent REST API access if user isn't authenticated and "only logged in
646 + * users can see the site" is enabled.
647 + *
648 + * Filter: rest_authentication_errors
649 + *
650 + * @param WP_Error|null|true $errors WP_Error if authentication error, null if authentication method wasn't used, true if authentication succeeded.
651 + * @return WP_Error|null|true WP_Error if not logged in and "only logged in users can see the site" is enabled.
652 + */
653 + public function restrict_rest_api( $errors ) {
654 + // If there is already an error, just return that.
655 + if ( ! empty( $errors ) ) {
656 + return $errors;
657 + }
658 +
659 + // If user isn't logged in, check for "only logged in users can see the site".
660 + if ( ! is_user_logged_in() ) {
661 + // Grab plugin settings.
662 + $options = Options::get_instance();
663 + $auth_settings = $options->get_all( Helper::SINGLE_CONTEXT, 'allow override' );
664 +
665 + if (
666 + 'logged_in_users' === $auth_settings['access_who_can_view'] &&
667 + false === apply_filters( 'authorizer_has_access', false, $GLOBALS['wp'] )
668 + ) {
669 + return new \WP_Error(
670 + 'rest_cannot_view',
671 + wp_strip_all_tags( $auth_settings['access_redirect_to_message'] ),
672 + array(
673 + 'status' => 401,
674 + )
675 + );
676 + }
677 + }
678 +
679 + return $errors;
680 + }
681 +
682 +
683 + /**
622 684 * Helper function to determine whether a given email is in one of
623 685 * the lists (pending, approved, blocked). Defaults to the list of
624 686 * approved users.
625 687 *
@@ -665,10 +727,10 @@
665 727 default:
666 728 if ( 'single' !== $multisite_mode ) {
667 729 // Get multisite users only.
668 730 $auth_settings_access_users_approved = $options->get( 'access_users_approved', Helper::NETWORK_CONTEXT );
669 - } elseif ( is_multisite() && 1 === intval( $options->get( 'advanced_override_multisite' ) ) ) {
670 - // This site has overridden any multisite settings, so only get its users.
731 + } elseif ( is_multisite() && 1 === intval( $options->get( 'advanced_override_multisite' ) ) && empty( $options->get( 'prevent_override_multisite', Helper::NETWORK_CONTEXT ) ) ) {
732 + // This site has overridden any multisite settings (and is not prevented from doing so), so only get its users.
671 733 $auth_settings_access_users_approved = $options->get( 'access_users_approved', Helper::SINGLE_CONTEXT );
672 734 } else {
673 735 // Get all site users and all multisite users.
674 736 $auth_settings_access_users_approved = array_merge(