From 12d116a796856e60c37337ca438e2674943479fd Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Wed, 31 Dec 2025 19:09:30 +0100 Subject: [PATCH 01/11] Only allow redirects for approved hosts On multisite configurations, we add a filter so that other hosts on this multisite are in the approved list. --- shibboleth.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/shibboleth.php b/shibboleth.php index 3a4c14e..3575b7b 100644 --- a/shibboleth.php +++ b/shibboleth.php @@ -491,6 +491,9 @@ function shibboleth_authenticate( $user, $username, $password ) { if ( isset( $_REQUEST['redirect_to'] ) ) { $redirect_to = esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ); + + // Make sure the redirect is in the allowed list. + $redirect_to = wp_validate_redirect( $redirect_to ); } $initiator_url = shibboleth_session_initiator_url( $redirect_to, $idp ); @@ -499,6 +502,28 @@ function shibboleth_authenticate( $user, $username, $password ) { } } +/** + * Add a filter for allowed_redirect_hosts so that safe redirects work on multisite. + * + * @since 2.5.3 + * @param array $hosts An array of allowed host names. + * @param string $host The host name of the redirect destination; empty string if not set. + * @return array + */ +function shibboleth_allowed_redirect_hosts( $hosts, $host ) { + // If the host is blank or already in the list, return early. + if ( empty( $host ) || in_array( $host, $hosts, true ) ) { + return $hosts; + } + + // Check if this host belongs to any site in our Multisite network. + $site = get_site_by_path( $host, '/' ); + if ( $site ) { + $hosts[] = $site->domain; + } + + return $hosts; +} /** * When wp-login.php is loaded with 'action=shibboleth', hook Shibboleth @@ -508,6 +533,10 @@ function shibboleth_authenticate( $user, $username, $password ) { */ function shibboleth_login_form_shibboleth() { add_filter( 'authenticate', 'shibboleth_authenticate', 10, 3 ); + + if ( is_multisite() ) { + add_filter( 'allowed_redirect_hosts', 'shibboleth_allowed_redirect_hosts', 20, 2 ); + } } add_action( 'login_form_shibboleth', 'shibboleth_login_form_shibboleth' ); From 3f8d2645cd31998ef26797d8a5d5c35cbbc75a55 Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Mon, 23 Feb 2026 17:05:34 +0100 Subject: [PATCH 02/11] redirect: Add trusted values to allowed_redirect_hosts --- options-user.php | 3 ++- shibboleth.php | 40 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/options-user.php b/options-user.php index 45f30af..ea20de8 100644 --- a/options-user.php +++ b/options-user.php @@ -269,7 +269,8 @@ function shibboleth_link_accounts() { // If there is no existing shibboleth session, kick to the shibboleth_session_initiator_url // and redirect to this page with the ?shibboleth=link action. $initiator_url = shibboleth_session_initiator_url( wp_nonce_url( get_edit_user_link() . '?shibboleth=link', 'shibboleth-link' ) ); - wp_redirect( $initiator_url ); + shibboleth_allow_redirect( $initiator_url ); + wp_safe_redirect( $initiator_url ); exit; } // If manual merging is disabled, fail. diff --git a/shibboleth.php b/shibboleth.php index 3575b7b..9166bb8 100644 --- a/shibboleth.php +++ b/shibboleth.php @@ -465,6 +465,34 @@ function shibboleth_session_active( $auto_login = false ) { return $active; } +/** + * Add an allowed_redirect_hosts filter to trust the provided URL's host. + * + * @since 2.5.3 + * @param string $url The trusted URL. + */ +function shibboleth_allow_redirect( $url ) { + // Note: When this plugin requires at least WordPress 4.4/4.7, we can use wp_parse_url. + // phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url + $allow_host = parse_url( $url, PHP_URL_HOST ); + + if ( empty( $allow_host ) ) { + return; + } + + add_filter( + 'allowed_redirect_hosts', + function ( $hosts, $host ) use ( $allow_host ) { + if ( ! in_array( $allow_host, $hosts, true ) ) { + $hosts[] = $allow_host; + } + + return $hosts; + }, + 10, + 2 + ); +} /** * Authenticate the user using Shibboleth. If a Shibboleth session is active, @@ -497,7 +525,8 @@ function shibboleth_authenticate( $user, $username, $password ) { } $initiator_url = shibboleth_session_initiator_url( $redirect_to, $idp ); - wp_redirect( $initiator_url ); + shibboleth_allow_redirect( $initiator_url ); + wp_safe_redirect( $initiator_url ); exit; } } @@ -587,7 +616,8 @@ function shibboleth_retrieve_password( $user_login ) { $password_reset_url = shibboleth_get_password_reset_url( $user_login ); if ( ! empty( $password_reset_url ) ) { - wp_redirect( $password_reset_url ); + shibboleth_allow_redirect( $password_reset_url ); + wp_safe_redirect( $password_reset_url ); exit; } } @@ -629,7 +659,8 @@ function shibboleth_logout() { $logout_url = shibboleth_getoption( 'shibboleth_logout_url' ); if ( ! empty( $logout_url ) && shibboleth_session_active() ) { - wp_redirect( $logout_url ); + shibboleth_allow_redirect( $logout_url ); + wp_safe_redirect( $logout_url ); exit; } } @@ -642,7 +673,7 @@ function shibboleth_logout() { * @param string $redirect the final URL to redirect the user to after all login is complete. * @param string $idp_code The chosen IdP to use for the login process. * @return the URL to direct the user to in order to initiate Shibboleth login - * @uses apply_filters() Calls 'shibboleth_session_initiator_url' before returning session intiator URL + * @uses apply_filters() Calls 'shibboleth_session_initiator_url' before returning session initiator URL * @since 1.3 */ function shibboleth_session_initiator_url( $redirect = null, $idp_code = null ) { @@ -697,6 +728,7 @@ function shibboleth_log_message( $message_type, $message ) { } if ( ( defined( 'WP_DEBUG' ) && WP_DEBUG ) || in_array( $message_type, $shib_logging, true ) ) { + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log( '[Shibboleth WordPress Plugin Logging] ' . $message ); } } From 3ed10af730d41e6c42930730813879fdb4ebc37e Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Wed, 31 Dec 2025 19:59:01 +0100 Subject: [PATCH 03/11] Add ABSPATH check to options-user.php Add a check to ensure the script is run within WordPress. --- options-user.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/options-user.php b/options-user.php index ea20de8..6f4e24c 100644 --- a/options-user.php +++ b/options-user.php @@ -5,6 +5,8 @@ * @package shibboleth */ +defined( 'ABSPATH' ) || exit; + /** * For WordPress accounts that were created by Shibboleth, limit what administrators and users * can edit via user-edit.php and profile.php. From 28aaca34b5bd26bc2d9f5695ce2078d949ded4e5 Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Wed, 31 Dec 2025 19:59:35 +0100 Subject: [PATCH 04/11] Add exit condition for direct access to options-admin.php --- options-admin.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/options-admin.php b/options-admin.php index a4fa67c..979d4cc 100644 --- a/options-admin.php +++ b/options-admin.php @@ -6,6 +6,8 @@ * @package shibboleth */ +defined( 'ABSPATH' ) || exit; + /** * Setup admin tabs for the Shibboleth option page. * From 1b6c1055dbc34e1be0628a5186543ccddfdc62ba Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Wed, 31 Dec 2025 20:08:38 +0100 Subject: [PATCH 05/11] ci: cleaner exclusion of eslintrc and .github from plugin check --- .github/workflows/wp-plugin-ci-full.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/wp-plugin-ci-full.yml b/.github/workflows/wp-plugin-ci-full.yml index 158d877..844ec48 100644 --- a/.github/workflows/wp-plugin-ci-full.yml +++ b/.github/workflows/wp-plugin-ci-full.yml @@ -29,9 +29,8 @@ jobs: run: | vendor/bin/phpcs -q -n --ignore=vendor --standard=WordPress --report=checkstyle $GITHUB_WORKSPACE | cs2pr - - name: Be evil and hide .eslintrc from plugin check - run: | - rm $GITHUB_WORKSPACE/.eslintrc - - name: Run plugin check uses: wordpress/plugin-check-action@v1 + with: + exclude-directories: '.github' + exclude-files: '.eslintrc' From 43eeeb33df313bed541a93ea2289349b7a534c96 Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Mon, 5 Jan 2026 19:27:19 +0100 Subject: [PATCH 06/11] ci: ignore unnecessary nonce warnings --- options-user.php | 1 + shibboleth.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/options-user.php b/options-user.php index 6f4e24c..34d68cb 100644 --- a/options-user.php +++ b/options-user.php @@ -316,6 +316,7 @@ function shibboleth_disable_password_changes() { * @since 1.9 */ function shibboleth_link_accounts_notice() { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended $message_code = isset( $_GET['shibboleth'] ) ? sanitize_key( wp_unslash( $_GET['shibboleth'] ) ) : ''; if ( 'failed' === $message_code ) { diff --git a/shibboleth.php b/shibboleth.php index 9166bb8..a3fa405 100644 --- a/shibboleth.php +++ b/shibboleth.php @@ -513,11 +513,15 @@ function shibboleth_authenticate( $user, $username, $password ) { $idp = key( $idps ); $redirect_to = null; + // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_REQUEST['idp'] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended $idp = sanitize_text_field( wp_unslash( $_REQUEST['idp'] ) ); } + // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_REQUEST['redirect_to'] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended $redirect_to = esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ); // Make sure the redirect is in the allowed list. From 298e017a1664db7358f5c056dea3a102d0baf469 Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Mon, 5 Jan 2026 20:41:35 +0100 Subject: [PATCH 07/11] ci: ignore unnecessary warnings --- options-admin.php | 1 + shibboleth.php | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/options-admin.php b/options-admin.php index 979d4cc..53ee92f 100644 --- a/options-admin.php +++ b/options-admin.php @@ -984,6 +984,7 @@ function shibboleth_options_logging() { * @since ? */ function shibboleth_options_page() { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'general'; ?>
diff --git a/shibboleth.php b/shibboleth.php index a3fa405..c4074cc 100644 --- a/shibboleth.php +++ b/shibboleth.php @@ -161,6 +161,7 @@ function shibboleth_auto_login() { $shibboleth_auto_login = shibboleth_getoption( 'shibboleth_auto_login' ); if ( ! is_user_logged_in() && shibboleth_session_active( true ) && $shibboleth_auto_login ) { + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound do_action( 'login_form_shibboleth' ); $userobj = wp_signon( '', true ); @@ -285,6 +286,7 @@ function shibboleth_update_idp_users( $new_idp_code, $old_idp_code ) { // Update the shibboleth_account rows to have the new IdP code value. $shibboleth_users = get_users( array( + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 'meta_key' => 'shibboleth_account', 'fields' => 'ID', ) @@ -1073,8 +1075,8 @@ function shibboleth_update_user_data( $user_id, $force_update = false ) { $managed = $shib_headers[ $header ]['managed']; } if ( $force_update || $managed ) { - $filter = 'shibboleth_' . ( strpos( $field, 'user_' ) === 0 ? '' : 'user_' ) . $field; - $user_data[ $field ] = apply_filters( $filter, shibboleth_getenv( $shib_headers[ $header ]['name'] ) ); + $filter = ( strpos( $field, 'user_' ) === 0 ? '' : 'user_' ) . $field; + $user_data[ $field ] = apply_filters( 'shibboleth_' . $filter, shibboleth_getenv( $shib_headers[ $header ]['name'] ) ); } } From 33eafd116e34ba8f1b8b947349244d0483ab5374 Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Mon, 23 Feb 2026 17:41:04 +0100 Subject: [PATCH 08/11] options-user: reduce nesting in shibboleth_link_accounts() --- options-user.php | 170 +++++++++++++++++++++++------------------------ 1 file changed, 83 insertions(+), 87 deletions(-) diff --git a/options-user.php b/options-user.php index 34d68cb..95224c8 100644 --- a/options-user.php +++ b/options-user.php @@ -197,97 +197,93 @@ function shibboleth_link_accounts_button( $user ) { * @since 1.9 */ function shibboleth_link_accounts() { - $screen = get_current_screen(); - - if ( is_admin() && 'profile' === $screen->id ) { - $user_id = get_current_user_id(); - - // If profile page has ?shibboleth=link action and current user can edit their profile, proceed. - if ( isset( $_GET['shibboleth'] ) && 'link' === $_GET['shibboleth'] && current_user_can( 'edit_user', $user_id ) ) { - check_admin_referer( 'shibboleth-link' ); - - $allowed = shibboleth_getoption( 'shibboleth_manually_combine_accounts', 'disallow' ); - - $user_idp = shibboleth_get_user_idp( $user_id ); - - // If user's account is not already linked with shibboleth, proceed. - if ( empty( $user_idp ) ) { - // If manual account merging is enabled, proceed. - if ( 'allow' === $allowed || 'bypass' === $allowed ) { - // If there is an existing shibboleth session, proceed. - if ( shibboleth_session_active() ) { - $shib_headers = shibboleth_getoption( 'shibboleth_headers', false, true ); - - $username = shibboleth_getenv( $shib_headers['username']['name'] ); - $email = shibboleth_getenv( $shib_headers['email']['name'] ); - - $user = get_user_by( 'id', $user_id ); - - $set_user_idp = false; - - if ( $user->user_login === $username && strtolower( $user->user_email ) === strtolower( $email ) ) { - // If username and email match, safe to merge. - $set_user_idp = true; - } elseif ( $user->user_login === $username ) { - // If username matches, check if there is a conflict with the email. - $prevent_conflict = get_user_by( 'email', $email ); - - // If username matches and there is no existing account with the email, safe to merge. - if ( ! $prevent_conflict->ID ) { - $set_user_idp = true; - } else { - // If username matches and there is an existing account with the email, fail. - shibboleth_log_message( 'account_merge', 'ERROR: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts. Reason: An account already exists with the email: ' . $email . ' .' ); - } - } elseif ( strtolower( $user->user_email ) === strtolower( $email ) && 'bypass' === $allowed ) { - // If email matches and username bypass is enabled, check if there is a conflict with the username. - $prevent_conflict = get_user_by( 'user_login', $username ); - - // If email matches and there is no existing account with the username, safe to merge. - if ( ! $prevent_conflict->ID ) { - $set_user_idp = true; - } else { - // If there is an existing account with the email, fail. - shibboleth_log_message( 'account_merge', 'ERROR: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts using username bypass. Reason: An account already exists with the email: ' . $email . ' .' ); - } - } else { - // If no other conditions are met, fail. - shibboleth_log_message( 'account_merge', 'ERROR: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts. Reason: Username and email do not match what is provided by attributes. Username provided by attribute is: ' . $username . ' and email provided by attribute is ' . $email . '.' ); - } - - if ( $set_user_idp ) { - if ( shibboleth_set_user_idp( $user->ID ) ) { - shibboleth_log_message( 'account_merge', 'SUCCESS: User ' . $user->user_login . ' (ID: ' . $user->ID . ') merged accounts manually for IdP: ' . shibboleth_get_user_idp( $user->ID ) . '.' ); - wp_safe_redirect( get_edit_user_link() . '?shibboleth=linked' ); - exit; - } else { - shibboleth_log_message( 'account_merge', 'ERROR: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts. Reason: Unable to automatically determine IdP.' ); - } - } - - wp_safe_redirect( get_edit_user_link() . '?shibboleth=failed' ); - exit; - } else { - // If there is no existing shibboleth session, kick to the shibboleth_session_initiator_url - // and redirect to this page with the ?shibboleth=link action. - $initiator_url = shibboleth_session_initiator_url( wp_nonce_url( get_edit_user_link() . '?shibboleth=link', 'shibboleth-link' ) ); - shibboleth_allow_redirect( $initiator_url ); - wp_safe_redirect( $initiator_url ); - exit; - } - // If manual merging is disabled, fail. - } else { - shibboleth_log_message( 'account_merge', 'ERROR: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts. Reason: Manual account merging is disabled.' ); - wp_safe_redirect( get_edit_user_link() . '?shibboleth=failed' ); - exit; - } - // If account is already merged, warn. + if ( ! is_admin() || 'profile' !== get_current_screen()->id ) { + return; + } + + $user_id = get_current_user_id(); + + // If profile page has ?shibboleth=link action and current user can edit their profile, proceed. + if ( isset( $_GET['shibboleth'] ) && 'link' === $_GET['shibboleth'] && current_user_can( 'edit_user', $user_id ) ) { + check_admin_referer( 'shibboleth-link' ); + + $allowed = shibboleth_getoption( 'shibboleth_manually_combine_accounts', 'disallow' ); + + $user_idp = shibboleth_get_user_idp( $user_id ); + + // If account is already merged, warn. + if ( ! empty( $user_idp ) ) { + shibboleth_log_message( 'account_merge', 'WARN: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts. Reason: User\'s account is already merged.' ); + wp_safe_redirect( get_edit_user_link() . '?shibboleth=duplicate' ); + exit; + } + + // If manual account merging is disabled, fail. + if ( 'allow' !== $allowed && 'bypass' !== $allowed ) { + shibboleth_log_message( 'account_merge', 'ERROR: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts. Reason: Manual account merging is disabled.' ); + wp_safe_redirect( get_edit_user_link() . '?shibboleth=failed' ); + exit; + } + + // If there is no existing shibboleth session, initiate a session with the ?shibboleth=link action. + if ( ! shibboleth_session_active() ) { + $initiator_url = shibboleth_session_initiator_url( wp_nonce_url( get_edit_user_link() . '?shibboleth=link', 'shibboleth-link' ) ); + shibboleth_allow_redirect( $initiator_url ); + wp_safe_redirect( $initiator_url ); + exit; + } + + $shib_headers = shibboleth_getoption( 'shibboleth_headers', array(), true ); + + $username = shibboleth_getenv( $shib_headers['username']['name'] ); + $email = shibboleth_getenv( $shib_headers['email']['name'] ); + + $user = get_user_by( 'id', $user_id ); + + $set_user_idp = false; + + if ( $user->user_login === $username && strtolower( $user->user_email ) === strtolower( $email ) ) { + // If username and email match, safe to merge. + $set_user_idp = true; + } elseif ( $user->user_login === $username ) { + // If username matches, check if there is a conflict with the email. + $prevent_conflict = get_user_by( 'email', $email ); + + // If username matches and there is no existing account with the email, safe to merge. + if ( ! $prevent_conflict->ID ) { + $set_user_idp = true; + } else { + // If username matches and there is an existing account with the email, fail. + shibboleth_log_message( 'account_merge', 'ERROR: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts. Reason: An account already exists with the email: ' . $email . ' .' ); + } + } elseif ( strtolower( $user->user_email ) === strtolower( $email ) && 'bypass' === $allowed ) { + // If email matches and username bypass is enabled, check if there is a conflict with the username. + $prevent_conflict = get_user_by( 'user_login', $username ); + + // If email matches and there is no existing account with the username, safe to merge. + if ( ! $prevent_conflict->ID ) { + $set_user_idp = true; } else { - shibboleth_log_message( 'account_merge', 'WARN: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts. Reason: User\'s account is already merged.' ); - wp_safe_redirect( get_edit_user_link() . '?shibboleth=duplicate' ); + // If there is an existing account with the email, fail. + shibboleth_log_message( 'account_merge', 'ERROR: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts using username bypass. Reason: An account already exists with the email: ' . $email . ' .' ); + } + } else { + // If no other conditions are met, fail. + shibboleth_log_message( 'account_merge', 'ERROR: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts. Reason: Username and email do not match what is provided by attributes. Username provided by attribute is: ' . $username . ' and email provided by attribute is ' . $email . '.' ); + } + + if ( $set_user_idp ) { + if ( shibboleth_set_user_idp( $user->ID ) ) { + shibboleth_log_message( 'account_merge', 'SUCCESS: User ' . $user->user_login . ' (ID: ' . $user->ID . ') merged accounts manually for IdP: ' . shibboleth_get_user_idp( $user->ID ) . '.' ); + wp_safe_redirect( get_edit_user_link() . '?shibboleth=linked' ); exit; + } else { + shibboleth_log_message( 'account_merge', 'ERROR: User ' . $user->user_login . ' (ID: ' . $user->ID . ') failed to manually merge accounts. Reason: Unable to automatically determine IdP.' ); } } + + wp_safe_redirect( get_edit_user_link() . '?shibboleth=failed' ); + exit; } } add_action( 'current_screen', 'shibboleth_link_accounts' ); From 520e5103d17a3a2a47c08a86ca8d525bbdee2c70 Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Mon, 23 Feb 2026 17:42:43 +0100 Subject: [PATCH 09/11] shibboleth_getoption: cleanup deprecated parameter and variable names --- options-admin.php | 2 +- options-user.php | 2 +- shibboleth.php | 35 ++++++++++++++++------------------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/options-admin.php b/options-admin.php index 53ee92f..f90138e 100644 --- a/options-admin.php +++ b/options-admin.php @@ -385,7 +385,7 @@ function shibboleth_options_idps() { if ( isset( $_POST['submit'] ) ) { check_admin_referer( 'shibboleth_update_options' ); - $idp_options = shibboleth_getoption( 'shibboleth_idps', array(), true, false ); + $idp_options = shibboleth_getoption( 'shibboleth_idps', array() ); if ( ! defined( 'SHIBBOLETH_IDPS' ) ) { if ( isset( $_POST['idps'] ) ) { diff --git a/options-user.php b/options-user.php index 95224c8..7e76bf0 100644 --- a/options-user.php +++ b/options-user.php @@ -233,7 +233,7 @@ function shibboleth_link_accounts() { exit; } - $shib_headers = shibboleth_getoption( 'shibboleth_headers', array(), true ); + $shib_headers = shibboleth_getoption( 'shibboleth_headers', array() ); $username = shibboleth_getenv( $shib_headers['username']['name'] ); $email = shibboleth_getenv( $shib_headers['email']['name'] ); diff --git a/shibboleth.php b/shibboleth.php index c4074cc..aabef4d 100644 --- a/shibboleth.php +++ b/shibboleth.php @@ -33,33 +33,30 @@ /** * Determine if a constant is defined. If it is, return the value of the constant. - * If it isn't, return the value from get_site_option(). If you'd like to pass a default - * for get_site_option(), set $default to the requested default. If you'd like to check - * for arrays in constants, set $array to true. If you'd like to return that the object - * was obtained as a constant, set $compact to true and the result is an array. To get the - * value of the constant or option, look at the value key. To check if the value was - * retreived from a constant, look at the constant key. + * If it isn't, return the value from get_site_option(). + * If you'd like to known whether or not the value was obtained as a constant, + * set $return_array to true and check the resulting array. * * @since 2.1 * @param string $option Option identifier. - * @param bool $default Default value. - * @param bool $array If we expect the value to be an array. - * @param bool $compact If you want the constant and value returned as an array. + * @param bool $default_value Default value. + * @param bool $unused Deprecated parameter; unused. + * @param bool $return_array If you want the constant and value returned as an array. * @return mixed */ -function shibboleth_getoption( $option, $default = false, $array = false, $compact = false ) { +function shibboleth_getoption( $option, $default_value = false, $unused = false, $return_array = false ) { // If a constant is defined with the provided option name, get the value of the constant. if ( defined( strtoupper( $option ) ) ) { $value = constant( strtoupper( $option ) ); $constant = true; } else { // If no constant is set, just get the value from get_site_option(). - $value = get_site_option( $option, $default ); + $value = get_site_option( $option, $default_value ); $constant = false; } - // If compact is set to true, we compact $value and $constant together for easy use. - if ( $compact ) { + // If $return_array, we return the $value and $constant together for easy use. + if ( $return_array ) { return array( $value, $constant, @@ -427,7 +424,7 @@ function shibboleth_admin_hooks() { function shibboleth_session_active( $auto_login = false ) { $active = false; $method = shibboleth_getoption( 'shibboleth_attribute_access_method' ); - $shib_headers = shibboleth_getoption( 'shibboleth_headers', array(), true ); + $shib_headers = shibboleth_getoption( 'shibboleth_headers', array() ); $session = null; if ( isset( $shib_headers['username']['name'] ) ) { $session = shibboleth_getenv( $shib_headers['username']['name'] ); @@ -730,7 +727,7 @@ function shibboleth_log_message( $message_type, $message ) { static $shib_logging; if ( ! isset( $shib_logging ) ) { - $shib_logging = shibboleth_getoption( 'shibboleth_logging', array(), true ); + $shib_logging = shibboleth_getoption( 'shibboleth_logging', array() ); } if ( ( defined( 'WP_DEBUG' ) && WP_DEBUG ) || in_array( $message_type, $shib_logging, true ) ) { @@ -812,7 +809,7 @@ function shibboleth_set_user_idp( $user_id, $user_idp = null ) { * @since 1.0 */ function shibboleth_authenticate_user() { - $shib_headers = shibboleth_getoption( 'shibboleth_headers', array(), true ); + $shib_headers = shibboleth_getoption( 'shibboleth_headers', array() ); $auto_combine_accounts = shibboleth_getoption( 'shibboleth_auto_combine_accounts' ); $manually_combine_accounts = shibboleth_getoption( 'shibboleth_manually_combine_accounts' ); @@ -994,7 +991,7 @@ function shibboleth_get_user_role() { } } - $shib_roles = apply_filters( 'shibboleth_roles', shibboleth_getoption( 'shibboleth_roles', array(), true ) ); + $shib_roles = apply_filters( 'shibboleth_roles', shibboleth_getoption( 'shibboleth_roles', array() ) ); $user_role = shibboleth_getoption( 'shibboleth_default_role' ); foreach ( $roles->role_names as $key => $name ) { @@ -1024,7 +1021,7 @@ function shibboleth_get_user_role() { * @since 1.3 */ function shibboleth_get_managed_user_fields() { - $shib_headers = shibboleth_getoption( 'shibboleth_headers', array(), true ); + $shib_headers = shibboleth_getoption( 'shibboleth_headers', array() ); $managed = array(); @@ -1053,7 +1050,7 @@ function shibboleth_get_managed_user_fields() { * @since 1.0 */ function shibboleth_update_user_data( $user_id, $force_update = false ) { - $shib_headers = shibboleth_getoption( 'shibboleth_headers', array(), true ); + $shib_headers = shibboleth_getoption( 'shibboleth_headers', array() ); $user_fields = array( 'user_login' => 'username', From 5568612956d96441ef48729cbe1f2eb0a1901902 Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Mon, 23 Feb 2026 17:43:22 +0100 Subject: [PATCH 10/11] shibboleth_getenv: cleanup variable names --- shibboleth.php | 55 +++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/shibboleth.php b/shibboleth.php index aabef4d..efeaada 100644 --- a/shibboleth.php +++ b/shibboleth.php @@ -76,11 +76,11 @@ function shibboleth_getoption( $option, $default_value = false, $unused = false, * secure configuration possible. * * @since 1.8 - * @param string $var Environment variable. + * @param string $variable Environment variable. * @return string|bool */ -function shibboleth_getenv( $var ) { - if ( empty( $var ) ) { +function shibboleth_getenv( $variable ) { + if ( empty( $variable ) ) { return false; } @@ -93,55 +93,56 @@ function shibboleth_getenv( $var ) { // Use standard by default for security. case 'standard': default: - $var_method = ''; + $prefix = ''; break; + // If specified, use redirect. case 'redirect': - $var_method = 'REDIRECT_'; + $prefix = 'REDIRECT_'; break; + // If specified, use http. case 'http': - $var_method = 'HTTP_'; + $prefix = 'HTTP_'; break; + // If specified, use the custom specified method. case 'custom': $custom = shibboleth_getoption( 'shibboleth_attribute_custom_access_method', '' ); - $var_method = $custom; + $prefix = $custom; break; } // Disable fallback to prevent the same variables from being checked twice. - if ( empty( $var_method ) ) { + if ( empty( $prefix ) ) { $fallback = false; } // Using the selected attribute access method, check all possible cases. - $var_under = str_replace( '-', '_', $var ); - $var_upper = strtoupper( $var ); - $var_under_upper = strtoupper( $var_under ); - - $check_vars = array( - $var_method . $var => true, - $var_method . $var_under => true, - $var_method . $var_upper => true, - $var_method . $var_under_upper => true, + $variable_under = str_replace( '-', '_', $variable ); + $variable_upper = strtoupper( $variable ); + $variable_under_upper = strtoupper( $variable_under ); + + $check_variables = array( + $prefix . $variable => true, + $prefix . $variable_under => true, + $prefix . $variable_upper => true, + $prefix . $variable_under_upper => true, ); // If fallback is enabled, we will add the standard environment variables to the end of the array to allow for fallback. if ( $fallback ) { - $fallback_check_vars = array( - $var => true, - $var_under => true, - $var_upper => true, - $var_under_upper => true, + $check_variables += array( + $variable => true, + $variable_under => true, + $variable_upper => true, + $variable_under_upper => true, ); - - $check_vars = array_merge( $check_vars, $fallback_check_vars ); } - foreach ( $check_vars as $check_var => $true ) { - if ( isset( $_SERVER[ $check_var ] ) && false !== $_SERVER[ $check_var ] ) { - return sanitize_text_field( wp_unslash( $_SERVER[ $check_var ] ) ); + foreach ( $check_variables as $check_variable => $true ) { + if ( isset( $_SERVER[ $check_variable ] ) && false !== $_SERVER[ $check_variable ] ) { + return sanitize_text_field( wp_unslash( $_SERVER[ $check_variable ] ) ); } } From 710da7f358e33a4d2ac4dea80807ae9037776359 Mon Sep 17 00:00:00 2001 From: Jonathan Champ Date: Wed, 31 Dec 2025 19:31:44 +0100 Subject: [PATCH 11/11] release: 2.5.3 --- readme.txt | 7 +++++-- shibboleth.php | 10 ++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/readme.txt b/readme.txt index f52a1da..d5140a2 100644 --- a/readme.txt +++ b/readme.txt @@ -2,9 +2,9 @@ Contributors: michaelryanmcneill, willnorris, mitchoyoshitaka, jrchamp, dericcrago, bshelton229, Alhrath, dandalpiaz, masteradhoc, junaidkbr Tags: shibboleth, authentication, login, saml Requires at least: 4.0 -Tested up to: 6.8 +Tested up to: 6.9 Requires PHP: 5.6 -Stable tag: 2.5.2 +Stable tag: 2.5.3 License: Apache-2.0 Allows WordPress to externalize user authentication and account creation to a Shibboleth Service Provider. @@ -195,6 +195,9 @@ Accessing Shibboleth attributes has changed. Typically, no additional configurat Accessing Shibboleth attributes has changed. Typically, no additional configuration is necessary. Check the changelog if you have specialized server configurations, such as a Shibboleth Service Provider on a reverse proxy or a server configuration that prefixes environment variables with REDIRECT_. == Changelog == += version 2.5.3 (2026-02-23) = + - Security: Limit redirects to approved hosts [#112](https://github.com/michaelryanmcneill/shibboleth/issues/112) (thanks @Belippo, @Jefhumbe) + = version 2.5.2 (2025-07-22) = - Compatibility: PHP 8.0 and newer require count() argument to be countable [#108](https://github.com/michaelryanmcneill/shibboleth/issues/108) (thanks @Gameink, @frereut) diff --git a/shibboleth.php b/shibboleth.php index efeaada..ec30f50 100644 --- a/shibboleth.php +++ b/shibboleth.php @@ -9,16 +9,18 @@ * Plugin URI: https://wordpress.org/plugins/shibboleth/ * Description: Easily externalize user authentication to a Shibboleth Service Provider * Author: Michael McNeill, Jonathan Champ, Michael Erlewine, Will Norris - * Version: 2.5.2 + * Version: 2.5.3 * Requires PHP: 5.6 * Requires at least: 4.0 * License: Apache-2.0 * Text Domain: shibboleth */ +defined( 'ABSPATH' ) || exit; + define( 'SHIBBOLETH_MINIMUM_WP_VERSION', '4.0' ); define( 'SHIBBOLETH_MINIMUM_PHP_VERSION', '5.6' ); -define( 'SHIBBOLETH_PLUGIN_VERSION', '2.5.2' ); +define( 'SHIBBOLETH_PLUGIN_VERSION', '2.5.3' ); /** * Determine if this is a new install or upgrade and, if so, run the @@ -26,8 +28,8 @@ * * @since 1.0 */ -$plugin_version = get_site_option( 'shibboleth_plugin_version', '0' ); -if ( SHIBBOLETH_PLUGIN_VERSION !== $plugin_version ) { +$shibboleth_plugin_version = get_site_option( 'shibboleth_plugin_version', '0' ); +if ( SHIBBOLETH_PLUGIN_VERSION !== $shibboleth_plugin_version ) { add_action( 'admin_init', 'shibboleth_activate_plugin' ); }