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 +109 -54 2.9.113.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.
@@ -425,9 +444,9 @@
425 444 // it to the logout URL below (so we can fire custom logout routines in
426 445 // custom_logout() based on their external service. This is necessary
427 446 // because a pending user does not have a WP_User, and thus no
428 447 // "authenticated_by" usermeta that is normally used to do this.
429 - $external_param = isset($user_data['authenticated_by']) ? '&external=' . $user_data['authenticated_by'] : '';
448 + $external_param = isset( $user_data['authenticated_by'] ) ? '&external=' . $user_data['authenticated_by'] : '';
430 449
431 450 // Notify user about pending status and return without authenticating them.
432 451 // phpcs:ignore WordPress.Security.NonceVerification
433 452 $redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : home_url();
@@ -454,9 +473,9 @@
454 473 * Restrict access to WordPress site based on settings (everyone, logged_in_users).
455 474 *
456 475 * Action: parse_request
457 476 *
458 - * @param array $wp WordPress object.
477 + * @param WP $wp WordPress object.
459 478 * @return WP|void WP object when passing through to WordPress authentication, or void.
460 479 */
461 480 public function restrict_access( $wp ) {
462 481 // Grab plugin settings.
@@ -475,15 +494,11 @@
475 494 // Allow access if option is set to 'everyone'.
476 495 ( 'everyone' === $auth_settings['access_who_can_view'] ) ||
477 496 // Allow access to approved external users and logged in users if option is set to 'logged_in_users'.
478 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' ) ) ||
479 - // Allow access for requests to /wp-json/oauth1 so oauth clients can authenticate to use the REST API.
480 - ( property_exists( $wp, 'matched_query' ) && stripos( $wp->matched_query, 'rest_oauth1=' ) === 0 ) ||
481 - // Allow access for non-GET requests to /wp-json/*, since REST API authentication already covers them.
482 - ( property_exists( $wp, 'matched_query' ) && 0 === stripos( $wp->matched_query, 'rest_route=' ) && isset( $_SERVER['REQUEST_METHOD'] ) && 'GET' !== $_SERVER['REQUEST_METHOD'] ) ||
483 - // Allow access for GET requests to /wp-json/ (root), since REST API discovery calls rely on this.
484 - ( property_exists( $wp, 'matched_query' ) && 'rest_route=/' === $wp->matched_query )
485 - // 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'] ) )
486 501 );
487 502
488 503 /**
489 504 * Developers can use the `authorizer_has_access` filter to override
@@ -512,9 +527,9 @@
512 527 return $wp;
513 528 }
514 529
515 530 // Allow HEAD requests to the root (usually discovery from a REST client).
516 - 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 ) ) {
517 532 return $wp;
518 533 }
519 534
520 535 /* We've determined that the current user doesn't have access, so we deal with them now. */
@@ -575,9 +590,10 @@
575 590
576 591 // Check to see if the requested category is public. If so, show it.
577 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'] : '';
578 593 if ( $current_category_name ) {
579 - $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 );
580 596 if ( in_array( 'cat_' . $current_category_name, $auth_settings['access_public_pages'], true ) ) {
581 597 if ( 'no_warning' === $auth_settings['access_public_warning'] ) {
582 598 update_option( 'auth_settings_advanced_public_notice', false );
583 599 } else {
@@ -625,8 +641,47 @@
625 641 }
626 642
627 643
628 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 + /**
629 684 * Helper function to determine whether a given email is in one of
630 685 * the lists (pending, approved, blocked). Defaults to the list of
631 686 * approved users.
632 687 *
@@ -672,10 +727,10 @@
672 727 default:
673 728 if ( 'single' !== $multisite_mode ) {
674 729 // Get multisite users only.
675 730 $auth_settings_access_users_approved = $options->get( 'access_users_approved', Helper::NETWORK_CONTEXT );
676 - } elseif ( is_multisite() && 1 === intval( $options->get( 'advanced_override_multisite' ) ) ) {
677 - // 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.
678 733 $auth_settings_access_users_approved = $options->get( 'access_users_approved', Helper::SINGLE_CONTEXT );
679 734 } else {
680 735 // Get all site users and all multisite users.
681 736 $auth_settings_access_users_approved = array_merge(